mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
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:
@ -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(),
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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(),
|
||||
))?,
|
||||
|
||||
@ -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?,
|
||||
|
||||
@ -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 {}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,3 +71,4 @@ mod worldline;
|
||||
mod worldline_ui;
|
||||
mod worldpay;
|
||||
mod zen;
|
||||
mod zen_ui;
|
||||
|
||||
37
crates/router/tests/connectors/zen_ui.rs
Normal file
37
crates/router/tests/connectors/zen_ui.rs
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user