CAPI-95/HOOK-1 Payments extended search, Webhooks (#42)

* HOOK-1: Implement webhook management

* CAPI-95 Add new payments/invoices search with custom api_clientP

* CAPI-95 Move to the newest dependencies
This commit is contained in:
Artem Ocheredko 2017-04-27 19:53:03 +04:00 committed by GitHub
parent 69bf9f54b7
commit 4c85e3b84e
10 changed files with 451 additions and 109 deletions

View File

@ -126,8 +126,10 @@ get_operation_access('GetPayments' , #{'invoiceID' := ID}) ->
[{[{invoices, ID}, payments], read}];
get_operation_access('GetPaymentByID' , #{'invoiceID' := ID1, paymentID := ID2}) ->
[{[{invoices, ID1}, {payments, ID2}], read}];
get_operation_access('GetInvoices' , _) ->
get_operation_access('SearchInvoices' , _) ->
[{[invoices], read}];
get_operation_access('SearchPayments' , _) ->
[{[invoices, payments], read}];
get_operation_access('CreatePaymentToolToken' , _) ->
[{[payment_tool_tokens] , write}];
get_operation_access('GetPaymentConversionStats' , _) ->
@ -176,6 +178,14 @@ get_operation_access('CreateContract' , _) ->
[{[party], write}];
get_operation_access('GetContractByID' , _) ->
[{[party], read}];
get_operation_access('GetWebhooks' , _) ->
[{[party], read}];
get_operation_access('GetWebhookByID' , _) ->
[{[party], read}];
get_operation_access('CreateWebhook' , _) ->
[{[party], write}];
get_operation_access('DeleteWebhookByID' , _) ->
[{[party], write}];
get_operation_access('GetCategories' , _) ->
[];
get_operation_access('GetCategoryByRef' , _) ->

View File

@ -5,6 +5,7 @@
-include_lib("cp_proto/include/cp_cds_thrift.hrl").
-include_lib("cp_proto/include/cp_merch_stat_thrift.hrl").
-include_lib("cp_proto/include/cp_proxy_merch_config_thrift.hrl").
-include_lib("cp_proto/include/cp_webhooker_thrift.hrl").
-include_lib("cp_proto/include/cp_user_interaction_thrift.hrl").
-include_lib("cp_proto/include/cp_geo_ip_thrift.hrl").
@ -276,20 +277,24 @@ process_request(OperationID = 'GetPaymentByID', Req, Context, ReqCtx) ->
process_exception(OperationID, Exception)
end;
process_request(OperationID = 'GetInvoices', Req, Context, ReqCtx) ->
process_request(OperationID = 'SearchInvoices', Req, Context, ReqCtx) ->
Limit = genlib_map:get('limit', Req),
Offset = genlib_map:get('offset', Req),
InvoiceStatus = case genlib_map:get('status', Req) of
undefined -> undefined;
[Status | _] -> Status
end, %%@TODO deal with many statuses
Query = #{
<<"merchant_id">> => get_party_id(Context),
<<"shop_id">> => genlib_map:get('shopID', Req),
<<"invoice_id">> => genlib_map:get('invoiceID', Req),
<<"from_time">> => get_time('fromTime', Req),
<<"to_time">> => get_time('toTime', Req),
<<"invoice_status">> => InvoiceStatus
<<"invoice_status">> => genlib_map:get('invoiceStatus', Req),
<<"payment_status">> => genlib_map:get('paymentStatus', Req),
<<"payment_id">> => genlib_map:get('paymentID', Req),
<<"payment_email">> => genlib_map:get('payerEmail', Req),
<<"payment_ip">> => genlib_map:get('payerIP', Req),
<<"payment_fingerprint">> => genlib_map:get('payerFingerprint', Req),
<<"payment_pan_mask">> => genlib_map:get('cardNumberMask', Req),
<<"payment_amount">> => genlib_map:get('paymentAmount', Req),
<<"invoice_amount">> => genlib_map:get('invoiceAmount', Req)
},
QueryParams = #{
<<"size">> => Limit,
@ -304,7 +309,7 @@ process_request(OperationID = 'GetInvoices', Req, Context, ReqCtx) ->
),
case Result of
{ok, #merchstat_StatResponse{data = {'invoices', Invoices}, total_count = TotalCount}} ->
DecodedInvoices = [decode_invoice(I) || #merchstat_StatInvoice{invoice = I} <- Invoices],
DecodedInvoices = [decode_invoice_search_result(I) || #merchstat_StatInvoice{invoice = I} <- Invoices],
Resp = #{
<<"invoices">> => DecodedInvoices,
<<"totalCount">> => TotalCount
@ -314,6 +319,52 @@ process_request(OperationID = 'GetInvoices', Req, Context, ReqCtx) ->
process_exception(OperationID, Exception)
end;
process_request(OperationID = 'SearchPayments', Req, Context, ReqCtx) ->
Limit = genlib_map:get('limit', Req),
Offset = genlib_map:get('offset', Req),
Query = #{
<<"merchant_id">> => get_party_id(Context),
<<"shop_id">> => genlib_map:get('shopID', Req),
<<"invoice_id">> => genlib_map:get('invoiceID', Req),
<<"from_time">> => get_time('fromTime', Req),
<<"to_time">> => get_time('toTime', Req),
<<"payment_status">> => genlib_map:get('paymentStatus', Req),
<<"payment_id">> => genlib_map:get('paymentID', Req),
<<"payment_email">> => genlib_map:get('payerEmail', Req),
<<"payment_ip">> => genlib_map:get('payerIP', Req),
<<"payment_fingerprint">> => genlib_map:get('payerFingerprint', Req),
<<"payment_pan_mask">> => genlib_map:get('cardNumberMask', Req),
<<"payment_amount">> => genlib_map:get('paymentAmount', Req)
},
QueryParams = #{
<<"size">> => Limit,
<<"from">> => Offset
},
Dsl = create_dsl(payments, Query, QueryParams),
Result = service_call(
merchant_stat,
'GetPayments',
[encode_stat_request(Dsl)],
ReqCtx
),
case Result of
{ok, #merchstat_StatResponse{data = {'payments', Payments}, total_count = TotalCount}} ->
DecodedPayments = [
decode_payment_search_result(InvoiceID, I)
|| #merchstat_StatPayment{
invoice_id = InvoiceID,
payment = I
} <- Payments
],
Resp = #{
<<"payments">> => DecodedPayments,
<<"totalCount">> => TotalCount
},
{ok, {200, [], Resp}};
{exception, Exception} ->
process_exception(OperationID, Exception)
end;
process_request(OperationID = 'GetPaymentConversionStats', Req, Context, ReqCtx) ->
StatType = payments_conversion_stat,
Result = call_merchant_stat(StatType, Req, Context, ReqCtx),
@ -854,9 +905,83 @@ process_request(OperationID = 'GetAccountByID', Req, Context, ReqCtx) ->
process_exception(OperationID, Exception)
end;
process_request(OperationID = 'CreateWebhook', Req, Context, ReqCtx) ->
PartyID = get_party_id(Context),
Params = maps:get('WebhookParams', Req),
EventFilter = encode_event_filter(maps:get(<<"scope">>, Params)),
case validate_event_filter(EventFilter, Context, ReqCtx) of
{ok, _} ->
WebhookParams = #webhooker_WebhookParams{
party_id = PartyID,
url = maps:get(<<"url">>, Params),
event_filter = EventFilter
},
case service_call(webhook_manager, 'Create', [WebhookParams], ReqCtx) of
{ok, Webhook} ->
Resp = decode_webhook(Webhook),
{ok, {201, [], Resp}};
{exception, Exception} ->
process_exception(OperationID, Exception)
end;
{exception, Exception} ->
process_exception(OperationID, Exception)
end;
process_request(_OperationID = 'GetWebhooks', _Req, Context, ReqCtx) ->
PartyID = get_party_id(Context),
{ok, Webhooks} = service_call(webhook_manager, 'GetList', [PartyID], ReqCtx),
{ok, {200, [], [decode_webhook(V) || V <- Webhooks]}};
process_request(OperationID = 'GetWebhookByID', Req, Context, ReqCtx) ->
PartyID = get_party_id(Context),
WebhookID = binary_to_integer(maps:get(webhookID, Req)),
case get_webhook(PartyID, WebhookID, ReqCtx) of
{ok, Webhook} ->
{ok, {200, [], decode_webhook(Webhook)}};
{exception, Exception} ->
process_exception(OperationID, Exception)
end;
process_request(OperationID = 'DeleteWebhookByID', Req, Context, ReqCtx) ->
PartyID = get_party_id(Context),
WebhookID = binary_to_integer(maps:get(webhookID, Req)),
case get_webhook(PartyID, WebhookID, ReqCtx) of
{ok, #webhooker_Webhook{}} ->
case service_call(webhook_manager, 'Delete', [WebhookID], ReqCtx) of
{ok, _} ->
{ok, {204, [], undefined}};
{exception, #webhooker_WebhookNotFound{}} ->
{ok, {204, [], undefined}};
{exception, Exception} ->
process_exception(OperationID, Exception)
end;
{exception, #webhooker_WebhookNotFound{}} ->
{ok, {204, [], undefined}};
{exception, Exception} ->
process_exception(OperationID, Exception)
end;
process_request(_OperationID, _Req, _Context, _ReqCtx) ->
{501, [], <<"Not implemented">>}.
validate_event_filter({invoice, #webhooker_InvoiceEventFilter{
shop_id = ShopID
}}, Context, ReqCtx) when ShopID /= undefined ->
PartyID = get_party_id(Context),
UserInfo = get_user_info(Context),
service_call(party_management, 'GetShop', [UserInfo, PartyID, ShopID], ReqCtx).
get_webhook(PartyID, WebhookID, ReqCtx) ->
Result = service_call(webhook_manager, 'Get', [WebhookID], ReqCtx),
case Result of
{ok, Webhook = #webhooker_Webhook{party_id = PartyID}} ->
{ok, Webhook};
{ok, _Webhook} ->
{exception, #webhooker_WebhookNotFound{}};
{exception, Exception} ->
{exception, Exception}
end.
%%%
service_call(ServiceName, Function, Args, Context) ->
@ -1133,6 +1258,10 @@ decode_payment(InvoiceID, #domain_InvoicePayment{
payment_tool = PaymentTool,
session = PaymentSession,
contact_info = ContactInfo
},
cost = #domain_Cash{
amount = Amount,
currency = Currency
}
}) ->
genlib_map:compact(maps:merge(#{
@ -1141,9 +1270,15 @@ decode_payment(InvoiceID, #domain_InvoicePayment{
<<"createdAt">> => CreatedAt,
<<"paymentToolToken">> => decode_payment_tool_token(PaymentTool),
<<"contactInfo">> => decode_contact_info(ContactInfo),
<<"paymentSession">> => PaymentSession
<<"paymentSession">> => PaymentSession,
<<"amount">> => Amount,
<<"currency">> => decode_currency(Currency)
}, decode_payment_status(Status))).
decode_payment_search_result(InvoiceID, PaymentSearchResult) ->
decode_payment(InvoiceID, PaymentSearchResult).
decode_payment_tool_token({bank_card, BankCard}) ->
encode_bank_card(BankCard).
@ -1179,7 +1314,7 @@ decode_payment_status({Status, StatusInfo}) ->
decode_invoice(#domain_Invoice{
id = InvoiceID,
created_at = CreatedAt, %%@TODO add it to the swagger spec
created_at = CreatedAt,
status = InvoiceStatus,
due = DueDate,
details = #domain_InvoiceDetails{
@ -1205,6 +1340,9 @@ decode_invoice(#domain_Invoice{
<<"description">> => Description
}, decode_invoice_status(InvoiceStatus))).
decode_invoice_search_result(InvoiceSearchResult) ->
decode_invoice(InvoiceSearchResult).
decode_invoice_status({Status, StatusInfo}) ->
Reason = case StatusInfo of
#domain_InvoiceCancelled{details = Details} -> Details;
@ -1723,6 +1861,125 @@ encode_currency(SymbolicCode) ->
symbolic_code = SymbolicCode
}.
encode_event_filter(#{
<<"topic">> := <<"InvoicesTopic">>,
<<"shopID">> := ShopID,
<<"eventTypes">> := EventTypes
}) ->
{invoice, #webhooker_InvoiceEventFilter{
shop_id = ShopID,
types = ordsets:from_list([
encode_event_type(invoices, V) || V <- EventTypes
])
}}.
-define(invpaid() , {paid, #webhooker_InvoicePaid{}}).
-define(invcancelled() , {cancelled, #webhooker_InvoiceCancelled{}}).
-define(invfulfilled() , {fulfilled, #webhooker_InvoiceFulfilled{}}).
-define(pmtprocessed() , {processed, #webhooker_InvoicePaymentProcessed{}}).
-define(pmtcaptured() , {captured, #webhooker_InvoicePaymentCaptured{}}).
-define(pmtcancelled() , {cancelled, #webhooker_InvoicePaymentCancelled{}}).
-define(pmtfailed() , {failed, #webhooker_InvoicePaymentFailed{}}).
encode_event_type(invoices, <<"InvoiceCreated">>) ->
{created, #webhooker_InvoiceCreated{}};
encode_event_type(invoices, <<"InvoicePaid">>) ->
{status_changed, #webhooker_InvoiceStatusChanged{value = ?invpaid()}};
encode_event_type(invoices, <<"InvoiceCancelled">>) ->
{status_changed, #webhooker_InvoiceStatusChanged{value = ?invcancelled()}};
encode_event_type(invoices, <<"InvoiceFulfilled">>) ->
{status_changed, #webhooker_InvoiceStatusChanged{value = ?invfulfilled()}};
encode_event_type(invoices, <<"PaymentCreated">>) ->
{payment, {created, #webhooker_InvoicePaymentCreated{}}};
encode_event_type(invoices, <<"PaymentProcessed">>) ->
{payment, {status_changed, #webhooker_InvoicePaymentStatusChanged{value = ?pmtprocessed()}}};
encode_event_type(invoices, <<"PaymentCaptured">>) ->
{payment, {status_changed, #webhooker_InvoicePaymentStatusChanged{value = ?pmtcaptured()}}};
encode_event_type(invoices, <<"PaymentCancelled">>) ->
{payment, {status_changed, #webhooker_InvoicePaymentStatusChanged{value = ?pmtcancelled()}}};
encode_event_type(invoices, <<"PaymentFailed">>) ->
{payment, {status_changed, #webhooker_InvoicePaymentStatusChanged{value = ?pmtfailed()}}}.
decode_event_filter({invoice, #webhooker_InvoiceEventFilter{
shop_id = ShopID,
types = EventTypes
}}) ->
#{
<<"topic">> => <<"InvoicesTopic">>,
<<"shopID">> => ShopID,
<<"eventTypes">> => lists:flatmap(
fun (V) -> decode_event_type(invoice, V) end, ordsets:to_list(EventTypes)
)
}.
decode_event_type(
invoice,
{created, #webhooker_InvoiceCreated{}}
) ->
[<<"InvoiceCreated">>];
decode_event_type(
invoice,
{status_changed, #webhooker_InvoiceStatusChanged{value = undefined}}
) ->
% TODO seems unmaintainable
[decode_invoice_status_event_type(V) || V <- [
?invpaid(),
?invcancelled(),
?invfulfilled()
]];
decode_event_type(
invoice,
{status_changed, #webhooker_InvoiceStatusChanged{value = Value}}
) ->
[decode_invoice_status_event_type(Value)];
decode_event_type(
invoice,
{payment, {created, #webhooker_InvoicePaymentCreated{}}}
) ->
[<<"PaymentCreated">>];
decode_event_type(
invoice,
{payment, {status_changed, #webhooker_InvoicePaymentStatusChanged{value = undefined}}}
) ->
% TODO seems unmaintainable
[decode_payment_status_event_type(V) || V <- [
?pmtprocessed(),
?pmtcaptured(),
?pmtcancelled(),
?pmtfailed()
]];
decode_event_type(
invoice,
{payment, {status_changed, #webhooker_InvoicePaymentStatusChanged{value = Value}}}
) ->
[decode_payment_status_event_type(Value)].
decode_invoice_status_event_type(?invpaid()) -> <<"InvoicePaid">>;
decode_invoice_status_event_type(?invcancelled()) -> <<"InvoiceCancelled">>;
decode_invoice_status_event_type(?invfulfilled()) -> <<"InvoiceFulfilled">>.
decode_payment_status_event_type(?pmtprocessed()) -> <<"PaymentProcessed">>;
decode_payment_status_event_type(?pmtcaptured()) -> <<"PaymentCaptured">>;
decode_payment_status_event_type(?pmtcancelled()) -> <<"PaymentCancelled">>;
decode_payment_status_event_type(?pmtfailed()) -> <<"PaymentFailed">>.
decode_webhook(#webhooker_Webhook{
id = ID,
party_id = _PartyID,
event_filter = EventFilter,
url = URL,
pub_key = PubKey,
enabled = Enabled
}) ->
#{
<<"id">> => integer_to_binary(ID),
<<"active">> => Enabled,
<<"scope">> => decode_event_filter(EventFilter),
<<"url">> => URL,
<<"publicKey">> => PubKey
}.
encode_stat_request(Dsl) when is_map(Dsl) ->
encode_stat_request(jsx:encode(Dsl));
@ -1810,12 +2067,12 @@ parse_rfc3339_datetime(DateTime) ->
{ok, {DateFrom, TimeFrom, _, _}} = rfc3339:parse(DateTime),
{DateFrom, TimeFrom}.
process_exception(_, #payproc_InvalidUser{}) ->
{ok, {400, [], logic_error(invalidUser, <<"Invalid user">>)}};
process_exception(_, #'InvalidRequest'{errors = Errors}) ->
{ok, {400, [], logic_error(invalidRequest, format_request_errors(Errors))}};
process_exception(_, #payproc_InvalidUser{}) ->
{ok, {404, [], general_error(<<"Invoice not found">>)}};
process_exception(_, #payproc_UserInvoiceNotFound{}) ->
{ok, {404, [], general_error(<<"Invoice not found">>)}};
@ -1825,6 +2082,9 @@ process_exception(_, #payproc_ClaimNotFound{}) ->
process_exception(_, #payproc_ContractNotFound{}) ->
{ok, {404, [], general_error(<<"Contract not found">>)}};
process_exception(_, #webhooker_WebhookNotFound{}) ->
{ok, {404, [], general_error(<<"Webhook not found">>)}};
process_exception(_, #payproc_InvalidInvoiceStatus{}) ->
{ok, {400, [], logic_error(invalidInvoiceStatus, <<"Invalid invoice status">>)}};
@ -1852,6 +2112,9 @@ process_exception(_, #'KeyringLocked'{}) ->
process_exception(_, #payproc_EventNotFound{}) ->
{ok, {404, [], general_error(<<"Event not found">>)}};
process_exception('CreateWebhook', #payproc_ShopNotFound{}) ->
{ok, {400, [], general_error(<<"Shop not found">>)}};
process_exception(_, #payproc_ShopNotFound{}) ->
{ok, {404, [], general_error(<<"Shop not found">>)}};

View File

@ -38,7 +38,8 @@
get_payments_ok_test/1,
get_payment_by_id_ok_test/1,
%%%%
get_invoices_stats_ok_test/1,
search_invoices_ok_test/1,
search_payments_ok_test/1,
get_payment_conversion_stats_ok_test/1,
get_payment_revenue_stats_ok_test/1,
get_payment_geo_stats_ok_test/1,
@ -68,10 +69,9 @@
create_payout_tool_ok_test/1,
get_payout_tools_ok_test/1,
%%%%
get_locations_names_ok_test/1,
%%%%
set_merchant_callback_ok_test/1,
get_merchant_callback_ok_test/1
create_webhook_error_test/1,
create_webhook_receive_events_test/1,
get_locations_names_ok_test/1
]).
-define(KEYCLOAK_HOST, "keycloak").
@ -79,19 +79,20 @@
-define(KEYCLOAK_USER, "demo_merchant").
-define(KEYCLOAK_PASSWORD, "test").
-define(CAPI_IP, "::").
-define(CAPI_HOST, "localhost").
-define(CAPI_PORT, 8080).
-define(CAPI_SERVICE_TYPE, real).
-define(CAPI_CDS_STORAGE_URL, "http://cds:8022/v1/storage").
-define(CAPI_INVOICING_URL, "http://hellgate:8022/v1/processing/invoicing").
-define(CAPI_MERCHANT_STAT_URL, "http://magista:8022/stat").
-define(CAPI_PARTY_MANAGEMENT_URL, "http://hellgate:8022/v1/processing/partymgmt").
-define(CAPI_REPOSITORY_URL, "http://dominant:8022/v1/domain/repository").
-define(CAPI_ACCOUNTER_URL, "http://shumway:8022/accounter").
-define(GEO_IP_URL, "http://columbus:8022/repo").
-define(MERCHANT_CONFIG_URL, "http://pimp:8022/capi").
-define(CAPI_HOST_NAME, "capi").
-define(CAPI_IP , "::").
-define(CAPI_HOST , "localhost").
-define(CAPI_PORT , 8080).
-define(CAPI_SERVICE_TYPE , real).
-define(CAPI_PARTY_MANAGEMENT_URL , "http://hellgate:8022/v1/processing/partymgmt").
-define(CAPI_ACCOUNTER_URL , "http://shumway:8022/accounter").
-define(CAPI_INVOICING_URL , "http://hellgate:8022/v1/processing/invoicing").
-define(CAPI_MERCHANT_CONFIG_URL , "http://pimp:8022/capi").
-define(CAPI_WEBHOOK_MGR_URL , "http://hooker:8022/hook").
-define(CAPI_REPOSITORY_URL , "http://dominant:8022/v1/domain/repository").
-define(CAPI_CDS_STORAGE_URL , "http://cds:8022/v1/storage").
-define(CAPI_MERCHANT_STAT_URL , "http://magista:8022/stat").
-define(CAPI_GEO_IP_URL , "http://columbus:8022/repo").
-define(CAPI_HOST_NAME , "capi").
-define(MERCHANT_ID, <<"281220eb-a4ef-4d03-b666-bdec4b26c5f7">>).
-define(LIVE_CATEGORY_ID, 100).
@ -125,7 +126,7 @@ all() ->
{group, claims_management},
{group, shops_management},
{group, accounts_management},
{group, callback_management},
{group, webhook_management},
{group, geo_ip}
].
@ -172,14 +173,9 @@ groups() ->
create_payment_tool_token_w_access_token_ok_test,
create_payment_ok_w_access_token_test
]},
{callback_management, [sequence], [
get_categories_ok_test,
get_category_by_id_ok_test,
set_merchant_callback_ok_test,
get_merchant_callback_ok_test
]},
{statistics, [sequence], [
get_invoices_stats_ok_test,
search_invoices_ok_test,
search_payments_ok_test,
get_payment_conversion_stats_ok_test,
get_payment_revenue_stats_ok_test,
get_payment_geo_stats_ok_test,
@ -219,6 +215,10 @@ groups() ->
create_shop_ok_test,
get_account_by_id_ok_test
]},
{webhook_management, [sequence], [
create_webhook_error_test,
create_webhook_receive_events_test
]},
{geo_ip, [parallel], [
get_locations_names_ok_test
]}
@ -231,7 +231,7 @@ groups() ->
init_per_suite(Config) ->
% _ = dbg:tracer(),
% _ = dbg:p(all, c),
% _ = dbg:tpl({'capi_authorizer_jwt', '_', '_'}, x),
% _ = dbg:tpl({api_client_lib, 'handle_response', '_'}, x),
Apps =
capi_ct_helper:start_app(lager) ++
capi_ct_helper:start_app(cowlib) ++
@ -239,14 +239,15 @@ init_per_suite(Config) ->
capi_ct_helper:start_app(api_client) ++
capi_ct_helper:start_app(cp_proto, [
{service_urls, #{
cds_storage => ?CAPI_CDS_STORAGE_URL,
invoicing => ?CAPI_INVOICING_URL,
merchant_stat => ?CAPI_MERCHANT_STAT_URL,
party_management => ?CAPI_PARTY_MANAGEMENT_URL,
repository => ?CAPI_REPOSITORY_URL,
accounter => ?CAPI_ACCOUNTER_URL,
geo_ip_service => ?GEO_IP_URL,
merchant_config => ?MERCHANT_CONFIG_URL
accounter => ?CAPI_ACCOUNTER_URL,
invoicing => ?CAPI_INVOICING_URL,
merchant_config => ?CAPI_MERCHANT_CONFIG_URL,
webhook_manager => ?CAPI_WEBHOOK_MGR_URL,
repository => ?CAPI_REPOSITORY_URL,
cds_storage => ?CAPI_CDS_STORAGE_URL,
merchant_stat => ?CAPI_MERCHANT_STAT_URL,
geo_ip_service => ?CAPI_GEO_IP_URL
}}
]),
{ok, SupPid} = supervisor:start_link(?MODULE, []),
@ -575,9 +576,9 @@ get_payment_by_id_ok_test(Config) ->
{ok, _Body} = api_client_payments:get_payment_by_id(Context, InvoiceID, PaymentID),
{save_config, Info}.
-spec get_invoices_stats_ok_test(config()) -> _.
-spec search_invoices_ok_test(config()) -> _.
get_invoices_stats_ok_test(Config) ->
search_invoices_ok_test(Config) ->
Context = ?config(context, Config),
ShopID = ?config(shop_id, Config),
Query = [
@ -585,10 +586,44 @@ get_invoices_stats_ok_test(Config) ->
{offset, 2},
{from_time, {{2015, 08, 11},{19, 42, 35}}},
{to_time, {{2020, 08, 11},{19, 42, 35}}},
{status, unpaid}
{invoiceStatus, <<"fulfilled">>},
{payerEmail, <<"test@test.ru">>},
{payerIP, <<"192.168.0.0.1">>},
{paymentStatus, <<"processed">>},
{invoiceID, <<"testInvoiceID">>},
{paymentID, <<"testPaymentID">>},
{payerEmail, <<"test@test_rbk.ru">>},
{payerIP, <<"192.168.0.1">>},
{payerFingerprint, <<"blablablalbalbal">>},
% {cardNumberMask, <<"222222**2222">>},
{paymentAmount, 10000}
],
{ok, _, _} = api_client_searches:get_invoices(Context, ShopID, Query).
{ok, _, _} = api_client_searches:search_invoices(Context, ShopID, Query).
-spec search_payments_ok_test(config()) -> _.
search_payments_ok_test(Config) ->
Context = ?config(context, Config),
ShopID = ?config(shop_id, Config),
Query = [
{limit, 2},
{offset, 2},
{from_time, {{2015, 08, 11},{19, 42, 35}}},
{to_time, {{2020, 08, 11},{19, 42, 35}}},
{payerEmail, <<"test@test.ru">>},
{payerIP, <<"192.168.0.0.1">>},
{paymentStatus, <<"processed">>},
{invoiceID, <<"testInvoiceID">>},
{paymentID, <<"testPaymentID">>},
{payerEmail, <<"test@test_rbk.ru">>},
{payerIP, <<"192.168.0.1">>},
{payerFingerprint, <<"blablablalbalbal">>},
% {cardNumberMask, <<"222222**2222">>},
{paymentAmount, 10000}
],
{ok, _, _} = api_client_searches:search_payments(Context, ShopID, Query).
-spec get_payment_conversion_stats_ok_test(config()) -> _.
@ -922,46 +957,6 @@ activate_shop_idempotent_ok_test(Config) ->
} = default_get_shop_by_id(ShopID, Config),
{save_config, ShopID}.
-spec set_merchant_callback_ok_test(config()) -> _.
set_merchant_callback_ok_test(Config) ->
{get_category_by_id_ok_test,
CategoryID
} = ?config(saved_config, Config),
CallbackUrl = <<"http://www.test.com">>,
ClaimID = default_create_shop(CategoryID, CallbackUrl, Config),
{ok, _} = default_approve_claim(ClaimID),
#{
<<"id">> := ClaimID,
<<"status">> := #{<<"status">> := <<"ClaimAccepted">>},
<<"changeset">> := [
#{
<<"partyModificationType">> := <<"ShopCreation">>,
<<"shop">> := #{
<<"id">> := ShopID
}
} | _
]
} = default_get_claim_by_id(ClaimID, Config),
{save_config, #{shop_id => ShopID, callback_url => CallbackUrl}}.
-spec get_merchant_callback_ok_test(config()) -> _.
get_merchant_callback_ok_test(Config) ->
{set_merchant_callback_ok_test,
#{
shop_id := ShopID,
callback_url := CallbackUrl
}
} = ?config(saved_config, Config),
#{
<<"id">> := ShopID,
<<"callbackHandler">> := #{
<<"url">> := CallbackUrl,
<<"publicKey">> := _
}
} = default_get_shop_by_id(ShopID, Config).
-spec get_account_by_id_ok_test(config()) -> _.
get_account_by_id_ok_test(Config) ->
@ -982,6 +977,47 @@ get_account_by_id_ok_test(Config) ->
%% @FIXME changin Account ID to string is not ok
} = default_get_shop_account_by_id(GuaranteeID, ShopID, Config).
-spec create_webhook_error_test(config()) -> _.
create_webhook_error_test(Config) ->
Context = ?config(context, Config),
ShopID = -1, % nonexistent
{error, _} = api_client_webhooks:create_webhook(Context, #{
<<"url">> => <<"http://localhost:8080/TODO">>,
<<"scope">> => construct_invoices_scope(ShopID)
}).
-spec create_webhook_receive_events_test(config()) -> _.
create_webhook_receive_events_test(Config) ->
Context = ?config(context, Config),
% % list is empty?
% [] = api_client_webhooks:get_webhooks(Context),
% create successful?
Shop = get_latest(get_shops(Config)),
ShopID = maps:get(<<"id">>, Shop),
WebhookParams = #{
<<"url">> => <<"http://localhost:8080/TODO">>,
<<"scope">> => construct_invoices_scope(ShopID, ['InvoiceCancelled'])
},
{ok, Webhook = #{<<"id">> := WebhookID}} = api_client_webhooks:create_webhook(Context, WebhookParams),
{ok, Webhook} = api_client_webhooks:get_webhook_by_id(Context, WebhookID),
% list is not empty then?
true = lists:member(Webhook, api_client_webhooks:get_webhooks(Context)),
% delete succeeded idempotently?
ok = api_client_webhooks:delete_webhook_by_id(Context, WebhookID),
ok = api_client_webhooks:delete_webhook_by_id(Context, WebhookID),
[] = [W || #{<<"id">> := W} <- api_client_webhooks:get_webhooks(Context), W =:= WebhookID],
ok.
construct_invoices_scope(ShopID) ->
construct_invoices_scope(ShopID, []).
construct_invoices_scope(ShopID, EventTypes) ->
#{
<<"topic">> => <<"InvoicesTopic">>,
<<"shopID">> => ShopID,
<<"eventTypes">> => lists:map(fun genlib:to_binary/1, EventTypes)
}.
-spec get_locations_names_ok_test(config()) -> _.
get_locations_names_ok_test(Config) ->
{TestGeoID, TestName} = {53654, <<"Могадишо"/utf8>>},
@ -1225,9 +1261,6 @@ default_get_shop_by_id(ShopID, Config) ->
R.
default_create_shop(CategoryID, Config) ->
default_create_shop(CategoryID, undefined, Config).
default_create_shop(CategoryID, CallbackUrl, Config) ->
#{
<<"claimID">> := ClaimID0
} = default_create_contract(Config),
@ -1239,10 +1272,10 @@ default_create_shop(CategoryID, CallbackUrl, Config) ->
default_approve_claim(ClaimID1),
#{<<"id">> := PayoutToolID} = get_latest(get_payout_tools(ContractID, Config)),
default_create_shop(CategoryID, ContractID, PayoutToolID, CallbackUrl, Config).
default_create_shop(CategoryID, ContractID, PayoutToolID, Config).
default_create_shop(CategoryID, ContractID, PayoutToolID, CallbackUrl, Config) ->
default_create_shop(CategoryID, ContractID, PayoutToolID, Config) ->
Context = ?config(context, Config),
Req = genlib_map:compact(#{
<<"categoryID">> => CategoryID,
@ -1251,8 +1284,7 @@ default_create_shop(CategoryID, ContractID, PayoutToolID, CallbackUrl, Config) -
<<"description">> => <<"Goods for education">>
},
<<"contractID">> => ContractID,
<<"payoutToolID">> => PayoutToolID,
<<"callbackUrl">> => CallbackUrl
<<"payoutToolID">> => PayoutToolID
}),
{ok, ClaimID} = api_client_shops:create_shop(Context, Req),
ClaimID.

@ -1 +1 @@
Subproject commit 6ba6971bad2a64104c7e89abc5269dca1c029d38
Subproject commit 4018c4162f55c9d2e3a6b7224915404f4bce8915

View File

@ -21,7 +21,8 @@
"accounter.thrift",
"geo_ip.thrift",
"domain_config.thrift",
"proxy_merch_config.thrift"
"proxy_merch_config.thrift",
"webhooker.thrift"
]},
{gen, "erlang:app_prefix=cp,scoped_typenames"}
]}.

View File

@ -34,5 +34,7 @@ get_service_modname(geo_ip_service) ->
{cp_geo_ip_thrift, 'GeoIpService'};
get_service_modname(merchant_config) ->
{cp_proxy_merch_config_thrift, 'ConfigureMerchantProxy'};
get_service_modname(webhook_manager) ->
{cp_webhooker_thrift, 'WebhookManager'};
get_service_modname(party_management) ->
{cp_payment_processing_thrift, 'PartyManagement'}.

View File

@ -30,13 +30,15 @@
{cp_proto, [
{service_urls, #{
invoicing => "http://hellgate:8022/v1/processing/invoicing",
cds_storage => "http://cds:8022/v1/storage",
merchant_stat => "http://magista:8022/stat",
party_management => "http://hellgate:8022/v1/processing/partymgmt",
geo_ip_service => "http://columbus:8022/repo",
repository => "http://dominant:8022/v1/domain/repository",
merchant_config => "http://pimp:8022/capi"
accounter => "http://shumway:8022/accounter",
invoicing => "http://hellgate:8022/v1/processing/invoicing",
merchant_config => "http://pimp:8022/capi",
webhook_manager => "http://hooker:8022/hook",
repository => "http://dominant:8022/v1/domain/repository",
cds_storage => "http://cds:8022/v1/storage",
merchant_stat => "http://magista:8022/stat",
geo_ip_service => "http://columbus:8022/repo"
}}
]},

View File

@ -28,6 +28,8 @@ services:
condition: service_started
pimp:
condition: service_started
hooker:
condition: service_healthy
hellgate:
image: dr.rbkmoney.com/rbkmoney/hellgate:bf50fbffda6c65ebf055446dfb27ab31392a4881
@ -62,7 +64,7 @@ services:
retries: 12
magista:
image: dr.rbkmoney.com/rbkmoney/magista:74e1bbf7dde05b8af66ce62c8c55dc035b655f99
image: dr.rbkmoney.com/rbkmoney/magista:872cd681a5a26e986763897518afa5d30f80f187
restart: always
entrypoint:
- java
@ -198,6 +200,36 @@ services:
-Xmx512m
-jar /opt/pimp/pimp.jar
hooker:
image: dr.rbkmoney.com/rbkmoney/hooker:799d6994c9abc761b66a352c39b3287d59839b98
healthcheck:
test: "curl -sS -o /dev/null http://localhost:8022/"
interval: 5s
timeout: 2s
retries: 10
entrypoint:
- java
- -jar
- /opt/hooker/hooker.jar
- --spring.datasource.url=jdbc:postgresql://hooker-db:5432/hook
- --spring.datasource.username=postgres
- --spring.datasource.password=postgres
- --flyway.url=jdbc:postgresql://hooker-db:5432/hook
- --flyway.user=postgres
- --flyway.password=postgres
- --flyway.schemas=hook
- --bm.pooling.url=http://bustermaze:8022/repo
depends_on:
- hooker-db
- bustermaze
hooker-db:
image: dr.rbkmoney.com/rbkmoney/postgres:9.6
environment:
- POSTGRES_DB=hook
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
keycloak:
image: dr.rbkmoney.com/rbkmoney/keycloak:a4c082f48695cb02e0624deb559f9ec0378abdb4
healthcheck:

View File

@ -1,7 +1,7 @@
{"1.1.0",
[{<<"api_client">>,
{git,"git@github.com:rbkmoney/api_client.git",
{ref,"8dbbdffa8e5583e98067ee97343286dd76ec8381"}},
{ref,"c135f8c4e8302e6eb51c4e4a6fd9997503a196ed"}},
0},
{<<"base64url">>,{pkg,<<"base64url">>,<<"0.0.1">>},0},
{<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},1},

@ -1 +1 @@
Subproject commit 7fb63e90f3a622357da9781b0f9e410169db8bdb
Subproject commit 8e5f8583c3a95bc0722f12034c67bfb440fd240d