Skip to content

Commit 0f70473

Browse files
fix(router): store customer_acceptance in payment_attempt, use it in confirm flow for delayed authorizations like external 3ds flow (#5308)
1 parent 61b3aef commit 0f70473

File tree

12 files changed

+65
-5
lines changed

12 files changed

+65
-5
lines changed

crates/diesel_models/src/payment_attempt.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use common_utils::pii;
12
use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
23
use serde::{Deserialize, Serialize};
34
use time::PrimitiveDateTime;
@@ -72,6 +73,7 @@ pub struct PaymentAttempt {
7273
pub charge_id: Option<String>,
7374
pub client_source: Option<String>,
7475
pub client_version: Option<String>,
76+
pub customer_acceptance: Option<pii::SecretSerdeValue>,
7577
}
7678

7779
impl PaymentAttempt {
@@ -155,6 +157,7 @@ pub struct PaymentAttemptNew {
155157
pub charge_id: Option<String>,
156158
pub client_source: Option<String>,
157159
pub client_version: Option<String>,
160+
pub customer_acceptance: Option<pii::SecretSerdeValue>,
158161
}
159162

160163
impl PaymentAttemptNew {
@@ -240,6 +243,7 @@ pub enum PaymentAttemptUpdate {
240243
payment_method_billing_address_id: Option<String>,
241244
client_source: Option<String>,
242245
client_version: Option<String>,
246+
customer_acceptance: Option<pii::SecretSerdeValue>,
243247
},
244248
VoidUpdate {
245249
status: storage_enums::AttemptStatus,
@@ -410,6 +414,7 @@ pub struct PaymentAttemptUpdateInternal {
410414
charge_id: Option<String>,
411415
client_source: Option<String>,
412416
client_version: Option<String>,
417+
customer_acceptance: Option<pii::SecretSerdeValue>,
413418
}
414419

415420
impl PaymentAttemptUpdateInternal {
@@ -478,6 +483,7 @@ impl PaymentAttemptUpdate {
478483
charge_id,
479484
client_source,
480485
client_version,
486+
customer_acceptance,
481487
} = PaymentAttemptUpdateInternal::from(self).populate_derived_fields(&source);
482488
PaymentAttempt {
483489
amount: amount.unwrap_or(source.amount),
@@ -529,6 +535,7 @@ impl PaymentAttemptUpdate {
529535
charge_id: charge_id.or(source.charge_id),
530536
client_source: client_source.or(source.client_source),
531537
client_version: client_version.or(source.client_version),
538+
customer_acceptance: customer_acceptance.or(source.customer_acceptance),
532539
..source
533540
}
534541
}
@@ -617,6 +624,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
617624
payment_method_id,
618625
client_source,
619626
client_version,
627+
customer_acceptance,
620628
} => Self {
621629
amount: Some(amount),
622630
currency: Some(currency),
@@ -648,6 +656,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
648656
capture_method,
649657
client_source,
650658
client_version,
659+
customer_acceptance,
651660
..Default::default()
652661
},
653662
PaymentAttemptUpdate::VoidUpdate {

crates/diesel_models/src/schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ diesel::table! {
820820
client_source -> Nullable<Varchar>,
821821
#[max_length = 64]
822822
client_version -> Nullable<Varchar>,
823+
customer_acceptance -> Nullable<Jsonb>,
823824
}
824825
}
825826

crates/diesel_models/src/user/sample_data.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub struct PaymentAttemptBatchNew {
7676
pub charge_id: Option<String>,
7777
pub client_source: Option<String>,
7878
pub client_version: Option<String>,
79+
pub customer_acceptance: Option<common_utils::pii::SecretSerdeValue>,
7980
}
8081

8182
#[allow(dead_code)]
@@ -139,6 +140,7 @@ impl PaymentAttemptBatchNew {
139140
charge_id: self.charge_id,
140141
client_source: self.client_source,
141142
client_version: self.client_version,
143+
customer_acceptance: self.customer_acceptance,
142144
}
143145
}
144146
}

crates/hyperswitch_domain_models/src/mandates.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub struct MandateData {
4343
pub mandate_type: Option<MandateDataType>,
4444
}
4545

46-
#[derive(Default, Eq, PartialEq, Debug, Clone, serde::Deserialize)]
46+
#[derive(Default, Eq, PartialEq, Debug, Clone, serde::Deserialize, serde::Serialize)]
4747
pub struct CustomerAcceptance {
4848
/// Type of acceptance provided by the
4949
pub acceptance_type: AcceptanceType,
@@ -54,15 +54,15 @@ pub struct CustomerAcceptance {
5454
pub online: Option<OnlineMandate>,
5555
}
5656

57-
#[derive(Default, Debug, PartialEq, Eq, Clone, serde::Deserialize)]
57+
#[derive(Default, Debug, PartialEq, Eq, Clone, serde::Deserialize, serde::Serialize)]
5858
#[serde(rename_all = "lowercase")]
5959
pub enum AcceptanceType {
6060
Online,
6161
#[default]
6262
Offline,
6363
}
6464

65-
#[derive(Default, Eq, PartialEq, Debug, Clone, serde::Deserialize)]
65+
#[derive(Default, Eq, PartialEq, Debug, Clone, serde::Deserialize, serde::Serialize)]
6666
pub struct OnlineMandate {
6767
/// Ip address of the customer machine from which the mandate was created
6868
#[serde(skip_deserializing)]

crates/hyperswitch_domain_models/src/payments/payment_attempt.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use api_models::enums::Connector;
22
use common_enums as storage_enums;
33
use common_utils::{
44
errors::{CustomResult, ValidationError},
5+
pii,
56
types::MinorUnit,
67
};
78
use error_stack::ResultExt;
@@ -175,6 +176,7 @@ pub struct PaymentAttempt {
175176
pub charge_id: Option<String>,
176177
pub client_source: Option<String>,
177178
pub client_version: Option<String>,
179+
pub customer_acceptance: Option<pii::SecretSerdeValue>,
178180
}
179181

180182
impl PaymentAttempt {
@@ -264,6 +266,7 @@ pub struct PaymentAttemptNew {
264266
pub charge_id: Option<String>,
265267
pub client_source: Option<String>,
266268
pub client_version: Option<String>,
269+
pub customer_acceptance: Option<pii::SecretSerdeValue>,
267270
}
268271

269272
impl PaymentAttemptNew {
@@ -346,6 +349,7 @@ pub enum PaymentAttemptUpdate {
346349
payment_method_id: Option<String>,
347350
client_source: Option<String>,
348351
client_version: Option<String>,
352+
customer_acceptance: Option<pii::SecretSerdeValue>,
349353
},
350354
RejectUpdate {
351355
status: storage_enums::AttemptStatus,

crates/router/src/core/payments/helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3712,6 +3712,7 @@ impl AttemptType {
37123712
charge_id: None,
37133713
client_source: old_payment_attempt.client_source,
37143714
client_version: old_payment_attempt.client_version,
3715+
customer_acceptance: old_payment_attempt.customer_acceptance,
37153716
}
37163717
}
37173718

crates/router/src/core/payments/operations/payment_confirm.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,17 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
341341
.change_context(errors::ApiErrorResponse::InvalidDataValue {
342342
field_name: "browser_info",
343343
})?;
344-
let customer_acceptance = request.customer_acceptance.clone().map(From::from);
344+
let customer_acceptance = request.customer_acceptance.clone().or(payment_attempt
345+
.customer_acceptance
346+
.clone()
347+
.map(|customer_acceptance| {
348+
customer_acceptance
349+
.expose()
350+
.parse_value("CustomerAcceptance")
351+
.change_context(errors::ApiErrorResponse::InternalServerError)
352+
.attach_printable("Failed while deserializing customer_acceptance")
353+
})
354+
.transpose()?);
345355

346356
let recurring_details = request.recurring_details.clone();
347357

@@ -360,6 +370,16 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
360370

361371
payment_attempt.capture_method = request.capture_method.or(payment_attempt.capture_method);
362372

373+
payment_attempt.customer_acceptance = request
374+
.customer_acceptance
375+
.clone()
376+
.map(|customer_acceptance| customer_acceptance.encode_to_value())
377+
.transpose()
378+
.change_context(errors::ApiErrorResponse::InternalServerError)
379+
.attach_printable("Failed while encoding customer_acceptance to value")?
380+
.map(masking::Secret::new)
381+
.or(payment_attempt.customer_acceptance);
382+
363383
currency = payment_attempt.currency.get_required_value("currency")?;
364384
amount = payment_attempt.get_total_amount().into();
365385

@@ -616,7 +636,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
616636
mandate_id: None,
617637
mandate_connector,
618638
setup_mandate,
619-
customer_acceptance,
639+
customer_acceptance: customer_acceptance.map(From::from),
620640
token,
621641
address: PaymentAddress::new(
622642
shipping_address.as_ref().map(From::from),
@@ -1211,6 +1231,7 @@ impl<F: Clone> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for Paymen
12111231
payment_method_id: m_payment_method_id,
12121232
client_source,
12131233
client_version,
1234+
customer_acceptance: payment_data.payment_attempt.customer_acceptance,
12141235
},
12151236
storage_scheme,
12161237
)

crates/router/src/core/payments/operations/payment_create.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
281281
&payment_method_info,
282282
merchant_key_store,
283283
profile_id,
284+
&customer_acceptance,
284285
)
285286
.await?;
286287

@@ -796,6 +797,7 @@ impl PaymentCreate {
796797
payment_method_info: &Option<PaymentMethod>,
797798
key_store: &domain::MerchantKeyStore,
798799
profile_id: String,
800+
customer_acceptance: &Option<payments::CustomerAcceptance>,
799801
) -> RouterResult<(
800802
storage::PaymentAttemptNew,
801803
Option<api_models::payments::AdditionalPaymentData>,
@@ -966,6 +968,13 @@ impl PaymentCreate {
966968
charge_id: None,
967969
client_source: None,
968970
client_version: None,
971+
customer_acceptance: customer_acceptance
972+
.clone()
973+
.map(|customer_acceptance| customer_acceptance.encode_to_value())
974+
.transpose()
975+
.change_context(errors::ApiErrorResponse::InternalServerError)
976+
.attach_printable("Failed to serialize customer_acceptance")?
977+
.map(Secret::new),
969978
},
970979
additional_pm_data,
971980
))

crates/storage_impl/src/mock_db/payment_attempt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ impl PaymentAttemptInterface for MockDb {
157157
charge_id: payment_attempt.charge_id,
158158
client_source: payment_attempt.client_source,
159159
client_version: payment_attempt.client_version,
160+
customer_acceptance: payment_attempt.customer_acceptance,
160161
};
161162
payment_attempts.push(payment_attempt.clone());
162163
Ok(payment_attempt)

crates/storage_impl/src/payments/payment_attempt.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
418418
charge_id: payment_attempt.charge_id.clone(),
419419
client_source: payment_attempt.client_source.clone(),
420420
client_version: payment_attempt.client_version.clone(),
421+
customer_acceptance: payment_attempt.customer_acceptance.clone(),
421422
};
422423

423424
let field = format!("pa_{}", created_attempt.attempt_id);
@@ -1202,6 +1203,7 @@ impl DataModelExt for PaymentAttempt {
12021203
charge_id: self.charge_id,
12031204
client_source: self.client_source,
12041205
client_version: self.client_version,
1206+
customer_acceptance: self.customer_acceptance,
12051207
}
12061208
}
12071209

@@ -1268,6 +1270,7 @@ impl DataModelExt for PaymentAttempt {
12681270
charge_id: storage_model.charge_id,
12691271
client_source: storage_model.client_source,
12701272
client_version: storage_model.client_version,
1273+
customer_acceptance: storage_model.customer_acceptance,
12711274
}
12721275
}
12731276
}
@@ -1339,6 +1342,7 @@ impl DataModelExt for PaymentAttemptNew {
13391342
charge_id: self.charge_id,
13401343
client_source: self.client_source,
13411344
client_version: self.client_version,
1345+
customer_acceptance: self.customer_acceptance,
13421346
}
13431347
}
13441348

