-module(handler_subscription). -include("records.hrl"). -export([init/2]). init(Req, Opts) -> handle(Req, Opts). handle(Req, _Opts) -> case cowboy_req:method(Req) of <<"GET">> -> get_subscription(Req); <<"POST">> -> create_subscription(Req); _ -> send_error(Req, 405, <<"Method not allowed">>) end. %% GET /v1/subscription - получить подписку текущего пользователя get_subscription(Req) -> case handler_auth:authenticate(Req) of {ok, UserId, Req1} -> case logic_subscription:get_user_subscription(UserId) of {ok, Subscription} -> send_json(Req1, 200, Subscription); {error, _} -> send_error(Req1, 500, <<"Internal server error">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. %% POST /v1/subscription - активировать подписку create_subscription(Req) -> case handler_auth:authenticate(Req) of {ok, UserId, Req1} -> {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of Decoded when is_map(Decoded) -> case Decoded of #{<<"action">> := <<"start_trial">>} -> case logic_subscription:start_trial(UserId) of {ok, Subscription} -> Response = subscription_to_json(Subscription), send_json(Req2, 201, Response); {error, already_has_subscription} -> send_error(Req2, 409, <<"Already has active subscription">>); {error, _} -> send_error(Req2, 500, <<"Internal server error">>) end; #{<<"action">> := <<"activate">>, <<"plan">> := PlanBin} -> Plan = parse_plan(PlanBin), PaymentInfo = maps:get(<<"payment_info">>, Decoded, #{}), case logic_subscription:activate_subscription(UserId, Plan, PaymentInfo) of {ok, Subscription} -> Response = subscription_to_json(Subscription), send_json(Req2, 201, Response); {error, payment_failed} -> send_error(Req2, 402, <<"Payment failed">>); {error, _} -> send_error(Req2, 500, <<"Internal server error">>) end; _ -> send_error(Req2, 400, <<"Invalid action. Use 'start_trial' or 'activate'">>) end; _ -> send_error(Req2, 400, <<"Invalid JSON">>) catch _:_ -> send_error(Req2, 400, <<"Invalid JSON format">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. %% Вспомогательные функции parse_plan(<<"monthly">>) -> monthly; parse_plan(<<"quarterly">>) -> quarterly; parse_plan(<<"biannual">>) -> biannual; parse_plan(<<"annual">>) -> annual; parse_plan(_) -> monthly. subscription_to_json(Subscription) when is_tuple(Subscription) -> #{ id => Subscription#subscription.id, plan => Subscription#subscription.plan, status => Subscription#subscription.status, trial_used => Subscription#subscription.trial_used, started_at => datetime_to_iso8601(Subscription#subscription.started_at), expires_at => datetime_to_iso8601(Subscription#subscription.expires_at) }; subscription_to_json(Subscription) when is_map(Subscription) -> Subscription. datetime_to_iso8601({{Year, Month, Day}, {Hour, Minute, Second}}) -> iolist_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ", [Year, Month, Day, Hour, Minute, Second])). send_json(Req, Status, Data) -> Body = jsx:encode(Data), cowboy_req:reply(Status, #{<<"content-type">> => <<"application/json">>}, Body, Req), {ok, Body, []}. send_error(Req, Status, Message) -> Body = jsx:encode(#{error => Message}), cowboy_req:reply(Status, #{<<"content-type">> => <<"application/json">>}, Body, Req), {ok, Body, []}.