From f71090a94c55c421ffffd1d7608c33bac24a84e4 Mon Sep 17 00:00:00 2001 From: sweta-sharma <77436883+swetasharma03@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:54:32 +0530 Subject: [PATCH] feat(connector): [BRAINTREE] Paypal wallet added (#8984) --- api-reference/v1/openapi_spec_v1.json | 40 ++++++++++ api-reference/v2/openapi_spec_v2.json | 40 ++++++++++ crates/api_models/src/payments.rs | 23 ++++++ .../connector_configs/toml/development.toml | 5 ++ crates/connector_configs/toml/production.toml | 5 ++ crates/connector_configs/toml/sandbox.toml | 5 ++ .../src/connectors/braintree.rs | 11 +++ .../src/connectors/braintree/transformers.rs | 79 ++++++++++++++++++- crates/openapi/src/openapi.rs | 2 + crates/openapi/src/openapi_v2.rs | 2 + .../src/core/payments/flows/session_flow.rs | 2 + 11 files changed, 212 insertions(+), 2 deletions(-) diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index 7dc338b101..ba2e1252e7 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -28196,6 +28196,12 @@ } } }, + "PaypalFlow": { + "type": "string", + "enum": [ + "checkout" + ] + }, "PaypalRedirection": { "type": "object", "properties": { @@ -28226,6 +28232,40 @@ }, "sdk_next_action": { "$ref": "#/components/schemas/SdkNextAction" + }, + "client_token": { + "type": "string", + "description": "Authorization token used by client to initiate sdk", + "nullable": true + }, + "transaction_info": { + "allOf": [ + { + "$ref": "#/components/schemas/PaypalTransactionInfo" + } + ], + "nullable": true + } + } + }, + "PaypalTransactionInfo": { + "type": "object", + "required": [ + "flow", + "currency_code", + "total_price" + ], + "properties": { + "flow": { + "$ref": "#/components/schemas/PaypalFlow" + }, + "currency_code": { + "$ref": "#/components/schemas/Currency" + }, + "total_price": { + "type": "string", + "description": "Total price", + "example": "38.02" } } }, diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 0d7cbd6279..bf4404b607 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -21900,6 +21900,12 @@ } } }, + "PaypalFlow": { + "type": "string", + "enum": [ + "checkout" + ] + }, "PaypalRedirection": { "type": "object", "properties": { @@ -21930,6 +21936,40 @@ }, "sdk_next_action": { "$ref": "#/components/schemas/SdkNextAction" + }, + "client_token": { + "type": "string", + "description": "Authorization token used by client to initiate sdk", + "nullable": true + }, + "transaction_info": { + "allOf": [ + { + "$ref": "#/components/schemas/PaypalTransactionInfo" + } + ], + "nullable": true + } + } + }, + "PaypalTransactionInfo": { + "type": "object", + "required": [ + "flow", + "currency_code", + "total_price" + ], + "properties": { + "flow": { + "$ref": "#/components/schemas/PaypalFlow" + }, + "currency_code": { + "$ref": "#/components/schemas/Currency" + }, + "total_price": { + "type": "string", + "description": "Total price", + "example": "38.02" } } }, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index dfc2808814..5d8c7a3f0b 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -7966,6 +7966,25 @@ pub struct KlarnaSessionTokenResponse { pub session_id: String, } +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum PaypalFlow { + Checkout, +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] +pub struct PaypalTransactionInfo { + /// Paypal flow type + #[schema(value_type = PaypalFlow, example = "checkout")] + pub flow: PaypalFlow, + /// Currency code + #[schema(value_type = Currency, example = "USD")] + pub currency_code: api_enums::Currency, + /// Total price + #[schema(value_type = String, example = "38.02")] + pub total_price: StringMajorUnit, +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] #[serde(rename_all = "lowercase")] pub struct PaypalSessionTokenResponse { @@ -7975,6 +7994,10 @@ pub struct PaypalSessionTokenResponse { pub session_token: String, /// The next action for the sdk (ex: calling confirm or sync call) pub sdk_next_action: SdkNextAction, + /// Authorization token used by client to initiate sdk + pub client_token: Option, + /// The transaction info Paypal requires + pub transaction_info: Option, } #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 0bc14f42dc..8ea4a288da 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -1405,6 +1405,8 @@ merchant_secret="Source verification key" payment_method_type = "apple_pay" [[braintree.wallet]] payment_method_type = "google_pay" +[[braintree.wallet]] + payment_method_type = "paypal" [braintree.connector_webhook_details] merchant_secret="Source verification key" @@ -1455,6 +1457,9 @@ placeholder="Enter Display Name" required=true type="Text" +[braintree.metadata.paypal_sdk] +client_id="Client ID" + [cashtocode] [[cashtocode.reward]] payment_method_type = "classic" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 99042fae46..6ebc6282a3 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -738,6 +738,8 @@ payment_method_type = "UnionPay" payment_method_type = "apple_pay" [[braintree.wallet]] payment_method_type = "google_pay" +[[braintree.wallet]] + payment_method_type = "paypal" [braintree.connector_auth.SignatureKey] api_key = "Public Key" @@ -787,6 +789,9 @@ placeholder="Enter Display Name" required=true type="Text" +[braintree.metadata.paypal_sdk] +client_id="Client ID" + [bambora] [[bambora.credit]] payment_method_type = "Mastercard" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 7aba1e4db0..70a45b657d 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -1405,6 +1405,8 @@ payment_method_type = "UnionPay" payment_method_type = "apple_pay" [[braintree.wallet]] payment_method_type = "google_pay" +[[braintree.wallet]] + payment_method_type = "paypal" [braintree.connector_webhook_details] merchant_secret = "Source verification key" @@ -1454,6 +1456,9 @@ placeholder="Enter Display Name" required=true type="Text" +[braintree.metadata.paypal_sdk] +client_id="Client ID" + [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 614f6b7173..c670128a44 100644 --- a/crates/hyperswitch_connectors/src/connectors/braintree.rs +++ b/crates/hyperswitch_connectors/src/connectors/braintree.rs @@ -1404,6 +1404,16 @@ static BRAINTREE_SUPPORTED_PAYMENT_METHODS: LazyLock = specific_features: None, }, ); + braintree_supported_payment_methods.add( + enums::PaymentMethod::Wallet, + enums::PaymentMethodType::Paypal, + PaymentMethodDetails { + mandates: enums::FeatureStatus::NotSupported, + refunds: enums::FeatureStatus::Supported, + supported_capture_methods: supported_capture_methods.clone(), + specific_features: None, + }, + ); braintree_supported_payment_methods }); @@ -1441,6 +1451,7 @@ impl ConnectorSpecifications for Braintree { vec![ enums::PaymentMethodType::ApplePay, enums::PaymentMethodType::GooglePay, + enums::PaymentMethodType::Paypal, ] } } diff --git a/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs b/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs index c3d502f89d..4f9b555353 100644 --- a/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs @@ -5,7 +5,7 @@ use api_models::{ }; use common_enums::enums; use common_utils::{ - ext_traits::ValueExt, + ext_traits::{OptionExt, ValueExt}, pii, types::{AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector}, }; @@ -61,6 +61,8 @@ pub const CHARGE_GOOGLE_PAY_MUTATION: &str = "mutation ChargeGPay($input: Charge 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 const CHARGE_PAYPAL_MUTATION: &str = "mutation ChargePaypal($input: ChargePaymentMethodInput!) { chargePaymentMethod(input: $input) { transaction { id status amount { value currencyCode } } } }"; +pub const AUTHORIZE_PAYPAL_MUTATION: &str = "mutation authorizePaypal($input: AuthorizePaymentMethodInput!) { authorizePaymentMethod(input: $input) { transaction { id legacyId amount { value currencyCode } status } } }"; pub type CardPaymentRequest = GenericBraintreeRequest; pub type MandatePaymentRequest = GenericBraintreeRequest; @@ -434,6 +436,29 @@ impl TryFrom<&BraintreeRouterData<&types::PaymentsAuthorizeRouterData>> }, })) } + WalletData::PaypalSdk(ref req_wallet) => { + let payment_method_id = req_wallet.token.clone(); + let query = match item.router_data.request.is_auto_capture()? { + true => CHARGE_PAYPAL_MUTATION.to_string(), + false => AUTHORIZE_PAYPAL_MUTATION.to_string(), + }; + Ok(Self::Wallet(BraintreeWalletRequest { + query, + variables: GenericVariableInput { + input: WalletPaymentInput { + payment_method_id: payment_method_id.clone().into(), + 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"), ) @@ -1543,13 +1568,26 @@ impl TryFrom for payment_types::PaypalFlow { + fn from(item: PaypalFlow) -> Self { + match item { + PaypalFlow::Checkout => Self::Checkout, + } + } +} + impl ForeignTryFrom<( PaymentsSessionResponseRouterData, @@ -1704,6 +1742,43 @@ impl ), )) } + Some(common_enums::PaymentMethodType::Paypal) => { + let paypal_sdk_data = data + .connector_meta_data + .clone() + .parse_value::( + "PaypalSdkSessionTokenData", + ) + .change_context(errors::ConnectorError::NoConnectorMetaData) + .attach_printable("Failed to parse paypal_sdk metadata.".to_string())?; + + SessionToken::Paypal(Box::new( + api_models::payments::PaypalSessionTokenResponse { + connector: data.connector.clone(), + session_token: paypal_sdk_data.data.client_id, + sdk_next_action: api_models::payments::SdkNextAction { + next_action: api_models::payments::NextActionCall::Confirm, + }, + client_token: Some( + res.data.create_client_token.client_token.clone().expose(), + ), + transaction_info: Some( + api_models::payments::PaypalTransactionInfo { + flow: PaypalFlow::Checkout.into(), + currency_code: data.request.currency, + total_price: StringMajorUnitForConnector + .convert( + MinorUnit::new(data.request.amount), + data.request.currency, + ) + .change_context( + errors::ConnectorError::AmountConversionFailed, + )?, + }, + ), + }, + )) + } _ => { return Err(errors::ConnectorError::NotImplemented( format!( diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index b87d57370f..567f54c2d2 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -537,6 +537,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::GooglePayThirdPartySdkData, api_models::payments::KlarnaSessionTokenResponse, api_models::payments::PaypalSessionTokenResponse, + api_models::payments::PaypalFlow, + api_models::payments::PaypalTransactionInfo, api_models::payments::ApplepaySessionTokenResponse, api_models::payments::SdkNextAction, api_models::payments::NextActionCall, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index c4b67a2cd5..4e4bb85e4a 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -493,6 +493,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::GooglePayThirdPartySdkData, api_models::payments::KlarnaSessionTokenResponse, api_models::payments::PaypalSessionTokenResponse, + api_models::payments::PaypalFlow, + api_models::payments::PaypalTransactionInfo, api_models::payments::ApplepaySessionTokenResponse, api_models::payments::SdkNextAction, api_models::payments::NextActionCall, diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 244646bc2c..de4454d99c 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -1250,6 +1250,8 @@ fn create_paypal_sdk_session_token( sdk_next_action: payment_types::SdkNextAction { next_action: payment_types::NextActionCall::PostSessionTokens, }, + client_token: None, + transaction_info: None, }, )), }),