feat(core): [Stripe] add bank debits payment method to stripe (#906)

This commit is contained in:
Narayan Bhat
2023-04-21 13:49:20 +05:30
committed by GitHub
parent c1a25b30bd
commit f624eb52d6
8 changed files with 345 additions and 43 deletions

View File

@ -409,11 +409,14 @@ pub enum PaymentExperience {
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PaymentMethodType {
Ach,
Affirm,
AfterpayClearpay,
AliPay,
ApplePay,
Bacs,
BancontactCard,
Becs,
Blik,
Credit,
CryptoCurrency,
@ -432,6 +435,7 @@ pub enum PaymentMethodType {
PayBright,
Paypal,
Przelewy24,
Sepa,
Sofort,
Swish,
Trustly,
@ -463,6 +467,7 @@ pub enum PaymentMethod {
Wallet,
BankRedirect,
Crypto,
BankDebit,
}
#[derive(

View File

@ -462,13 +462,57 @@ pub enum PayLaterData {
Walley {},
}
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, ToSchema, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum BankDebitData {
/// Payment Method data for Ach bank debit
AchBankDebit {
/// Billing details for bank debit
billing_details: BankDebitBilling,
/// Account number for ach bank debit payment
#[schema(value_type = String, example = "000123456789")]
account_number: Secret<String>,
/// Routing number for ach bank debit payment
#[schema(value_type = String, example = "110000000")]
routing_number: Secret<String>,
},
SepaBankDebit {
/// Billing details for bank debit
billing_details: BankDebitBilling,
/// International bank account number (iban) for SEPA
#[schema(value_type = String, example = "DE89370400440532013000")]
iban: Secret<String>,
},
BecsBankDebit {
/// Billing details for bank debit
billing_details: BankDebitBilling,
/// Account number for Becs payment method
#[schema(value_type = String, example = "000123456")]
account_number: Secret<String>,
/// Bank-State-Branch (bsb) number
#[schema(value_type = String, example = "000000")]
bsb_number: Secret<String>,
},
BacsBankDebit {
/// Billing details for bank debit
billing_details: BankDebitBilling,
/// Account number for Bacs payment method
#[schema(value_type = String, example = "00012345")]
account_number: Secret<String>,
/// Sort code for Bacs payment method
#[schema(value_type = String, example = "108800")]
sort_code: Secret<String>,
},
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethodData {
Card(Card),
Wallet(WalletData),
PayLater(PayLaterData),
BankRedirect(BankRedirectData),
BankDebit(BankDebitData),
Crypto(CryptoData),
}
@ -485,6 +529,7 @@ pub enum AdditionalPaymentData {
Wallet {},
PayLater {},
Crypto {},
BankDebit {},
}
impl From<&PaymentMethodData> for AdditionalPaymentData {
@ -509,6 +554,7 @@ impl From<&PaymentMethodData> for AdditionalPaymentData {
PaymentMethodData::Wallet(_) => Self::Wallet {},
PaymentMethodData::PayLater(_) => Self::PayLater {},
PaymentMethodData::Crypto(_) => Self::Crypto {},
PaymentMethodData::BankDebit(_) => Self::BankDebit {},
}
}
}
@ -607,6 +653,18 @@ pub struct BankRedirectBilling {
pub billing_name: Secret<String>,
}
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, ToSchema, Eq, PartialEq)]
pub struct BankDebitBilling {
/// The billing name for bank debits
#[schema(value_type = String, example = "John Doe")]
pub name: Secret<String>,
/// The billing email for bank debits
#[schema(value_type = String, example = "example@example.com")]
pub email: Secret<String, pii::Email>,
/// The billing address for bank debits
pub address: Option<AddressDetails>,
}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum WalletData {
@ -722,6 +780,7 @@ pub enum PaymentMethodDataResponse {
Paypal,
BankRedirect(BankRedirectData),
Crypto(CryptoData),
BankDebit(BankDebitData),
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, ToSchema)]
@ -1137,7 +1196,7 @@ pub struct PaymentListResponse {
pub data: Vec<PaymentsResponse>,
}
#[derive(Setter, Clone, Default, Debug, Eq, PartialEq, serde::Serialize)]
#[derive(Setter, Clone, Default, Debug, serde::Serialize)]
pub struct VerifyResponse {
pub verify_id: Option<String>,
pub merchant_id: Option<String>,
@ -1262,6 +1321,7 @@ impl From<PaymentMethodData> for PaymentMethodDataResponse {
Self::BankRedirect(bank_redirect_data)
}
PaymentMethodData::Crypto(crpto_data) => Self::Crypto(crpto_data),
PaymentMethodData::BankDebit(bank_debit_data) => Self::BankDebit(bank_debit_data),
}
}
}

