mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +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(), | ||||
|                                         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(), | ||||
|                                     } | ||||
|                                 ), | ||||
|                                 ) | ||||
|                                ]), | ||||
|                         }, | ||||
|                     ), | ||||
|  | ||||
| @ -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, | ||||
| @ -16,6 +16,7 @@ use crate::{ | ||||
|         api::{self, enums as api_enums}, | ||||
|         storage::enums, | ||||
|         transformers::ForeignFrom, | ||||
|         ApplePayPredecryptData, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| @ -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)] | ||||
| @ -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 { | ||||
| @ -215,6 +233,12 @@ impl From<PaymentSolution> for String { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Serialize)] | ||||
| pub enum TransactionType { | ||||
|     #[serde(rename = "1")] | ||||
|     ApplePay, | ||||
| } | ||||
|  | ||||
| impl | ||||
|     From<( | ||||
|         &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 | ||||
|     TryFrom<( | ||||
|         &BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>, | ||||
| @ -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)) | ||||
|                 } | ||||
| @ -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 {} | ||||
|  | ||||
| @ -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, | ||||
| @ -304,24 +304,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, | ||||
|  | ||||
| @ -8,7 +8,7 @@ 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; | ||||
| @ -16,7 +16,9 @@ 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::{ | ||||
| @ -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 { | ||||
|  | ||||
| @ -28,7 +28,7 @@ use crate::{ | ||||
|     pii::PeekInterface, | ||||
|     types::{ | ||||
|         self, api, storage::payment_attempt::PaymentAttemptExt, transformers::ForeignTryFrom, | ||||
|         PaymentsCancelData, ResponseId, | ||||
|         ApplePayPredecryptData, PaymentsCancelData, ResponseId, | ||||
|     }, | ||||
|     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 { | ||||
|     fn get_pay_currency(&self) -> Result<String, Error>; | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 DEEPANSHU BANSAL
					DEEPANSHU BANSAL