mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(connector): [Iatapay] add upi qr support (#4728)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: SamraatBansal <55536657+SamraatBansal@users.noreply.github.com>
This commit is contained in:
@ -141,10 +141,10 @@ impl From<StripeWallet> for payments::WalletData {
|
||||
}
|
||||
|
||||
impl From<StripeUpi> for payments::UpiData {
|
||||
fn from(upi: StripeUpi) -> Self {
|
||||
Self {
|
||||
vpa_id: Some(upi.vpa_id),
|
||||
}
|
||||
fn from(upi_data: StripeUpi) -> Self {
|
||||
Self::UpiCollect(payments::UpiCollectData {
|
||||
vpa_id: Some(upi_data.vpa_id),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,6 +315,18 @@ impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest {
|
||||
|
||||
let amount = item.amount.map(|amount| MinorUnit::new(amount).into());
|
||||
|
||||
let payment_method_data = item.payment_method_data.as_ref().map(|pmd| {
|
||||
let payment_method_data = match pmd.payment_method_details.as_ref() {
|
||||
Some(spmd) => Some(payments::PaymentMethodData::from(spmd.to_owned())),
|
||||
None => get_pmd_based_on_payment_method_type(item.payment_method_types),
|
||||
};
|
||||
|
||||
payments::PaymentMethodDataRequest {
|
||||
payment_method_data,
|
||||
billing: pmd.billing_details.clone().map(payments::Address::from),
|
||||
}
|
||||
});
|
||||
|
||||
let request = Ok(Self {
|
||||
payment_id: item.id.map(payments::PaymentIdType::PaymentIntentId),
|
||||
amount,
|
||||
@ -334,16 +346,7 @@ impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest {
|
||||
phone: item.shipping.as_ref().and_then(|s| s.phone.clone()),
|
||||
description: item.description,
|
||||
return_url: item.return_url,
|
||||
payment_method_data: item.payment_method_data.as_ref().and_then(|pmd| {
|
||||
pmd.payment_method_details
|
||||
.as_ref()
|
||||
.map(|spmd| payments::PaymentMethodDataRequest {
|
||||
payment_method_data: Some(payments::PaymentMethodData::from(
|
||||
spmd.to_owned(),
|
||||
)),
|
||||
billing: pmd.billing_details.clone().map(payments::Address::from),
|
||||
})
|
||||
}),
|
||||
payment_method_data,
|
||||
payment_method: item
|
||||
.payment_method_data
|
||||
.as_ref()
|
||||
@ -816,6 +819,9 @@ pub enum StripeNextAction {
|
||||
display_to_timestamp: Option<i64>,
|
||||
qr_code_url: Option<url::Url>,
|
||||
},
|
||||
FetchQrCodeInformation {
|
||||
qr_code_fetch_url: url::Url,
|
||||
},
|
||||
DisplayVoucherInformation {
|
||||
voucher_details: payments::VoucherNextStepData,
|
||||
},
|
||||
@ -858,6 +864,9 @@ pub(crate) fn into_stripe_next_action(
|
||||
display_to_timestamp,
|
||||
qr_code_url,
|
||||
},
|
||||
payments::NextActionData::FetchQrCodeInformation { qr_code_fetch_url } => {
|
||||
StripeNextAction::FetchQrCodeInformation { qr_code_fetch_url }
|
||||
}
|
||||
payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
|
||||
StripeNextAction::DisplayVoucherInformation { voucher_details }
|
||||
}
|
||||
@ -884,3 +893,15 @@ pub(crate) fn into_stripe_next_action(
|
||||
pub struct StripePaymentRetrieveBody {
|
||||
pub client_secret: Option<String>,
|
||||
}
|
||||
|
||||
//To handle payment types that have empty payment method data
|
||||
fn get_pmd_based_on_payment_method_type(
|
||||
payment_method_type: Option<api_enums::PaymentMethodType>,
|
||||
) -> Option<payments::PaymentMethodData> {
|
||||
match payment_method_type {
|
||||
Some(api_enums::PaymentMethodType::UpiIntent) => Some(payments::PaymentMethodData::Upi(
|
||||
payments::UpiData::UpiIntent(payments::UpiIntentData {}),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,6 +382,9 @@ pub enum StripeNextAction {
|
||||
display_to_timestamp: Option<i64>,
|
||||
qr_code_url: Option<url::Url>,
|
||||
},
|
||||
FetchQrCodeInformation {
|
||||
qr_code_fetch_url: url::Url,
|
||||
},
|
||||
DisplayVoucherInformation {
|
||||
voucher_details: payments::VoucherNextStepData,
|
||||
},
|
||||
@ -424,6 +427,9 @@ pub(crate) fn into_stripe_next_action(
|
||||
display_to_timestamp,
|
||||
qr_code_url,
|
||||
},
|
||||
payments::NextActionData::FetchQrCodeInformation { qr_code_fetch_url } => {
|
||||
StripeNextAction::FetchQrCodeInformation { qr_code_fetch_url }
|
||||
}
|
||||
payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
|
||||
StripeNextAction::DisplayVoucherInformation { voucher_details }
|
||||
}
|
||||
|
||||
@ -214,7 +214,8 @@ impl ConnectorValidation for Adyen {
|
||||
| PaymentMethodType::SamsungPay
|
||||
| PaymentMethodType::Evoucher
|
||||
| PaymentMethodType::Cashapp
|
||||
| PaymentMethodType::UpiCollect => {
|
||||
| PaymentMethodType::UpiCollect
|
||||
| PaymentMethodType::UpiIntent => {
|
||||
capture_method_not_supported!(connector, capture_method, payment_method_type)
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use api_models::enums::PaymentMethod;
|
||||
use common_utils::errors::CustomResult;
|
||||
use common_utils::{errors::CustomResult, ext_traits::Encode};
|
||||
use error_stack::ResultExt;
|
||||
use masking::{Secret, SwitchStrategy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -84,6 +85,13 @@ pub struct PayerInfo {
|
||||
token_id: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum PreferredCheckoutMethod {
|
||||
Vpa,
|
||||
Qr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IatapayPaymentsRequest {
|
||||
@ -95,7 +103,9 @@ pub struct IatapayPaymentsRequest {
|
||||
locale: String,
|
||||
redirect_urls: RedirectUrls,
|
||||
notification_url: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
payer_info: Option<PayerInfo>,
|
||||
preferred_checkout_method: Option<PreferredCheckoutMethod>,
|
||||
}
|
||||
|
||||
impl
|
||||
@ -136,24 +146,31 @@ impl
|
||||
| PaymentMethod::GiftCard => item.router_data.get_billing_country()?.to_string(),
|
||||
};
|
||||
let return_url = item.router_data.get_return_url()?;
|
||||
let payer_info = match item.router_data.request.payment_method_data.clone() {
|
||||
domain::PaymentMethodData::Upi(upi_data) => upi_data.vpa_id.map(|id| PayerInfo {
|
||||
token_id: id.switch_strategy(),
|
||||
}),
|
||||
domain::PaymentMethodData::Card(_)
|
||||
| domain::PaymentMethodData::CardRedirect(_)
|
||||
| domain::PaymentMethodData::Wallet(_)
|
||||
| domain::PaymentMethodData::PayLater(_)
|
||||
| domain::PaymentMethodData::BankRedirect(_)
|
||||
| domain::PaymentMethodData::BankDebit(_)
|
||||
| domain::PaymentMethodData::BankTransfer(_)
|
||||
| domain::PaymentMethodData::Crypto(_)
|
||||
| domain::PaymentMethodData::MandatePayment
|
||||
| domain::PaymentMethodData::Reward
|
||||
| domain::PaymentMethodData::Voucher(_)
|
||||
| domain::PaymentMethodData::GiftCard(_)
|
||||
| domain::PaymentMethodData::CardToken(_) => None,
|
||||
};
|
||||
let (payer_info, preferred_checkout_method) =
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
domain::PaymentMethodData::Upi(upi_type) => match upi_type {
|
||||
domain::UpiData::UpiCollect(upi_data) => (
|
||||
upi_data.vpa_id.map(|id| PayerInfo {
|
||||
token_id: id.switch_strategy(),
|
||||
}),
|
||||
Some(PreferredCheckoutMethod::Vpa),
|
||||
),
|
||||
domain::UpiData::UpiIntent(_) => (None, Some(PreferredCheckoutMethod::Qr)),
|
||||
},
|
||||
domain::PaymentMethodData::Card(_)
|
||||
| domain::PaymentMethodData::CardRedirect(_)
|
||||
| domain::PaymentMethodData::Wallet(_)
|
||||
| domain::PaymentMethodData::PayLater(_)
|
||||
| domain::PaymentMethodData::BankRedirect(_)
|
||||
| domain::PaymentMethodData::BankDebit(_)
|
||||
| domain::PaymentMethodData::BankTransfer(_)
|
||||
| domain::PaymentMethodData::Crypto(_)
|
||||
| domain::PaymentMethodData::MandatePayment
|
||||
| domain::PaymentMethodData::Reward
|
||||
| domain::PaymentMethodData::Voucher(_)
|
||||
| domain::PaymentMethodData::GiftCard(_)
|
||||
| domain::PaymentMethodData::CardToken(_) => (None, None),
|
||||
};
|
||||
let payload = Self {
|
||||
merchant_id: IatapayAuthType::try_from(&item.router_data.connector_auth_type)?
|
||||
.merchant_id,
|
||||
@ -165,6 +182,7 @@ impl
|
||||
redirect_urls: get_redirect_url(return_url),
|
||||
payer_info,
|
||||
notification_url: item.router_data.request.get_webhook_url()?,
|
||||
preferred_checkout_method,
|
||||
};
|
||||
Ok(payload)
|
||||
}
|
||||
@ -291,8 +309,46 @@ fn get_iatpay_response(
|
||||
};
|
||||
let connector_response_reference_id = response.merchant_payment_id.or(response.iata_payment_id);
|
||||
|
||||
let payment_response_data = response.checkout_methods.map_or(
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
let payment_response_data = match response.checkout_methods {
|
||||
Some(checkout_methods) => {
|
||||
let (connector_metadata, redirection_data) =
|
||||
match checkout_methods.redirect.redirect_url.ends_with("qr") {
|
||||
true => {
|
||||
let qr_code_info = api_models::payments::FetchQrCodeInformation {
|
||||
qr_code_fetch_url: url::Url::parse(
|
||||
&checkout_methods.redirect.redirect_url,
|
||||
)
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
|
||||
};
|
||||
(
|
||||
Some(qr_code_info.encode_to_value())
|
||||
.transpose()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
|
||||
None,
|
||||
)
|
||||
}
|
||||
false => (
|
||||
None,
|
||||
Some(services::RedirectForm::Form {
|
||||
endpoint: checkout_methods.redirect.redirect_url,
|
||||
method: services::Method::Get,
|
||||
form_fields,
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: id,
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
connector_metadata,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: connector_response_reference_id.clone(),
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}
|
||||
}
|
||||
None => types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: id.clone(),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
@ -302,21 +358,8 @@ fn get_iatpay_response(
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
},
|
||||
|checkout_methods| types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: id,
|
||||
redirection_data: Some(services::RedirectForm::Form {
|
||||
endpoint: checkout_methods.redirect.redirect_url,
|
||||
method: services::Method::Get,
|
||||
form_fields,
|
||||
}),
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: connector_response_reference_id.clone(),
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
Ok((status, error, payment_response_data))
|
||||
}
|
||||
|
||||
|
||||
@ -398,6 +398,7 @@ impl
|
||||
| common_enums::PaymentMethodType::Trustly
|
||||
| common_enums::PaymentMethodType::Twint
|
||||
| common_enums::PaymentMethodType::UpiCollect
|
||||
| common_enums::PaymentMethodType::UpiIntent
|
||||
| common_enums::PaymentMethodType::Venmo
|
||||
| common_enums::PaymentMethodType::Vipps
|
||||
| common_enums::PaymentMethodType::Walley
|
||||
|
||||
@ -675,6 +675,7 @@ impl TryFrom<enums::PaymentMethodType> for StripePaymentMethodType {
|
||||
| enums::PaymentMethodType::Paypal
|
||||
| enums::PaymentMethodType::Pix
|
||||
| enums::PaymentMethodType::UpiCollect
|
||||
| enums::PaymentMethodType::UpiIntent
|
||||
| enums::PaymentMethodType::Cashapp
|
||||
| enums::PaymentMethodType::Oxxo => Err(errors::ConnectorError::NotImplemented(
|
||||
connector_util::get_unimplemented_payment_method_error_message("stripe"),
|
||||
|
||||
@ -1009,6 +1009,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize {
|
||||
api_models::payments::NextActionData::DisplayBankTransferInformation { .. } => None,
|
||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None,
|
||||
api_models::payments::NextActionData::QrCodeInformation{..} => None,
|
||||
api_models::payments::NextActionData::FetchQrCodeInformation{..} => None,
|
||||
api_models::payments::NextActionData::DisplayVoucherInformation{ .. } => None,
|
||||
api_models::payments::NextActionData::WaitScreenInformation{..} => None,
|
||||
api_models::payments::NextActionData::ThreeDsInvoke{..} => None,
|
||||
|
||||
@ -2338,7 +2338,7 @@ pub fn validate_payment_method_type_against_payment_method(
|
||||
),
|
||||
api_enums::PaymentMethod::Upi => matches!(
|
||||
payment_method_type,
|
||||
api_enums::PaymentMethodType::UpiCollect
|
||||
api_enums::PaymentMethodType::UpiCollect | api_enums::PaymentMethodType::UpiIntent
|
||||
),
|
||||
api_enums::PaymentMethod::Voucher => matches!(
|
||||
payment_method_type,
|
||||
@ -4252,9 +4252,9 @@ pub fn get_key_params_for_surcharge_details(
|
||||
)),
|
||||
api_models::payments::PaymentMethodData::MandatePayment => None,
|
||||
api_models::payments::PaymentMethodData::Reward => None,
|
||||
api_models::payments::PaymentMethodData::Upi(_) => Some((
|
||||
api_models::payments::PaymentMethodData::Upi(upi_data) => Some((
|
||||
common_enums::PaymentMethod::Upi,
|
||||
common_enums::PaymentMethodType::UpiCollect,
|
||||
upi_data.get_payment_method_type(),
|
||||
None,
|
||||
)),
|
||||
api_models::payments::PaymentMethodData::Voucher(voucher) => Some((
|
||||
|
||||
@ -541,6 +541,9 @@ where
|
||||
|
||||
let papal_sdk_next_action = paypal_sdk_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
let next_action_containing_fetch_qr_code_url =
|
||||
fetch_qr_code_url_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
let next_action_containing_wait_screen =
|
||||
wait_screen_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
@ -550,6 +553,7 @@ where
|
||||
|| next_action_containing_qr_code_url.is_some()
|
||||
|| next_action_containing_wait_screen.is_some()
|
||||
|| papal_sdk_next_action.is_some()
|
||||
|| next_action_containing_fetch_qr_code_url.is_some()
|
||||
|| payment_data.authentication.is_some()
|
||||
{
|
||||
next_action_response = bank_transfer_next_steps
|
||||
@ -566,6 +570,11 @@ where
|
||||
.or(next_action_containing_qr_code_url.map(|qr_code_data| {
|
||||
api_models::payments::NextActionData::foreign_from(qr_code_data)
|
||||
}))
|
||||
.or(next_action_containing_fetch_qr_code_url.map(|fetch_qr_code_data| {
|
||||
api_models::payments::NextActionData::FetchQrCodeInformation {
|
||||
qr_code_fetch_url: fetch_qr_code_data.qr_code_fetch_url
|
||||
}
|
||||
}))
|
||||
.or(papal_sdk_next_action.map(|paypal_next_action_data| {
|
||||
api_models::payments::NextActionData::InvokeSdkClient{
|
||||
next_action_data: paypal_next_action_data
|
||||
@ -899,6 +908,18 @@ pub fn paypal_sdk_next_steps_check(
|
||||
Ok(paypal_next_steps)
|
||||
}
|
||||
|
||||
pub fn fetch_qr_code_url_next_steps_check(
|
||||
payment_attempt: storage::PaymentAttempt,
|
||||
) -> RouterResult<Option<api_models::payments::FetchQrCodeInformation>> {
|
||||
let qr_code_steps: Option<Result<api_models::payments::FetchQrCodeInformation, _>> =
|
||||
payment_attempt
|
||||
.connector_metadata
|
||||
.map(|metadata| metadata.parse_value("FetchQrCodeInformation"));
|
||||
|
||||
let qr_code_fetch_url = qr_code_steps.transpose().ok().flatten();
|
||||
Ok(qr_code_fetch_url)
|
||||
}
|
||||
|
||||
pub fn wait_screen_next_steps_check(
|
||||
payment_attempt: storage::PaymentAttempt,
|
||||
) -> RouterResult<Option<api_models::payments::WaitScreenInstructions>> {
|
||||
@ -1108,8 +1129,8 @@ impl ForeignFrom<api_models::payments::QrCodeInformation> for api_models::paymen
|
||||
display_to_timestamp,
|
||||
} => Self::QrCodeInformation {
|
||||
qr_code_url: Some(qr_code_url),
|
||||
display_to_timestamp,
|
||||
image_data_url: None,
|
||||
display_to_timestamp,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,6 @@ pub use hyperswitch_domain_models::payment_method_data::{
|
||||
GooglePayPaymentMethodInfo, GooglePayRedirectData, GooglePayThirdPartySdkData,
|
||||
GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, KakaoPayRedirection,
|
||||
MbWayRedirection, PayLaterData, PaymentMethodData, SamsungPayWalletData,
|
||||
SepaAndBacsBillingDetails, SwishQrData, TouchNGoRedirection, VoucherData, WalletData,
|
||||
WeChatPayQr,
|
||||
SepaAndBacsBillingDetails, SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData,
|
||||
UpiIntentData, VoucherData, WalletData, WeChatPayQr,
|
||||
};
|
||||
|
||||
@ -461,7 +461,9 @@ impl ForeignFrom<api_enums::PaymentMethodType> for api_enums::PaymentMethod {
|
||||
| api_enums::PaymentMethodType::Trustly
|
||||
| api_enums::PaymentMethodType::Bizum
|
||||
| api_enums::PaymentMethodType::Interac => Self::BankRedirect,
|
||||
api_enums::PaymentMethodType::UpiCollect => Self::Upi,
|
||||
api_enums::PaymentMethodType::UpiCollect | api_enums::PaymentMethodType::UpiIntent => {
|
||||
Self::Upi
|
||||
}
|
||||
api_enums::PaymentMethodType::CryptoCurrency => Self::Crypto,
|
||||
api_enums::PaymentMethodType::Ach
|
||||
| api_enums::PaymentMethodType::Sepa
|
||||
|
||||
Reference in New Issue
Block a user