Skip to content

Commit a1fd36a

Browse files
authored
feat(core): store customer_acceptance in the payment_methods table (#3885)
1 parent 289b20a commit a1fd36a

File tree

83 files changed

+574
-126
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+574
-126
lines changed

crates/api_models/src/payments.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,12 @@ pub struct PaymentsRequest {
348348
#[remove_in(PaymentsUpdateRequest, PaymentsCreateRequest)]
349349
pub client_secret: Option<String>,
350350

351-
/// Passing this object during payments creates a mandate. The mandate_type sub object is passed by the server usually and the customer_acceptance sub object is usually passed by the SDK or client
351+
/// Passing this object during payments creates a mandate. The mandate_type sub object is passed by the server.
352352
pub mandate_data: Option<MandateData>,
353353

354+
/// Passing this object during payments confirm . The customer_acceptance sub object is usually passed by the SDK or client
355+
pub customer_acceptance: Option<CustomerAcceptance>,
356+
354357
/// A unique identifier to link the payment to a mandate. To do Recurring payments after a mandate has been created, pass the mandate_id instead of payment_method_data
355358
#[schema(max_length = 255, example = "mandate_iwer89rnjef349dni3")]
356359
#[remove_in(PaymentsUpdateRequest)]

crates/common_enums/src/enums.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,8 +1145,8 @@ pub enum IntentStatus {
11451145
#[serde(rename_all = "snake_case")]
11461146
#[strum(serialize_all = "snake_case")]
11471147
pub enum FutureUsage {
1148-
#[default]
11491148
OffSession,
1149+
#[default]
11501150
OnSession,
11511151
}
11521152

crates/data_models/src/mandates.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ impl From<ApiCustomerAcceptance> for CustomerAcceptance {
113113
}
114114
}
115115

116+
impl From<CustomerAcceptance> for ApiCustomerAcceptance {
117+
fn from(value: CustomerAcceptance) -> Self {
118+
Self {
119+
acceptance_type: value.acceptance_type.into(),
120+
accepted_at: value.accepted_at,
121+
online: value.online.map(|d| d.into()),
122+
}
123+
}
124+
}
125+
116126
impl From<ApiAcceptanceType> for AcceptanceType {
117127
fn from(value: ApiAcceptanceType) -> Self {
118128
match value {
@@ -121,6 +131,14 @@ impl From<ApiAcceptanceType> for AcceptanceType {
121131
}
122132
}
123133
}
134+
impl From<AcceptanceType> for ApiAcceptanceType {
135+
fn from(value: AcceptanceType) -> Self {
136+
match value {
137+
AcceptanceType::Online => Self::Online,
138+
AcceptanceType::Offline => Self::Offline,
139+
}
140+
}
141+
}
124142

125143
impl From<ApiOnlineMandate> for OnlineMandate {
126144
fn from(value: ApiOnlineMandate) -> Self {
@@ -130,6 +148,14 @@ impl From<ApiOnlineMandate> for OnlineMandate {
130148
}
131149
}
132150
}
151+
impl From<OnlineMandate> for ApiOnlineMandate {
152+
fn from(value: OnlineMandate) -> Self {
153+
Self {
154+
ip_address: value.ip_address,
155+
user_agent: value.user_agent,
156+
}
157+
}
158+
}
133159

134160
impl CustomerAcceptance {
135161
pub fn get_ip_address(&self) -> Option<String> {

crates/router/src/core/mandate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,4 +495,5 @@ pub trait MandateBehaviour {
495495
fn set_mandate_id(&mut self, new_mandate_id: Option<api_models::payments::MandateIds>);
496496
fn get_payment_method_data(&self) -> api_models::payments::PaymentMethodData;
497497
fn get_setup_mandate_details(&self) -> Option<&data_models::mandates::MandateData>;
498+
fn get_customer_acceptance(&self) -> Option<api_models::payments::CustomerAcceptance>;
498499
}

crates/router/src/core/payment_methods/cards.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub async fn create_payment_method(
8181
locker_id: Option<String>,
8282
merchant_id: &str,
8383
pm_metadata: Option<serde_json::Value>,
84+
customer_acceptance: Option<serde_json::Value>,
8485
payment_method_data: Option<Encryption>,
8586
key_store: &domain::MerchantKeyStore,
8687
) -> errors::CustomResult<storage::PaymentMethod, errors::ApiErrorResponse> {
@@ -100,6 +101,7 @@ pub async fn create_payment_method(
100101
scheme: req.card_network.clone(),
101102
metadata: pm_metadata.map(masking::Secret::new),
102103
payment_method_data,
104+
customer_acceptance: customer_acceptance.map(masking::Secret::new),
103105
..storage::PaymentMethodNew::default()
104106
})
105107
.await
@@ -183,6 +185,7 @@ pub async fn get_or_insert_payment_method(
183185
&merchant_account.merchant_id,
184186
customer_id,
185187
resp.metadata.clone().map(|val| val.expose()),
188+
None,
186189
locker_id,
187190
)
188191
.await
@@ -367,6 +370,7 @@ pub async fn add_payment_method(
367370
merchant_id,
368371
&customer_id,
369372
pm_metadata.cloned(),
373+
None,
370374
locker_id,
371375
)
372376
.await?;
@@ -385,6 +389,7 @@ pub async fn insert_payment_method(
385389
merchant_id: &str,
386390
customer_id: &str,
387391
pm_metadata: Option<serde_json::Value>,
392+
customer_acceptance: Option<serde_json::Value>,
388393
locker_id: Option<String>,
389394
) -> errors::RouterResult<diesel_models::PaymentMethod> {
390395
let pm_card_details = resp
@@ -400,6 +405,7 @@ pub async fn insert_payment_method(
400405
locker_id,
401406
merchant_id,
402407
pm_metadata,
408+
customer_acceptance,
403409
pm_data_encrypted,
404410
key_store,
405411
)

crates/router/src/core/payments.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoI
1515

1616
use api_models::{self, enums, payments::HeaderPayload};
1717
use common_utils::{ext_traits::AsyncExt, pii, types::Surcharge};
18-
use data_models::mandates::MandateData;
18+
use data_models::mandates::{CustomerAcceptance, MandateData};
1919
use diesel_models::{ephemeral_key, fraud_check::FraudCheck};
2020
use error_stack::{IntoReport, ResultExt};
2121
use futures::future::join_all;
@@ -2050,6 +2050,7 @@ where
20502050
pub mandate_connector: Option<MandateConnectorDetails>,
20512051
pub currency: storage_enums::Currency,
20522052
pub setup_mandate: Option<MandateData>,
2053+
pub customer_acceptance: Option<CustomerAcceptance>,
20532054
pub address: PaymentAddress,
20542055
pub token: Option<String>,
20552056
pub confirm: Option<bool>,

crates/router/src/core/payments/flows/authorize_flow.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
9999
merchant_account,
100100
self.request.payment_method_type,
101101
key_store,
102-
is_mandate,
103102
))
104103
.await?;
105104
Ok(mandate::mandate_procedure(
@@ -131,7 +130,6 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
131130
&merchant_account,
132131
self.request.payment_method_type,
133132
&key_store,
134-
is_mandate,
135133
))
136134
.await;
137135

@@ -294,6 +292,9 @@ impl mandate::MandateBehaviour for types::PaymentsAuthorizeData {
294292
fn set_mandate_id(&mut self, new_mandate_id: Option<api_models::payments::MandateIds>) {
295293
self.mandate_id = new_mandate_id;
296294
}
295+
fn get_customer_acceptance(&self) -> Option<api_models::payments::CustomerAcceptance> {
296+
self.customer_acceptance.clone().map(From::from)
297+
}
297298
}
298299

299300
pub async fn authorize_preprocessing_steps<F: Clone>(

crates/router/src/core/payments/flows/setup_mandate_flow.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
9898
)
9999
.await
100100
.to_setup_mandate_failed_response()?;
101-
let is_mandate = resp.request.setup_mandate_details.is_some();
101+
102102
let pm_id = Box::pin(tokenization::save_payment_method(
103103
state,
104104
connector,
@@ -107,7 +107,6 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
107107
merchant_account,
108108
self.request.payment_method_type,
109109
key_store,
110-
is_mandate,
111110
))
112111
.await?;
113112
mandate::mandate_procedure(
@@ -234,8 +233,6 @@ impl types::SetupMandateRouterData {
234233

235234
let payment_method_type = self.request.payment_method_type;
236235

237-
let is_mandate = resp.request.setup_mandate_details.is_some();
238-
239236
let pm_id = Box::pin(tokenization::save_payment_method(
240237
state,
241238
connector,
@@ -244,7 +241,6 @@ impl types::SetupMandateRouterData {
244241
merchant_account,
245242
payment_method_type,
246243
key_store,
247-
is_mandate,
248244
))
249245
.await?;
250246

@@ -320,7 +316,6 @@ impl types::SetupMandateRouterData {
320316
)
321317
.await
322318
.to_setup_mandate_failed_response()?;
323-
let is_mandate = resp.request.setup_mandate_details.is_some();
324319
let pm_id = Box::pin(tokenization::save_payment_method(
325320
state,
326321
connector,
@@ -329,7 +324,6 @@ impl types::SetupMandateRouterData {
329324
merchant_account,
330325
self.request.payment_method_type,
331326
key_store,
332-
is_mandate,
333327
))
334328
.await?;
335329
let mandate = state
@@ -430,6 +424,9 @@ impl mandate::MandateBehaviour for types::SetupMandateRequestData {
430424
fn get_setup_mandate_details(&self) -> Option<&data_models::mandates::MandateData> {
431425
self.setup_mandate_details.as_ref()
432426
}
427+
fn get_customer_acceptance(&self) -> Option<api_models::payments::CustomerAcceptance> {
428+
self.customer_acceptance.clone().map(From::from)
429+
}
433430
}
434431

435432
impl TryFrom<types::SetupMandateRequestData> for types::PaymentMethodTokenizationData {

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -825,16 +825,6 @@ fn validate_new_mandate_request(
825825
.clone()
826826
.get_required_value("mandate_data")?;
827827

828-
if api_enums::FutureUsage::OnSession
829-
== req
830-
.setup_future_usage
831-
.get_required_value("setup_future_usage")?
832-
{
833-
Err(report!(errors::ApiErrorResponse::PreconditionFailed {
834-
message: "`setup_future_usage` must be `off_session` for mandates".into()
835-
}))?
836-
};
837-
838828
// Only use this validation if the customer_acceptance is present
839829
if mandate_data
840830
.customer_acceptance
@@ -3862,3 +3852,17 @@ pub fn validate_session_expiry(session_expiry: u32) -> Result<(), errors::ApiErr
38623852
Ok(())
38633853
}
38643854
}
3855+
3856+
// This function validates the mandate_data with its setup_future_usage
3857+
pub fn validate_mandate_data_and_future_usage(
3858+
setup_future_usages: Option<api_enums::FutureUsage>,
3859+
mandate_details_present: bool,
3860+
) -> Result<(), errors::ApiErrorResponse> {
3861+
if Some(api_enums::FutureUsage::OnSession) == setup_future_usages && mandate_details_present {
3862+
Err(errors::ApiErrorResponse::PreconditionFailed {
3863+
message: "`setup_future_usage` must be `off_session` for mandates".into(),
3864+
})
3865+
} else {
3866+
Ok(())
3867+
}
3868+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
147147
mandate_id: None,
148148
mandate_connector: None,
149149
setup_mandate: None,
150+
customer_acceptance: None,
150151
token: None,
151152
address: PaymentAddress {
152153
shipping: shipping_address.as_ref().map(|a| a.into()),

0 commit comments

Comments
 (0)