feat(connector): [AUTHORIZEDOTNET] Support payment_method_id in recurring mandate payment (#4841)

This commit is contained in:
DEEPANSHU BANSAL
2024-06-03 16:37:37 +05:30
committed by GitHub
parent e5da133fe0
commit a1788b8da9

View File

@ -1,16 +1,15 @@
use common_utils::{ use common_utils::{
errors::CustomResult, errors::CustomResult,
ext_traits::{Encode, ValueExt}, ext_traits::{Encode, ValueExt},
id_type, pii,
}; };
use error_stack::ResultExt; use error_stack::ResultExt;
use masking::{ExposeInterface, PeekInterface, Secret, StrongSecret}; use masking::{ExposeInterface, PeekInterface, Secret, StrongSecret};
use rand::distributions::{Alphanumeric, DistString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
connector::utils::{ connector::utils::{
self, missing_field_err, CardData, PaymentsSyncRequestData, RefundsRequestData, RouterData, self, CardData, PaymentsSyncRequestData, RefundsRequestData, RouterData, WalletData,
WalletData,
}, },
core::errors, core::errors,
services, services,
@ -179,7 +178,7 @@ struct PaymentProfileDetails {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CustomerDetails { pub struct CustomerDetails {
id: id_type::CustomerId, id: String,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -273,10 +272,7 @@ pub struct AuthorizedotnetZeroMandateRequest {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct Profile { struct Profile {
merchant_customer_id: id_type::CustomerId, description: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
email: Option<pii::Email>,
payment_profiles: PaymentProfiles, payment_profiles: PaymentProfiles,
} }
@ -318,12 +314,8 @@ impl TryFrom<&types::SetupMandateRouterData> for CreateCustomerProfileRequest {
create_customer_profile_request: AuthorizedotnetZeroMandateRequest { create_customer_profile_request: AuthorizedotnetZeroMandateRequest {
merchant_authentication, merchant_authentication,
profile: Profile { profile: Profile {
merchant_customer_id: item //The payment ID is included in the description because the connector requires unique description when creating a mandate.
.customer_id description: item.payment_id.clone(),
.clone()
.ok_or_else(missing_field_err("customer_id"))?,
description: item.description.clone(),
email: item.request.email.clone(),
payment_profiles: PaymentProfiles { payment_profiles: PaymentProfiles {
customer_type: CustomerType::Individual, customer_type: CustomerType::Individual,
payment: PaymentDetails::CreditCard(CreditCardDetails { payment: PaymentDetails::CreditCard(CreditCardDetails {
@ -393,16 +385,18 @@ impl<F, T>
response: Ok(types::PaymentsResponseData::TransactionResponse { response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::NoResponseId, resource_id: types::ResponseId::NoResponseId,
redirection_data: None, redirection_data: None,
mandate_reference: item.response.customer_profile_id.map(|mandate_id| { mandate_reference: item.response.customer_profile_id.map(
types::MandateReference { |customer_profile_id| types::MandateReference {
connector_mandate_id: Some(mandate_id), connector_mandate_id: item
payment_method_id: item
.response .response
.customer_payment_profile_id_list .customer_payment_profile_id_list
.first() .first()
.cloned(), .map(|payment_profile_id| {
} format!("{customer_profile_id}-{payment_profile_id}")
}), }),
payment_method_id: None,
},
),
connector_metadata: None, connector_metadata: None,
network_txn_id: None, network_txn_id: None,
connector_response_reference_id: None, connector_response_reference_id: None,
@ -635,27 +629,24 @@ impl
api_models::payments::ConnectorMandateReferenceId, api_models::payments::ConnectorMandateReferenceId,
), ),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let mandate_id = connector_mandate_id
.connector_mandate_id
.ok_or(errors::ConnectorError::MissingConnectorMandateID)?;
Ok(Self { Ok(Self {
transaction_type: TransactionType::try_from(item.router_data.request.capture_method)?, transaction_type: TransactionType::try_from(item.router_data.request.capture_method)?,
amount: item.amount, amount: item.amount,
currency_code: item.router_data.request.currency, currency_code: item.router_data.request.currency,
payment: None, payment: None,
profile: Some(ProfileDetails::CustomerProfileDetails( profile: mandate_id
CustomerProfileDetails { .split_once('-')
customer_profile_id: Secret::from( .map(|(customer_profile_id, payment_profile_id)| {
connector_mandate_id ProfileDetails::CustomerProfileDetails(CustomerProfileDetails {
.connector_mandate_id customer_profile_id: Secret::from(customer_profile_id.to_string()),
.ok_or(errors::ConnectorError::MissingConnectorMandateID)?, payment_profile: PaymentProfileDetails {
), payment_profile_id: Secret::from(payment_profile_id.to_string()),
payment_profile: PaymentProfileDetails { },
payment_profile_id: Secret::from( })
connector_mandate_id }),
.payment_method_id
.ok_or(errors::ConnectorError::MissingConnectorMandateID)?,
),
},
},
)),
order: Order { order: Order {
description: item.router_data.connector_request_reference_id.clone(), description: item.router_data.connector_request_reference_id.clone(),
}, },
@ -709,11 +700,13 @@ impl
create_profile: true, create_profile: true,
})), })),
Some(CustomerDetails { Some(CustomerDetails {
id: item //The payment ID is included in the customer details because the connector requires unique customer information with a length of fewer than 20 characters when creating a mandate.
.router_data //If the length exceeds 20 characters, a random alphanumeric string is used instead.
.customer_id id: if item.router_data.payment_id.len() <= 20 {
.clone() item.router_data.payment_id.clone()
.ok_or_else(missing_field_err("customer_id"))?, } else {
Alphanumeric.sample_string(&mut rand::thread_rng(), 20)
},
}), }),
) )
} else { } else {
@ -1087,6 +1080,24 @@ impl<F, T>
.and_then(|x| x.secure_acceptance_url.to_owned()); .and_then(|x| x.secure_acceptance_url.to_owned());
let redirection_data = let redirection_data =
url.map(|url| services::RedirectForm::from((url, services::Method::Get))); url.map(|url| services::RedirectForm::from((url, services::Method::Get)));
let mandate_reference = item.response.profile_response.map(|profile_response| {
let payment_profile_id = profile_response
.customer_payment_profile_id_list
.and_then(|customer_payment_profile_id_list| {
customer_payment_profile_id_list.first().cloned()
});
types::MandateReference {
connector_mandate_id: profile_response.customer_profile_id.and_then(
|customer_profile_id| {
payment_profile_id.map(|payment_profile_id| {
format!("{customer_profile_id}-{payment_profile_id}")
})
},
),
payment_method_id: None,
}
});
Ok(Self { Ok(Self {
status, status,
response: match error { response: match error {
@ -1096,16 +1107,7 @@ impl<F, T>
transaction_response.transaction_id.clone(), transaction_response.transaction_id.clone(),
), ),
redirection_data, redirection_data,
mandate_reference: item.response.profile_response.map( mandate_reference,
|profile_response| types::MandateReference {
connector_mandate_id: profile_response.customer_profile_id,
payment_method_id: profile_response
.customer_payment_profile_id_list
.and_then(|customer_payment_profile_id_list| {
customer_payment_profile_id_list.first().cloned()
}),
},
),
connector_metadata: metadata, connector_metadata: metadata,
network_txn_id: transaction_response network_txn_id: transaction_response
.network_trans_id .network_trans_id