View File

@ -111,11 +111,14 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest {
api::PaymentMethodData::PayLater(_) => PaymentDetails::Klarna,
api::PaymentMethodData::Wallet(_) => PaymentDetails::Wallet,
api::PaymentMethodData::BankRedirect(_) => PaymentDetails::BankRedirect,
api::PaymentMethodData::Crypto(_) => Err(errors::ConnectorError::NotSupported {
payment_method: format!("{:?}", item.payment_method),
connector: "Aci",
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),
})?,
api::PaymentMethodData::Crypto(_) | api::PaymentMethodData::BankDebit(_) => {
Err(errors::ConnectorError::NotSupported {
payment_method: format!("{:?}", item.payment_method),
connector: "Aci",
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl
.to_string(),
})?
}
};
let auth = AciAuthType::try_from(&item.connector_auth_type)?;

View File

@ -87,11 +87,14 @@ impl TryFrom<api_models::payments::PaymentMethodData> for PaymentDetails {
api::PaymentMethodData::PayLater(_) => Ok(Self::Klarna),
api::PaymentMethodData::Wallet(_) => Ok(Self::Wallet),
api::PaymentMethodData::BankRedirect(_) => Ok(Self::BankRedirect),
api::PaymentMethodData::Crypto(_) => Err(errors::ConnectorError::NotSupported {
payment_method: format!("{value:?}"),
connector: "AuthorizeDotNet",
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),
})?,
api::PaymentMethodData::Crypto(_) | api::PaymentMethodData::BankDebit(_) => {
Err(errors::ConnectorError::NotSupported {
payment_method: format!("{value:?}"),
connector: "AuthorizeDotNet",
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl
.to_string(),
})?
}
}
}
}

View File

