From 55ffca48ad1220a2cf5b6f28e0b73aa0a409083c Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Thu, 10 Oct 2024 13:28:51 +0530 Subject: [PATCH] feat(connector): add dynamic duitnow qr code, google pay and applpepay for fiuu (#6284) Co-authored-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Co-authored-by: Chikke Srujan Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- .../connector_configs/toml/development.toml | 76 ++- crates/connector_configs/toml/production.toml | 76 ++- crates/connector_configs/toml/sandbox.toml | 76 ++- .../src/connectors/fiuu.rs | 20 +- .../src/connectors/fiuu/transformers.rs | 527 +++++++++++++----- crates/hyperswitch_connectors/src/utils.rs | 27 +- 6 files changed, 634 insertions(+), 168 deletions(-) diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 2fefc5c957c1..48c7e785a9d3 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -4106,9 +4106,83 @@ api_secret="Shared Secret" payment_method_type = "UnionPay" [[fiuu.real_time_payment]] payment_method_type = "duit_now" +[[fiuu.wallet]] + payment_method_type = "google_pay" +[[fiuu.wallet]] + payment_method_type = "apple_pay" [[fiuu.bank_redirect]] payment_method_type = "online_banking_fpx" [fiuu.connector_auth.SignatureKey] api_key="Verify Key" key1="Merchant ID" -api_secret="Secret Key" \ No newline at end of file +api_secret="Secret Key" +[[fiuu.metadata.google_pay]] +name="merchant_name" +label="Google Pay Merchant Name" +placeholder="Enter Google Pay Merchant Name" +required=true +type="Text" +[[fiuu.metadata.google_pay]] +name="merchant_id" +label="Google Pay Merchant Id" +placeholder="Enter Google Pay Merchant Id" +required=true +type="Text" +[[fiuu.metadata.google_pay]] +name="gateway_merchant_id" +label="Google Pay Merchant Key" +placeholder="Enter Google Pay Merchant Key" +required=true +type="Text" + +[[fiuu.metadata.apple_pay]] +name="certificate" +label="Merchant Certificate (Base64 Encoded)" +placeholder="Enter Merchant Certificate (Base64 Encoded)" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="certificate_keys" +label="Merchant PrivateKey (Base64 Encoded)" +placeholder="Enter Merchant PrivateKey (Base64 Encoded)" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="merchant_identifier" +label="Apple Merchant Identifier" +placeholder="Enter Apple Merchant Identifier" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="display_name" +label="Display Name" +placeholder="Enter Display Name" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="initiative" +label="Domain" +placeholder="Enter Domain" +required=true +type="Select" +options=["web","ios"] +[[fiuu.metadata.apple_pay]] +name="initiative_context" +label="Domain Name" +placeholder="Enter Domain Name" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="merchant_business_country" +label="Merchant Business Country" +placeholder="Enter Merchant Business Country" +required=true +type="Select" +options=[] +[[fiuu.metadata.apple_pay]] +name="payment_processing_details_at" +label="Payment Processing Details At" +placeholder="Enter Payment Processing Details At" +required=true +type="Radio" +options=["Connector","Hyperswitch"] \ No newline at end of file diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 660dd4fea2b7..373e65f91911 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -3103,9 +3103,83 @@ api_secret="Shared Secret" payment_method_type = "UnionPay" [[fiuu.real_time_payment]] payment_method_type = "duit_now" +[[fiuu.wallet]] + payment_method_type = "google_pay" +[[fiuu.wallet]] + payment_method_type = "apple_pay" [[fiuu.bank_redirect]] payment_method_type = "online_banking_fpx" [fiuu.connector_auth.SignatureKey] api_key="Verify Key" key1="Merchant ID" -api_secret="Secret Key" \ No newline at end of file +api_secret="Secret Key" +[[fiuu.metadata.google_pay]] +name="merchant_name" +label="Google Pay Merchant Name" +placeholder="Enter Google Pay Merchant Name" +required=true +type="Text" +[[fiuu.metadata.google_pay]] +name="merchant_id" +label="Google Pay Merchant Id" +placeholder="Enter Google Pay Merchant Id" +required=true +type="Text" +[[fiuu.metadata.google_pay]] +name="gateway_merchant_id" +label="Google Pay Merchant Key" +placeholder="Enter Google Pay Merchant Key" +required=true +type="Text" + +[[fiuu.metadata.apple_pay]] +name="certificate" +label="Merchant Certificate (Base64 Encoded)" +placeholder="Enter Merchant Certificate (Base64 Encoded)" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="certificate_keys" +label="Merchant PrivateKey (Base64 Encoded)" +placeholder="Enter Merchant PrivateKey (Base64 Encoded)" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="merchant_identifier" +label="Apple Merchant Identifier" +placeholder="Enter Apple Merchant Identifier" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="display_name" +label="Display Name" +placeholder="Enter Display Name" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="initiative" +label="Domain" +placeholder="Enter Domain" +required=true +type="Select" +options=["web","ios"] +[[fiuu.metadata.apple_pay]] +name="initiative_context" +label="Domain Name" +placeholder="Enter Domain Name" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="merchant_business_country" +label="Merchant Business Country" +placeholder="Enter Merchant Business Country" +required=true +type="Select" +options=[] +[[fiuu.metadata.apple_pay]] +name="payment_processing_details_at" +label="Payment Processing Details At" +placeholder="Enter Payment Processing Details At" +required=true +type="Radio" +options=["Connector","Hyperswitch"] \ No newline at end of file diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 2e27ff2bd196..72f95e60fa25 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -4100,9 +4100,83 @@ api_secret="Shared Secret" payment_method_type = "UnionPay" [[fiuu.real_time_payment]] payment_method_type = "duit_now" +[[fiuu.wallet]] + payment_method_type = "google_pay" +[[fiuu.wallet]] + payment_method_type = "apple_pay" [[fiuu.bank_redirect]] payment_method_type = "online_banking_fpx" [fiuu.connector_auth.SignatureKey] api_key="Verify Key" key1="Merchant ID" -api_secret="Secret Key" \ No newline at end of file +api_secret="Secret Key" +[[fiuu.metadata.google_pay]] +name="merchant_name" +label="Google Pay Merchant Name" +placeholder="Enter Google Pay Merchant Name" +required=true +type="Text" +[[fiuu.metadata.google_pay]] +name="merchant_id" +label="Google Pay Merchant Id" +placeholder="Enter Google Pay Merchant Id" +required=true +type="Text" +[[fiuu.metadata.google_pay]] +name="gateway_merchant_id" +label="Google Pay Merchant Key" +placeholder="Enter Google Pay Merchant Key" +required=true +type="Text" + +[[fiuu.metadata.apple_pay]] +name="certificate" +label="Merchant Certificate (Base64 Encoded)" +placeholder="Enter Merchant Certificate (Base64 Encoded)" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="certificate_keys" +label="Merchant PrivateKey (Base64 Encoded)" +placeholder="Enter Merchant PrivateKey (Base64 Encoded)" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="merchant_identifier" +label="Apple Merchant Identifier" +placeholder="Enter Apple Merchant Identifier" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="display_name" +label="Display Name" +placeholder="Enter Display Name" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="initiative" +label="Domain" +placeholder="Enter Domain" +required=true +type="Select" +options=["web","ios"] +[[fiuu.metadata.apple_pay]] +name="initiative_context" +label="Domain Name" +placeholder="Enter Domain Name" +required=true +type="Text" +[[fiuu.metadata.apple_pay]] +name="merchant_business_country" +label="Merchant Business Country" +placeholder="Enter Merchant Business Country" +required=true +type="Select" +options=[] +[[fiuu.metadata.apple_pay]] +name="payment_processing_details_at" +label="Payment Processing Details At" +placeholder="Enter Payment Processing Details At" +required=true +type="Radio" +options=["Connector","Hyperswitch"] \ No newline at end of file diff --git a/crates/hyperswitch_connectors/src/connectors/fiuu.rs b/crates/hyperswitch_connectors/src/connectors/fiuu.rs index df79159c653b..c17de26d7ec4 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiuu.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiuu.rs @@ -2,7 +2,7 @@ pub mod transformers; use std::collections::HashMap; -use common_enums::{CaptureMethod, PaymentMethod, PaymentMethodType}; +use common_enums::{CaptureMethod, PaymentMethodType}; use common_utils::{ errors::{self as common_errors, CustomResult}, ext_traits::BytesExt, @@ -230,19 +230,13 @@ impl ConnectorIntegration CustomResult { - match req.payment_method { - PaymentMethod::RealTimePayment => { - let base_url = connectors.fiuu.third_base_url.clone(); - Ok(format!("{}RMS/API/staticqr/index.php", base_url)) - } - _ => Ok(format!( - "{}RMS/API/Direct/1.4.0/index.php", - self.base_url(connectors) - )), - } + Ok(format!( + "{}RMS/API/Direct/1.4.0/index.php", + self.base_url(connectors) + )) } fn get_request_body( @@ -257,7 +251,7 @@ impl ConnectorIntegration { @@ -74,9 +84,9 @@ impl TryFrom<&ConnectorAuthType> for FiuuAuthType { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "UPPERCASE")] -enum TxnType { +pub enum TxnType { Sals, Auts, } @@ -92,18 +102,17 @@ impl TryFrom> for TxnType { } } -#[derive(Serialize, Deserialize, Display, Debug)] -#[serde(rename_all = "UPPERCASE")] +#[derive(Serialize, Deserialize, Display, Debug, Clone)] enum TxnChannel { #[serde(rename = "CREDITAN")] #[strum(serialize = "CREDITAN")] Creditan, - #[serde(rename = "DuitNowSQR")] - #[strum(serialize = "DuitNowSQR")] - DuitNowSqr, + #[serde(rename = "RPP_DUITNOWQR")] + #[strum(serialize = "RPP_DUITNOWQR")] + RppDuitNowQr, } -#[derive(Serialize, Deserialize, Display, Debug)] +#[derive(Serialize, Deserialize, Display, Debug, Clone)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "SCREAMING_SNAKE_CASE")] pub enum FPXTxnChannel { @@ -157,63 +166,108 @@ impl TryFrom for FPXTxnChannel { } } -#[derive(Serialize, Debug, Deserialize)] -#[serde(untagged)] -#[serde(rename_all = "PascalCase")] -pub enum FiuuPaymentsRequest { - QRPaymentRequest(FiuuQRPaymentRequest), - CardPaymentRequest(FiuuCardPaymentRequest), - FpxPaymentRequest(FiuuFPXPyamentRequest), -} - -#[derive(Serialize, Debug, Deserialize)] +#[derive(Serialize, Debug, Clone)] #[serde(rename_all = "PascalCase")] -pub struct FiuuFPXPyamentRequest { +pub struct FiuuPaymentRequest { #[serde(rename = "MerchantID")] merchant_id: Secret, reference_no: String, txn_type: TxnType, - txn_channel: FPXTxnChannel, txn_currency: Currency, txn_amount: StringMajorUnit, signature: Secret, #[serde(rename = "ReturnURL")] return_url: Option, + #[serde(flatten)] + payment_method_data: FiuuPaymentMethodData, } -#[derive(Serialize, Debug, Deserialize)] -pub struct FiuuQRPaymentRequest { - #[serde(rename = "merchantID")] - merchant_id: Secret, - channel: TxnChannel, - orderid: String, - currency: Currency, - amount: StringMajorUnit, - checksum: Secret, + +#[derive(Serialize, Debug, Clone)] +#[serde(untagged)] +pub enum FiuuPaymentMethodData { + FiuuQRData(Box), + FiuuCardData(Box), + FiuuFpxData(Box), + FiuuGooglePayData(Box), + FiuuApplePayData(Box), } -#[derive(Serialize, Debug, Deserialize)] +#[derive(Serialize, Debug, Clone)] #[serde(rename_all = "PascalCase")] -pub struct FiuuCardPaymentRequest { - #[serde(rename = "MerchantID")] - merchant_id: Secret, - reference_no: String, - txn_type: TxnType, +pub struct FiuuFPXData { + #[serde(rename = "non_3DS")] + non_3ds: i32, + txn_channel: FPXTxnChannel, +} +#[derive(Serialize, Debug, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct FiuuQRData { + txn_channel: TxnChannel, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub struct FiuuCardData { + #[serde(rename = "non_3DS")] + non_3ds: i32, + #[serde(rename = "TxnChannel")] txn_channel: TxnChannel, - txn_currency: Currency, - txn_amount: StringMajorUnit, - signature: Secret, - #[serde(rename = "CC_PAN")] cc_pan: CardNumber, - #[serde(rename = "CC_CVV2")] cc_cvv2: Secret, - #[serde(rename = "CC_MONTH")] cc_month: Secret, - #[serde(rename = "CC_YEAR")] cc_year: Secret, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub struct FiuuApplePayData { + #[serde(rename = "TxnChannel")] + txn_channel: TxnChannel, + cc_month: Secret, + cc_year: Secret, + cc_token: Secret, + eci: Option, + token_cryptogram: Secret, + token_type: FiuuTokenType, + #[serde(rename = "non_3DS")] + non_3ds: i32, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(rename_all = "PascalCase")] +pub enum FiuuTokenType { + ApplePay, + GooglePay, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct FiuuGooglePayData { + txn_channel: TxnChannel, + #[serde(rename = "GooglePay[apiVersion]")] + api_version: u8, + #[serde(rename = "GooglePay[apiVersionMinor]")] + api_version_minor: u8, + #[serde(rename = "GooglePay[paymentMethodData][info][assuranceDetails][accountVerified]")] + account_verified: Option, + #[serde( + rename = "GooglePay[paymentMethodData][info][assuranceDetails][cardHolderAuthenticated]" + )] + card_holder_authenticated: Option, + #[serde(rename = "GooglePay[paymentMethodData][info][cardDetails]")] + card_details: String, + #[serde(rename = "GooglePay[paymentMethodData][info][cardNetwork]")] + card_network: String, + #[serde(rename = "GooglePay[paymentMethodData][tokenizationData][token]")] + token: Secret, + #[serde(rename = "GooglePay[paymentMethodData][tokenizationData][type]")] + tokenization_data_type: Secret, + #[serde(rename = "GooglePay[paymentMethodData][type]")] + pm_type: String, + #[serde(rename = "SCREAMING_SNAKE_CASE")] + token_type: FiuuTokenType, #[serde(rename = "non_3DS")] non_3ds: i32, - #[serde(rename = "ReturnURL")] - return_url: Option, } pub fn calculate_signature( @@ -228,7 +282,7 @@ pub fn calculate_signature( Ok(Secret::new(encoded_data)) } -impl TryFrom<&FiuuRouterData<&PaymentsAuthorizeRouterData>> for FiuuPaymentsRequest { +impl TryFrom<&FiuuRouterData<&PaymentsAuthorizeRouterData>> for FiuuPaymentRequest { type Error = Report; fn try_from(item: &FiuuRouterData<&PaymentsAuthorizeRouterData>) -> Result { let auth = FiuuAuthType::try_from(&item.router_data.connector_auth_type)?; @@ -237,79 +291,202 @@ impl TryFrom<&FiuuRouterData<&PaymentsAuthorizeRouterData>> for FiuuPaymentsRequ let txn_amount = item.amount.clone(); let reference_no = item.router_data.connector_request_reference_id.clone(); let verify_key = auth.verify_key.peek().to_string(); - match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::Card(req_card) => { - let signature = calculate_signature(format!( - "{}{merchant_id}{reference_no}{verify_key}", - txn_amount.get_amount_as_string() - ))?; - - Ok(Self::CardPaymentRequest(FiuuCardPaymentRequest { - merchant_id: auth.merchant_id, - reference_no, - txn_type: match item.router_data.request.is_auto_capture()? { - true => TxnType::Sals, - false => TxnType::Auts, - }, - txn_channel: TxnChannel::Creditan, - txn_currency, - txn_amount, - signature, - cc_pan: req_card.card_number, - cc_cvv2: req_card.card_cvc, - cc_month: req_card.card_exp_month, - cc_year: req_card.card_exp_year, - non_3ds: match item.router_data.is_three_ds() { - false => 1, - true => 0, - }, - return_url: item.router_data.request.router_return_url.clone(), - })) - } - PaymentMethodData::RealTimePayment(real_time_payment_data) => { - match *real_time_payment_data { + let signature = calculate_signature(format!( + "{}{merchant_id}{reference_no}{verify_key}", + txn_amount.get_amount_as_string() + ))?; + let txn_type = match item.router_data.request.is_auto_capture()? { + true => TxnType::Sals, + false => TxnType::Auts, + }; + let return_url = item.router_data.request.router_return_url.clone(); + let non_3ds = match item.router_data.is_three_ds() { + false => 1, + true => 0, + }; + let payment_method_data = match item.router_data.request.payment_method_data { + PaymentMethodData::Card(ref card) => FiuuPaymentMethodData::try_from((card, &non_3ds)), + PaymentMethodData::RealTimePayment(ref real_time_payment_data) => { + match *real_time_payment_data.clone() { RealTimePaymentData::DuitNow {} => { - Ok(Self::QRPaymentRequest(FiuuQRPaymentRequest { - merchant_id: auth.merchant_id, - channel: TxnChannel::DuitNowSqr, - orderid: reference_no.clone(), - currency: txn_currency, - amount: txn_amount.clone(), - checksum: calculate_signature(format!( - "{merchant_id}{}{reference_no}{txn_currency}{}{verify_key}", - TxnChannel::DuitNowSqr, - txn_amount.get_amount_as_string() - ))?, - })) + Ok(FiuuPaymentMethodData::FiuuQRData(Box::new(FiuuQRData { + txn_channel: TxnChannel::RppDuitNowQr, + }))) } RealTimePaymentData::Fps {} | RealTimePaymentData::PromptPay {} - | RealTimePaymentData::VietQr {} => Err( - errors::ConnectorError::NotImplemented("Payment methods".to_string()) - .into(), - ), + | RealTimePaymentData::VietQr {} => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("fiuu"), + ) + .into()) + } } } - PaymentMethodData::BankRedirect(BankRedirectData::OnlineBankingFpx { issuer }) => { - Ok(Self::FpxPaymentRequest(FiuuFPXPyamentRequest { - merchant_id: auth.merchant_id.clone(), - reference_no: reference_no.clone(), - txn_type: match item.router_data.request.is_auto_capture()? { - true => TxnType::Sals, - false => TxnType::Auts, - }, - txn_channel: FPXTxnChannel::try_from(issuer)?, - txn_currency, - txn_amount: txn_amount.clone(), - signature: calculate_signature(format!( - "{}{merchant_id}{reference_no}{verify_key}", - txn_amount.get_amount_as_string() - ))?, - return_url: item.router_data.request.router_return_url.clone(), - })) - } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), - } + PaymentMethodData::BankRedirect(ref bank_redirect_data) => match bank_redirect_data { + BankRedirectData::OnlineBankingFpx { ref issuer } => { + Ok(FiuuPaymentMethodData::FiuuFpxData(Box::new(FiuuFPXData { + txn_channel: FPXTxnChannel::try_from(*issuer)?, + non_3ds, + }))) + } + BankRedirectData::BancontactCard { .. } + | BankRedirectData::Bizum {} + | BankRedirectData::Blik { .. } + | BankRedirectData::Eps { .. } + | BankRedirectData::Giropay { .. } + | BankRedirectData::Ideal { .. } + | BankRedirectData::Interac { .. } + | BankRedirectData::OnlineBankingCzechRepublic { .. } + | BankRedirectData::OnlineBankingFinland { .. } + | BankRedirectData::OnlineBankingPoland { .. } + | BankRedirectData::OnlineBankingSlovakia { .. } + | BankRedirectData::OpenBankingUk { .. } + | BankRedirectData::Przelewy24 { .. } + | BankRedirectData::Sofort { .. } + | BankRedirectData::Trustly { .. } + | BankRedirectData::OnlineBankingThailand { .. } + | BankRedirectData::LocalBankRedirect {} => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("fiuu"), + ) + .into()) + } + }, + PaymentMethodData::Wallet(ref wallet_data) => match wallet_data { + WalletData::GooglePay(google_pay_data) => { + FiuuPaymentMethodData::try_from(google_pay_data) + } + WalletData::ApplePay(_apple_pay_data) => { + let payment_method_token = item.router_data.get_payment_method_token()?; + match payment_method_token { + PaymentMethodToken::Token(_) => { + Err(unimplemented_payment_method!("Apple Pay", "Manual", "Fiuu"))? + } + PaymentMethodToken::ApplePayDecrypt(decrypt_data) => { + FiuuPaymentMethodData::try_from(decrypt_data) + } + } + } + WalletData::AliPayQr(_) + | WalletData::AliPayRedirect(_) + | WalletData::AliPayHkRedirect(_) + | WalletData::MomoRedirect(_) + | WalletData::KakaoPayRedirect(_) + | WalletData::GoPayRedirect(_) + | WalletData::GcashRedirect(_) + | WalletData::ApplePayRedirect(_) + | WalletData::ApplePayThirdPartySdk(_) + | WalletData::DanaRedirect {} + | WalletData::GooglePayRedirect(_) + | WalletData::GooglePayThirdPartySdk(_) + | WalletData::MbWayRedirect(_) + | WalletData::MobilePayRedirect(_) + | WalletData::PaypalRedirect(_) + | WalletData::PaypalSdk(_) + | WalletData::SamsungPay(_) + | WalletData::TwintRedirect {} + | WalletData::VippsRedirect {} + | WalletData::TouchNGoRedirect(_) + | WalletData::WeChatPayRedirect(_) + | WalletData::WeChatPayQr(_) + | WalletData::CashappQr(_) + | WalletData::SwishQr(_) + | WalletData::Mifinity(_) => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("fiuu"), + ) + .into()), + }, + PaymentMethodData::CardRedirect(_) + | PaymentMethodData::PayLater(_) + | PaymentMethodData::BankDebit(_) + | PaymentMethodData::BankTransfer(_) + | PaymentMethodData::Crypto(_) + | PaymentMethodData::MandatePayment + | PaymentMethodData::Reward + | PaymentMethodData::Upi(_) + | PaymentMethodData::Voucher(_) + | PaymentMethodData::GiftCard(_) + | PaymentMethodData::CardToken(_) + | PaymentMethodData::OpenBanking(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("fiuu"), + ) + .into()), + }?; + + Ok(Self { + merchant_id: auth.merchant_id, + reference_no, + txn_type, + txn_currency, + txn_amount, + return_url, + payment_method_data, + signature, + }) + } +} + +impl TryFrom<(&Card, &i32)> for FiuuPaymentMethodData { + type Error = Report; + fn try_from((req_card, non_3ds): (&Card, &i32)) -> Result { + Ok(Self::FiuuCardData(Box::new(FiuuCardData { + txn_channel: TxnChannel::Creditan, + non_3ds: *non_3ds, + cc_pan: req_card.card_number.clone(), + cc_cvv2: req_card.card_cvc.clone(), + cc_month: req_card.card_exp_month.clone(), + cc_year: req_card.card_exp_year.clone(), + }))) + } +} + +impl TryFrom<&GooglePayWalletData> for FiuuPaymentMethodData { + type Error = Report; + fn try_from(data: &GooglePayWalletData) -> Result { + Ok(Self::FiuuGooglePayData(Box::new(FiuuGooglePayData { + txn_channel: TxnChannel::Creditan, + api_version: GOOGLEPAY_API_VERSION, + api_version_minor: GOOGLEPAY_API_VERSION_MINOR, + account_verified: data + .info + .assurance_details + .as_ref() + .map(|details| details.account_verified), + card_holder_authenticated: data + .info + .assurance_details + .as_ref() + .map(|details| details.card_holder_authenticated), + card_details: data.info.card_details.clone(), + card_network: data.info.card_network.clone(), + token: data.tokenization_data.token.clone().into(), + tokenization_data_type: data.tokenization_data.token_type.clone().into(), + pm_type: data.pm_type.clone(), + token_type: FiuuTokenType::GooglePay, + // non_3ds field Applicable to card processing via specific processor using specific currency for pre-approved partner only. + // Equal to 0 by default and 1 for non-3DS transaction, That is why it is hardcoded to 1 for googlepay transactions. + non_3ds: 1, + }))) + } +} + +impl TryFrom> for FiuuPaymentMethodData { + type Error = Report; + fn try_from(decrypt_data: Box) -> Result { + Ok(Self::FiuuApplePayData(Box::new(FiuuApplePayData { + txn_channel: TxnChannel::Creditan, + cc_month: decrypt_data.get_expiry_month()?, + cc_year: decrypt_data.get_four_digit_expiry_year()?, + cc_token: decrypt_data.application_primary_account_number, + eci: decrypt_data.payment_data.eci_indicator, + token_cryptogram: decrypt_data.payment_data.online_payment_cryptogram, + token_type: FiuuTokenType::ApplePay, + // non_3ds field Applicable to card processing via specific processor using specific currency for pre-approved partner only. + // Equal to 0 by default and 1 for non-3DS transaction, That is why it is hardcoded to 1 for apple pay decrypt flow transactions. + non_3ds: 1, + }))) } } @@ -319,17 +496,35 @@ pub struct PaymentsResponse { pub reference_no: String, #[serde(rename = "TxnID")] pub txn_id: String, - pub txn_type: String, - pub txn_currency: String, - pub txn_amount: String, + pub txn_type: TxnType, + pub txn_currency: Currency, + pub txn_amount: StringMajorUnit, pub txn_channel: String, pub txn_data: TxnData, } #[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] pub struct DuitNowQrCodeResponse { - status: bool, - qrcode_data: Secret, + pub reference_no: String, + pub txn_type: TxnType, + pub txn_currency: Currency, + pub txn_amount: StringMajorUnit, + pub txn_channel: String, + #[serde(rename = "TxnID")] + pub txn_id: String, + pub txn_data: QrTxnData, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct QrTxnData { + pub request_data: QrRequestData, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct QrRequestData { + pub qr_data: Secret, } #[derive(Debug, Serialize, Deserialize)] @@ -363,6 +558,14 @@ pub enum RequestData { NonThreeDS(NonThreeDSResponseData), RedirectData(Option>), } + +#[derive(Debug, Serialize, Deserialize)] +pub struct QrCodeData { + #[serde(rename = "tranID")] + pub tran_id: String, + pub status: String, +} + #[derive(Debug, Serialize, Deserialize)] pub struct NonThreeDSResponseData { #[serde(rename = "tranID")] @@ -387,16 +590,13 @@ impl >, ) -> Result { match item.response { - FiuuPaymentsResponse::QRPaymentResponse(response) => Ok(Self { - status: match response.status { - false => enums::AttemptStatus::Failure, - true => enums::AttemptStatus::AuthenticationPending, - }, + FiuuPaymentsResponse::QRPaymentResponse(ref response) => Ok(Self { + status: enums::AttemptStatus::AuthenticationPending, response: Ok(PaymentsResponseData::TransactionResponse { - resource_id: ResponseId::NoResponseId, + resource_id: ResponseId::ConnectorTransactionId(response.txn_id.clone()), redirection_data: None, mandate_reference: None, - connector_metadata: get_qr_metadata(&response)?, + connector_metadata: get_qr_metadata(response)?, network_txn_id: None, connector_response_reference_id: None, incremental_authorization_allowed: None, @@ -588,13 +788,13 @@ impl TryFrom> } } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] pub struct FiuuErrorResponse { pub error_code: String, pub error_desc: String, } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] pub struct FiuuPaymentSyncRequest { amount: StringMajorUnit, #[serde(rename = "txID")] @@ -603,10 +803,10 @@ pub struct FiuuPaymentSyncRequest { skey: Secret, } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "PascalCase")] pub struct FiuuPaymentSyncResponse { - stat_code: String, + stat_code: StatCode, stat_name: StatName, #[serde(rename = "TranID")] tran_id: String, @@ -616,6 +816,16 @@ pub struct FiuuPaymentSyncResponse { miscellaneous: Option>>, } +#[derive(Debug, Serialize, Deserialize, Display, Clone, PartialEq)] +pub enum StatCode { + #[serde(rename = "00")] + Success, + #[serde(rename = "11")] + Failure, + #[serde(rename = "22")] + Pending, +} + #[derive(Debug, Serialize, Deserialize, Display, Clone, Copy, PartialEq)] #[serde(rename_all = "lowercase")] pub enum StatName { @@ -670,24 +880,15 @@ impl TryFrom> for Paymen item: PaymentsSyncResponseRouterData, ) -> Result { let stat_name = item.response.stat_name; - let status = match item.response.stat_code.as_str() { - "00" => { - if stat_name == StatName::Captured || stat_name == StatName::Settled { - Ok(enums::AttemptStatus::Charged) - } else { - Ok(enums::AttemptStatus::Authorized) - } - } - "22" => Ok(enums::AttemptStatus::Pending), - "11" => Ok(enums::AttemptStatus::Failure), - other => Err(errors::ConnectorError::UnexpectedResponseError( - bytes::Bytes::from(other.to_owned()), - )), - }?; + let stat_code = item.response.stat_code.clone(); + let status = enums::AttemptStatus::try_from(FiuuSyncStatus { + stat_name, + stat_code, + })?; let error_response = if status == enums::AttemptStatus::Failure { Some(ErrorResponse { status_code: item.http_code, - code: item.response.stat_code.as_str().to_owned(), + code: item.response.stat_code.to_string(), message: item.response.stat_name.clone().to_string(), reason: Some(item.response.stat_name.clone().to_string()), attempt_status: Some(enums::AttemptStatus::Failure), @@ -714,7 +915,7 @@ impl TryFrom> for Paymen } } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] pub struct PaymentCaptureRequest { domain: String, #[serde(rename = "tranID")] @@ -725,7 +926,7 @@ pub struct PaymentCaptureRequest { skey: Secret, } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct PaymentCaptureResponse { #[serde(rename = "TranID")] @@ -733,6 +934,30 @@ pub struct PaymentCaptureResponse { stat_code: String, } +pub struct FiuuSyncStatus { + pub stat_name: StatName, + pub stat_code: StatCode, +} + +impl TryFrom for enums::AttemptStatus { + type Error = errors::ConnectorError; + fn try_from(sync_status: FiuuSyncStatus) -> Result { + match (sync_status.stat_code, sync_status.stat_name) { + (StatCode::Success, StatName::Captured | StatName::Settled) => Ok(Self::Charged), // For Success as StatCode we can only expect Captured,Settled and Authorized as StatName. + (StatCode::Success, StatName::Authorized) => Ok(Self::Authorized), + (StatCode::Pending, StatName::Pending) => Ok(Self::AuthenticationPending), // For Pending as StatCode we can only expect Pending and Unknow as StatName. + (StatCode::Pending, StatName::Unknown) => Ok(Self::Pending), + (StatCode::Failure, StatName::Cancelled) | (StatCode::Failure, StatName::ReqCancel) => { + Ok(Self::Voided) + } + (StatCode::Failure, _) => Ok(Self::Failure), + (other, _) => Err(errors::ConnectorError::UnexpectedResponseError( + bytes::Bytes::from(other.to_string()), + )), + } + } +} + impl TryFrom<&FiuuRouterData<&PaymentsCaptureRouterData>> for PaymentCaptureRequest { type Error = Report; fn try_from(item: &FiuuRouterData<&PaymentsCaptureRouterData>) -> Result { @@ -833,7 +1058,7 @@ impl TryFrom> } } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] pub struct FiuuPaymentCancelRequest { #[serde(rename = "txnID")] txn_id: String, @@ -841,7 +1066,7 @@ pub struct FiuuPaymentCancelRequest { skey: Secret, } -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct FiuuPaymentCancelResponse { #[serde(rename = "TranID")] @@ -1050,7 +1275,7 @@ impl From for enums::RefundStatus { pub fn get_qr_metadata( response: &DuitNowQrCodeResponse, ) -> CustomResult, errors::ConnectorError> { - let image_data = QrImage::new_from_data(response.qrcode_data.peek().clone()) + let image_data = QrImage::new_from_data(response.txn_data.request_data.qr_data.peek().clone()) .change_context(errors::ConnectorError::ResponseHandlingFailed)?; let image_data_url = Url::parse(image_data.data.clone().as_str()).ok(); diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 0d74f1fb6e3a..4c7c9adaf8ee 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -17,7 +17,7 @@ use common_utils::{ use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ payment_method_data::{Card, PaymentMethodData}, - router_data::{PaymentMethodToken, RecurringMandatePaymentData}, + router_data::{ApplePayPredecryptData, PaymentMethodToken, RecurringMandatePaymentData}, router_request_types::{ AuthenticationData, BrowserInformation, CompleteAuthorizeData, PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, @@ -722,6 +722,31 @@ impl RouterData } } +pub trait ApplePayDecrypt { + fn get_expiry_month(&self) -> Result, Error>; + fn get_four_digit_expiry_year(&self) -> Result, Error>; +} + +impl ApplePayDecrypt for Box { + fn get_four_digit_expiry_year(&self) -> Result, Error> { + Ok(Secret::new(format!( + "20{}", + self.application_expiration_date + .get(0..2) + .ok_or(errors::ConnectorError::RequestEncodingFailed)? + ))) + } + + fn get_expiry_month(&self) -> Result, Error> { + Ok(Secret::new( + self.application_expiration_date + .get(2..4) + .ok_or(errors::ConnectorError::RequestEncodingFailed)? + .to_owned(), + )) + } +} + #[derive(Debug, Copy, Clone, strum::Display, Eq, Hash, PartialEq)] pub enum CardIssuer { AmericanExpress,