mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(connector): [BANKOFAMERICA] Implement Apple Pay (#3061)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -4454,6 +4454,93 @@ impl Default for super::settings::RequiredFields {
|
|||||||
non_mandate: HashMap::new(),
|
non_mandate: HashMap::new(),
|
||||||
common: HashMap::new(),
|
common: HashMap::new(),
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
enums::Connector::Bankofamerica,
|
||||||
|
RequiredFieldFinal {
|
||||||
|
mandate: HashMap::new(),
|
||||||
|
non_mandate: HashMap::from(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"email".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "email".to_string(),
|
||||||
|
display_name: "email".to_string(),
|
||||||
|
field_type: enums::FieldType::UserEmailAddress,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.first_name".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.first_name".to_string(),
|
||||||
|
display_name: "billing_first_name".to_string(),
|
||||||
|
field_type: enums::FieldType::UserBillingName,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.last_name".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.last_name".to_string(),
|
||||||
|
display_name: "billing_last_name".to_string(),
|
||||||
|
field_type: enums::FieldType::UserBillingName,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.city".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.city".to_string(),
|
||||||
|
display_name: "city".to_string(),
|
||||||
|
field_type: enums::FieldType::UserAddressCity,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.state".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.state".to_string(),
|
||||||
|
display_name: "state".to_string(),
|
||||||
|
field_type: enums::FieldType::UserAddressState,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.zip".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.zip".to_string(),
|
||||||
|
display_name: "zip".to_string(),
|
||||||
|
field_type: enums::FieldType::UserAddressPincode,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.country".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.country".to_string(),
|
||||||
|
display_name: "country".to_string(),
|
||||||
|
field_type: enums::FieldType::UserAddressCountry{
|
||||||
|
options: vec![
|
||||||
|
"ALL".to_string(),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing.address.line1".to_string(),
|
||||||
|
RequiredFieldInfo {
|
||||||
|
required_field: "billing.address.line1".to_string(),
|
||||||
|
display_name: "line1".to_string(),
|
||||||
|
field_type: enums::FieldType::UserAddressLine1,
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
common: HashMap::new(),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
@ -4556,7 +4643,7 @@ impl Default for super::settings::RequiredFields {
|
|||||||
),
|
),
|
||||||
common: HashMap::new(),
|
common: HashMap::new(),
|
||||||
}
|
}
|
||||||
),
|
)
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connector::utils::{
|
connector::utils::{
|
||||||
self, AddressDetailsData, CardData, CardIssuer, PaymentsAuthorizeRequestData,
|
self, AddressDetailsData, ApplePayDecrypt, CardData, CardIssuer,
|
||||||
PaymentsSyncRequestData, RouterData,
|
PaymentsAuthorizeRequestData, PaymentsSyncRequestData, RouterData,
|
||||||
},
|
},
|
||||||
consts,
|
consts,
|
||||||
core::errors,
|
core::errors,
|
||||||
@ -16,6 +16,7 @@ use crate::{
|
|||||||
api::{self, enums as api_enums},
|
api::{self, enums as api_enums},
|
||||||
storage::enums,
|
storage::enums,
|
||||||
transformers::ForeignFrom,
|
transformers::ForeignFrom,
|
||||||
|
ApplePayPredecryptData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,11 +111,18 @@ pub struct GooglePayPaymentInformation {
|
|||||||
fluid_data: FluidData,
|
fluid_data: FluidData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ApplePayPaymentInformation {
|
||||||
|
tokenized_card: TokenizedCard,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum PaymentInformation {
|
pub enum PaymentInformation {
|
||||||
Cards(CardPaymentInformation),
|
Cards(CardPaymentInformation),
|
||||||
GooglePay(GooglePayPaymentInformation),
|
GooglePay(GooglePayPaymentInformation),
|
||||||
|
ApplePay(ApplePayPaymentInformation),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
@ -128,6 +136,16 @@ pub struct Card {
|
|||||||
card_type: Option<String>,
|
card_type: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TokenizedCard {
|
||||||
|
number: Secret<String>,
|
||||||
|
expiration_month: Secret<String>,
|
||||||
|
expiration_year: Secret<String>,
|
||||||
|
cryptogram: Secret<String>,
|
||||||
|
transaction_type: TransactionType,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct FluidData {
|
pub struct FluidData {
|
||||||
@ -215,6 +233,12 @@ impl From<PaymentSolution> for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub enum TransactionType {
|
||||||
|
#[serde(rename = "1")]
|
||||||
|
ApplePay,
|
||||||
|
}
|
||||||
|
|
||||||
impl
|
impl
|
||||||
From<(
|
From<(
|
||||||
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||||
@ -320,6 +344,48 @@ impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl
|
||||||
|
TryFrom<(
|
||||||
|
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||||
|
Box<ApplePayPredecryptData>,
|
||||||
|
)> for BankOfAmericaPaymentsRequest
|
||||||
|
{
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(
|
||||||
|
(item, apple_pay_data): (
|
||||||
|
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||||
|
Box<ApplePayPredecryptData>,
|
||||||
|
),
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
let email = item.router_data.request.get_email()?;
|
||||||
|
let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
|
||||||
|
let order_information = OrderInformationWithBill::from((item, bill_to));
|
||||||
|
let processing_information =
|
||||||
|
ProcessingInformation::from((item, Some(PaymentSolution::ApplePay)));
|
||||||
|
let client_reference_information = ClientReferenceInformation::from(item);
|
||||||
|
|
||||||
|
let expiration_month = apple_pay_data.get_expiry_month()?;
|
||||||
|
let expiration_year = apple_pay_data.get_four_digit_expiry_year()?;
|
||||||
|
|
||||||
|
let payment_information = PaymentInformation::ApplePay(ApplePayPaymentInformation {
|
||||||
|
tokenized_card: TokenizedCard {
|
||||||
|
number: apple_pay_data.application_primary_account_number,
|
||||||
|
cryptogram: apple_pay_data.payment_data.online_payment_cryptogram,
|
||||||
|
transaction_type: TransactionType::ApplePay,
|
||||||
|
expiration_year,
|
||||||
|
expiration_month,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
processing_information,
|
||||||
|
payment_information,
|
||||||
|
order_information,
|
||||||
|
client_reference_information,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl
|
impl
|
||||||
TryFrom<(
|
TryFrom<(
|
||||||
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||||
@ -368,6 +434,17 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
|
|||||||
match item.router_data.request.payment_method_data.clone() {
|
match item.router_data.request.payment_method_data.clone() {
|
||||||
payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
|
payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
|
||||||
payments::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
|
payments::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
|
||||||
|
payments::WalletData::ApplePay(_) => {
|
||||||
|
let payment_method_token = item.router_data.get_payment_method_token()?;
|
||||||
|
match payment_method_token {
|
||||||
|
types::PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
|
||||||
|
Self::try_from((item, decrypt_data))
|
||||||
|
}
|
||||||
|
types::PaymentMethodToken::Token(_) => {
|
||||||
|
Err(errors::ConnectorError::InvalidWalletToken)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
payments::WalletData::GooglePay(google_pay_data) => {
|
payments::WalletData::GooglePay(google_pay_data) => {
|
||||||
Self::try_from((item, google_pay_data))
|
Self::try_from((item, google_pay_data))
|
||||||
}
|
}
|
||||||
@ -378,7 +455,6 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
|
|||||||
| payments::WalletData::KakaoPayRedirect(_)
|
| payments::WalletData::KakaoPayRedirect(_)
|
||||||
| payments::WalletData::GoPayRedirect(_)
|
| payments::WalletData::GoPayRedirect(_)
|
||||||
| payments::WalletData::GcashRedirect(_)
|
| payments::WalletData::GcashRedirect(_)
|
||||||
| payments::WalletData::ApplePay(_)
|
|
||||||
| payments::WalletData::ApplePayRedirect(_)
|
| payments::WalletData::ApplePayRedirect(_)
|
||||||
| payments::WalletData::ApplePayThirdPartySdk(_)
|
| payments::WalletData::ApplePayThirdPartySdk(_)
|
||||||
| payments::WalletData::DanaRedirect {}
|
| payments::WalletData::DanaRedirect {}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt};
|
use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt};
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::{ExposeInterface, PeekInterface, Secret};
|
use masking::{ExposeInterface, Secret};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connector::utils::{self, PaymentsCaptureRequestData, RouterData, WalletData},
|
connector::utils::{self, ApplePayDecrypt, PaymentsCaptureRequestData, RouterData, WalletData},
|
||||||
consts,
|
consts,
|
||||||
core::errors,
|
core::errors,
|
||||||
services,
|
services,
|
||||||
@ -304,24 +304,8 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
types::PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
|
types::PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
|
||||||
let expiry_year_4_digit = Secret::new(format!(
|
let exp_month = decrypt_data.get_expiry_month()?;
|
||||||
"20{}",
|
let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year()?;
|
||||||
decrypt_data
|
|
||||||
.clone()
|
|
||||||
.application_expiration_date
|
|
||||||
.peek()
|
|
||||||
.get(0..2)
|
|
||||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
|
||||||
));
|
|
||||||
let exp_month = Secret::new(
|
|
||||||
decrypt_data
|
|
||||||
.clone()
|
|
||||||
.application_expiration_date
|
|
||||||
.peek()
|
|
||||||
.get(2..4)
|
|
||||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
|
||||||
.to_owned(),
|
|
||||||
);
|
|
||||||
Ok(PaymentSource::ApplePayPredecrypt(Box::new(
|
Ok(PaymentSource::ApplePayPredecrypt(Box::new(
|
||||||
ApplePayPredecrypt {
|
ApplePayPredecrypt {
|
||||||
token: decrypt_data.application_primary_account_number,
|
token: decrypt_data.application_primary_account_number,
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use common_utils::{
|
|||||||
};
|
};
|
||||||
use data_models::mandates::AcceptanceType;
|
use data_models::mandates::AcceptanceType;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::{ExposeInterface, ExposeOptionInterface, PeekInterface, Secret};
|
use masking::{ExposeInterface, ExposeOptionInterface, Secret};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -16,7 +16,9 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
collect_missing_value_keys,
|
collect_missing_value_keys,
|
||||||
connector::utils::{self as connector_util, ApplePay, PaymentsPreProcessingData, RouterData},
|
connector::utils::{
|
||||||
|
self as connector_util, ApplePay, ApplePayDecrypt, PaymentsPreProcessingData, RouterData,
|
||||||
|
},
|
||||||
core::errors,
|
core::errors,
|
||||||
services,
|
services,
|
||||||
types::{
|
types::{
|
||||||
@ -1473,24 +1475,8 @@ impl TryFrom<(&payments::WalletData, Option<types::PaymentMethodToken>)>
|
|||||||
if let Some(types::PaymentMethodToken::ApplePayDecrypt(decrypt_data)) =
|
if let Some(types::PaymentMethodToken::ApplePayDecrypt(decrypt_data)) =
|
||||||
payment_method_token
|
payment_method_token
|
||||||
{
|
{
|
||||||
let expiry_year_4_digit = Secret::new(format!(
|
let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year()?;
|
||||||
"20{}",
|
let exp_month = decrypt_data.get_expiry_month()?;
|
||||||
decrypt_data
|
|
||||||
.clone()
|
|
||||||
.application_expiration_date
|
|
||||||
.peek()
|
|
||||||
.get(0..2)
|
|
||||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
|
||||||
));
|
|
||||||
let exp_month = Secret::new(
|
|
||||||
decrypt_data
|
|
||||||
.clone()
|
|
||||||
.application_expiration_date
|
|
||||||
.peek()
|
|
||||||
.get(2..4)
|
|
||||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
|
||||||
.to_owned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(Self::Wallet(StripeWallet::ApplePayPredecryptToken(
|
Some(Self::Wallet(StripeWallet::ApplePayPredecryptToken(
|
||||||
Box::new(StripeApplePayPredecrypt {
|
Box::new(StripeApplePayPredecrypt {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ use crate::{
|
|||||||
pii::PeekInterface,
|
pii::PeekInterface,
|
||||||
types::{
|
types::{
|
||||||
self, api, storage::payment_attempt::PaymentAttemptExt, transformers::ForeignTryFrom,
|
self, api, storage::payment_attempt::PaymentAttemptExt, transformers::ForeignTryFrom,
|
||||||
PaymentsCancelData, ResponseId,
|
ApplePayPredecryptData, PaymentsCancelData, ResponseId,
|
||||||
},
|
},
|
||||||
utils::{OptionExt, ValueExt},
|
utils::{OptionExt, ValueExt},
|
||||||
};
|
};
|
||||||
@ -855,6 +855,33 @@ impl ApplePay for payments::ApplePayWalletData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ApplePayDecrypt {
|
||||||
|
fn get_expiry_month(&self) -> Result<Secret<String>, Error>;
|
||||||
|
fn get_four_digit_expiry_year(&self) -> Result<Secret<String>, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplePayDecrypt for Box<ApplePayPredecryptData> {
|
||||||
|
fn get_four_digit_expiry_year(&self) -> Result<Secret<String>, Error> {
|
||||||
|
Ok(Secret::new(format!(
|
||||||
|
"20{}",
|
||||||
|
self.application_expiration_date
|
||||||
|
.peek()
|
||||||
|
.get(0..2)
|
||||||
|
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_expiry_month(&self) -> Result<Secret<String>, Error> {
|
||||||
|
Ok(Secret::new(
|
||||||
|
self.application_expiration_date
|
||||||
|
.peek()
|
||||||
|
.get(2..4)
|
||||||
|
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
||||||
|
.to_owned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait CryptoData {
|
pub trait CryptoData {
|
||||||
fn get_pay_currency(&self) -> Result<String, Error>;
|
fn get_pay_currency(&self) -> Result<String, Error>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user