mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(connector): [Stripe] Add support for WeChat Pay and Qr code support in next action (#1555)
Co-authored-by: AkshayaFoiger <akshaya.shankar@juspay.in> Co-authored-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
ca4e242d20
commit
a15a77dea3
@ -9,6 +9,7 @@ use common_utils::{
|
|||||||
use masking::{PeekInterface, Secret};
|
use masking::{PeekInterface, Secret};
|
||||||
use router_derive::Setter;
|
use router_derive::Setter;
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
|
use url::Url;
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -177,7 +178,7 @@ pub struct PaymentsRequest {
|
|||||||
|
|
||||||
/// The URL to redirect after the completion of the operation
|
/// The URL to redirect after the completion of the operation
|
||||||
#[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
|
#[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
|
||||||
pub return_url: Option<url::Url>,
|
pub return_url: Option<Url>,
|
||||||
/// Indicates that you intend to make future payments with this Payment’s payment method. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.
|
/// Indicates that you intend to make future payments with this Payment’s payment method. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.
|
||||||
#[schema(value_type = Option<FutureUsage>, example = "off_session")]
|
#[schema(value_type = Option<FutureUsage>, example = "off_session")]
|
||||||
pub setup_future_usage: Option<api_enums::FutureUsage>,
|
pub setup_future_usage: Option<api_enums::FutureUsage>,
|
||||||
@ -920,6 +921,8 @@ pub enum WalletData {
|
|||||||
SamsungPay(Box<SamsungPayWalletData>),
|
SamsungPay(Box<SamsungPayWalletData>),
|
||||||
/// The wallet data for WeChat Pay Redirection
|
/// The wallet data for WeChat Pay Redirection
|
||||||
WeChatPayRedirect(Box<WeChatPayRedirection>),
|
WeChatPayRedirect(Box<WeChatPayRedirection>),
|
||||||
|
/// The wallet data for WeChat Pay
|
||||||
|
WeChatPay(Box<WeChatPay>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||||
@ -959,6 +962,9 @@ pub struct ApplePayThirdPartySdkData {}
|
|||||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||||
pub struct WeChatPayRedirection {}
|
pub struct WeChatPayRedirection {}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||||
|
pub struct WeChatPay {}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||||
pub struct PaypalRedirection {}
|
pub struct PaypalRedirection {}
|
||||||
|
|
||||||
@ -1229,8 +1235,13 @@ pub enum NextActionData {
|
|||||||
DisplayBankTransferInformation {
|
DisplayBankTransferInformation {
|
||||||
bank_transfer_steps_and_charges_details: BankTransferNextStepsData,
|
bank_transfer_steps_and_charges_details: BankTransferNextStepsData,
|
||||||
},
|
},
|
||||||
/// contains third party sdk session token response
|
/// Contains third party sdk session token response
|
||||||
ThirdPartySdkSessionToken { session_token: Option<SessionToken> },
|
ThirdPartySdkSessionToken { session_token: Option<SessionToken> },
|
||||||
|
/// Contains url for Qr code image, this qr code has to be shown in sdk
|
||||||
|
QrCodeInformation {
|
||||||
|
#[schema(value_type = String)]
|
||||||
|
image_data_url: Url,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
@ -1242,6 +1253,11 @@ pub struct BankTransferNextStepsData {
|
|||||||
pub receiver: ReceiverDetails,
|
pub receiver: ReceiverDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
|
pub struct QrCodeNextStepsInstruction {
|
||||||
|
pub image_data_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum BankTransferInstructions {
|
pub enum BankTransferInstructions {
|
||||||
|
|||||||
@ -687,6 +687,9 @@ pub enum StripeNextAction {
|
|||||||
ThirdPartySdkSessionToken {
|
ThirdPartySdkSessionToken {
|
||||||
session_token: Option<payments::SessionToken>,
|
session_token: Option<payments::SessionToken>,
|
||||||
},
|
},
|
||||||
|
QrCodeInformation {
|
||||||
|
image_data_url: url::Url,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_stripe_next_action(
|
pub(crate) fn into_stripe_next_action(
|
||||||
@ -710,5 +713,8 @@ pub(crate) fn into_stripe_next_action(
|
|||||||
payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
|
payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
|
||||||
StripeNextAction::ThirdPartySdkSessionToken { session_token }
|
StripeNextAction::ThirdPartySdkSessionToken { session_token }
|
||||||
}
|
}
|
||||||
|
payments::NextActionData::QrCodeInformation { image_data_url } => {
|
||||||
|
StripeNextAction::QrCodeInformation { image_data_url }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -345,6 +345,9 @@ pub enum StripeNextAction {
|
|||||||
ThirdPartySdkSessionToken {
|
ThirdPartySdkSessionToken {
|
||||||
session_token: Option<payments::SessionToken>,
|
session_token: Option<payments::SessionToken>,
|
||||||
},
|
},
|
||||||
|
QrCodeInformation {
|
||||||
|
image_data_url: url::Url,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_stripe_next_action(
|
pub(crate) fn into_stripe_next_action(
|
||||||
@ -368,6 +371,9 @@ pub(crate) fn into_stripe_next_action(
|
|||||||
payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
|
payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
|
||||||
StripeNextAction::ThirdPartySdkSessionToken { session_token }
|
StripeNextAction::ThirdPartySdkSessionToken { session_token }
|
||||||
}
|
}
|
||||||
|
payments::NextActionData::QrCodeInformation { image_data_url } => {
|
||||||
|
StripeNextAction::QrCodeInformation { image_data_url }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1060,8 +1060,7 @@ fn create_stripe_payment_method(
|
|||||||
StripePaymentMethodType::ApplePay,
|
StripePaymentMethodType::ApplePay,
|
||||||
StripeBillingAddress::default(),
|
StripeBillingAddress::default(),
|
||||||
)),
|
)),
|
||||||
|
payments::WalletData::WeChatPay(_) => Ok((
|
||||||
payments::WalletData::WeChatPayRedirect(_) => Ok((
|
|
||||||
StripePaymentMethodData::Wallet(StripeWallet::WechatpayPayment(WechatpayPayment {
|
StripePaymentMethodData::Wallet(StripeWallet::WechatpayPayment(WechatpayPayment {
|
||||||
client: WechatClient::Web,
|
client: WechatClient::Web,
|
||||||
payment_method_types: StripePaymentMethodType::Wechatpay,
|
payment_method_types: StripePaymentMethodType::Wechatpay,
|
||||||
@ -1515,6 +1514,12 @@ pub struct SepaAndBacsBankTransferInstructions {
|
|||||||
pub receiver: SepaAndBacsReceiver,
|
pub receiver: SepaAndBacsReceiver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[serde_with::skip_serializing_none]
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
pub struct WechatPayNextInstructions {
|
||||||
|
pub image_data_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct SepaAndBacsReceiver {
|
pub struct SepaAndBacsReceiver {
|
||||||
pub amount_received: i64,
|
pub amount_received: i64,
|
||||||
@ -1699,26 +1704,47 @@ pub fn get_connector_metadata(
|
|||||||
amount: i64,
|
amount: i64,
|
||||||
) -> CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
|
) -> CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
|
||||||
let next_action_response = next_action
|
let next_action_response = next_action
|
||||||
.and_then(|next_action_response| match next_action_response {
|
.and_then(|next_action_response| match next_action_response {
|
||||||
StripeNextActionResponse::DisplayBankTransferInstructions(response) => {
|
StripeNextActionResponse::DisplayBankTransferInstructions(response) => {
|
||||||
Some(SepaAndBacsBankTransferInstructions {
|
let bank_instructions = response.financial_addresses.get(0);
|
||||||
sepa_bank_instructions: response.financial_addresses[0].iban.to_owned(),
|
let (sepa_bank_instructions, bacs_bank_instructions) =
|
||||||
bacs_bank_instructions: response.financial_addresses[0]
|
bank_instructions.map_or((None, None), |financial_address| {
|
||||||
.sort_code
|
(
|
||||||
.to_owned(),
|
financial_address.iban.to_owned(),
|
||||||
receiver: SepaAndBacsReceiver {
|
financial_address.sort_code.to_owned(),
|
||||||
amount_received: amount - response.amount_remaining,
|
)
|
||||||
amount_remaining: response.amount_remaining,
|
});
|
||||||
},
|
|
||||||
})
|
let bank_transfer_instructions = SepaAndBacsBankTransferInstructions {
|
||||||
}
|
sepa_bank_instructions,
|
||||||
_ => None,
|
bacs_bank_instructions,
|
||||||
}).map(|response| {
|
receiver: SepaAndBacsReceiver {
|
||||||
common_utils::ext_traits::Encode::<SepaAndBacsBankTransferInstructions>::encode_to_value(
|
amount_received: amount - response.amount_remaining,
|
||||||
&response,
|
amount_remaining: response.amount_remaining,
|
||||||
)
|
},
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
};
|
||||||
}).transpose()?;
|
|
||||||
|
Some(common_utils::ext_traits::Encode::<
|
||||||
|
SepaAndBacsBankTransferInstructions,
|
||||||
|
>::encode_to_value(
|
||||||
|
&bank_transfer_instructions
|
||||||
|
))
|
||||||
|
}
|
||||||
|
StripeNextActionResponse::WechatPayDisplayQrCode(response) => {
|
||||||
|
let wechat_pay_instructions = WechatPayNextInstructions {
|
||||||
|
image_data_url: response.image_data_url.to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(
|
||||||
|
common_utils::ext_traits::Encode::<WechatPayNextInstructions>::encode_to_value(
|
||||||
|
&wechat_pay_instructions,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
|
||||||
Ok(next_action_response)
|
Ok(next_action_response)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1848,7 +1874,7 @@ impl StripeNextActionResponse {
|
|||||||
Self::RedirectToUrl(redirect_to_url) | Self::AlipayHandleRedirect(redirect_to_url) => {
|
Self::RedirectToUrl(redirect_to_url) | Self::AlipayHandleRedirect(redirect_to_url) => {
|
||||||
Some(redirect_to_url.url.to_owned())
|
Some(redirect_to_url.url.to_owned())
|
||||||
}
|
}
|
||||||
Self::WechatPayDisplayQrCode(redirect_to_url) => Some(redirect_to_url.data.to_owned()),
|
Self::WechatPayDisplayQrCode(_) => None,
|
||||||
Self::VerifyWithMicrodeposits(verify_with_microdeposits) => {
|
Self::VerifyWithMicrodeposits(verify_with_microdeposits) => {
|
||||||
Some(verify_with_microdeposits.hosted_verification_url.to_owned())
|
Some(verify_with_microdeposits.hosted_verification_url.to_owned())
|
||||||
}
|
}
|
||||||
@ -1885,7 +1911,11 @@ pub struct StripeRedirectToUrlResponse {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct StripeRedirectToQr {
|
pub struct StripeRedirectToQr {
|
||||||
|
// This data contains url, it should be converted to QR code.
|
||||||
|
// Note: The url in this data is not redirection url
|
||||||
data: Url,
|
data: Url,
|
||||||
|
// This is the image source, this image_data_url can directly be used by sdk to show the QR code
|
||||||
|
image_data_url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
|
||||||
|
|||||||
@ -431,7 +431,8 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize {
|
|||||||
.and_then(|next_action_data| match next_action_data {
|
.and_then(|next_action_data| match next_action_data {
|
||||||
api_models::payments::NextActionData::RedirectToUrl { redirect_to_url } => Some(redirect_to_url),
|
api_models::payments::NextActionData::RedirectToUrl { redirect_to_url } => Some(redirect_to_url),
|
||||||
api_models::payments::NextActionData::DisplayBankTransferInformation { .. } => None,
|
api_models::payments::NextActionData::DisplayBankTransferInformation { .. } => None,
|
||||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None
|
api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None,
|
||||||
|
api_models::payments::NextActionData::QrCodeInformation{..} => None
|
||||||
})
|
})
|
||||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
.into_report()
|
.into_report()
|
||||||
|
|||||||
@ -322,6 +322,9 @@ where
|
|||||||
let bank_transfer_next_steps =
|
let bank_transfer_next_steps =
|
||||||
bank_transfer_next_steps_check(payment_attempt.clone())?;
|
bank_transfer_next_steps_check(payment_attempt.clone())?;
|
||||||
|
|
||||||
|
let next_action_containing_qr_code =
|
||||||
|
qr_code_next_steps_check(payment_attempt.clone())?;
|
||||||
|
|
||||||
if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
|
if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
|
||||||
|| bank_transfer_next_steps.is_some()
|
|| bank_transfer_next_steps.is_some()
|
||||||
{
|
{
|
||||||
@ -331,6 +334,11 @@ where
|
|||||||
bank_transfer_steps_and_charges_details: bank_transfer,
|
bank_transfer_steps_and_charges_details: bank_transfer,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.or(next_action_containing_qr_code.map(|qr_code_data| {
|
||||||
|
api_models::payments::NextActionData::QrCodeInformation {
|
||||||
|
image_data_url: qr_code_data.image_data_url,
|
||||||
|
}
|
||||||
|
}))
|
||||||
.or(Some(api_models::payments::NextActionData::RedirectToUrl {
|
.or(Some(api_models::payments::NextActionData::RedirectToUrl {
|
||||||
redirect_to_url: helpers::create_startpay_url(
|
redirect_to_url: helpers::create_startpay_url(
|
||||||
server,
|
server,
|
||||||
@ -554,6 +562,18 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn qr_code_next_steps_check(
|
||||||
|
payment_attempt: storage::PaymentAttempt,
|
||||||
|
) -> RouterResult<Option<api_models::payments::QrCodeNextStepsInstruction>> {
|
||||||
|
let qr_code_steps: Option<Result<api_models::payments::QrCodeNextStepsInstruction, _>> =
|
||||||
|
payment_attempt
|
||||||
|
.connector_metadata
|
||||||
|
.map(|metadata| metadata.parse_value("QrCodeNextStepsInstruction"));
|
||||||
|
|
||||||
|
let qr_code_instructions = qr_code_steps.transpose().ok().flatten();
|
||||||
|
Ok(qr_code_instructions)
|
||||||
|
}
|
||||||
|
|
||||||
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
||||||
fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
|
fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
|
||||||
let pi = item.0;
|
let pi = item.0;
|
||||||
|
|||||||
@ -233,6 +233,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
|||||||
api_models::payments::SdkNextAction,
|
api_models::payments::SdkNextAction,
|
||||||
api_models::payments::NextActionCall,
|
api_models::payments::NextActionCall,
|
||||||
api_models::payments::SamsungPayWalletData,
|
api_models::payments::SamsungPayWalletData,
|
||||||
|
api_models::payments::WeChatPay,
|
||||||
api_models::payments::GpayTokenizationData,
|
api_models::payments::GpayTokenizationData,
|
||||||
api_models::payments::GooglePayPaymentMethodInfo,
|
api_models::payments::GooglePayPaymentMethodInfo,
|
||||||
api_models::payments::ApplePayWalletData,
|
api_models::payments::ApplePayWalletData,
|
||||||
|
|||||||
@ -5530,7 +5530,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "contains third party sdk session token response",
|
"description": "Contains third party sdk session token response",
|
||||||
"required": [
|
"required": [
|
||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
@ -5550,6 +5550,25 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"description": "Contains url for Qr code image, this qr code has to be shown in sdk",
|
||||||
|
"required": [
|
||||||
|
"image_data_url",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"image_data_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"qr_code_information"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"discriminator": {
|
"discriminator": {
|
||||||
@ -8548,9 +8567,23 @@
|
|||||||
"$ref": "#/components/schemas/WeChatPayRedirection"
|
"$ref": "#/components/schemas/WeChatPayRedirection"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"we_chat_pay"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"we_chat_pay": {
|
||||||
|
"$ref": "#/components/schemas/WeChatPay"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"WeChatPay": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"WeChatPayRedirection": {
|
"WeChatPayRedirection": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user