Skip to content

Commit fb3cabd

Browse files
authored
BG-834: Recurrent cascade (#200)
* BG-834: Recurrent cascade * Fixes * Fixes * Fix * Fix * Changes * Review fixes * Fix checks * Parent route doesn't matter * Fixes * Review fixes * Update damsel * Fix damsel version
1 parent 1c153f7 commit fb3cabd

16 files changed

Lines changed: 946 additions & 20 deletions

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
# You SHOULD specify point releases here so that build time and run time Erlang/OTPs
33
# are the same. See: https://github.com/erlware/relx/pull/902
44
SERVICE_NAME=hellgate
5-
OTP_VERSION=27.1.2
5+
OTP_VERSION=27.3.4
66
REBAR_VERSION=3.24
77
THRIFT_VERSION=0.14.2.3

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ tags
1414
/.image.*
1515
Makefile.env
1616
*.iml
17+
CLAUDE.md
18+
.claude/

.tool-versions

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rebar 3.24.0
2+
erlang 27.3.4

apps/hellgate/include/hg_invoice_payment.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
chargebacks = #{} :: #{hg_invoice_payment_chargeback:id() => hg_invoice_payment_chargeback:state()},
2424
adjustments = [] :: [hg_invoice_payment:adjustment()],
2525
recurrent_token :: undefined | dmsl_domain_thrift:'Token'(),
26+
cascade_recurrent_tokens :: undefined | hg_customer_client:cascade_tokens(),
2627
opts :: undefined | hg_invoice_payment:opts(),
2728
repair_scenario :: undefined | hg_invoice_repair:scenario(),
2829
capture_data :: undefined | hg_invoice_payment:capture_data(),

apps/hellgate/include/payment_events.hrl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@
9494
{invoice_payment_rec_token_acquired, #payproc_InvoicePaymentRecTokenAcquired{token = Token}}
9595
).
9696

97+
-define(cascade_tokens_loaded(Tokens),
98+
{invoice_payment_cascade_tokens_loaded, #payproc_InvoicePaymentCascadeTokensLoaded{tokens = Tokens}}
99+
).
100+
97101
-define(cash_changed(OldCash, NewCash),
98102
{invoice_payment_cash_changed, #payproc_InvoicePaymentCashChanged{old_cash = OldCash, new_cash = NewCash}}
99103
).
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
-module(hg_customer_client).
2+
3+
-include_lib("damsel/include/dmsl_customer_thrift.hrl").
4+
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
5+
6+
%% BankCard operations
7+
-export([find_or_create_bank_card/2]).
8+
-export([get_recurrent_tokens_by_card/2]).
9+
-export([save_recurrent_token_by_card/3]).
10+
-export([tokens_to_map/1]).
11+
12+
%% Customer operations
13+
-export([create_customer/1]).
14+
-export([get_by_parent_payment/2]).
15+
-export([get_recurrent_tokens/2]).
16+
-export([add_payment/3]).
17+
-export([link_bank_card/2]).
18+
19+
-export_type([cascade_tokens/0]).
20+
21+
-type invoice_id() :: dmsl_domain_thrift:'InvoiceID'().
22+
-type payment_id() :: dmsl_domain_thrift:'InvoicePaymentID'().
23+
-type provider_terminal_key() :: dmsl_customer_thrift:'ProviderTerminalKey'().
24+
-type token() :: dmsl_domain_thrift:'Token'().
25+
-type recurrent_token() :: dmsl_customer_thrift:'RecurrentToken'().
26+
-type cascade_tokens() :: #{provider_terminal_key() => token()}.
27+
28+
%% BankCard operations
29+
30+
-spec find_or_create_bank_card(dmsl_domain_thrift:'PartyConfigRef'(), token()) ->
31+
dmsl_customer_thrift:'BankCard'().
32+
find_or_create_bank_card(PartyConfigRef, BankCardToken) ->
33+
case find_bank_card(PartyConfigRef, BankCardToken) of
34+
{ok, BankCard} ->
35+
BankCard;
36+
{exception, #customer_BankCardNotFound{}} ->
37+
{ok, BankCard} = call(
38+
bank_card_storage,
39+
'Create',
40+
{PartyConfigRef, #customer_BankCardParams{bank_card_token = BankCardToken}}
41+
),
42+
BankCard
43+
end.
44+
45+
-spec get_recurrent_tokens_by_card(dmsl_domain_thrift:'PartyConfigRef'(), token()) ->
46+
[recurrent_token()].
47+
get_recurrent_tokens_by_card(PartyConfigRef, BankCardToken) ->
48+
case find_bank_card(PartyConfigRef, BankCardToken) of
49+
{ok, #customer_BankCard{id = BankCardID}} ->
50+
{ok, Tokens} = call(bank_card_storage, 'GetRecurrentTokens', {BankCardID}),
51+
Tokens;
52+
{exception, #customer_BankCardNotFound{}} ->
53+
[]
54+
end.
55+
56+
-spec save_recurrent_token_by_card(
57+
dmsl_domain_thrift:'PartyConfigRef'(),
58+
token(),
59+
{dmsl_domain_thrift:'PaymentRoute'(), token()}
60+
) -> recurrent_token().
61+
save_recurrent_token_by_card(
62+
PartyConfigRef,
63+
BankCardToken,
64+
{#domain_PaymentRoute{provider = ProviderRef, terminal = TerminalRef}, RecToken}
65+
) ->
66+
#customer_BankCard{id = BankCardID} = find_or_create_bank_card(PartyConfigRef, BankCardToken),
67+
{ok, SavedToken} = call(
68+
bank_card_storage,
69+
'AddRecurrentToken',
70+
{#customer_RecurrentTokenParams{
71+
bank_card_id = BankCardID,
72+
provider_ref = ProviderRef,
73+
terminal_ref = TerminalRef,
74+
token = RecToken
75+
}}
76+
),
77+
SavedToken.
78+
79+
-spec tokens_to_map([recurrent_token()]) -> cascade_tokens().
80+
tokens_to_map(Tokens) ->
81+
lists:foldl(fun token_to_map_entry/2, #{}, Tokens).
82+
83+
%% Customer operations
84+
85+
-spec create_customer(dmsl_domain_thrift:'PartyConfigRef'()) -> dmsl_customer_thrift:'Customer'().
86+
create_customer(PartyConfigRef) ->
87+
{ok, Customer} = call(customer_management, 'Create', {#customer_CustomerParams{party_ref = PartyConfigRef}}),
88+
Customer.
89+
90+
-spec get_by_parent_payment(invoice_id(), payment_id()) ->
91+
{ok, dmsl_customer_thrift:'CustomerState'()} | {exception, term()}.
92+
get_by_parent_payment(InvoiceID, PaymentID) ->
93+
call(customer_management, 'GetByParentPayment', {InvoiceID, PaymentID}).
94+
95+
-spec get_recurrent_tokens(invoice_id(), payment_id()) -> [recurrent_token()].
96+
get_recurrent_tokens(InvoiceID, PaymentID) ->
97+
case call(customer_management, 'GetByParentPayment', {InvoiceID, PaymentID}) of
98+
{ok, #customer_CustomerState{bank_card_refs = BankCardRefs}} ->
99+
lists:flatmap(fun collect_bank_card_tokens/1, BankCardRefs);
100+
{exception, #customer_CustomerNotFound{}} ->
101+
[];
102+
{exception, #customer_InvalidRecurrentParent{}} ->
103+
[]
104+
end.
105+
106+
-spec add_payment(dmsl_customer_thrift:'CustomerID'(), invoice_id(), payment_id()) -> ok.
107+
add_payment(CustomerID, InvoiceID, PaymentID) ->
108+
{ok, ok} = call(customer_management, 'AddPayment', {CustomerID, InvoiceID, PaymentID}),
109+
ok.
110+
111+
-spec link_bank_card(dmsl_customer_thrift:'CustomerID'(), token()) -> ok.
112+
link_bank_card(CustomerID, BankCardToken) ->
113+
{ok, _} = call(
114+
customer_management,
115+
'AddBankCard',
116+
{CustomerID, #customer_BankCardParams{bank_card_token = BankCardToken}}
117+
),
118+
ok.
119+
120+
%% Internal
121+
122+
find_bank_card(PartyConfigRef, BankCardToken) ->
123+
SearchParams = #customer_BankCardSearchParams{
124+
bank_card_token = BankCardToken,
125+
party_ref = PartyConfigRef
126+
},
127+
call(bank_card_storage, 'Find', {SearchParams}).
128+
129+
collect_bank_card_tokens(#customer_BankCardRef{id = BankCardID}) ->
130+
{ok, Tokens} = call(bank_card_storage, 'GetRecurrentTokens', {BankCardID}),
131+
Tokens.
132+
133+
token_to_map_entry(
134+
#customer_RecurrentToken{
135+
provider_ref = ProviderRef,
136+
terminal_ref = TerminalRef,
137+
token = Token
138+
},
139+
Acc
140+
) ->
141+
Key = #customer_ProviderTerminalKey{
142+
provider_ref = ProviderRef,
143+
terminal_ref = TerminalRef
144+
},
145+
Acc#{Key => Token}.
146+
147+
call(ServiceName, Function, Args) ->
148+
Service = hg_proto:get_service(ServiceName),
149+
Opts = hg_woody_wrapper:get_service_options(ServiceName),
150+
WoodyContext =
151+
try
152+
hg_context:get_woody_context(hg_context:load())
153+
catch
154+
error:badarg -> woody_context:new()
155+
end,
156+
Request = {Service, Function, Args},
157+
woody_client:call(
158+
Request,
159+
Opts#{
160+
event_handler => {
161+
scoper_woody_event_handler,
162+
genlib_app:env(hellgate, scoper_event_handler_options, #{})
163+
}
164+
},
165+
WoodyContext
166+
).

0 commit comments

Comments
 (0)