From c172f03c31e9b29b69490c7c5dfb3b4c78206f79 Mon Sep 17 00:00:00 2001 From: sweta-sharma <77436883+swetasharma03@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:42:12 +0530 Subject: [PATCH] feat(connector): [BRAINTREE] Googlepay, Applepay wallets added (#8728) --- api-reference/v1/openapi_spec_v1.json | 16 +- api-reference/v2/openapi_spec_v2.json | 16 +- crates/api_models/src/payments.rs | 12 +- .../connector_configs/toml/development.toml | 31 ++ crates/connector_configs/toml/production.toml | 31 ++ crates/connector_configs/toml/sandbox.toml | 31 ++ .../src/connectors/braintree.rs | 125 ++++- .../src/connectors/braintree/transformers.rs | 460 +++++++++++++++++- .../src/connectors/trustpay/transformers.rs | 2 +- crates/hyperswitch_connectors/src/utils.rs | 1 + .../src/payment_method_data.rs | 20 +- .../src/router_data.rs | 1 + .../src/router_request_types.rs | 3 + crates/hyperswitch_interfaces/src/api.rs | 23 + .../src/connector_integration_interface.rs | 35 ++ .../src/conversion_impls.rs | 1 + .../src/core/authentication/transformers.rs | 1 + crates/router/src/core/fraud_check.rs | 2 + .../core/fraud_check/flows/checkout_flow.rs | 3 + .../fraud_check/flows/fulfillment_flow.rs | 8 +- .../core/fraud_check/flows/record_return.rs | 5 +- .../src/core/fraud_check/flows/sale_flow.rs | 3 + .../fraud_check/flows/transaction_flow.rs | 4 + crates/router/src/core/mandate/utils.rs | 1 + crates/router/src/core/payment_methods.rs | 1 + .../router/src/core/payment_methods/cards.rs | 8 +- crates/router/src/core/payments.rs | 10 + crates/router/src/core/payments/flows.rs | 2 + .../src/core/payments/flows/approve_flow.rs | 4 + .../src/core/payments/flows/authorize_flow.rs | 4 + .../src/core/payments/flows/cancel_flow.rs | 4 + .../flows/cancel_post_capture_flow.rs | 4 + .../src/core/payments/flows/capture_flow.rs | 4 + .../payments/flows/complete_authorize_flow.rs | 4 + .../flows/incremental_authorization_flow.rs | 4 + .../flows/post_session_tokens_flow.rs | 4 + .../src/core/payments/flows/psync_flow.rs | 4 + .../src/core/payments/flows/reject_flow.rs | 4 + .../src/core/payments/flows/session_flow.rs | 4 + .../payments/flows/session_update_flow.rs | 2 + .../core/payments/flows/setup_mandate_flow.rs | 4 + .../payments/flows/update_metadata_flow.rs | 2 + crates/router/src/core/payments/helpers.rs | 62 ++- .../payments/operations/payment_session.rs | 28 +- .../operations/payment_session_intent.rs | 24 +- .../router/src/core/payments/transformers.rs | 36 +- crates/router/src/core/relay/utils.rs | 1 + .../unified_authentication_service/utils.rs | 1 + crates/router/src/core/utils.rs | 25 +- crates/router/src/core/webhooks/utils.rs | 1 + crates/router/src/types.rs | 2 + .../router/src/types/api/verify_connector.rs | 1 + crates/router/tests/connectors/utils.rs | 1 + 53 files changed, 998 insertions(+), 92 deletions(-) diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index d1a364b438..2a80307a43 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -8042,7 +8042,13 @@ } }, "ApplePayThirdPartySdkData": { - "type": "object" + "type": "object", + "properties": { + "token": { + "type": "string", + "nullable": true + } + } }, "ApplePayWalletData": { "type": "object", @@ -16347,7 +16353,13 @@ } }, "GooglePayThirdPartySdkData": { - "type": "object" + "type": "object", + "properties": { + "token": { + "type": "string", + "nullable": true + } + } }, "GooglePayWalletData": { "type": "object", diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 9b196b6256..37bbc56c7e 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -5033,7 +5033,13 @@ } }, "ApplePayThirdPartySdkData": { - "type": "object" + "type": "object", + "properties": { + "token": { + "type": "string", + "nullable": true + } + } }, "ApplePayWalletData": { "type": "object", @@ -12498,7 +12504,13 @@ } }, "GooglePayThirdPartySdkData": { - "type": "object" + "type": "object", + "properties": { + "token": { + "type": "string", + "nullable": true + } + } }, "GooglePayWalletData": { "type": "object", diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 7fcb045759..ecc34cbd43 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -4239,10 +4239,16 @@ pub struct PayseraData {} pub struct GooglePayRedirectData {} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] -pub struct GooglePayThirdPartySdkData {} +pub struct GooglePayThirdPartySdkData { + #[schema(value_type = Option)] + pub token: Option>, +} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] -pub struct ApplePayThirdPartySdkData {} +pub struct ApplePayThirdPartySdkData { + #[schema(value_type = Option)] + pub token: Option>, +} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct WeChatPayRedirection {} @@ -8064,7 +8070,7 @@ pub struct SecretInfoToInitiateSdk { pub display: Secret, // Authorization secrets used by client for payment #[schema(value_type = String)] - pub payment: Secret, + pub payment: Option>, } #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema, serde::Deserialize)] diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index f1b7a4d30f..480991d227 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -1401,6 +1401,10 @@ merchant_secret="Source verification key" payment_method_type = "UnionPay" [[braintree.debit]] payment_method_type = "UnionPay" +[[braintree.wallet]] + payment_method_type = "apple_pay" +[[braintree.wallet]] + payment_method_type = "google_pay" [braintree.connector_webhook_details] merchant_secret="Source verification key" @@ -1424,6 +1428,33 @@ required=true type="Select" options=[] +[[braintree.metadata.google_pay]] +name = "merchant_name" +label = "Google Pay Merchant Name" +placeholder = "Enter Google Pay Merchant Name" +required = true +type = "Text" +[[braintree.metadata.google_pay]] +name="merchant_id" +label="Google Pay Merchant Id" +placeholder="Enter Google Pay Merchant Id" +required=false +type="Text" +[[braintree.metadata.google_pay]] +name="allowed_auth_methods" +label="Allowed Auth Methods" +placeholder="Enter Allowed Auth Methods" +required=false +type="MultiSelect" +options=["PAN_ONLY", "CRYPTOGRAM_3DS"] + +[[braintree.metadata.apple_pay]] +name="display_name" +label="Display Name" +placeholder="Enter Display Name" +required=true +type="Text" + [cashtocode] [[cashtocode.reward]] payment_method_type = "classic" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 6d081e629b..fc54ac0411 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -734,6 +734,10 @@ payment_method_type = "CartesBancaires" payment_method_type = "UnionPay" [[braintree.debit]] payment_method_type = "UnionPay" +[[braintree.wallet]] + payment_method_type = "apple_pay" +[[braintree.wallet]] + payment_method_type = "google_pay" [braintree.connector_auth.SignatureKey] api_key = "Public Key" @@ -756,6 +760,33 @@ required = true type = "Select" options = [] +[[braintree.metadata.google_pay]] +name = "merchant_name" +label = "Google Pay Merchant Name" +placeholder = "Enter Google Pay Merchant Name" +required = true +type = "Text" +[[braintree.metadata.google_pay]] +name="merchant_id" +label="Google Pay Merchant Id" +placeholder="Enter Google Pay Merchant Id" +required=false +type="Text" +[[braintree.metadata.google_pay]] +name="allowed_auth_methods" +label="Allowed Auth Methods" +placeholder="Enter Allowed Auth Methods" +required=false +type="MultiSelect" +options=["PAN_ONLY", "CRYPTOGRAM_3DS"] + +[[braintree.metadata.apple_pay]] +name="display_name" +label="Display Name" +placeholder="Enter Display Name" +required=true +type="Text" + [bambora] [[bambora.credit]] payment_method_type = "Mastercard" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index fdd5c91295..5b97befb34 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -1401,6 +1401,10 @@ payment_method_type = "CartesBancaires" payment_method_type = "UnionPay" [[braintree.debit]] payment_method_type = "UnionPay" +[[braintree.wallet]] + payment_method_type = "apple_pay" +[[braintree.wallet]] + payment_method_type = "google_pay" [braintree.connector_webhook_details] merchant_secret = "Source verification key" @@ -1423,6 +1427,33 @@ required = true type = "Select" options = [] +[[braintree.metadata.google_pay]] +name = "merchant_name" +label = "Google Pay Merchant Name" +placeholder = "Enter Google Pay Merchant Name" +required = true +type = "Text" +[[braintree.metadata.google_pay]] +name="merchant_id" +label="Google Pay Merchant Id" +placeholder="Enter Google Pay Merchant Id" +required=false +type="Text" +[[braintree.metadata.google_pay]] +name="allowed_auth_methods" +label="Allowed Auth Methods" +placeholder="Enter Allowed Auth Methods" +required=false +type="MultiSelect" +options=["PAN_ONLY", "CRYPTOGRAM_3DS"] + +[[braintree.metadata.apple_pay]] +name="display_name" +label="Display Name" +placeholder="Enter Display Name" +required=true +type="Text" + [cashtocode] [[cashtocode.reward]] payment_method_type = "classic" diff --git a/crates/hyperswitch_connectors/src/connectors/braintree.rs b/crates/hyperswitch_connectors/src/connectors/braintree.rs index acd56ab7cf..614f6b7173 100644 --- a/crates/hyperswitch_connectors/src/connectors/braintree.rs +++ b/crates/hyperswitch_connectors/src/connectors/braintree.rs @@ -39,8 +39,8 @@ use hyperswitch_domain_models::{ }, types::{ MandateRevokeRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData, - PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData, PaymentsSyncRouterData, - RefundSyncRouterData, RefundsRouterData, TokenizationRouterData, + PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData, PaymentsSessionRouterData, + PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, TokenizationRouterData, }, }; use hyperswitch_interfaces::{ @@ -55,8 +55,8 @@ use hyperswitch_interfaces::{ events::connector_api_logs::ConnectorEvent, types::{ MandateRevokeType, PaymentsAuthorizeType, PaymentsCaptureType, - PaymentsCompleteAuthorizeType, PaymentsSyncType, PaymentsVoidType, RefundExecuteType, - RefundSyncType, Response, TokenizationType, + PaymentsCompleteAuthorizeType, PaymentsSessionType, PaymentsSyncType, PaymentsVoidType, + RefundExecuteType, RefundSyncType, Response, TokenizationType, }, webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails}, }; @@ -237,7 +237,89 @@ impl ConnectorIntegration // Not Implemented (R) } -impl ConnectorIntegration for Braintree {} +impl ConnectorIntegration for Braintree { + fn get_headers( + &self, + req: &PaymentsSessionRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsSessionRouterData, + connectors: &Connectors, + ) -> CustomResult { + Ok(self.base_url(connectors).to_string()) + } + + fn get_request_body( + &self, + req: &PaymentsSessionRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let metadata: braintree::BraintreeMeta = + braintree::BraintreeMeta::try_from(&req.connector_meta_data)?; + let connector_req = braintree::BraintreeClientTokenRequest::try_from(metadata)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PaymentsSessionRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&PaymentsSessionType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(PaymentsSessionType::get_headers(self, req, connectors)?) + .set_body(PaymentsSessionType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsSessionRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult + where + PaymentsResponseData: Clone, + { + let response: braintree::BraintreeSessionResponse = res + .response + .parse_struct("BraintreeSessionResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + utils::ForeignTryFrom::foreign_try_from(( + crate::types::PaymentsSessionResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }, + data.clone(), + )) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} impl api::PaymentToken for Braintree {} @@ -1302,6 +1384,26 @@ static BRAINTREE_SUPPORTED_PAYMENT_METHODS: LazyLock = ), }, ); + braintree_supported_payment_methods.add( + enums::PaymentMethod::Wallet, + enums::PaymentMethodType::GooglePay, + PaymentMethodDetails { + mandates: enums::FeatureStatus::NotSupported, + refunds: enums::FeatureStatus::Supported, + supported_capture_methods: supported_capture_methods.clone(), + specific_features: None, + }, + ); + braintree_supported_payment_methods.add( + enums::PaymentMethod::Wallet, + enums::PaymentMethodType::ApplePay, + PaymentMethodDetails { + mandates: enums::FeatureStatus::NotSupported, + refunds: enums::FeatureStatus::Supported, + supported_capture_methods: supported_capture_methods.clone(), + specific_features: None, + }, + ); braintree_supported_payment_methods }); @@ -1328,4 +1430,17 @@ impl ConnectorSpecifications for Braintree { fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> { Some(&BRAINTREE_SUPPORTED_WEBHOOK_FLOWS) } + + fn is_sdk_client_token_generation_enabled(&self) -> bool { + true + } + + fn supported_payment_method_types_for_sdk_client_token_generation( + &self, + ) -> Vec { + vec![ + enums::PaymentMethodType::ApplePay, + enums::PaymentMethodType::GooglePay, + ] + } } diff --git a/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs b/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs index 89072159e6..c3d502f89d 100644 --- a/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs @@ -1,12 +1,17 @@ -use api_models::webhooks::IncomingWebhookEvent; +use api_models::{ + payments as payment_types, + payments::{ApplePaySessionResponse, SessionToken}, + webhooks::IncomingWebhookEvent, +}; use common_enums::enums; use common_utils::{ + ext_traits::ValueExt, pii, - types::{MinorUnit, StringMajorUnit}, + types::{AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector}, }; use error_stack::ResultExt; use hyperswitch_domain_models::{ - payment_method_data::PaymentMethodData, + payment_method_data::{PaymentMethodData, WalletData}, router_data::{ConnectorAuthType, PaymentMethodToken, RouterData}, router_flow_types::refunds::{Execute, RSync}, router_request_types::{ @@ -24,16 +29,21 @@ use hyperswitch_interfaces::{ }; use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; +use strum::Display; use time::PrimitiveDateTime; use crate::{ - types::{PaymentsCaptureResponseRouterData, RefundsResponseRouterData, ResponseRouterData}, + types::{ + PaymentsCaptureResponseRouterData, PaymentsSessionResponseRouterData, + RefundsResponseRouterData, ResponseRouterData, + }, unimplemented_payment_method, utils::{ - self, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, + self, ForeignTryFrom, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, RefundsRequestData, RouterData as _, }, }; + pub const CHANNEL_CODE: &str = "HyperSwitchBT_Ecom"; pub const CLIENT_TOKEN_MUTATION: &str = "mutation createClientToken($input: CreateClientTokenInput!) { createClientToken(input: $input) { clientToken}}"; pub const TOKENIZE_CREDIT_CARD: &str = "mutation tokenizeCreditCard($input: TokenizeCreditCardInput!) { tokenizeCreditCard(input: $input) { clientMutationId paymentMethod { id } } }"; @@ -47,6 +57,10 @@ pub const CHARGE_AND_VAULT_TRANSACTION_MUTATION: &str ="mutation ChargeCreditCar pub const DELETE_PAYMENT_METHOD_FROM_VAULT_MUTATION: &str = "mutation deletePaymentMethodFromVault($input: DeletePaymentMethodFromVaultInput!) { deletePaymentMethodFromVault(input: $input) { clientMutationId } }"; pub const TRANSACTION_QUERY: &str = "query($input: TransactionSearchInput!) { search { transactions(input: $input) { edges { node { id status } } } } }"; pub const REFUND_QUERY: &str = "query($input: RefundSearchInput!) { search { refunds(input: $input, first: 1) { edges { node { id status createdAt amount { value currencyCode } orderId } } } } }"; +pub const CHARGE_GOOGLE_PAY_MUTATION: &str = "mutation ChargeGPay($input: ChargePaymentMethodInput!) { chargePaymentMethod(input: $input) { transaction { id status amount { value currencyCode } } } }"; +pub const AUTHORIZE_GOOGLE_PAY_MUTATION: &str = "mutation authorizeGPay($input: AuthorizePaymentMethodInput!) { authorizePaymentMethod(input: $input) { transaction { id legacyId amount { value currencyCode } status } } }"; +pub const CHARGE_APPLE_PAY_MUTATION: &str = "mutation ChargeApplepay($input: ChargePaymentMethodInput!) { chargePaymentMethod(input: $input) { transaction { id status amount { value currencyCode } } } }"; +pub const AUTHORIZE_APPLE_PAY_MUTATION: &str = "mutation authorizeApplepay($input: AuthorizePaymentMethodInput!) { authorizePaymentMethod(input: $input) { transaction { id legacyId amount { value currencyCode } status } } }"; pub type CardPaymentRequest = GenericBraintreeRequest; pub type MandatePaymentRequest = GenericBraintreeRequest; @@ -69,6 +83,23 @@ pub type BraintreeRefundVariables = GenericVariableInput; pub type PSyncInput = GenericVariableInput; pub type RSyncInput = GenericVariableInput; +pub type BraintreeWalletRequest = GenericBraintreeRequest>; + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WalletTransactionBody { + amount: StringMajorUnit, + merchant_account_id: Secret, + order_id: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WalletPaymentInput { + payment_method_id: Secret, + transaction: WalletTransactionBody, +} + #[derive(Debug, Clone, Serialize)] pub struct GenericBraintreeRequest { query: String, @@ -188,6 +219,8 @@ pub enum BraintreePaymentsRequest { Card(CardPaymentRequest), CardThreeDs(BraintreeClientTokenRequest), Mandate(MandatePaymentRequest), + Wallet(BraintreeWalletRequest), + Session(BraintreeClientTokenRequest), } #[derive(Debug, Deserialize)] @@ -342,8 +375,71 @@ impl TryFrom<&BraintreeRouterData<&types::PaymentsAuthorizeRouterData>> metadata, ))?)) } + PaymentMethodData::Wallet(ref wallet_data) => match wallet_data { + WalletData::GooglePayThirdPartySdk(ref req_wallet) => { + let payment_method_id = &req_wallet.token; + let query = match item.router_data.request.is_auto_capture()? { + true => CHARGE_GOOGLE_PAY_MUTATION.to_string(), + false => AUTHORIZE_GOOGLE_PAY_MUTATION.to_string(), + }; + + Ok(Self::Wallet(BraintreeWalletRequest { + query, + variables: GenericVariableInput { + input: WalletPaymentInput { + payment_method_id: payment_method_id.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "google_pay token", + }, + )?, + + transaction: WalletTransactionBody { + amount: item.amount.clone(), + merchant_account_id: metadata.merchant_account_id, + order_id: item + .router_data + .connector_request_reference_id + .clone(), + }, + }, + }, + })) + } + WalletData::ApplePayThirdPartySdk(ref req_wallet) => { + let payment_method_id = &req_wallet.token; + + let query = match item.router_data.request.is_auto_capture()? { + true => CHARGE_APPLE_PAY_MUTATION.to_string(), + false => AUTHORIZE_APPLE_PAY_MUTATION.to_string(), + }; + + Ok(Self::Wallet(BraintreeWalletRequest { + query, + variables: GenericVariableInput { + input: WalletPaymentInput { + payment_method_id: payment_method_id.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "apple_pay token", + }, + )?, + transaction: WalletTransactionBody { + amount: item.amount.clone(), + merchant_account_id: metadata.merchant_account_id, + order_id: item + .router_data + .connector_request_reference_id + .clone(), + }, + }, + }, + })) + } + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("braintree"), + ) + .into()), + }, PaymentMethodData::CardRedirect(_) - | PaymentMethodData::Wallet(_) | PaymentMethodData::PayLater(_) | PaymentMethodData::BankRedirect(_) | PaymentMethodData::BankDebit(_) @@ -415,6 +511,7 @@ pub enum BraintreeAuthResponse { AuthResponse(Box), ClientTokenResponse(Box), ErrorResponse(Box), + WalletAuthResponse(Box), } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -473,9 +570,9 @@ impl let status = enums::AttemptStatus::from(transaction_data.status.clone()); let response = if utils::is_payment_failure(status) { Err(hyperswitch_domain_models::router_data::ErrorResponse { - code: transaction_data.status.to_string().clone(), - message: transaction_data.status.to_string().clone(), - reason: Some(transaction_data.status.to_string().clone()), + code: transaction_data.status.to_string(), + message: transaction_data.status.to_string(), + reason: Some(transaction_data.status.to_string()), attempt_status: None, connector_transaction_id: Some(transaction_data.id), status_code: item.http_code, @@ -528,6 +625,42 @@ impl }), ..item.data }), + BraintreeAuthResponse::WalletAuthResponse(wallet_response) => { + let txn = &wallet_response.data.authorize_payment_method.transaction; + let status = enums::AttemptStatus::from(txn.status.clone()); + + let response = if utils::is_payment_failure(status) { + Err(hyperswitch_domain_models::router_data::ErrorResponse { + code: txn.status.to_string(), + message: txn.status.to_string(), + reason: Some(txn.status.to_string()), + attempt_status: None, + connector_transaction_id: Some(txn.id.clone()), + status_code: item.http_code, + network_advice_code: None, + network_decline_code: None, + network_error_message: None, + connector_metadata: None, + }) + } else { + Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(txn.id.clone()), + redirection_data: Box::new(None), + mandate_reference: Box::new(None), + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: txn.legacy_id.clone(), + incremental_authorization_allowed: None, + charges: None, + }) + }; + + Ok(Self { + status, + response, + ..item.data + }) + } } } } @@ -716,6 +849,45 @@ impl }), ..item.data }), + BraintreePaymentsResponse::WalletPaymentsResponse(google_pay_payments_response) => { + let txn = &google_pay_payments_response + .data + .charge_payment_method + .transaction; + let status = enums::AttemptStatus::from(txn.status.clone()); + + let response = if utils::is_payment_failure(status) { + Err(hyperswitch_domain_models::router_data::ErrorResponse { + code: txn.status.to_string(), + message: txn.status.to_string(), + reason: Some(txn.status.to_string()), + attempt_status: None, + connector_transaction_id: Some(txn.id.clone()), + status_code: item.http_code, + network_advice_code: None, + network_decline_code: None, + network_error_message: None, + connector_metadata: None, + }) + } else { + Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(txn.id.clone()), + redirection_data: Box::new(None), + mandate_reference: Box::new(None), + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charges: None, + }) + }; + + Ok(Self { + status, + response, + ..item.data + }) + } } } } @@ -865,10 +1037,55 @@ pub struct PaymentsResponse { data: DataResponse, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct WalletPaymentsResponse { + pub data: WalletDataResponse, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WalletDataResponse { + pub charge_payment_method: WalletTransactionWrapper, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct WalletTransactionWrapper { + pub transaction: WalletTransaction, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WalletTransaction { + pub id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub legacy_id: Option, + pub status: BraintreePaymentStatus, + pub amount: Amount, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Amount { + pub value: String, + pub currency_code: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct WalletAuthResponse { + pub data: WalletAuthDataResponse, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WalletAuthDataResponse { + pub authorize_payment_method: WalletTransactionWrapper, +} + #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(untagged)] pub enum BraintreePaymentsResponse { PaymentsResponse(Box), + WalletPaymentsResponse(Box), ClientTokenResponse(Box), ErrorResponse(Box), } @@ -981,7 +1198,6 @@ pub struct BraintreeRefundTransactionBody { pub id: String, pub status: BraintreeRefundStatus, } - #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BraintreeRefundTransaction { pub refund: BraintreeRefundTransactionBody, @@ -1259,9 +1475,16 @@ pub struct ClientTokenData { create_client_token: ClientToken, } +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ClientTokenExtensions { + request_id: String, +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ClientTokenResponse { data: ClientTokenData, + extensions: ClientTokenExtensions, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -1281,6 +1504,13 @@ pub enum BraintreeTokenResponse { ErrorResponse(Box), } +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum BraintreeSessionResponse { + SessionTokenResponse(Box), + ErrorResponse(Box), +} + impl TryFrom> for RouterData { @@ -1312,6 +1542,196 @@ impl TryFrom, + Self, + )> for types::PaymentsSessionRouterData +{ + type Error = error_stack::Report; + fn foreign_try_from( + (item, data): ( + PaymentsSessionResponseRouterData, + Self, + ), + ) -> Result { + let response = &item.response; + + match response { + BraintreeSessionResponse::SessionTokenResponse(res) => { + let session_token = match data.payment_method_type { + Some(common_enums::PaymentMethodType::ApplePay) => { + let payment_request_data: payment_types::PaymentRequestMetadata = + if let Some(connector_meta) = data.connector_meta_data.clone() { + let meta_value: serde_json::Value = connector_meta.expose(); + meta_value + .get("apple_pay_combined") + .ok_or(errors::ConnectorError::NoConnectorMetaData) + .attach_printable("Missing apple_pay_combined metadata")? + .get("manual") + .ok_or(errors::ConnectorError::NoConnectorMetaData) + .attach_printable("Missing manual metadata")? + .get("payment_request_data") + .ok_or(errors::ConnectorError::NoConnectorMetaData) + .attach_printable("Missing payment_request_data metadata")? + .clone() + .parse_value("PaymentRequestMetadata") + .change_context(errors::ConnectorError::ParsingFailed) + .attach_printable( + "Failed to parse apple_pay_combined.manual.payment_request_data metadata", + )? + } else { + return Err(errors::ConnectorError::NoConnectorMetaData) + .attach_printable("connector_meta_data is None"); + }; + + let session_token_data = Some(ApplePaySessionResponse::ThirdPartySdk( + payment_types::ThirdPartySdkSessionResponse { + secrets: payment_types::SecretInfoToInitiateSdk { + display: res.data.create_client_token.client_token.clone(), + payment: None, + }, + }, + )); + SessionToken::ApplePay(Box::new( + api_models::payments::ApplepaySessionTokenResponse { + session_token_data, + payment_request_data: Some( + api_models::payments::ApplePayPaymentRequest { + country_code: data.request.country.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "country", + }, + )?, + currency_code: data.request.currency, + total: api_models::payments::AmountInfo { + label: payment_request_data.label, + total_type: None, + amount: StringMajorUnitForConnector + .convert( + MinorUnit::new(data.request.amount), + data.request.currency, + ) + .change_context( + errors::ConnectorError::AmountConversionFailed, + )?, + }, + merchant_capabilities: Some( + payment_request_data.merchant_capabilities, + ), + supported_networks: Some( + payment_request_data.supported_networks, + ), + merchant_identifier: None, + required_billing_contact_fields: None, + required_shipping_contact_fields: None, + recurring_payment_request: None, + }, + ), + connector: data.connector.clone(), + delayed_session_token: false, + sdk_next_action: api_models::payments::SdkNextAction { + next_action: api_models::payments::NextActionCall::Confirm, + }, + connector_reference_id: None, + connector_sdk_public_key: None, + connector_merchant_id: None, + }, + )) + } + Some(common_enums::PaymentMethodType::GooglePay) => { + let gpay_data: payment_types::GpaySessionTokenData = + if let Some(connector_meta) = data.connector_meta_data.clone() { + connector_meta + .expose() + .parse_value("GpaySessionTokenData") + .change_context(errors::ConnectorError::ParsingFailed) + .attach_printable("Failed to parse gpay metadata")? + } else { + return Err(errors::ConnectorError::NoConnectorMetaData) + .attach_printable("connector_meta_data is None"); + }; + + SessionToken::GooglePay(Box::new( + api_models::payments::GpaySessionTokenResponse::GooglePaySession( + api_models::payments::GooglePaySessionResponse { + merchant_info: payment_types::GpayMerchantInfo { + merchant_name: gpay_data.data.merchant_info.merchant_name, + merchant_id: gpay_data.data.merchant_info.merchant_id, + }, + shipping_address_required: false, + email_required: false, + shipping_address_parameters: + payment_types::GpayShippingAddressParameters { + phone_number_required: false, + }, + allowed_payment_methods: gpay_data.data.allowed_payment_methods, + transaction_info: payment_types::GpayTransactionInfo { + country_code: data.request.country.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "country", + }, + )?, + currency_code: data.request.currency, + total_price_status: GooglePayPriceStatus::Final.to_string(), + total_price: StringMajorUnitForConnector + .convert( + MinorUnit::new(data.request.amount), + data.request.currency, + ) + .change_context( + errors::ConnectorError::AmountConversionFailed, + )?, + }, + secrets: Some(payment_types::SecretInfoToInitiateSdk { + display: res.data.create_client_token.client_token.clone(), + payment: None, + }), + delayed_session_token: false, + connector: data.connector.clone(), + sdk_next_action: payment_types::SdkNextAction { + next_action: payment_types::NextActionCall::Confirm, + }, + }, + ), + )) + } + _ => { + return Err(errors::ConnectorError::NotImplemented( + format!( + "SDK session token generation is not supported for payment method: {:?}", + data.payment_method_type + ) + ) + .into()); + } + }; + + Ok(Self { + response: Ok(PaymentsResponseData::SessionResponse { session_token }), + ..data + }) + } + BraintreeSessionResponse::ErrorResponse(error_response) => { + let err = build_error_response(error_response.errors.as_ref(), item.http_code) + .map_err(|err| *err); + Ok(Self { + response: err, + ..data + }) + } + } + } +} + #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct CaptureTransactionBody { @@ -1634,6 +2054,26 @@ impl TryFrom<&types::PaymentsSyncRouterData> for BraintreePSyncRequest { } } +impl TryFrom<&types::PaymentsSessionRouterData> for BraintreePaymentsRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsSessionRouterData) -> Result { + let metadata: BraintreeMeta = utils::to_connector_meta_from_secret( + item.connector_meta_data.clone(), + ) + .change_context(errors::ConnectorError::InvalidConnectorConfig { config: "metadata" })?; + Ok(Self::Session(BraintreeClientTokenRequest { + query: CLIENT_TOKEN_MUTATION.to_owned(), + variables: VariableClientTokenInput { + input: InputClientTokenData { + client_token: ClientTokenInput { + merchant_account_id: metadata.merchant_account_id, + }, + }, + }, + })) + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct NodeData { id: String, diff --git a/crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs index 3fda5a2460..6f51dc5bb0 100644 --- a/crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs @@ -1535,7 +1535,7 @@ impl From for api_models::payments::SecretInfoToInitiateSdk { fn from(value: SdkSecretInfo) -> Self { Self { display: value.display, - payment: value.payment, + payment: Some(value.payment), } } } diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 486c2f4d8e..787f2255a2 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -6786,6 +6786,7 @@ pub(crate) fn convert_payment_authorize_router_response( tenant_id: data.tenant_id.clone(), status: data.status, payment_method: data.payment_method, + payment_method_type: data.payment_method_type, connector_auth_type: data.connector_auth_type.clone(), description: data.description.clone(), address: data.address.clone(), diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 7046bb18c2..241523db11 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -400,10 +400,14 @@ pub struct RevolutPayData {} pub struct GooglePayRedirectData {} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct GooglePayThirdPartySdkData {} +pub struct GooglePayThirdPartySdkData { + pub token: Option>, +} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct ApplePayThirdPartySdkData {} +pub struct ApplePayThirdPartySdkData { + pub token: Option>, +} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct WeChatPayRedirection {} @@ -1191,8 +1195,10 @@ impl From for WalletData { api_models::payments::WalletData::ApplePayRedirect(_) => { Self::ApplePayRedirect(Box::new(ApplePayRedirectData {})) } - api_models::payments::WalletData::ApplePayThirdPartySdk(_) => { - Self::ApplePayThirdPartySdk(Box::new(ApplePayThirdPartySdkData {})) + api_models::payments::WalletData::ApplePayThirdPartySdk(apple_pay_sdk_data) => { + Self::ApplePayThirdPartySdk(Box::new(ApplePayThirdPartySdkData { + token: apple_pay_sdk_data.token, + })) } api_models::payments::WalletData::DanaRedirect {} => Self::DanaRedirect {}, api_models::payments::WalletData::GooglePay(google_pay_data) => { @@ -1201,8 +1207,10 @@ impl From for WalletData { api_models::payments::WalletData::GooglePayRedirect(_) => { Self::GooglePayRedirect(Box::new(GooglePayRedirectData {})) } - api_models::payments::WalletData::GooglePayThirdPartySdk(_) => { - Self::GooglePayThirdPartySdk(Box::new(GooglePayThirdPartySdkData {})) + api_models::payments::WalletData::GooglePayThirdPartySdk(google_pay_sdk_data) => { + Self::GooglePayThirdPartySdk(Box::new(GooglePayThirdPartySdkData { + token: google_pay_sdk_data.token, + })) } api_models::payments::WalletData::MbWayRedirect(..) => { Self::MbWayRedirect(Box::new(MbWayRedirection {})) diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index 0dcf6f86fe..5f942e83b3 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -37,6 +37,7 @@ pub struct RouterData { pub tenant_id: id_type::TenantId, pub status: common_enums::enums::AttemptStatus, pub payment_method: common_enums::enums::PaymentMethod, + pub payment_method_type: Option, pub connector_auth_type: ConnectorAuthType, pub description: Option, pub address: PaymentAddress, diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index 0eee01af34..87cf479339 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -1381,6 +1381,9 @@ pub struct PaymentsSessionData { pub order_tax_amount: Option, pub shipping_cost: Option, pub metadata: Option>, + /// The specific payment method type for which the session token is being generated + pub payment_method_type: Option, + pub payment_method: Option, } #[derive(Debug, Clone, Default)] diff --git a/crates/hyperswitch_interfaces/src/api.rs b/crates/hyperswitch_interfaces/src/api.rs index f8a813c7ea..65089db607 100644 --- a/crates/hyperswitch_interfaces/src/api.rs +++ b/crates/hyperswitch_interfaces/src/api.rs @@ -410,6 +410,29 @@ pub trait ConnectorSpecifications { false } + /// Whether SDK session token generation is enabled for this connector + fn is_sdk_client_token_generation_enabled(&self) -> bool { + false + } + + /// Payment method types that support SDK session token generation + fn supported_payment_method_types_for_sdk_client_token_generation( + &self, + ) -> Vec { + vec![] + } + + /// Validate if SDK session token generation is allowed for given payment method type + fn validate_sdk_session_token_for_payment_method( + &self, + current_core_payment_method_type: &PaymentMethodType, + ) -> bool { + self.is_sdk_client_token_generation_enabled() + && self + .supported_payment_method_types_for_sdk_client_token_generation() + .contains(current_core_payment_method_type) + } + #[cfg(not(feature = "v2"))] /// Generate connector request reference ID fn generate_connector_request_reference_id( diff --git a/crates/hyperswitch_interfaces/src/connector_integration_interface.rs b/crates/hyperswitch_interfaces/src/connector_integration_interface.rs index a876258f39..b6455d45b4 100644 --- a/crates/hyperswitch_interfaces/src/connector_integration_interface.rs +++ b/crates/hyperswitch_interfaces/src/connector_integration_interface.rs @@ -539,6 +539,41 @@ impl ConnectorSpecifications for ConnectorEnum { } } + /// If connector supports session token generation + fn is_sdk_client_token_generation_enabled(&self) -> bool { + match self { + Self::Old(connector) => connector.is_sdk_client_token_generation_enabled(), + Self::New(connector) => connector.is_sdk_client_token_generation_enabled(), + } + } + + /// Supported payment methods for session token generation + fn supported_payment_method_types_for_sdk_client_token_generation( + &self, + ) -> Vec { + match self { + Self::Old(connector) => { + connector.supported_payment_method_types_for_sdk_client_token_generation() + } + Self::New(connector) => { + connector.supported_payment_method_types_for_sdk_client_token_generation() + } + } + } + + /// Validate whether session token is generated for payment payment type + fn validate_sdk_session_token_for_payment_method( + &self, + current_core_payment_method_type: &common_enums::PaymentMethodType, + ) -> bool { + match self { + Self::Old(connector) => connector + .validate_sdk_session_token_for_payment_method(current_core_payment_method_type), + Self::New(connector) => connector + .validate_sdk_session_token_for_payment_method(current_core_payment_method_type), + } + } + #[cfg(feature = "v1")] fn generate_connector_request_reference_id( &self, diff --git a/crates/hyperswitch_interfaces/src/conversion_impls.rs b/crates/hyperswitch_interfaces/src/conversion_impls.rs index eb394fca05..9d27c6daa7 100644 --- a/crates/hyperswitch_interfaces/src/conversion_impls.rs +++ b/crates/hyperswitch_interfaces/src/conversion_impls.rs @@ -87,6 +87,7 @@ fn get_default_router_data( psd2_sca_exemption_type: None, raw_connector_response: None, is_payment_id_from_merchant: None, + payment_method_type: None, l2_l3_data: None, minor_amount_capturable: None, authorized_amount: None, diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 6ed04f1522..fb74955ab6 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -167,6 +167,7 @@ pub fn construct_router_data( attempt_id: IRRELEVANT_ATTEMPT_ID_IN_AUTHENTICATION_FLOW.to_owned(), status: common_enums::AttemptStatus::default(), payment_method, + payment_method_type: None, connector_auth_type: auth_type, description: None, address, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index ae8849851e..833fbe3e3e 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -116,6 +116,8 @@ where &merchant_connector_account, None, None, + None, + None, ) .await?; diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index c222ff3b40..ec6b014f6a 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -50,6 +50,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult> { use crate::connector::utils::PaymentsAttemptData; @@ -79,6 +81,7 @@ impl ConstructFlowSpecificData( .get_connector_account_details() .parse_value("ConnectorAuthType") .change_context(errors::ApiErrorResponse::InternalServerError)?; - let payment_method = utils::OptionExt::get_required_value( - payment_attempt.payment_method, - "payment_method_type", - )?; + let payment_method = + utils::OptionExt::get_required_value(payment_attempt.payment_method, "payment_method")?; + let router_data = RouterData { flow: std::marker::PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), @@ -77,6 +76,7 @@ pub async fn construct_fulfillment_router_data<'a>( attempt_id: payment_attempt.attempt_id.clone(), status: payment_attempt.status, payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index 1853f48db6..a1dbf4d210 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -51,6 +51,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -75,8 +77,9 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -73,6 +75,7 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult< RouterData, > { @@ -85,6 +87,8 @@ impl .payment_attempt .payment_method .ok_or(errors::ApiErrorResponse::PaymentMethodNotFound)?, + payment_method_type: self.payment_attempt.payment_method_type, + connector_auth_type: auth_type, description: None, address: self.address.clone(), diff --git a/crates/router/src/core/mandate/utils.rs b/crates/router/src/core/mandate/utils.rs index 023473d53f..3981db1a67 100644 --- a/crates/router/src/core/mandate/utils.rs +++ b/crates/router/src/core/mandate/utils.rs @@ -43,6 +43,7 @@ pub async fn construct_mandate_revoke_router_data( attempt_id: IRRELEVANT_ATTEMPT_ID_IN_MANDATE_REVOKE_FLOW.to_string(), status: diesel_models::enums::AttemptStatus::default(), payment_method: diesel_models::enums::PaymentMethod::default(), + payment_method_type: None, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index a7d23edde0..25569ef8bb 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -3789,6 +3789,7 @@ async fn create_single_use_tokenization_flow( tenant_id: state.tenant.tenant_id.clone(), status: common_enums::enums::AttemptStatus::default(), payment_method: common_enums::enums::PaymentMethod::Card, + payment_method_type: None, connector_auth_type: auth_type, description: None, address: payment_method_session_address, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index d91f100723..af091faedc 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2834,11 +2834,11 @@ pub async fn list_payment_methods( if routing_enabled_pm_types.contains(&intermediate.payment_method_type) || routing_enabled_pms.contains(&intermediate.payment_method) { - let connector_data = api::ConnectorData::get_connector_by_name( - &state.clone().conf.connectors, - &intermediate.connector, - api::GetToken::from(intermediate.payment_method_type), + let connector_data = helpers::get_connector_data_with_token( + &state, + intermediate.connector.to_string(), None, + intermediate.payment_method_type, ) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("invalid connector name received")?; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index e9a917153f..5068ac7471 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -4205,6 +4205,8 @@ where &merchant_connector_account, merchant_recipient_data, None, + payment_data.get_payment_attempt().payment_method, + payment_data.get_payment_attempt().payment_method_type, ) .await?; @@ -5166,6 +5168,8 @@ where &merchant_connector_account, merchant_recipient_data, Some(header_payload.clone()), + payment_data.get_payment_attempt().payment_method, + payment_data.get_payment_attempt().payment_method_type, ) .await?; @@ -6153,6 +6157,8 @@ where &merchant_connector_account, None, Some(header_payload.clone()), + Some(session_connector_data.payment_method_type), + Some(session_connector_data.payment_method_sub_type), ) .await?; @@ -6480,6 +6486,8 @@ where merchant_connector_account, None, None, + payment_data.get_payment_attempt().payment_method, + payment_data.get_payment_attempt().payment_method_type, ) .await?; @@ -6889,6 +6897,8 @@ where merchant_conn_account, None, header_payload, + payment_data.get_payment_attempt().payment_method, + payment_data.get_payment_attempt().payment_method_type, ) .await?; diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 240f129833..3fd25d05d6 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -50,6 +50,8 @@ pub trait ConstructFlowSpecificData { merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + payment_method: Option, + payment_method_type: Option, ) -> RouterResult>; #[cfg(feature = "v2")] diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index 4008e0debe..af79bbd5b3 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -40,6 +40,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Approve, @@ -53,6 +55,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 68b0abf008..6ed1613aae 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -122,6 +122,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult< types::RouterData< api::Authorize, @@ -141,6 +143,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index 1190cef244..72b7fd4efe 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -24,6 +24,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Void, @@ -37,6 +39,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::PostCaptureVoid, @@ -56,6 +58,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index 61f54eb3d5..2401644506 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -26,6 +26,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Capture, @@ -39,6 +41,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index 30ed347715..9ac2b073ea 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -30,6 +30,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult< types::RouterData< api::CompleteAuthorize, @@ -49,6 +51,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 23a4c788e8..85ee5821b9 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -29,6 +29,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::IncrementalAuthorization, @@ -42,6 +44,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/post_session_tokens_flow.rs b/crates/router/src/core/payments/flows/post_session_tokens_flow.rs index 68ecc97e8a..bbcf666c70 100644 --- a/crates/router/src/core/payments/flows/post_session_tokens_flow.rs +++ b/crates/router/src/core/payments/flows/post_session_tokens_flow.rs @@ -29,6 +29,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::PostSessionTokens, @@ -42,6 +44,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 3c07948bea..eaaa76e3b3 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -38,6 +38,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult< types::RouterData, > { @@ -53,6 +55,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Reject, @@ -38,6 +40,8 @@ impl ConstructFlowSpecificData, header_payload: Option, + payment_method: Option, + payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Session, @@ -86,6 +88,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + payment_method, + payment_method_type, )) .await } diff --git a/crates/router/src/core/payments/flows/session_update_flow.rs b/crates/router/src/core/payments/flows/session_update_flow.rs index a52eef0b1d..49962f5998 100644 --- a/crates/router/src/core/payments/flows/session_update_flow.rs +++ b/crates/router/src/core/payments/flows/session_update_flow.rs @@ -29,6 +29,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, _header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin( transformers::construct_router_data_to_update_calculated_tax::< diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index 06463255f1..c1c243064c 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -48,6 +48,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::SetupMandate, @@ -61,6 +63,8 @@ impl merchant_connector_account, merchant_recipient_data, header_payload, + None, + None, )) .await } diff --git a/crates/router/src/core/payments/flows/update_metadata_flow.rs b/crates/router/src/core/payments/flows/update_metadata_flow.rs index 538ed55d11..fa683935d9 100644 --- a/crates/router/src/core/payments/flows/update_metadata_flow.rs +++ b/crates/router/src/core/payments/flows/update_metadata_flow.rs @@ -29,6 +29,8 @@ impl merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + _payment_method: Option, + _payment_method_type: Option, ) -> RouterResult { Box::pin( transformers::construct_payment_router_data_for_update_metadata( diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index bd8ecb8253..601d9bc5f9 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -41,7 +41,10 @@ use hyperswitch_domain_models::{ }, router_data::KlarnaSdkResponse, }; -use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject}; +use hyperswitch_interfaces::{ + api::ConnectorSpecifications, + integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject}, +}; use josekit::jwe; use masking::{ExposeInterface, PeekInterface, SwitchStrategy}; use num_traits::{FromPrimitive, ToPrimitive}; @@ -4511,6 +4514,7 @@ pub fn router_data_type_conversion( description: router_data.description, payment_id: router_data.payment_id, payment_method: router_data.payment_method, + payment_method_type: router_data.payment_method_type, status: router_data.status, attempt_id: router_data.attempt_id, access_token: router_data.access_token, @@ -6870,6 +6874,62 @@ pub fn get_recipient_id_for_open_banking( }, } } + +pub fn get_connector_data_with_token( + state: &SessionState, + connector_name: String, + merchant_connector_account_id: Option, + payment_method_type: api_models::enums::PaymentMethodType, +) -> RouterResult { + let connector_data_result = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &connector_name.to_string(), + // Default value, will be replaced by the result of decide_session_token_flow + api::GetToken::Connector, + merchant_connector_account_id.clone(), + ); + let connector_type = decide_session_token_flow( + &connector_data_result?.connector, + payment_method_type, + connector_name.clone(), + ); + + logger::debug!(session_token_flow=?connector_type, "Session token flow decided for payment method type: {:?}", payment_method_type); + + api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &connector_name.to_string(), + connector_type, + merchant_connector_account_id, + ) + .inspect_err(|err| { + logger::error!(session_token_error=?err); + }) +} + +/// Decides the session token flow based on payment method type +pub fn decide_session_token_flow( + connector: &hyperswitch_interfaces::connector_integration_interface::ConnectorEnum, + payment_method_type: api_models::enums::PaymentMethodType, + connector_name: String, +) -> api::GetToken { + if connector.validate_sdk_session_token_for_payment_method(&payment_method_type) { + logger::debug!( + "SDK session token validation succeeded for payment_method_type {:?} in connector {} , proceeding with Connector token flow", + payment_method_type, connector_name + ); + return api::GetToken::Connector; + } + + match payment_method_type { + api_models::enums::PaymentMethodType::ApplePay => api::GetToken::ApplePayMetadata, + api_models::enums::PaymentMethodType::GooglePay => api::GetToken::GpayMetadata, + api_models::enums::PaymentMethodType::Paypal => api::GetToken::PaypalSdkMetadata, + api_models::enums::PaymentMethodType::SamsungPay => api::GetToken::SamsungPayMetadata, + api_models::enums::PaymentMethodType::Paze => api::GetToken::PazeMetadata, + _ => api::GetToken::Connector, + } +} // This function validates the intent fulfillment time expiry set by the merchant in the request pub fn validate_intent_fulfillment_expiry( intent_fulfillment_time: u32, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 09f24e5710..0e36d1485e 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -462,16 +462,12 @@ where for (merchant_connector_account, payment_method_type, payment_method) in connector_and_supporting_payment_method_type { - let connector_type = api::GetToken::from(payment_method_type); - if let Ok(connector_data) = api::ConnectorData::get_connector_by_name( - &state.conf.connectors, - &merchant_connector_account.connector_name.to_string(), - connector_type, + if let Ok(connector_data) = helpers::get_connector_data_with_token( + state, + merchant_connector_account.connector_name.to_string(), Some(merchant_connector_account.get_id()), - ) - .inspect_err(|err| { - logger::error!(session_token_error=?err); - }) { + payment_method_type, + ) { #[cfg(feature = "v1")] { let new_session_connector_data = api::SessionConnectorData::new( @@ -506,17 +502,3 @@ where Ok(false) } } - -impl From for api::GetToken { - fn from(value: api_models::enums::PaymentMethodType) -> Self { - match value { - api_models::enums::PaymentMethodType::GooglePay => Self::GpayMetadata, - api_models::enums::PaymentMethodType::ApplePay => Self::ApplePayMetadata, - api_models::enums::PaymentMethodType::SamsungPay => Self::SamsungPayMetadata, - api_models::enums::PaymentMethodType::Paypal => Self::PaypalSdkMetadata, - api_models::enums::PaymentMethodType::Paze => Self::PazeMetadata, - api_models::enums::PaymentMethodType::AmazonPay => Self::AmazonPayMetadata, - _ => Self::Connector, - } - } -} diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 2376dd93a9..aa2e75b264 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -306,13 +306,11 @@ impl Domain Some(api::SessionConnectorData::new( payment_method_type, @@ -386,17 +384,3 @@ impl Domain for api::GetToken { - fn from(value: api_models::enums::PaymentMethodType) -> Self { - match value { - api_models::enums::PaymentMethodType::GooglePay => Self::GpayMetadata, - api_models::enums::PaymentMethodType::ApplePay => Self::ApplePayMetadata, - api_models::enums::PaymentMethodType::SamsungPay => Self::SamsungPayMetadata, - api_models::enums::PaymentMethodType::Paypal => Self::PaypalSdkMetadata, - api_models::enums::PaymentMethodType::Paze => Self::PazeMetadata, - api_models::enums::PaymentMethodType::AmazonPay => Self::AmazonPayMetadata, - _ => Self::Connector, - } - } -} diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index a9a87497b6..e0859f7642 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -135,6 +135,7 @@ where attempt_id: payment_data.payment_attempt.get_id().to_owned(), status: payment_data.payment_attempt.status, payment_method: diesel_models::enums::PaymentMethod::default(), + payment_method_type: payment_data.payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: payment_data.address.clone(), @@ -468,6 +469,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>( .to_owned(), status: payment_data.payment_attempt.status, payment_method, + payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype), connector_auth_type: auth_type, description: payment_data .payment_intent @@ -816,6 +818,7 @@ pub async fn construct_payment_router_data_for_capture<'a>( .to_owned(), status: payment_data.payment_attempt.status, payment_method, + payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype), connector_auth_type: auth_type, description: payment_data .payment_intent @@ -949,6 +952,7 @@ pub async fn construct_router_data_for_psync<'a>( attempt_id: attempt.get_id().get_string_repr().to_owned(), status: attempt.status, payment_method: attempt.payment_method_type, + payment_method_type: Some(attempt.payment_method_subtype), connector_auth_type: auth_type, description: payment_intent .description @@ -1245,6 +1249,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( let apple_pay_recurring_details = payment_data .payment_intent .feature_metadata + .clone() .and_then(|feature_metadata| feature_metadata.apple_pay_recurring_details) .map(|apple_pay_recurring_details| { ForeignInto::foreign_into((apple_pay_recurring_details, apple_pay_amount)) @@ -1257,6 +1262,10 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( .clone() .and_then(|tax| tax.get_default_tax_amount()); + let payment_attempt = payment_data.get_payment_attempt(); + let payment_method = Some(payment_attempt.payment_method_type); + let payment_method_type = Some(payment_attempt.payment_method_subtype); + // TODO: few fields are repeated in both routerdata and request let request = types::PaymentsSessionData { amount: payment_data @@ -1285,6 +1294,8 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( metadata: payment_data.payment_intent.metadata, order_tax_amount, shipping_cost: payment_data.payment_intent.amount_details.shipping_cost, + payment_method, + payment_method_type, }; // TODO: evaluate the fields in router data, if they are required or not @@ -1301,6 +1312,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( attempt_id: "".to_string(), status: enums::AttemptStatus::Started, payment_method: enums::PaymentMethod::Wallet, + payment_method_type, connector_auth_type: auth_type, description: payment_data .payment_intent @@ -1531,6 +1543,7 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>( .to_owned(), status: payment_data.payment_attempt.status, payment_method, + payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype), connector_auth_type: auth_type, description: payment_data .payment_intent @@ -1601,6 +1614,8 @@ pub async fn construct_payment_router_data<'a, F, T>( merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, header_payload: Option, + payment_method: Option, + payment_method_type: Option, ) -> RouterResult> where T: TryFrom>, @@ -1609,8 +1624,6 @@ where error_stack::Report: From<>>::Error>, { - let (payment_method, router_data); - fp_utils::when(merchant_connector_account.is_disabled(), || { Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled) })?; @@ -1623,11 +1636,16 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while parsing value for ConnectorAuthType")?; - payment_method = payment_data + let payment_method = payment_data .payment_attempt .payment_method - .or(payment_data.payment_attempt.payment_method) - .get_required_value("payment_method_type")?; + .or(payment_method) + .get_required_value("payment_method")?; + + let payment_method_type = payment_data + .payment_attempt + .payment_method_type + .or(payment_method_type); let resource_id = match payment_data .payment_attempt @@ -1781,7 +1799,7 @@ where }); crate::logger::debug!("unified address details {:?}", unified_address); - router_data = types::RouterData { + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), customer_id, @@ -1795,6 +1813,7 @@ where attempt_id: payment_data.payment_attempt.attempt_id.clone(), status: payment_data.payment_attempt.status, payment_method, + payment_method_type, connector_auth_type: auth_type, description: payment_data.payment_intent.description.clone(), address: unified_address, @@ -1992,6 +2011,7 @@ pub async fn construct_payment_router_data_for_update_metadata<'a>( attempt_id: payment_data.payment_attempt.attempt_id.clone(), status: payment_data.payment_attempt.status, payment_method, + payment_method_type: payment_data.payment_attempt.payment_method_type, connector_auth_type: auth_type, description: payment_data.payment_intent.description.clone(), address: unified_address, @@ -5199,6 +5219,8 @@ impl TryFrom> for types::PaymentsSessionD metadata: payment_data.payment_intent.metadata, order_tax_amount, shipping_cost: payment_data.payment_intent.amount_details.shipping_cost, + payment_method: Some(payment_data.payment_attempt.payment_method_type), + payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype), }) } } @@ -5302,6 +5324,8 @@ impl TryFrom> for types::PaymentsSessionD order_tax_amount, shipping_cost, metadata, + payment_method: payment_data.payment_attempt.payment_method, + payment_method_type: payment_data.payment_attempt.payment_method_type, }) } } diff --git a/crates/router/src/core/relay/utils.rs b/crates/router/src/core/relay/utils.rs index fb54ec4693..b372f903ad 100644 --- a/crates/router/src/core/relay/utils.rs +++ b/crates/router/src/core/relay/utils.rs @@ -82,6 +82,7 @@ pub async fn construct_relay_refund_router_data( attempt_id: IRRELEVANT_PAYMENT_ATTEMPT_ID.to_string(), status: common_enums::AttemptStatus::Charged, payment_method: common_enums::PaymentMethod::default(), + payment_method_type: None, connector_auth_type, description: None, address: hyperswitch_domain_models::payment_address::PaymentAddress::default(), diff --git a/crates/router/src/core/unified_authentication_service/utils.rs b/crates/router/src/core/unified_authentication_service/utils.rs index 586dbed99f..3cfb2d31fa 100644 --- a/crates/router/src/core/unified_authentication_service/utils.rs +++ b/crates/router/src/core/unified_authentication_service/utils.rs @@ -87,6 +87,7 @@ pub fn construct_uas_router_data( attempt_id: IRRELEVANT_ATTEMPT_ID_IN_AUTHENTICATION_FLOW.to_owned(), status: common_enums::AttemptStatus::default(), payment_method, + payment_method_type: None, connector_auth_type: auth_type, description: None, address: address.unwrap_or_default(), diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 5f9cf67223..753129f031 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -167,6 +167,7 @@ pub async fn construct_payout_router_data<'a, F>( attempt_id: "".to_string(), status: enums::AttemptStatus::Failure, payment_method: enums::PaymentMethod::default(), + payment_method_type: None, connector_auth_type, description: None, address, @@ -331,6 +332,7 @@ pub async fn construct_refund_router_data<'a, F>( attempt_id: payment_attempt.id.get_string_repr().to_string().clone(), status, payment_method: payment_method_type, + payment_method_type: Some(payment_attempt.payment_method_subtype), connector_auth_type: auth_type, description: None, // Does refund need shipping/billing address ? @@ -453,10 +455,11 @@ pub async fn construct_refund_router_data<'a, F>( let (payment_amount, currency) = money; - let payment_method_type = payment_attempt + let payment_method = payment_attempt .payment_method - .get_required_value("payment_method_type") + .get_required_value("payment_method") .change_context(errors::ApiErrorResponse::InternalServerError)?; + let merchant_connector_account_id_or_connector_name = payment_attempt .merchant_connector_id .as_ref() @@ -541,7 +544,8 @@ pub async fn construct_refund_router_data<'a, F>( payment_id: payment_attempt.payment_id.get_string_repr().to_owned(), attempt_id: payment_attempt.attempt_id.clone(), status, - payment_method: payment_method_type, + payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, // Does refund need shipping/billing address ? @@ -991,7 +995,8 @@ pub async fn construct_accept_dispute_router_data<'a>( .change_context(errors::ApiErrorResponse::InternalServerError)?; let payment_method = payment_attempt .payment_method - .get_required_value("payment_method_type")?; + .get_required_value("payment_method")?; + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), @@ -1001,6 +1006,7 @@ pub async fn construct_accept_dispute_router_data<'a>( attempt_id: payment_attempt.attempt_id.clone(), status: payment_attempt.status, payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), @@ -1100,6 +1106,7 @@ pub async fn construct_submit_evidence_router_data<'a>( let payment_method = payment_attempt .payment_method .get_required_value("payment_method_type")?; + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), @@ -1109,6 +1116,7 @@ pub async fn construct_submit_evidence_router_data<'a>( attempt_id: payment_attempt.attempt_id.clone(), status: payment_attempt.status, payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), @@ -1206,6 +1214,7 @@ pub async fn construct_upload_file_router_data<'a>( let payment_method = payment_attempt .payment_method .get_required_value("payment_method_type")?; + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), @@ -1215,6 +1224,7 @@ pub async fn construct_upload_file_router_data<'a>( attempt_id: payment_attempt.attempt_id.clone(), status: payment_attempt.status, payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), @@ -1304,6 +1314,7 @@ pub async fn construct_dispute_list_router_data<'a>( attempt_id: consts::IRRELEVANT_PAYMENT_ATTEMPT_ID.to_owned(), status: common_enums::AttemptStatus::default(), payment_method: common_enums::PaymentMethod::default(), + payment_method_type: None, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), @@ -1390,6 +1401,7 @@ pub async fn construct_dispute_sync_router_data<'a>( let payment_method = payment_attempt .payment_method .get_required_value("payment_method")?; + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), @@ -1399,6 +1411,7 @@ pub async fn construct_dispute_sync_router_data<'a>( attempt_id: payment_attempt.attempt_id.clone(), status: payment_attempt.status, payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), @@ -1529,6 +1542,7 @@ pub async fn construct_payments_dynamic_tax_calculation_router_data( tenant_id: state.tenant.tenant_id.clone(), status: payment_attempt.status, payment_method: diesel_models::enums::PaymentMethod::default(), + payment_method_type: payment_attempt.payment_method_type, connector_auth_type, description: None, address: payment_data.address.clone(), @@ -1626,6 +1640,7 @@ pub async fn construct_defend_dispute_router_data<'a>( let payment_method = payment_attempt .payment_method .get_required_value("payment_method_type")?; + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_context.get_merchant_account().get_id().clone(), @@ -1635,6 +1650,7 @@ pub async fn construct_defend_dispute_router_data<'a>( attempt_id: payment_attempt.attempt_id.clone(), status: payment_attempt.status, payment_method, + payment_method_type: payment_attempt.payment_method_type, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), @@ -1741,6 +1757,7 @@ pub async fn construct_retrieve_file_router_data<'a>( attempt_id: IRRELEVANT_ATTEMPT_ID_IN_DISPUTE_FLOW.to_string(), status: diesel_models::enums::AttemptStatus::default(), payment_method: diesel_models::enums::PaymentMethod::default(), + payment_method_type: None, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index ec10eb2fb7..c892adead5 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -95,6 +95,7 @@ pub async fn construct_webhook_router_data( attempt_id: IRRELEVANT_ATTEMPT_ID_IN_SOURCE_VERIFICATION_FLOW.to_string(), status: diesel_models::enums::AttemptStatus::default(), payment_method: diesel_models::enums::PaymentMethod::default(), + payment_method_type: None, connector_auth_type: auth_type, description: None, address: PaymentAddress::default(), diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 8205f7c7b9..a2958d7508 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -1263,6 +1263,7 @@ impl ForeignFrom<(&RouterData, T2) tenant_id: data.tenant_id.clone(), status: data.status, payment_method: data.payment_method, + payment_method_type: data.payment_method_type, connector_auth_type: data.connector_auth_type.clone(), description: data.description.clone(), address: data.address.clone(), @@ -1338,6 +1339,7 @@ impl tenant_id: data.tenant_id.clone(), status: data.status, payment_method: data.payment_method, + payment_method_type: data.payment_method_type, connector_auth_type: data.connector_auth_type.clone(), description: data.description.clone(), address: data.address.clone(), diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index fae9d3ed19..bb8a52b62d 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -99,6 +99,7 @@ impl VerifyConnectorData { access_token, session_token: None, payment_method: storage_enums::PaymentMethod::Card, + payment_method_type: None, amount_captured: None, minor_amount_captured: None, preprocessing_id: None, diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index f548245d17..a7c1e60898 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -510,6 +510,7 @@ pub trait ConnectorActions: Connector { .map_or(enums::AuthenticationType::NoThreeDs, |a| a) }), payment_method: enums::PaymentMethod::Card, + payment_method_type: None, connector_auth_type: self.get_auth_token(), description: Some("This is a test".to_string()), payment_method_status: None,