@@ -2,11 +2,13 @@ use std::str::FromStr;
2
2
3
3
use error_stack:: report;
4
4
use masking:: Secret ;
5
+ use reqwest:: Url ;
5
6
use serde:: { Deserialize , Serialize } ;
6
7
7
8
use super :: result_codes:: { FAILURE_CODES , PENDING_CODES , SUCCESSFUL_CODES } ;
8
9
use crate :: {
9
10
core:: errors,
11
+ services,
10
12
types:: { self , api, storage:: enums} ,
11
13
} ;
12
14
@@ -47,16 +49,39 @@ pub struct AciCancelRequest {
47
49
pub payment_type : AciPaymentType ,
48
50
}
49
51
50
- #[ derive( Debug , Clone , Eq , PartialEq , Serialize ) ]
52
+ #[ derive( Debug , Clone , Serialize ) ]
51
53
#[ serde( untagged) ]
52
54
pub enum PaymentDetails {
53
55
#[ serde( rename = "card" ) ]
54
- Card ( CardDetails ) ,
56
+ AciCard ( Box < CardDetails > ) ,
57
+ BankRedirect ( Box < BankRedirectionPMData > ) ,
55
58
#[ serde( rename = "bank" ) ]
56
59
Wallet ,
57
60
Klarna ,
58
- #[ serde( rename = "bankRedirect" ) ]
59
- BankRedirect ,
61
+ }
62
+
63
+ #[ derive( Debug , Clone , Serialize ) ]
64
+ #[ serde( rename_all = "camelCase" ) ]
65
+ pub struct BankRedirectionPMData {
66
+ payment_brand : PaymentBrand ,
67
+ #[ serde( rename = "bankAccount.country" ) ]
68
+ bank_account_country : Option < api_models:: enums:: CountryCode > ,
69
+ #[ serde( rename = "bankAccount.bankName" ) ]
70
+ bank_account_bank_name : Option < String > ,
71
+ #[ serde( rename = "bankAccount.bic" ) ]
72
+ bank_account_bic : Option < Secret < String > > ,
73
+ #[ serde( rename = "bankAccount.iban" ) ]
74
+ bank_account_iban : Option < Secret < String > > ,
75
+ shopper_result_url : Option < String > ,
76
+ }
77
+
78
+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
79
+ #[ serde( rename_all = "UPPERCASE" ) ]
80
+ pub enum PaymentBrand {
81
+ Eps ,
82
+ Ideal ,
83
+ Giropay ,
84
+ Sofortueberweisung ,
60
85
}
61
86
62
87
#[ derive( Debug , Clone , Eq , PartialEq , Serialize ) ]
@@ -101,16 +126,64 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest {
101
126
type Error = error_stack:: Report < errors:: ConnectorError > ;
102
127
fn try_from ( item : & types:: PaymentsAuthorizeRouterData ) -> Result < Self , Self :: Error > {
103
128
let payment_details: PaymentDetails = match item. request . payment_method_data . clone ( ) {
104
- api:: PaymentMethodData :: Card ( ccard) => PaymentDetails :: Card ( CardDetails {
129
+ api:: PaymentMethodData :: Card ( ccard) => PaymentDetails :: AciCard ( Box :: new ( CardDetails {
105
130
card_number : ccard. card_number ,
106
131
card_holder : ccard. card_holder_name ,
107
132
card_expiry_month : ccard. card_exp_month ,
108
133
card_expiry_year : ccard. card_exp_year ,
109
134
card_cvv : ccard. card_cvc ,
110
- } ) ,
135
+ } ) ) ,
111
136
api:: PaymentMethodData :: PayLater ( _) => PaymentDetails :: Klarna ,
112
137
api:: PaymentMethodData :: Wallet ( _) => PaymentDetails :: Wallet ,
113
- api:: PaymentMethodData :: BankRedirect ( _) => PaymentDetails :: BankRedirect ,
138
+ api:: PaymentMethodData :: BankRedirect ( ref redirect_banking_data) => {
139
+ match redirect_banking_data {
140
+ api_models:: payments:: BankRedirectData :: Eps { .. } => {
141
+ PaymentDetails :: BankRedirect ( Box :: new ( BankRedirectionPMData {
142
+ payment_brand : PaymentBrand :: Eps ,
143
+ bank_account_country : Some ( api_models:: enums:: CountryCode :: AT ) ,
144
+ bank_account_bank_name : None ,
145
+ bank_account_bic : None ,
146
+ bank_account_iban : None ,
147
+ shopper_result_url : item. request . router_return_url . clone ( ) ,
148
+ } ) )
149
+ }
150
+ api_models:: payments:: BankRedirectData :: Giropay {
151
+ bank_account_bic,
152
+ bank_account_iban,
153
+ ..
154
+ } => PaymentDetails :: BankRedirect ( Box :: new ( BankRedirectionPMData {
155
+ payment_brand : PaymentBrand :: Giropay ,
156
+ bank_account_country : Some ( api_models:: enums:: CountryCode :: DE ) ,
157
+ bank_account_bank_name : None ,
158
+ bank_account_bic : bank_account_bic. clone ( ) ,
159
+ bank_account_iban : bank_account_iban. clone ( ) ,
160
+ shopper_result_url : item. request . router_return_url . clone ( ) ,
161
+ } ) ) ,
162
+ api_models:: payments:: BankRedirectData :: Ideal { bank_name, .. } => {
163
+ PaymentDetails :: BankRedirect ( Box :: new ( BankRedirectionPMData {
164
+ payment_brand : PaymentBrand :: Ideal ,
165
+ bank_account_country : Some ( api_models:: enums:: CountryCode :: NL ) ,
166
+ bank_account_bank_name : Some ( bank_name. to_string ( ) ) ,
167
+ bank_account_bic : None ,
168
+ bank_account_iban : None ,
169
+ shopper_result_url : item. request . router_return_url . clone ( ) ,
170
+ } ) )
171
+ }
172
+ api_models:: payments:: BankRedirectData :: Sofort { country, .. } => {
173
+ PaymentDetails :: BankRedirect ( Box :: new ( BankRedirectionPMData {
174
+ payment_brand : PaymentBrand :: Sofortueberweisung ,
175
+ bank_account_country : Some ( * country) ,
176
+ bank_account_bank_name : None ,
177
+ bank_account_bic : None ,
178
+ bank_account_iban : None ,
179
+ shopper_result_url : item. request . router_return_url . clone ( ) ,
180
+ } ) )
181
+ }
182
+ _ => Err ( errors:: ConnectorError :: NotImplemented (
183
+ "Payment method" . to_string ( ) ,
184
+ ) ) ?,
185
+ }
186
+ }
114
187
api:: PaymentMethodData :: Crypto ( _) | api:: PaymentMethodData :: BankDebit ( _) => {
115
188
Err ( errors:: ConnectorError :: NotSupported {
116
189
payment_method : format ! ( "{:?}" , item. payment_method) ,
@@ -152,6 +225,7 @@ pub enum AciPaymentStatus {
152
225
Failed ,
153
226
#[ default]
154
227
Pending ,
228
+ RedirectShopper ,
155
229
}
156
230
157
231
impl From < AciPaymentStatus > for enums:: AttemptStatus {
@@ -160,6 +234,7 @@ impl From<AciPaymentStatus> for enums::AttemptStatus {
160
234
AciPaymentStatus :: Succeeded => Self :: Charged ,
161
235
AciPaymentStatus :: Failed => Self :: Failure ,
162
236
AciPaymentStatus :: Pending => Self :: Authorizing ,
237
+ AciPaymentStatus :: RedirectShopper => Self :: AuthenticationPending ,
163
238
}
164
239
}
165
240
}
@@ -180,7 +255,7 @@ impl FromStr for AciPaymentStatus {
180
255
}
181
256
}
182
257
183
- #[ derive( Default , Clone , Deserialize , PartialEq , Eq ) ]
258
+ #[ derive( Debug , Default , Clone , Deserialize , PartialEq , Eq ) ]
184
259
#[ serde( rename_all = "camelCase" ) ]
185
260
pub struct AciPaymentsResponse {
186
261
id : String ,
@@ -189,6 +264,21 @@ pub struct AciPaymentsResponse {
189
264
timestamp : String ,
190
265
build_number : String ,
191
266
pub ( super ) result : ResultCode ,
267
+ pub ( super ) redirect : Option < AciRedirectionData > ,
268
+ }
269
+
270
+ #[ derive( Clone , Debug , Deserialize , PartialEq , Eq ) ]
271
+ #[ serde( rename_all = "camelCase" ) ]
272
+ pub struct AciRedirectionData {
273
+ method : Option < services:: Method > ,
274
+ parameters : Vec < Parameters > ,
275
+ url : Url ,
276
+ }
277
+
278
+ #[ derive( Clone , Debug , Deserialize , PartialEq , Eq ) ]
279
+ pub struct Parameters {
280
+ name : String ,
281
+ value : String ,
192
282
}
193
283
194
284
#[ derive( Default , Debug , Clone , Deserialize , PartialEq , Eq ) ]
@@ -214,13 +304,37 @@ impl<F, T>
214
304
fn try_from (
215
305
item : types:: ResponseRouterData < F , AciPaymentsResponse , T , types:: PaymentsResponseData > ,
216
306
) -> Result < Self , Self :: Error > {
307
+ let redirection_data = item. response . redirect . map ( |data| {
308
+ let form_fields = std:: collections:: HashMap :: < _ , _ > :: from_iter (
309
+ data. parameters
310
+ . iter ( )
311
+ . map ( |parameter| ( parameter. name . clone ( ) , parameter. value . clone ( ) ) ) ,
312
+ ) ;
313
+
314
+ // If method is Get, parameters are appended to URL
315
+ // If method is post, we http Post the method to URL
316
+ services:: RedirectForm :: Form {
317
+ endpoint : data. url . to_string ( ) ,
318
+ // Handles method for Bank redirects currently.
319
+ // 3DS response have method within preconditions. That would require replacing below line with a function.
320
+ method : data. method . unwrap_or ( services:: Method :: Post ) ,
321
+ form_fields,
322
+ }
323
+ } ) ;
324
+
217
325
Ok ( Self {
218
- status : enums:: AttemptStatus :: from ( AciPaymentStatus :: from_str (
219
- & item. response . result . code ,
220
- ) ?) ,
326
+ status : {
327
+ if redirection_data. is_some ( ) {
328
+ enums:: AttemptStatus :: from ( AciPaymentStatus :: RedirectShopper )
329
+ } else {
330
+ enums:: AttemptStatus :: from ( AciPaymentStatus :: from_str (
331
+ & item. response . result . code ,
332
+ ) ?)
333
+ }
334
+ } ,
221
335
response : Ok ( types:: PaymentsResponseData :: TransactionResponse {
222
336
resource_id : types:: ResponseId :: ConnectorTransactionId ( item. response . id ) ,
223
- redirection_data : None ,
337
+ redirection_data,
224
338
mandate_reference : None ,
225
339
connector_metadata : None ,
226
340
} ) ,
0 commit comments