@@ -1404,6 +1408,7 @@ impl DataModelExt for PaymentAttemptNew {
14041408
charge_id: storage_model.charge_id,
14051409
client_source: storage_model.client_source,
14061410
client_version: storage_model.client_version,
1411+
customer_acceptance: storage_model.customer_acceptance,
14071412
}
14081413
}
14091414
}
@@ -1528,6 +1533,7 @@ impl DataModelExt for PaymentAttemptUpdate {
15281533
payment_method_billing_address_id,
15291534
client_source,
15301535
client_version,
1536+
customer_acceptance,
15311537
} => DieselPaymentAttemptUpdate::ConfirmUpdate {
15321538
amount: amount.get_amount_as_i64(),
15331539
currency,
@@ -1560,6 +1566,7 @@ impl DataModelExt for PaymentAttemptUpdate {
15601566
payment_method_billing_address_id,
15611567
client_source,
15621568
client_version,
1569+
customer_acceptance,
15631570
},
15641571
Self::VoidUpdate {
15651572
status,
@@ -1863,6 +1870,7 @@ impl DataModelExt for PaymentAttemptUpdate {
18631870
payment_method_billing_address_id,
18641871
client_source,
18651872
client_version,
1873+
customer_acceptance,
18661874
} => Self::ConfirmUpdate {
18671875
amount: MinorUnit::new(amount),
18681876
currency,
@@ -1893,6 +1901,7 @@ impl DataModelExt for PaymentAttemptUpdate {
18931901
payment_method_billing_address_id,
18941902
client_source,
18951903
client_version,
1904+
customer_acceptance,
18961905
},
18971906
DieselPaymentAttemptUpdate::VoidUpdate {
18981907
status,

0 commit comments

Comments
 (0)