feat: Add bank redirect mapping to adyen and stripe (#680)

Co-authored-by: Sangamesh <sangamesh.kulkarni@juspay.in>
This commit is contained in:
Kartikeya Hegde
2023-02-28 13:23:01 +05:30
committed by GitHub
parent 79aa8f3d3d
commit e6f627d931
3 changed files with 313 additions and 29 deletions

View File

@ -363,9 +363,10 @@ impl
req: &types::PaymentsAuthorizeRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> {
let connector_req = adyen::AdyenPaymentRequest::try_from(req)?;
let adyen_req =
utils::Encode::<adyen::AdyenPaymentRequest>::encode_to_string_of_json(&connector_req)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
let adyen_req = utils::Encode::<adyen::AdyenPaymentRequest<'_>>::encode_to_string_of_json(
&connector_req,
)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(adyen_req))
}

View File

@ -1,5 +1,5 @@
use base64::Engine;
use error_stack::{IntoReport, ResultExt};
use error_stack::ResultExt;
use masking::PeekInterface;
use reqwest::Url;
use serde::{Deserialize, Serialize};
@ -10,7 +10,11 @@ use crate::{
core::errors,
pii::{self, Email, Secret},
services,
types::{self, api, storage::enums as storage_enums},
types::{
self,
api::{self, enums as api_enums},
storage::enums as storage_enums,
},
};
// Adyen Types Definition
@ -75,10 +79,10 @@ pub struct LineItem {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AdyenPaymentRequest {
pub struct AdyenPaymentRequest<'a> {
amount: Amount,
merchant_account: String,
payment_method: AdyenPaymentMethod,
payment_method: AdyenPaymentMethod<'a>,
reference: String,
return_url: String,
browser_info: Option<AdyenBrowserInfo>,
@ -87,6 +91,7 @@ pub struct AdyenPaymentRequest {
recurring_processing_model: Option<AdyenRecurringModel>,
additional_data: Option<AdditionalData>,
shopper_name: Option<ShopperName>,
shopper_locale: Option<String>,
shopper_email: Option<Secret<String, Email>>,
telephone_number: Option<Secret<String>>,
billing_address: Option<Address>,
@ -203,9 +208,9 @@ pub struct Amount {
value: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type")]
pub enum AdyenPaymentMethod {
pub enum AdyenPaymentMethod<'a> {
AdyenCard(AdyenCard),
AdyenPaypal(AdyenPaypal),
Gpay(AdyenGPay),
@ -213,6 +218,23 @@ pub enum AdyenPaymentMethod {
AfterPay(AdyenPayLaterData),
AdyenKlarna(AdyenPayLaterData),
AdyenAffirm(AdyenPayLaterData),
Eps(BankRedirectionWithIssuer<'a>),
Ideal(BankRedirectionWithIssuer<'a>),
Giropay(BankRedirectionPMData),
Sofort(BankRedirectionPMData),
}
#[derive(Debug, Clone, Serialize)]
pub struct BankRedirectionPMData {
#[serde(rename = "type")]
payment_type: PaymentType,
}
#[derive(Debug, Clone, Serialize)]
pub struct BankRedirectionWithIssuer<'a> {
#[serde(rename = "type")]
payment_type: PaymentType,
issuer: &'a str,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -311,6 +333,74 @@ pub enum PaymentType {
Klarna,
Affirm,
Afterpaytouch,
Eps,
Ideal,
Giropay,
#[serde(rename = "directEbanking")]
Sofort,
}
pub struct AdyenTestBankNames<'a>(&'a str);
impl<'a> TryFrom<&api_enums::BankNames> for AdyenTestBankNames<'a> {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(bank: &api_enums::BankNames) -> Result<Self, Self::Error> {
Ok(match bank {
api_models::enums::BankNames::AbnAmro => Self("1121"),
api_models::enums::BankNames::AsnBank => Self("1151"),
api_models::enums::BankNames::Bunq => Self("1152"),
api_models::enums::BankNames::Handelsbanken => Self("1153"),
api_models::enums::BankNames::Ing => Self("1154"),
api_models::enums::BankNames::Knab => Self("1155"),
api_models::enums::BankNames::Moneyou => Self("1156"),
api_models::enums::BankNames::Rabobank => Self("1157"),
api_models::enums::BankNames::Regiobank => Self("1158"),
api_models::enums::BankNames::Revolut => Self("1159"),
api_models::enums::BankNames::SnsBank => Self("1159"),
api_models::enums::BankNames::TriodosBank => Self("1159"),
api_models::enums::BankNames::VanLanschot => Self("1159"),
api_models::enums::BankNames::BankAustria => {
Self("e6819e7a-f663-414b-92ec-cf7c82d2f4e5")
}
api_models::enums::BankNames::BawagPskAg => {
Self("ba7199cc-f057-42f2-9856-2378abf21638")
}
api_models::enums::BankNames::Dolomitenbank => {
Self("d5d5b133-1c0d-4c08-b2be-3c9b116dc326")
}
api_models::enums::BankNames::EasybankAg => {
Self("eff103e6-843d-48b7-a6e6-fbd88f511b11")
}
api_models::enums::BankNames::ErsteBankUndSparkassen => {
Self("3fdc41fc-3d3d-4ee3-a1fe-cd79cfd58ea3")
}
api_models::enums::BankNames::HypoTirolBankAg => {
Self("6765e225-a0dc-4481-9666-e26303d4f221")
}
api_models::enums::BankNames::PosojilnicaBankEGen => {
Self("65ef4682-4944-499f-828f-5d74ad288376")
}
api_models::enums::BankNames::RaiffeisenBankengruppeOsterreich => {
Self("ee9fc487-ebe0-486c-8101-17dce5141a67")
}
api_models::enums::BankNames::SchoellerbankAg => {
Self("1190c4d1-b37a-487e-9355-e0a067f54a9f")
}
api_models::enums::BankNames::SpardaBankWien => {
Self("8b0bfeea-fbb0-4337-b3a1-0e25c0f060fc")
}
api_models::enums::BankNames::VolksbankGruppe => {
Self("e2e97aaa-de4c-4e18-9431-d99790773433")
}
api_models::enums::BankNames::VolkskreditbankAg => {
Self("4a0a975b-0594-4b40-9068-39f77b3a91f9")
}
_ => Err(errors::ConnectorError::NotSupported {
payment_method: String::from("BankRedirect"),
connector: "Adyen",
})?,
})
}
}
impl TryFrom<&types::ConnectorAuthType> for AdyenAuthType {
@ -328,7 +418,7 @@ impl TryFrom<&types::ConnectorAuthType> for AdyenAuthType {
}
// Payment Request Transform
impl TryFrom<&types::PaymentsAuthorizeRouterData> for AdyenPaymentRequest {
impl<'a> TryFrom<&types::PaymentsAuthorizeRouterData> for AdyenPaymentRequest<'a> {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
match item.payment_method {
@ -337,11 +427,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for AdyenPaymentRequest {
get_paylater_specific_payment_data(item)
}
storage_models::enums::PaymentMethod::Wallet => get_wallet_specific_payment_data(item),
_ => Err(errors::ConnectorError::NotSupported {
payment_method: item.payment_method.to_string(),
connector: "adyen",
})
.into_report()?,
storage_models::enums::PaymentMethod::BankRedirect => {
get_bank_redirect_specific_payment_data(item)
}
}
}
}
@ -468,9 +556,9 @@ fn get_country_code(item: &types::PaymentsAuthorizeRouterData) -> Option<String>
address.and_then(|address| address.country.clone())
}
fn get_payment_method_data(
fn get_payment_method_data<'a>(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<AdyenPaymentMethod, error_stack::Report<errors::ConnectorError>> {
) -> Result<AdyenPaymentMethod<'a>, error_stack::Report<errors::ConnectorError>> {
match item.request.payment_method_data {
api::PaymentMethodData::Card(ref card) => {
let adyen_card = AdyenCard {
@ -539,13 +627,39 @@ fn get_payment_method_data(
),
}
}
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
api_models::payments::PaymentMethodData::BankRedirect(ref bank_redirect_data) => {
match bank_redirect_data {
api_models::payments::BankRedirectData::Eps { bank_name, .. } => {
Ok(AdyenPaymentMethod::Eps(BankRedirectionWithIssuer {
payment_type: PaymentType::Eps,
issuer: AdyenTestBankNames::try_from(bank_name)?.0,
}))
}
api_models::payments::BankRedirectData::Ideal { bank_name, .. } => {
Ok(AdyenPaymentMethod::Ideal(BankRedirectionWithIssuer {
payment_type: PaymentType::Ideal,
issuer: AdyenTestBankNames::try_from(bank_name)?.0,
}))
}
api_models::payments::BankRedirectData::Giropay { .. } => {
Ok(AdyenPaymentMethod::Giropay(BankRedirectionPMData {
payment_type: PaymentType::Giropay,
}))
}
api_models::payments::BankRedirectData::Sofort { .. } => {
Ok(AdyenPaymentMethod::Sofort(BankRedirectionPMData {
payment_type: PaymentType::Sofort,
}))
}
}
}
}
}
fn get_card_specific_payment_data(
fn get_card_specific_payment_data<'a>(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<AdyenPaymentRequest, error_stack::Report<errors::ConnectorError>> {
) -> Result<AdyenPaymentRequest<'a>, error_stack::Report<errors::ConnectorError>> {
let amount = get_amount_data(item);
let auth_type = AdyenAuthType::try_from(&item.connector_auth_type)?;
let shopper_interaction = AdyenShopperInteraction::from(item);
@ -567,6 +681,7 @@ fn get_card_specific_payment_data(
telephone_number: None,
shopper_name: None,
shopper_email: None,
shopper_locale: None,
billing_address: None,
delivery_address: None,
country_code: None,
@ -574,9 +689,64 @@ fn get_card_specific_payment_data(
})
}
fn get_wallet_specific_payment_data(
fn get_sofort_extra_details(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<AdyenPaymentRequest, error_stack::Report<errors::ConnectorError>> {
) -> (Option<String>, Option<String>) {
match item.request.payment_method_data {
api_models::payments::PaymentMethodData::BankRedirect(ref b) => {
if let api_models::payments::BankRedirectData::Sofort {
country,
preferred_language,
} = b
{
(
Some(preferred_language.to_string()),
Some(country.to_string()),
)
} else {
(None, None)
}
}
_ => (None, None),
}
}
fn get_bank_redirect_specific_payment_data<'a>(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<AdyenPaymentRequest<'a>, error_stack::Report<errors::ConnectorError>> {
let amount = get_amount_data(item);
let auth_type = AdyenAuthType::try_from(&item.connector_auth_type)?;
let shopper_interaction = AdyenShopperInteraction::from(item);
let recurring_processing_model = get_recurring_processing_model(item);
let browser_info = get_browser_info(item);
let additional_data = get_additional_data(item);
let return_url = item.get_return_url()?;
let payment_method = get_payment_method_data(item)?;
let (shopper_locale, country) = get_sofort_extra_details(item);
Ok(AdyenPaymentRequest {
amount,
merchant_account: auth_type.merchant_account,
payment_method,
reference: item.payment_id.to_string(),
return_url,
shopper_interaction,
recurring_processing_model,
browser_info,
additional_data,
telephone_number: None,
shopper_name: None,
shopper_email: None,
shopper_locale,
billing_address: None,
delivery_address: None,
country_code: country,
line_items: None,
})
}
fn get_wallet_specific_payment_data<'a>(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<AdyenPaymentRequest<'a>, error_stack::Report<errors::ConnectorError>> {
let amount = get_amount_data(item);
let auth_type = AdyenAuthType::try_from(&item.connector_auth_type)?;
let browser_info = get_browser_info(item);
@ -598,6 +768,7 @@ fn get_wallet_specific_payment_data(
telephone_number: None,
shopper_name: None,
shopper_email: None,
shopper_locale: None,
billing_address: None,
delivery_address: None,
country_code: None,
@ -605,9 +776,9 @@ fn get_wallet_specific_payment_data(
})
}
fn get_paylater_specific_payment_data(
fn get_paylater_specific_payment_data<'a>(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<AdyenPaymentRequest, error_stack::Report<errors::ConnectorError>> {
) -> Result<AdyenPaymentRequest<'a>, error_stack::Report<errors::ConnectorError>> {
let amount = get_amount_data(item);
let auth_type = AdyenAuthType::try_from(&item.connector_auth_type)?;
let browser_info = get_browser_info(item);
@ -636,6 +807,7 @@ fn get_paylater_specific_payment_data(
telephone_number,
shopper_name,
shopper_email,
shopper_locale: None,
billing_address,
delivery_address,
country_code,

View File

@ -136,11 +136,11 @@ pub struct StripePayLaterData {
pub enum StripeBankName {
Eps {
#[serde(rename = "payment_method_data[eps][bank]")]
bank_name: api_models::enums::BankNames,
bank_name: StripeBankNames,
},
Ideal {
#[serde(rename = "payment_method_data[ideal][bank]")]
ideal_bank_name: api_models::enums::BankNames,
ideal_bank_name: StripeBankNames,
},
}
@ -162,15 +162,15 @@ fn get_bank_name(
match (stripe_pm_type, bank_redirect_data) {
(
StripePaymentMethodType::Eps,
api_models::payments::BankRedirectData::Eps { bank_name, .. },
api_models::payments::BankRedirectData::Eps { ref bank_name, .. },
) => Ok(Some(StripeBankName::Eps {
bank_name: bank_name.to_owned(),
bank_name: StripeBankNames::try_from(bank_name)?,
})),
(
StripePaymentMethodType::Ideal,
api_models::payments::BankRedirectData::Ideal { bank_name, .. },
) => Ok(Some(StripeBankName::Ideal {
ideal_bank_name: bank_name.to_owned(),
ideal_bank_name: StripeBankNames::try_from(bank_name)?,
})),
(StripePaymentMethodType::Sofort | StripePaymentMethodType::Giropay, _) => Ok(None),
_ => Err(errors::ConnectorError::MismatchedPaymentData),
@ -211,6 +211,117 @@ pub enum StripePaymentMethodType {
Sofort,
}
#[derive(Debug, Eq, PartialEq, Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripeBankNames {
AbnAmro,
ArzteUndApothekerBank,
AsnBank,
AustrianAnadiBankAg,
BankAustria,
BankhausCarlSpangler,
BankhausSchelhammerUndSchatteraAg,
BawagPskAg,
BksBankAg,
BrullKallmusBankAg,
BtvVierLanderBank,
Bunq,
CapitalBankGraweGruppeAg,
Dolomitenbank,
EasybankAg,
ErsteBankUndSparkassen,
Handelsbanken,
HypoAlpeadriabankInternationalAg,
HypoNoeLbFurNiederosterreichUWien,
HypoOberosterreichSalzburgSteiermark,
HypoTirolBankAg,
HypoVorarlbergBankAg,
HypoBankBurgenlandAktiengesellschaft,
Ing,
Knab,
MarchfelderBank,
OberbankAg,
RaiffeisenBankengruppeOsterreich,
SchoellerbankAg,
SpardaBankWien,
VolksbankGruppe,
VolkskreditbankAg,
VrBankBraunau,
Moneyou,
Rabobank,
Regiobank,
Revolut,
SnsBank,
TriodosBank,
VanLanschot,
}
impl TryFrom<&api_models::enums::BankNames> for StripeBankNames {
type Error = errors::ConnectorError;
fn try_from(bank: &api_models::enums::BankNames) -> Result<Self, Self::Error> {
Ok(match bank {
api_models::enums::BankNames::AbnAmro => Self::AbnAmro,
api_models::enums::BankNames::ArzteUndApothekerBank => Self::ArzteUndApothekerBank,
api_models::enums::BankNames::AsnBank => Self::AsnBank,
api_models::enums::BankNames::AustrianAnadiBankAg => Self::AustrianAnadiBankAg,
api_models::enums::BankNames::BankAustria => Self::BankAustria,
api_models::enums::BankNames::BankhausCarlSpangler => Self::BankhausCarlSpangler,
api_models::enums::BankNames::BankhausSchelhammerUndSchatteraAg => {
Self::BankhausSchelhammerUndSchatteraAg
}
api_models::enums::BankNames::BawagPskAg => Self::BawagPskAg,
api_models::enums::BankNames::BksBankAg => Self::BksBankAg,
api_models::enums::BankNames::BrullKallmusBankAg => Self::BrullKallmusBankAg,
api_models::enums::BankNames::BtvVierLanderBank => Self::BtvVierLanderBank,
api_models::enums::BankNames::Bunq => Self::Bunq,
api_models::enums::BankNames::CapitalBankGraweGruppeAg => {
Self::CapitalBankGraweGruppeAg
}
api_models::enums::BankNames::Dolomitenbank => Self::Dolomitenbank,
api_models::enums::BankNames::EasybankAg => Self::EasybankAg,
api_models::enums::BankNames::ErsteBankUndSparkassen => Self::ErsteBankUndSparkassen,
api_models::enums::BankNames::Handelsbanken => Self::Handelsbanken,
api_models::enums::BankNames::HypoAlpeadriabankInternationalAg => {
Self::HypoAlpeadriabankInternationalAg
}
api_models::enums::BankNames::HypoNoeLbFurNiederosterreichUWien => {
Self::HypoNoeLbFurNiederosterreichUWien
}
api_models::enums::BankNames::HypoOberosterreichSalzburgSteiermark => {
Self::HypoOberosterreichSalzburgSteiermark
}
api_models::enums::BankNames::HypoTirolBankAg => Self::HypoTirolBankAg,
api_models::enums::BankNames::HypoVorarlbergBankAg => Self::HypoVorarlbergBankAg,
api_models::enums::BankNames::HypoBankBurgenlandAktiengesellschaft => {
Self::HypoBankBurgenlandAktiengesellschaft
}
api_models::enums::BankNames::Ing => Self::Ing,
api_models::enums::BankNames::Knab => Self::Knab,
api_models::enums::BankNames::MarchfelderBank => Self::MarchfelderBank,
api_models::enums::BankNames::OberbankAg => Self::OberbankAg,
api_models::enums::BankNames::RaiffeisenBankengruppeOsterreich => {
Self::RaiffeisenBankengruppeOsterreich
}
api_models::enums::BankNames::Rabobank => Self::Rabobank,
api_models::enums::BankNames::Regiobank => Self::Regiobank,
api_models::enums::BankNames::Revolut => Self::Revolut,
api_models::enums::BankNames::SnsBank => Self::SnsBank,
api_models::enums::BankNames::TriodosBank => Self::TriodosBank,
api_models::enums::BankNames::VanLanschot => Self::VanLanschot,
api_models::enums::BankNames::Moneyou => Self::Moneyou,
api_models::enums::BankNames::SchoellerbankAg => Self::SchoellerbankAg,
api_models::enums::BankNames::SpardaBankWien => Self::SpardaBankWien,
api_models::enums::BankNames::VolksbankGruppe => Self::VolksbankGruppe,
api_models::enums::BankNames::VolkskreditbankAg => Self::VolkskreditbankAg,
api_models::enums::BankNames::VrBankBraunau => Self::VrBankBraunau,
_ => Err(errors::ConnectorError::NotSupported {
payment_method: String::from("BankRedirect"),
connector: "Stripe",
})?,
})
}
}
fn validate_shipping_address_against_payment_method(
shipping_address: &StripeShippingAddress,
payment_method: &StripePaymentMethodType,