diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index cf0de8e088..a5647e7226 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -436,6 +436,7 @@ pub enum PaymentMethodType { Klarna, MbWay, MobilePay, + Multibanco, OnlineBankingCzechRepublic, OnlineBankingFinland, OnlineBankingPoland, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 206b504d3b..2d4a539c1e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -807,6 +807,12 @@ pub struct AchBillingDetails { pub email: Email, } +#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] +pub struct MultibancoBillingDetails { + #[schema(value_type = String, example = "example@me.com")] + pub email: Email, +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] pub struct SepaAndBacsBillingDetails { /// The Email ID for SEPA and BACS billing @@ -866,6 +872,10 @@ pub enum BankTransferData { /// The billing details for SEPA billing_details: SepaAndBacsBillingDetails, }, + MultibancoBankTransfer { + /// The billing details for Multibanco + billing_details: MultibancoBillingDetails, + }, } #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, ToSchema, Eq, PartialEq)] @@ -1241,6 +1251,8 @@ pub enum BankTransferInstructions { SepaBankInstructions(Box), /// The instructions for BACS bank transactions BacsBankInstructions(Box), + /// The instructions for Multibanco bank transactions + Multibanco(Box), } #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] @@ -1264,6 +1276,14 @@ pub struct BacsBankTransferInstructions { pub sort_code: Secret, } +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] +pub struct MultibancoTransferInstructions { + #[schema(value_type = String, example = "122385736258")] + pub reference: Secret, + #[schema(value_type = String, example = "12345")] + pub entity: String, +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] pub struct AchTransfer { #[schema(value_type = String, example = "122385736258")] diff --git a/crates/router/src/connector/stripe.rs b/crates/router/src/connector/stripe.rs index 9bfd7e9ba8..4f4f27c7b0 100644 --- a/crates/router/src/connector/stripe.rs +++ b/crates/router/src/connector/stripe.rs @@ -130,12 +130,13 @@ impl &self, req: &types::PaymentsPreProcessingRouterData, ) -> CustomResult, errors::ConnectorError> { - let req = stripe::StripeAchSourceRequest::try_from(req)?; + let req = stripe::StripeCreditTransferSourceRequest::try_from(req)?; let pre_processing_request = types::RequestBody::log_and_get_request_body( &req, - utils::Encode::::url_encode, + utils::Encode::::url_encode, ) .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(pre_processing_request)) } @@ -721,7 +722,8 @@ impl match &req.request.payment_method_data { api_models::payments::PaymentMethodData::BankTransfer(bank_transfer_data) => { match bank_transfer_data.deref() { - api_models::payments::BankTransferData::AchBankTransfer { .. } => { + api_models::payments::BankTransferData::AchBankTransfer { .. } + | api_models::payments::BankTransferData::MultibancoBankTransfer { .. } => { Ok(format!("{}{}", self.base_url(connectors), "v1/charges")) } _ => Ok(format!( @@ -1772,7 +1774,9 @@ impl api::IncomingWebhook for Stripe { } stripe::WebhookEventType::ChargeSucceeded => { if let Some(stripe::WebhookPaymentMethodDetails { - payment_method: stripe::WebhookPaymentMethodType::AchCreditTransfer, + payment_method: + stripe::WebhookPaymentMethodType::AchCreditTransfer + | stripe::WebhookPaymentMethodType::MultibancoBankTransfers, }) = details.event_data.event_object.payment_method_details { api::IncomingWebhookEvent::PaymentIntentSuccess diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 1ce506fbec..7a9c9ef4e2 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -15,10 +15,7 @@ use uuid::Uuid; use crate::{ collect_missing_value_keys, - connector::{ - self, - utils::{ApplePay, RouterData}, - }, + connector::utils::{ApplePay, PaymentsPreProcessingData, RouterData}, core::errors, services, types::{self, api, storage::enums, transformers::ForeignFrom}, @@ -292,7 +289,13 @@ pub struct StripeBankRedirectData { } #[derive(Debug, Eq, PartialEq, Serialize)] -pub struct AchBankTransferData { +pub struct AchTransferData { + #[serde(rename = "owner[email]")] + pub email: Email, +} + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct MultibancoTransferData { #[serde(rename = "owner[email]")] pub email: Email, } @@ -326,12 +329,31 @@ pub struct SepaBankTransferData { } #[derive(Debug, Eq, PartialEq, Serialize)] -pub struct StripeAchSourceRequest { +#[serde(untagged)] +pub enum StripeCreditTransferSourceRequest { + AchBankTansfer(AchCreditTransferSourceRequest), + MultibancoBankTansfer(MultibancoCreditTransferSourceRequest), +} + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct AchCreditTransferSourceRequest { #[serde(rename = "type")] pub transfer_type: StripePaymentMethodType, #[serde(flatten)] - pub payment_method_data: AchBankTransferData, - pub currency: String, + pub payment_method_data: AchTransferData, + pub currency: enums::Currency, +} + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct MultibancoCreditTransferSourceRequest { + #[serde(rename = "type")] + pub transfer_type: StripePaymentMethodType, + #[serde(flatten)] + pub payment_method_data: MultibancoTransferData, + pub currency: enums::Currency, + pub amount: Option, + #[serde(rename = "redirect[return_url]")] + pub return_url: Option, } // Remove untagged when Deserialize is added @@ -395,9 +417,10 @@ pub struct BankTransferData { #[derive(Debug, Eq, PartialEq, Serialize)] #[serde(untagged)] pub enum StripeBankTransferData { - AchBankTransfer(Box), + AchBankTransfer(Box), SepaBankTransfer(Box), BacsBankTransfers(Box), + MultibancoBankTransfers(Box), } #[derive(Debug, Eq, PartialEq, Serialize)] @@ -494,6 +517,7 @@ pub enum StripePaymentMethodType { #[serde(rename = "p24")] Przelewy24, CustomerBalance, + Multibanco, } #[derive(Debug, Eq, PartialEq, Serialize, Clone)] @@ -1078,13 +1102,24 @@ fn create_stripe_payment_method( match bank_transfer_data.deref() { payments::BankTransferData::AchBankTransfer { billing_details } => Ok(( StripePaymentMethodData::BankTransfer(StripeBankTransferData::AchBankTransfer( - Box::new(AchBankTransferData { + Box::new(AchTransferData { email: billing_details.email.to_owned(), }), )), StripePaymentMethodType::AchCreditTransfer, StripeBillingAddress::default(), )), + payments::BankTransferData::MultibancoBankTransfer { billing_details } => Ok(( + StripePaymentMethodData::BankTransfer( + StripeBankTransferData::MultibancoBankTransfers(Box::new( + MultibancoTransferData { + email: billing_details.email.to_owned(), + }, + )), + ), + StripePaymentMethodType::Multibanco, + StripeBillingAddress::default(), + )), payments::BankTransferData::SepaBankTransfer { billing_details, country, @@ -1444,7 +1479,10 @@ pub struct PaymentIntentResponse { #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] pub struct StripeSourceResponse { pub id: String, - pub ach_credit_transfer: AchCreditTransferResponse, + #[serde(skip_serializing_if = "Option::is_none")] + pub ach_credit_transfer: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub multibanco: Option, pub receiver: AchReceiverDetails, pub status: StripePaymentStatus, } @@ -1457,6 +1495,12 @@ pub struct AchCreditTransferResponse { pub swift_code: Secret, } +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct MultibancoCreditTansferResponse { + pub reference: Secret, + pub entity: Secret, +} + #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] pub struct AchReceiverDetails { pub amount_received: i64, @@ -1597,7 +1641,8 @@ impl ForeignFrom<(Option, String)> for types::Mandat | StripePaymentMethodOptions::Sepa {} | StripePaymentMethodOptions::Bancontact {} | StripePaymentMethodOptions::Przelewy24 {} - | StripePaymentMethodOptions::CustomerBalance {} => None, + | StripePaymentMethodOptions::CustomerBalance {} + | StripePaymentMethodOptions::Multibanco {} => None, }), payment_method_id: Some(payment_method_id), } @@ -2088,6 +2133,7 @@ pub enum StripePaymentMethodOptions { #[serde(rename = "p24")] Przelewy24 {}, CustomerBalance {}, + Multibanco {}, } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -2127,23 +2173,42 @@ impl TryFrom<&types::PaymentsCaptureRouterData> for CaptureRequest { } } -impl TryFrom<&types::PaymentsPreProcessingRouterData> for StripeAchSourceRequest { +impl TryFrom<&types::PaymentsPreProcessingRouterData> for StripeCreditTransferSourceRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result { - Ok(Self { - transfer_type: StripePaymentMethodType::AchCreditTransfer, - payment_method_data: AchBankTransferData { - email: connector::utils::PaymentsPreProcessingData::get_email(&item.request)?, - }, - currency: item - .request - .currency - .get_required_value("currency") - .change_context(errors::ConnectorError::MissingRequiredField { - field_name: "currency", - })? - .to_string(), - }) + let currency = item.request.get_currency()?; + + match &item.request.payment_method_data { + Some(payments::PaymentMethodData::BankTransfer(bank_transfer_data)) => { + match **bank_transfer_data { + payments::BankTransferData::MultibancoBankTransfer { .. } => Ok( + Self::MultibancoBankTansfer(MultibancoCreditTransferSourceRequest { + transfer_type: StripePaymentMethodType::Multibanco, + currency, + payment_method_data: MultibancoTransferData { + email: item.request.get_email()?, + }, + amount: Some(item.request.get_amount()?), + return_url: Some(item.get_return_url()?), + }), + ), + payments::BankTransferData::AchBankTransfer { .. } => { + Ok(Self::AchBankTansfer(AchCreditTransferSourceRequest { + transfer_type: StripePaymentMethodType::AchCreditTransfer, + payment_method_data: AchTransferData { + email: item.request.get_email()?, + }, + currency, + })) + } + _ => Err(errors::ConnectorError::NotImplemented( + "Bank Transfer Method".to_string(), + ) + .into()), + } + } + _ => Err(errors::ConnectorError::NotImplemented("Payment Method".to_string()).into()), + } } } @@ -2342,6 +2407,7 @@ pub struct WebhookStatusObjectData { #[serde(rename_all = "snake_case")] pub enum WebhookPaymentMethodType { AchCreditTransfer, + MultibancoBankTransfers, #[serde(other)] Unknown, } @@ -2546,11 +2612,18 @@ impl match bank_transfer_data.deref() { payments::BankTransferData::AchBankTransfer { billing_details } => { Ok(Self::BankTransfer(StripeBankTransferData::AchBankTransfer( - Box::new(AchBankTransferData { + Box::new(AchTransferData { email: billing_details.email.to_owned(), }), ))) } + payments::BankTransferData::MultibancoBankTransfer { billing_details } => Ok( + Self::BankTransfer(StripeBankTransferData::MultibancoBankTransfers( + Box::new(MultibancoTransferData { + email: billing_details.email.to_owned(), + }), + )), + ), payments::BankTransferData::SepaBankTransfer { country, .. } => Ok( Self::BankTransfer(StripeBankTransferData::SepaBankTransfer(Box::new( SepaBankTransferData { @@ -2594,7 +2667,8 @@ pub fn get_bank_transfer_request_data( bank_transfer_data: &api_models::payments::BankTransferData, ) -> CustomResult, errors::ConnectorError> { match bank_transfer_data { - api_models::payments::BankTransferData::AchBankTransfer { .. } => { + api_models::payments::BankTransferData::AchBankTransfer { .. } + | api_models::payments::BankTransferData::MultibancoBankTransfer { .. } => { let req = ChargesRequest::try_from(req)?; let request = types::RequestBody::log_and_get_request_body( &req, @@ -2621,7 +2695,8 @@ pub fn get_bank_transfer_authorize_response( bank_transfer_data: &api_models::payments::BankTransferData, ) -> CustomResult { match bank_transfer_data { - api_models::payments::BankTransferData::AchBankTransfer { .. } => { + api_models::payments::BankTransferData::AchBankTransfer { .. } + | api_models::payments::BankTransferData::MultibancoBankTransfer { .. } => { let response: ChargesResponse = res .response .parse_struct("ChargesResponse") diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 65278d6c21..0cb76064ea 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -171,6 +171,8 @@ impl RouterData for types::RouterData Result; fn get_payment_method_type(&self) -> Result; + fn get_currency(&self) -> Result; + fn get_amount(&self) -> Result; } impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { @@ -182,6 +184,12 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { .to_owned() .ok_or_else(missing_field_err("payment_method_type")) } + fn get_currency(&self) -> Result { + self.currency.ok_or_else(missing_field_err("currency")) + } + fn get_amount(&self) -> Result { + self.amount.ok_or_else(missing_field_err("amount")) + } } pub trait PaymentsAuthorizeRequestData { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 9008b97b97..c5a25b993e 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -838,7 +838,8 @@ where //TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check let router_data_and_should_continue_payment = match payment_data.payment_method_data.clone() { Some(api_models::payments::PaymentMethodData::BankTransfer(data)) => match data.deref() { - api_models::payments::BankTransferData::AchBankTransfer { .. } => { + api_models::payments::BankTransferData::AchBankTransfer { .. } + | api_models::payments::BankTransferData::MultibancoBankTransfer { .. } => { if payment_data.payment_attempt.preprocessing_step_id.is_none() { ( router_data.preprocessing_steps(state, connector).await?, diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 7a58fa5442..a9abcfde37 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -342,9 +342,10 @@ impl TryFrom for types::PaymentsPreProcessingData fn try_from(data: types::PaymentsAuthorizeData) -> Result { Ok(Self { + payment_method_data: Some(data.payment_method_data), + amount: Some(data.amount), email: data.email, currency: Some(data.currency), - amount: Some(data.amount), payment_method_type: data.payment_method_type, }) } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index a3ead3fb17..64cb0bc5e2 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -951,7 +951,10 @@ impl TryFrom> for types::PaymentsPreProce fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result { let payment_data = additional_data.payment_data; + let payment_method_data = payment_data.payment_method_data; + Ok(Self { + payment_method_data, email: payment_data.email, currency: Some(payment_data.currency), amount: Some(payment_data.amount.into()), diff --git a/crates/router/src/openapi.rs b/crates/router/src/openapi.rs index 3c7c177707..a53c57bc05 100644 --- a/crates/router/src/openapi.rs +++ b/crates/router/src/openapi.rs @@ -244,9 +244,11 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::BankTransferNextStepsData, api_models::payments::SepaAndBacsBillingDetails, api_models::payments::AchBillingDetails, + api_models::payments::MultibancoBillingDetails, api_models::payments::BankTransferInstructions, api_models::payments::ReceiverDetails, api_models::payments::AchTransfer, + api_models::payments::MultibancoTransferInstructions, api_models::payments::ApplePayRedirectData, api_models::payments::ApplePayThirdPartySdkData, api_models::payments::GooglePayRedirectData, diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 3b437d80c1..313b22fb80 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -268,9 +268,10 @@ pub struct PaymentMethodTokenizationData { #[derive(Debug, Clone)] pub struct PaymentsPreProcessingData { + pub payment_method_data: Option, + pub amount: Option, pub email: Option, pub currency: Option, - pub amount: Option, pub payment_method_type: Option, } diff --git a/crates/storage_models/src/enums.rs b/crates/storage_models/src/enums.rs index 0f0430511a..712e0f686f 100644 --- a/crates/storage_models/src/enums.rs +++ b/crates/storage_models/src/enums.rs @@ -696,6 +696,7 @@ pub enum PaymentMethodType { Klarna, MbWay, MobilePay, + Multibanco, OnlineBankingCzechRepublic, OnlineBankingFinland, OnlineBankingPoland, diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 88378d8ff4..53bc3c7c29 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -2648,6 +2648,25 @@ } } } + }, + { + "type": "object", + "required": [ + "multibanco_bank_transfer" + ], + "properties": { + "multibanco_bank_transfer": { + "type": "object", + "required": [ + "billing_details" + ], + "properties": { + "billing_details": { + "$ref": "#/components/schemas/MultibancoBillingDetails" + } + } + } + } } ] }, @@ -2685,6 +2704,17 @@ "$ref": "#/components/schemas/BacsBankTransferInstructions" } } + }, + { + "type": "object", + "required": [ + "multibanco" + ], + "properties": { + "multibanco": { + "$ref": "#/components/schemas/MultibancoTransferInstructions" + } + } } ] }, @@ -5422,6 +5452,35 @@ "MobilePayRedirection": { "type": "object" }, + "MultibancoBillingDetails": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string", + "example": "example@me.com" + } + } + }, + "MultibancoTransferInstructions": { + "type": "object", + "required": [ + "reference", + "entity" + ], + "properties": { + "reference": { + "type": "string", + "example": "122385736258" + }, + "entity": { + "type": "string", + "example": "12345" + } + } + }, "NextActionCall": { "type": "string", "enum": [ @@ -6304,6 +6363,7 @@ "klarna", "mb_way", "mobile_pay", + "multibanco", "online_banking_czech_republic", "online_banking_finland", "online_banking_poland",