feat(connector): [Zen] Add Latam Payment Methods (#1670)

Co-authored-by: swangi-kumari <swangi.12015941@lpu.in>
Co-authored-by: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com>
This commit is contained in:
SamraatBansal
2023-07-27 00:54:45 +05:30
committed by GitHub
parent 38f14b9f39
commit 4df67adb9b
21 changed files with 494 additions and 30 deletions

View File

@ -371,7 +371,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest {
| api::PaymentMethodData::BankTransfer(_)
| api::PaymentMethodData::Reward(_)
| api::PaymentMethodData::GiftCard(_)
| api::PaymentMethodData::Upi(_) => Err(errors::ConnectorError::NotSupported {
| api::PaymentMethodData::Upi(_)
| api::PaymentMethodData::Voucher(_) => Err(errors::ConnectorError::NotSupported {
message: format!("{:?}", item.payment_method),
connector: "Aci",
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),

View File

@ -167,7 +167,8 @@ impl TryFrom<&PaymentMethodData> for SalePaymentMethod {
| PaymentMethodData::MandatePayment
| PaymentMethodData::Reward(_)
| PaymentMethodData::GiftCard(_)
| PaymentMethodData::Upi(_) => {
| PaymentMethodData::Upi(_)
| api::PaymentMethodData::Voucher(_) => {
Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into())
}
}

View File

@ -1232,6 +1232,10 @@ fn create_stripe_payment_method(
billing_details,
))
}
payments::BankTransferData::Pix {} | payments::BankTransferData::Pse {} => Err(
errors::ConnectorError::NotImplemented("this payment method".to_string())
.into(),
),
}
}
_ => Err(errors::ConnectorError::NotImplemented(
@ -2900,13 +2904,17 @@ impl
payment_method_type: StripePaymentMethodType::CustomerBalance,
})),
)),
payments::BankTransferData::Pix {} | payments::BankTransferData::Pse {} => Err(
errors::ConnectorError::NotImplemented("payment method".to_string()).into(),
),
}
}
api::PaymentMethodData::MandatePayment
| api::PaymentMethodData::Crypto(_)
| api::PaymentMethodData::Reward(_)
| api::PaymentMethodData::GiftCard(_)
| api::PaymentMethodData::Upi(_) => Err(errors::ConnectorError::NotSupported {
| api::PaymentMethodData::Upi(_)
| api::PaymentMethodData::Voucher(_) => Err(errors::ConnectorError::NotSupported {
message: format!("{pm_type:?}"),
connector: "Stripe",
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),

View File

@ -41,7 +41,7 @@ pub struct ApiRequest {
payment_channel: ZenPaymentChannels,
amount: String,
currency: enums::Currency,
payment_specific_data: ZenPaymentData,
payment_specific_data: ZenPaymentSpecificData,
customer: ZenCustomerDetails,
custom_ipn_url: String,
items: Vec<ZenItemObject>,
@ -75,6 +75,14 @@ pub enum ZenPaymentChannels {
PclCard,
PclGooglepay,
PclApplepay,
PclBoacompraBoleto,
PclBoacompraEfecty,
PclBoacompraMultibanco,
PclBoacompraPagoefectivo,
PclBoacompraPix,
PclBoacompraPse,
PclBoacompraRedcompra,
PclBoacompraRedpagos,
}
#[derive(Debug, Serialize)]
@ -84,6 +92,13 @@ pub struct ZenCustomerDetails {
ip: Secret<String, pii::IpAddress>,
}
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum ZenPaymentSpecificData {
ZenOnetimePayment(Box<ZenPaymentData>),
ZenGeneralPayment(ZenGeneralPaymentData),
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenPaymentData {
@ -98,6 +113,14 @@ pub struct ZenPaymentData {
return_verify_url: Option<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenGeneralPaymentData {
#[serde(rename = "type")]
payment_type: ZenPaymentTypes,
return_url: String,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenBrowserDetails {
@ -116,6 +139,7 @@ pub struct ZenBrowserDetails {
#[serde(rename_all = "snake_case")]
pub enum ZenPaymentTypes {
Onetime,
General,
}
#[derive(Debug, Serialize)]
@ -155,19 +179,21 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, &Card)> for ZenPaymentsReques
let ip = browser_info.get_ip_address()?;
let browser_details = get_browser_details(&browser_info)?;
let amount = utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
let payment_specific_data = ZenPaymentData {
browser_details,
//Connector Specific for cards
payment_type: ZenPaymentTypes::Onetime,
token: None,
card: Some(ZenCardDetails {
number: ccard.card_number.clone(),
expiry_date: ccard.get_card_expiry_month_year_2_digit_with_delimiter("".to_owned()),
cvv: ccard.card_cvc.clone(),
}),
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
};
let payment_specific_data =
ZenPaymentSpecificData::ZenOnetimePayment(Box::new(ZenPaymentData {
browser_details,
//Connector Specific for cards
payment_type: ZenPaymentTypes::Onetime,
token: None,
card: Some(ZenCardDetails {
number: ccard.card_number.clone(),
expiry_date: ccard
.get_card_expiry_month_year_2_digit_with_delimiter("".to_owned()),
cvv: ccard.card_cvc.clone(),
}),
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
}));
Ok(Self::ApiRequest(Box::new(ApiRequest {
merchant_transaction_id: item.attempt_id.clone(),
payment_channel: ZenPaymentChannels::PclCard,
@ -181,6 +207,108 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, &Card)> for ZenPaymentsReques
}
}
impl
TryFrom<(
&types::PaymentsAuthorizeRouterData,
&api_models::payments::VoucherData,
)> for ZenPaymentsRequest
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
value: (
&types::PaymentsAuthorizeRouterData,
&api_models::payments::VoucherData,
),
) -> Result<Self, Self::Error> {
let (item, voucher_data) = value;
let browser_info = item.request.get_browser_info()?;
let ip = browser_info.get_ip_address()?;
let amount = utils::to_currency_base_unit_with_zero_decimal_check(
item.request.amount,
item.request.currency,
)?;
let payment_specific_data =
ZenPaymentSpecificData::ZenGeneralPayment(ZenGeneralPaymentData {
//Connector Specific for Latam Methods
payment_type: ZenPaymentTypes::General,
return_url: item.request.get_router_return_url()?,
});
let payment_channel = match voucher_data {
api_models::payments::VoucherData::Boleto { .. } => {
ZenPaymentChannels::PclBoacompraBoleto
}
api_models::payments::VoucherData::Efecty => ZenPaymentChannels::PclBoacompraEfecty,
api_models::payments::VoucherData::PagoEfectivo => {
ZenPaymentChannels::PclBoacompraPagoefectivo
}
api_models::payments::VoucherData::RedCompra => {
ZenPaymentChannels::PclBoacompraRedcompra
}
api_models::payments::VoucherData::RedPagos => ZenPaymentChannels::PclBoacompraRedpagos,
};
Ok(Self::ApiRequest(Box::new(ApiRequest {
merchant_transaction_id: item.attempt_id.clone(),
payment_channel,
currency: item.request.currency,
payment_specific_data,
customer: get_customer(item, ip)?,
custom_ipn_url: item.request.get_webhook_url()?,
items: get_item_object(item, amount.clone())?,
amount,
})))
}
}
impl
TryFrom<(
&types::PaymentsAuthorizeRouterData,
&Box<api_models::payments::BankTransferData>,
)> for ZenPaymentsRequest
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
value: (
&types::PaymentsAuthorizeRouterData,
&Box<api_models::payments::BankTransferData>,
),
) -> Result<Self, Self::Error> {
let (item, bank_transfer_data) = value;
let browser_info = item.request.get_browser_info()?;
let ip = browser_info.get_ip_address()?;
let amount = utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
let payment_specific_data =
ZenPaymentSpecificData::ZenGeneralPayment(ZenGeneralPaymentData {
//Connector Specific for Latam Methods
payment_type: ZenPaymentTypes::General,
return_url: item.request.get_router_return_url()?,
});
let payment_channel = match **bank_transfer_data {
api_models::payments::BankTransferData::MultibancoBankTransfer { .. } => {
ZenPaymentChannels::PclBoacompraMultibanco
}
api_models::payments::BankTransferData::Pix { .. } => {
ZenPaymentChannels::PclBoacompraPix
}
api_models::payments::BankTransferData::Pse { .. } => {
ZenPaymentChannels::PclBoacompraPse
}
_ => Err(errors::ConnectorError::NotImplemented(
"payment method".to_string(),
))?,
};
Ok(Self::ApiRequest(Box::new(ApiRequest {
merchant_transaction_id: item.attempt_id.clone(),
payment_channel,
currency: item.request.currency,
payment_specific_data,
customer: get_customer(item, ip)?,
custom_ipn_url: item.request.get_webhook_url()?,
items: get_item_object(item, amount.clone())?,
amount,
})))
}
}
/*
impl TryFrom<(&types::PaymentsAuthorizeRouterData, &GooglePayWalletData)> for ZenPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
@ -339,6 +467,14 @@ fn get_signature_data(checkout_request: &CheckoutRequest) -> String {
ZenPaymentChannels::PclCard => "pcl_card",
ZenPaymentChannels::PclGooglepay => "pcl_googlepay",
ZenPaymentChannels::PclApplepay => "pcl_applepay",
ZenPaymentChannels::PclBoacompraBoleto => "pcl_boacompra_boleto",
ZenPaymentChannels::PclBoacompraEfecty => "pcl_boacompra_efecty",
ZenPaymentChannels::PclBoacompraMultibanco => "pcl_boacompra_multibanco",
ZenPaymentChannels::PclBoacompraPagoefectivo => "pcl_boacompra_pagoefectivo",
ZenPaymentChannels::PclBoacompraPix => "pcl_boacompra_pix",
ZenPaymentChannels::PclBoacompraPse => "pcl_boacompra_pse",
ZenPaymentChannels::PclBoacompraRedcompra => "pcl_boacompra_redcompra",
ZenPaymentChannels::PclBoacompraRedpagos => "pcl_boacompra_redpagos",
};
let mut signature_data = vec![
format!("amount={}", checkout_request.amount),
@ -402,7 +538,10 @@ fn get_item_object(
Ok(ZenItemObject {
name: data.product_name.clone(),
quantity: data.quantity,
price: utils::to_currency_base_unit(data.amount, item.request.currency)?,
price: utils::to_currency_base_unit_with_zero_decimal_check(
data.amount,
item.request.currency,
)?,
line_amount_total: (f64::from(data.quantity)
* utils::to_currency_base_unit_asf64(data.amount, item.request.currency)?)
.to_string(),
@ -458,6 +597,12 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for ZenPaymentsRequest {
api_models::payments::PaymentMethodData::Wallet(wallet_data) => {
Self::try_from((item, wallet_data))
}
api_models::payments::PaymentMethodData::Voucher(voucher_data) => {
Self::try_from((item, voucher_data))
}
api_models::payments::PaymentMethodData::BankTransfer(bank_transfer_data) => {
Self::try_from((item, bank_transfer_data))
}
_ => Err(errors::ConnectorError::NotImplemented(
"payment method".to_string(),
))?,

View File

@ -838,7 +838,9 @@ where
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::MultibancoBankTransfer { .. } => {
| api_models::payments::BankTransferData::MultibancoBankTransfer { .. }
if connector.connector_name == types::Connector::Stripe =>
{
if payment_data.payment_attempt.preprocessing_step_id.is_none() {
(
router_data.preprocessing_steps(state, connector).await?,

View File

@ -1278,6 +1278,7 @@ pub async fn make_pm_data<'a, F: Clone, R>(
(pm @ Some(api::PaymentMethodData::Crypto(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::BankDebit(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::Upi(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::Voucher(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::Reward(_)), _) => Ok(pm.to_owned()),
(pm @ Some(api::PaymentMethodData::GiftCard(_)), _) => Ok(pm.to_owned()),
(pm_opt @ Some(pm @ api::PaymentMethodData::BankTransfer(_)), _) => {
@ -2624,6 +2625,9 @@ pub async fn get_additional_payment_data(
api_models::payments::PaymentMethodData::Upi(_) => {
api_models::payments::AdditionalPaymentData::Upi {}
}
api_models::payments::PaymentMethodData::Voucher(_) => {
api_models::payments::AdditionalPaymentData::Voucher {}
}
api_models::payments::PaymentMethodData::GiftCard(_) => {
api_models::payments::AdditionalPaymentData::GiftCard {}
}

View File

@ -193,6 +193,8 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::CryptoData,
api_models::payments::RewardData,
api_models::payments::UpiData,
api_models::payments::VoucherData,
api_models::payments::BoletoVoucherData,
api_models::payments::Address,
api_models::payments::BankRedirectData,
api_models::payments::BankRedirectBilling,

View File

@ -218,7 +218,14 @@ impl ForeignFrom<api_enums::PaymentMethodType> for api_enums::PaymentMethod {
}
api_enums::PaymentMethodType::Evoucher
| api_enums::PaymentMethodType::ClassicReward => Self::Reward,
api_enums::PaymentMethodType::Multibanco => Self::BankTransfer,
api_enums::PaymentMethodType::Boleto
| api_enums::PaymentMethodType::Efecty
| api_enums::PaymentMethodType::PagoEfectivo
| api_enums::PaymentMethodType::RedCompra
| api_enums::PaymentMethodType::RedPagos => Self::Voucher,
api_enums::PaymentMethodType::Multibanco
| api_enums::PaymentMethodType::Pix
| api_enums::PaymentMethodType::Pse => Self::BankTransfer,
}
}
}

View File

@ -71,3 +71,4 @@ mod worldline;
mod worldline_ui;
mod worldpay;
mod zen;
mod zen_ui;

View File

@ -0,0 +1,37 @@
use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};
use crate::{selenium::*, tester};
struct ZenSeleniumTest;
impl SeleniumTest for ZenSeleniumTest {
fn get_connector_name(&self) -> String {
"zen".to_string()
}
}
async fn should_make_zen_3ds_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
let mycon = ZenSeleniumTest {};
mycon
.make_redirection_payment(
web_driver,
vec![
Event::Trigger(Trigger::Goto(&format!("{CHEKOUT_BASE_URL}/saved/201"))),
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
Event::Trigger(Trigger::Sleep(10)), //url gets updated only after some time, so need this timeout to solve the issue
Event::Trigger(Trigger::SwitchFrame(By::Name("cko-3ds2-iframe"))),
Event::Trigger(Trigger::SendKeys(By::Id("password"), "Checkout1!")),
Event::Trigger(Trigger::Click(By::Id("txtButton"))),
Event::Assert(Assert::IsPresent("succeeded")),
],
)
.await?;
Ok(())
}
#[test]
#[serial]
fn should_make_zen_3ds_payment_test() {
tester!(should_make_zen_3ds_payment);
}