Skip to content

Commit

Permalink
feat(connector): [BANKOFAMERICA] Implement Apple Pay (#3061)
Browse files Browse the repository at this point in the history
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
deepanshu-iiitu and hyperswitch-bot[bot] committed Dec 20, 2023
1 parent fe1e1af commit bfbcbad
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 45 deletions.
89 changes: 88 additions & 1 deletion crates/router/src/configs/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4190,6 +4190,93 @@ impl Default for super::settings::RequiredFields {
non_mandate: 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(),
}
)
]),
},
Expand Down Expand Up @@ -4292,7 +4379,7 @@ impl Default for super::settings::RequiredFields {
),
common: HashMap::new(),
}
),
)
]),
},
),
Expand Down
82 changes: 79 additions & 3 deletions crates/router/src/connector/bankofamerica/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};

use crate::{
connector::utils::{
self, AddressDetailsData, CardData, CardIssuer, PaymentsAuthorizeRequestData,
PaymentsSyncRequestData, RouterData,
self, AddressDetailsData, ApplePayDecrypt, CardData, CardIssuer,
PaymentsAuthorizeRequestData, PaymentsSyncRequestData, RouterData,
},
consts,
core::errors,
Expand All @@ -16,6 +16,7 @@ use crate::{
api::{self, enums as api_enums},
storage::enums,
transformers::ForeignFrom,
ApplePayPredecryptData,
},
};

Expand Down Expand Up @@ -110,11 +111,18 @@ pub struct GooglePayPaymentInformation {
fluid_data: FluidData,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayPaymentInformation {
tokenized_card: TokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum PaymentInformation {
Cards(CardPaymentInformation),
GooglePay(GooglePayPaymentInformation),
ApplePay(ApplePayPaymentInformation),
}

#[derive(Debug, Serialize)]
Expand All @@ -128,6 +136,16 @@ pub struct Card {
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)]
#[serde(rename_all = "camelCase")]
pub struct FluidData {
Expand Down Expand Up @@ -215,6 +233,12 @@ impl From<PaymentSolution> for String {
}
}

#[derive(Debug, Serialize)]
pub enum TransactionType {
#[serde(rename = "1")]
ApplePay,
}

impl
From<(
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
Expand Down Expand Up @@ -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
TryFrom<(
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
Expand Down Expand Up @@ -368,6 +434,17 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
match item.router_data.request.payment_method_data.clone() {
payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
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) => {
Self::try_from((item, google_pay_data))
}
Expand All @@ -378,7 +455,6 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
| payments::WalletData::KakaoPayRedirect(_)
| payments::WalletData::GoPayRedirect(_)
| payments::WalletData::GcashRedirect(_)
| payments::WalletData::ApplePay(_)
| payments::WalletData::ApplePayRedirect(_)
| payments::WalletData::ApplePayThirdPartySdk(_)
| payments::WalletData::DanaRedirect {}
Expand Down
24 changes: 4 additions & 20 deletions crates/router/src/connector/checkout/transformers.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt};
use error_stack::{IntoReport, ResultExt};
use masking::{ExposeInterface, PeekInterface, Secret};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;

use crate::{
connector::utils::{self, PaymentsCaptureRequestData, RouterData, WalletData},
connector::utils::{self, ApplePayDecrypt, PaymentsCaptureRequestData, RouterData, WalletData},
consts,
core::errors,
services,
Expand Down Expand Up @@ -303,24 +303,8 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme
}))
}
types::PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
let expiry_year_4_digit = Secret::new(format!(
"20{}",
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(),
);
let exp_month = decrypt_data.get_expiry_month()?;
let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year()?;
Ok(PaymentSource::ApplePayPredecrypt(Box::new(
ApplePayPredecrypt {
token: decrypt_data.application_primary_account_number,
Expand Down
26 changes: 6 additions & 20 deletions crates/router/src/connector/stripe/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ use common_utils::{
};
use data_models::mandates::AcceptanceType;
use error_stack::{IntoReport, ResultExt};
use masking::{ExposeInterface, ExposeOptionInterface, PeekInterface, Secret};
use masking::{ExposeInterface, ExposeOptionInterface, Secret};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;
use uuid::Uuid;

use crate::{
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,
services,
types::{
Expand Down Expand Up @@ -1473,24 +1475,8 @@ impl TryFrom<(&payments::WalletData, Option<types::PaymentMethodToken>)>
if let Some(types::PaymentMethodToken::ApplePayDecrypt(decrypt_data)) =
payment_method_token
{
let expiry_year_4_digit = Secret::new(format!(
"20{}",
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(),
);
let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year()?;
let exp_month = decrypt_data.get_expiry_month()?;

Some(Self::Wallet(StripeWallet::ApplePayPredecryptToken(
Box::new(StripeApplePayPredecrypt {
Expand Down
29 changes: 28 additions & 1 deletion crates/router/src/connector/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
pii::PeekInterface,
types::{
self, api, storage::payment_attempt::PaymentAttemptExt, transformers::ForeignTryFrom,
PaymentsCancelData, ResponseId,
ApplePayPredecryptData, PaymentsCancelData, ResponseId,
},
utils::{OptionExt, ValueExt},
};
Expand Down Expand Up @@ -848,6 +848,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 {
fn get_pay_currency(&self) -> Result<String, Error>;
}
Expand Down

0 comments on commit bfbcbad

Please sign in to comment.