@ -1,8 +1,8 @@
use api_models::{self, enums as api_enums, payments};
use base64::Engine;
use common_utils::{fp_utils, pii::Email};
use common_utils::{fp_utils, pii};
use error_stack::{IntoReport, ResultExt};
use masking::ExposeInterface;
use masking::{ExposeInterface, ExposeOptionInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid;
@ -10,7 +10,6 @@ use uuid::Uuid;
use crate::{
consts,
core::errors,
pii::{self, ExposeOptionInterface, Secret},
services,
types::{self, api, storage::enums},
utils::OptionExt,
@ -63,6 +62,22 @@ pub enum Auth3ds {
Any,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StripeMandateType {
Online,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct StripeMandateRequest {
#[serde(rename = "mandate_data[customer_acceptance][type]")]
pub mandate_type: StripeMandateType,
#[serde(rename = "mandate_data[customer_acceptance][online][ip_address]")]
pub ip_address: Secret<String, pii::IpAddress>,
#[serde(rename = "mandate_data[customer_acceptance][online][user_agent]")]
pub user_agent: String,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct PaymentIntentRequest {
pub amount: i64, //amount in cents, hence passed as integer
@ -78,6 +93,8 @@ pub struct PaymentIntentRequest {
pub return_url: String,
pub confirm: bool,
pub mandate: Option<String>,
#[serde(flatten)]
pub setup_mandate_details: Option<StripeMandateRequest>,
pub description: Option<String>,
#[serde(flatten)]
pub shipping: StripeShippingAddress,
@ -198,6 +215,47 @@ pub struct StripeBankRedirectData {
pub bank_specific_data: Option<BankSpecificData>,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
#[serde(tag = "payment_method_data[type]")]
pub enum BankDebitData {
#[serde(rename = "us_bank_account")]
Ach {
#[serde(rename = "payment_method_data[us_bank_account][account_holder_type]")]
account_holder_type: String,
#[serde(rename = "payment_method_data[us_bank_account][account_number]")]
account_number: Secret<String>,
#[serde(rename = "payment_method_data[us_bank_account][routing_number]")]
routing_number: Secret<String>,
},
#[serde(rename = "sepa_debit")]
Sepa {
#[serde(rename = "payment_method_data[sepa_debit][iban]")]
iban: Secret<String>,
},
#[serde(rename = "au_becs_debit")]
Becs {
#[serde(rename = "payment_method_data[au_becs_debit][account_number]")]
account_number: Secret<String>,
#[serde(rename = "payment_method_data[au_becs_debit][bsb_number]")]
bsb_number: Secret<String>,
},
#[serde(rename = "bacs_debit")]
Bacs {
#[serde(rename = "payment_method_data[bacs_debit][account_number]")]
account_number: Secret<String>,
#[serde(rename = "payment_method_data[bacs_debit][sort_code]")]
sort_code: Secret<String>,
},
}
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct StripeBankDebitData {
#[serde(rename = "payment_method_types[]")]
pub payment_method_types: StripePaymentMethodType,
#[serde(flatten)]
pub bank_specific_data: BankDebitData,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum StripePaymentMethodData {
@ -205,6 +263,7 @@ pub enum StripePaymentMethodData {
PayLater(StripePayLaterData),
Wallet(StripeWallet),
BankRedirect(StripeBankRedirectData),
BankDebit(StripeBankDebitData),
}
#[derive(Debug, Eq, PartialEq, Serialize)]
@ -230,7 +289,7 @@ pub struct ApplepayPayment {
pub payment_method_types: StripePaymentMethodType,
}
#[derive(Debug, Eq, PartialEq, Serialize, Clone)]
#[derive(Debug, Eq, PartialEq, Serialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodType {
Card,
@ -242,6 +301,14 @@ pub enum StripePaymentMethodType {
Ideal,
Sofort,
ApplePay,
#[serde(rename = "us_bank_account")]
Ach,
#[serde(rename = "sepa_debit")]
Sepa,
#[serde(rename = "au_becs_debit")]
Becs,
#[serde(rename = "bacs_debit")]
Bacs,
}
#[derive(Debug, Eq, PartialEq, Serialize, Clone)]
@ -472,6 +539,35 @@ impl TryFrom<(&api_models::payments::PayLaterData, StripePaymentMethodType)>
}
}
impl From<&payments::BankDebitBilling> for StripeBillingAddress {
fn from(item: &payments::BankDebitBilling) -> Self {
Self {
email: Some(item.email.to_owned()),
country: item
.address
.as_ref()
.and_then(|address| address.country.to_owned()),
name: Some(item.name.to_owned()),
city: item
.address
.as_ref()
.and_then(|address| address.city.to_owned()),
address_line1: item
.address
.as_ref()
.and_then(|address| address.line1.to_owned()),
address_line2: item
.address
.as_ref()
.and_then(|address| address.line2.to_owned()),
zip_code: item
.address
.as_ref()
.and_then(|address| address.zip.to_owned()),
}
}
}
impl TryFrom<&payments::BankRedirectData> for StripeBillingAddress {
type Error = errors::ConnectorError;
@ -514,6 +610,64 @@ fn get_bank_specific_data(
}
}
fn get_bank_debit_data(
bank_debit_data: &payments::BankDebitData,
) -> (StripePaymentMethodType, BankDebitData, StripeBillingAddress) {
match bank_debit_data {
payments::BankDebitData::AchBankDebit {
billing_details,
account_number,
routing_number,
} => {
let ach_data = BankDebitData::Ach {
account_holder_type: "individual".to_string(),
account_number: account_number.to_owned(),
routing_number: routing_number.to_owned(),
};
let billing_data = StripeBillingAddress::from(billing_details);
(StripePaymentMethodType::Ach, ach_data, billing_data)
}
payments::BankDebitData::SepaBankDebit {
billing_details,
iban,
} => {
let sepa_data = BankDebitData::Sepa {
iban: iban.to_owned(),
};
let billing_data = StripeBillingAddress::from(billing_details);
(StripePaymentMethodType::Sepa, sepa_data, billing_data)
}
payments::BankDebitData::BecsBankDebit {
billing_details,
account_number,
bsb_number,
} => {
let becs_data = BankDebitData::Becs {
account_number: account_number.to_owned(),
bsb_number: bsb_number.to_owned(),
};
let billing_data = StripeBillingAddress::from(billing_details);
(StripePaymentMethodType::Becs, becs_data, billing_data)
}
payments::BankDebitData::BacsBankDebit {
billing_details,
account_number,
sort_code,
} => {
let bacs_data = BankDebitData::Bacs {
account_number: account_number.to_owned(),
sort_code: sort_code.to_owned(),
};
let billing_data = StripeBillingAddress::from(billing_details);
(StripePaymentMethodType::Bacs, bacs_data, billing_data)
}
}
}
fn create_stripe_payment_method(
pm_type: Option<&enums::PaymentMethodType>,
experience: Option<&enums::PaymentExperience>,
@ -558,13 +712,12 @@ fn create_stripe_payment_method(
let stripe_pm_type = infer_stripe_pay_later_type(pm_type, pm_experience)?;
let billing_address =
StripeBillingAddress::try_from((pay_later_data, stripe_pm_type.clone()))?;
let billing_address = StripeBillingAddress::try_from((pay_later_data, stripe_pm_type))?;
Ok((
StripePaymentMethodData::PayLater(StripePayLaterData {
payment_method_types: stripe_pm_type.clone(),
payment_method_data_type: stripe_pm_type.clone(),
payment_method_types: stripe_pm_type,
payment_method_data_type: stripe_pm_type,
}),
stripe_pm_type,
billing_address,
@ -577,8 +730,8 @@ fn create_stripe_payment_method(
let bank_name = get_bank_name(&pm_type, bank_redirect_data)?;
Ok((
StripePaymentMethodData::BankRedirect(StripeBankRedirectData {
payment_method_types: pm_type.clone(),
payment_method_data_type: pm_type.clone(),
payment_method_types: pm_type,
payment_method_data_type: pm_type,
bank_name,
bank_specific_data,
}),
@ -609,6 +762,16 @@ fn create_stripe_payment_method(
)
.into()),
},
payments::PaymentMethodData::BankDebit(bank_debit_data) => {
let (pm_type, bank_debit_data, billing_address) = get_bank_debit_data(bank_debit_data);
let pm_data = StripePaymentMethodData::BankDebit(StripeBankDebitData {
payment_method_types: pm_type,
bank_specific_data: bank_debit_data,
});
Ok((pm_data, pm_type, billing_address))
}
_ => Err(errors::ConnectorError::NotImplemented(
"stripe does not support this payment method".to_string(),
)
@ -694,6 +857,22 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentIntentRequest {
_ => payment_data,
};
let setup_mandate_details =
item.request
.setup_mandate_details
.as_ref()
.and_then(|mandate_details| {
mandate_details
.customer_acceptance
.online
.as_ref()
.map(|online_details| StripeMandateRequest {
mandate_type: StripeMandateType::Online,
ip_address: online_details.ip_address.to_owned(),
user_agent: online_details.user_agent.to_owned(),
})
});
Ok(Self {
amount: item.request.amount, //hopefully we don't loose some cents here
currency: item.request.currency.to_string(), //we need to copy the value and not transfer ownership
@ -715,6 +894,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentIntentRequest {
capture_method: StripeCaptureMethod::from(item.request.capture_method),
payment_data,
mandate,
setup_mandate_details,
})
}
}
@ -881,12 +1061,9 @@ impl<F, T>
fn try_from(
item: types::ResponseRouterData<F, PaymentIntentResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data =
item.response
.next_action
.map(|StripeNextActionResponse::RedirectToUrl(response)| {
services::RedirectForm::from((response.url, services::Method::Get))
});
let redirection_data = item.response.next_action.map(|next_action_response| {
services::RedirectForm::from((next_action_response.get_url(), services::Method::Get))
});
let mandate_reference =
item.response
@ -929,11 +1106,16 @@ impl<F, T>
types::PaymentsResponseData,
>,
) -> Result<Self, Self::Error> {
let redirection_data = item.response.next_action.as_ref().map(
|StripeNextActionResponse::RedirectToUrl(response)| {
services::RedirectForm::from((response.url.clone(), services::Method::Get))
},
);
let redirection_data = item
.response
.next_action
.as_ref()
.map(|next_action_response| {
services::RedirectForm::from((
next_action_response.get_url(),
services::Method::Get,
))
});
let mandate_reference =
item.response
@ -949,7 +1131,11 @@ impl<F, T>
| StripePaymentMethodOptions::Eps {}
| StripePaymentMethodOptions::Giropay {}
| StripePaymentMethodOptions::Ideal {}
| StripePaymentMethodOptions::Sofort {} => None,
| StripePaymentMethodOptions::Sofort {}
| StripePaymentMethodOptions::Ach {}
| StripePaymentMethodOptions::Bacs {}
| StripePaymentMethodOptions::Becs {}
| StripePaymentMethodOptions::Sepa {} => None,
});
let error_res =
@ -990,12 +1176,9 @@ impl<F, T>
fn try_from(
item: types::ResponseRouterData<F, SetupIntentResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data =
item.response
.next_action
.map(|StripeNextActionResponse::RedirectToUrl(response)| {
services::RedirectForm::from((response.url, services::Method::Get))
});
let redirection_data = item.response.next_action.map(|next_action_response| {
services::RedirectForm::from((next_action_response.get_url(), services::Method::Get))
});
let mandate_reference =
item.response
@ -1024,6 +1207,18 @@ impl<F, T>
#[serde(rename_all = "snake_case", remote = "Self")]
pub enum StripeNextActionResponse {
RedirectToUrl(StripeRedirectToUrlResponse),
VerifyWithMicrodeposits(StripeVerifyWithMicroDepositsResponse),
}
impl StripeNextActionResponse {
fn get_url(&self) -> Url {
match self {
Self::RedirectToUrl(redirect_to_url) => redirect_to_url.url.to_owned(),
Self::VerifyWithMicrodeposits(verify_with_microdeposits) => {
verify_with_microdeposits.hosted_verification_url.to_owned()
}
}
}
}
// This impl is required because Stripe's response is of the below format, which is externally
@ -1052,6 +1247,11 @@ pub struct StripeRedirectToUrlResponse {
url: Url,
}
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
pub struct StripeVerifyWithMicroDepositsResponse {
hosted_verification_url: Url,
}
// REFUND :
// Type definition for Stripe RefundRequest
@ -1189,11 +1389,19 @@ pub struct StripeShippingAddress {
#[derive(Debug, Default, Eq, PartialEq, Serialize)]
pub struct StripeBillingAddress {
#[serde(rename = "payment_method_data[billing_details][email]")]
pub email: Option<Secret<String, Email>>,
pub email: Option<Secret<String, pii::Email>>,
#[serde(rename = "payment_method_data[billing_details][address][country]")]
pub country: Option<api_enums::CountryCode>,
#[serde(rename = "payment_method_data[billing_details][name]")]
pub name: Option<Secret<String>>,
#[serde(rename = "payment_method_data[billing_details][address][city]")]
pub city: Option<String>,
#[serde(rename = "payment_method_data[billing_details][address][line1]")]
pub address_line1: Option<Secret<String>>,
#[serde(rename = "payment_method_data[billing_details][address][line2]")]
pub address_line2: Option<Secret<String>>,
#[serde(rename = "payment_method_data[billing_details][address][postal_code]")]
pub zip_code: Option<Secret<String>>,
}
#[derive(Debug, Clone, serde::Deserialize, Eq, PartialEq)]
@ -1233,6 +1441,14 @@ pub enum StripePaymentMethodOptions {
Giropay {},
Ideal {},
Sofort {},
#[serde(rename = "us_bank_account")]
Ach {},
#[serde(rename = "sepa_debit")]
Sepa {},
#[serde(rename = "au_becs_debit")]
Becs {},
#[serde(rename = "bacs_debit")]
Bacs {},
}
// #[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
// pub struct Card
@ -1373,12 +1589,12 @@ impl
}
})),
api::PaymentMethodData::PayLater(_) => Ok(Self::PayLater(StripePayLaterData {
payment_method_types: pm_type.clone(),
payment_method_types: pm_type,
payment_method_data_type: pm_type,
})),
api::PaymentMethodData::BankRedirect(_) => {
Ok(Self::BankRedirect(StripeBankRedirectData {
payment_method_types: pm_type.clone(),
payment_method_types: pm_type,
payment_method_data_type: pm_type,
bank_name: None,
bank_specific_data: None,
@ -1403,6 +1619,14 @@ impl
}
_ => Err(errors::ConnectorError::InvalidWallet.into()),
},
api::PaymentMethodData::BankDebit(bank_debit_data) => {
let (pm_type, bank_data, _) = get_bank_debit_data(&bank_debit_data);
Ok(Self::BankDebit(StripeBankDebitData {
payment_method_types: pm_type,
bank_specific_data: bank_data,
}))
}
api::PaymentMethodData::Crypto(_) => Err(errors::ConnectorError::NotSupported {
payment_method: format!("{pm_type:?}"),
connector: "Stripe",

View File

@ -802,6 +802,7 @@ pub async fn make_pm_data<'a, F: Clone, R>(
(pm @ Some(api::PaymentMethodData::PayLater(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::BankRedirect(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::Crypto(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::BankDebit(_)), _) => Ok(pm.to_owned()),
(pm_opt @ Some(pm @ api::PaymentMethodData::Wallet(_)), _) => {
let token = vault::Vault::store_payment_method_data_in_locker(
state,

View File

@ -155,6 +155,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::Address,
api_models::payments::BankRedirectData,
api_models::payments::BankRedirectBilling,
api_models::payments::BankRedirectBilling,
api_models::payments::OrderDetails,
api_models::payments::NextActionType,
api_models::payments::Metadata,

View File

@ -456,6 +456,7 @@ pub enum PaymentMethod {
Wallet,
BankRedirect,
Crypto,
BankDebit,
}
#[derive(
@ -612,11 +613,14 @@ pub enum MandateStatus {
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethodType {
Ach,
Affirm,
AfterpayClearpay,
AliPay,
ApplePay,
Bacs,
BancontactCard,
Becs,
Blik,
Credit,
CryptoCurrency,
@ -635,6 +639,7 @@ pub enum PaymentMethodType {
PayBright,
Paypal,
Przelewy24,
Sepa,
Sofort,
Swish,
Trustly,