mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
fix(payments): update preprocessing steps and handle session token in payment flow (#9947)
This commit is contained in:
@ -426,7 +426,10 @@ impl Connector {
|
||||
)
|
||||
}
|
||||
pub fn requires_order_creation_before_payment(self, payment_method: PaymentMethod) -> bool {
|
||||
matches!((self, payment_method), (Self::Razorpay, PaymentMethod::Upi))
|
||||
matches!(
|
||||
(self, payment_method),
|
||||
(Self::Razorpay, PaymentMethod::Upi) | (Self::Airwallex, PaymentMethod::Card)
|
||||
)
|
||||
}
|
||||
pub fn supports_file_storage_module(self) -> bool {
|
||||
matches!(self, Self::Stripe | Self::Checkout | Self::Worldpayvantiv)
|
||||
@ -575,10 +578,6 @@ impl Connector {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pre_processing_required_before_authorize(self) -> bool {
|
||||
matches!(self, Self::Airwallex)
|
||||
}
|
||||
|
||||
pub fn get_payment_methods_supporting_extended_authorization(self) -> HashSet<PaymentMethod> {
|
||||
HashSet::from([PaymentMethod::Card])
|
||||
}
|
||||
|
||||
@ -19,23 +19,26 @@ use hyperswitch_domain_models::{
|
||||
router_data::{AccessToken, ErrorResponse, RouterData},
|
||||
router_flow_types::{
|
||||
access_token_auth::AccessTokenAuth,
|
||||
payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
|
||||
payments::{
|
||||
Authorize, Capture, CreateOrder, PSync, PaymentMethodToken, Session, SetupMandate, Void,
|
||||
},
|
||||
refunds::{Execute, RSync},
|
||||
CompleteAuthorize, PreProcessing,
|
||||
CompleteAuthorize,
|
||||
},
|
||||
router_request_types::{
|
||||
AccessTokenRequestData, CompleteAuthorizeData, PaymentMethodTokenizationData,
|
||||
PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData,
|
||||
PaymentsSessionData, PaymentsSyncData, RefundsData, SetupMandateRequestData,
|
||||
AccessTokenRequestData, CompleteAuthorizeData, CreateOrderRequestData,
|
||||
PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData,
|
||||
PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, RefundsData,
|
||||
SetupMandateRequestData,
|
||||
},
|
||||
router_response_types::{
|
||||
ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
|
||||
SupportedPaymentMethods, SupportedPaymentMethodsExt,
|
||||
},
|
||||
types::{
|
||||
PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
|
||||
PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData,
|
||||
PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, SetupMandateRouterData,
|
||||
CreateOrderRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData,
|
||||
PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData, PaymentsSyncRouterData,
|
||||
RefundSyncRouterData, RefundsRouterData, SetupMandateRouterData,
|
||||
},
|
||||
};
|
||||
use hyperswitch_interfaces::{
|
||||
@ -47,7 +50,7 @@ use hyperswitch_interfaces::{
|
||||
disputes::DisputePayload,
|
||||
errors,
|
||||
events::connector_api_logs::ConnectorEvent,
|
||||
types::{self, Response},
|
||||
types::{self, CreateOrderType, Response},
|
||||
webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
|
||||
};
|
||||
use masking::{Mask, PeekInterface};
|
||||
@ -58,7 +61,10 @@ use crate::{
|
||||
connectors::airwallex::transformers::AirwallexAuthorizeResponse,
|
||||
constants::headers,
|
||||
types::{RefreshTokenRouterData, ResponseRouterData},
|
||||
utils::{convert_amount, AccessTokenRequestInfo, ForeignTryFrom, RefundsRequestData},
|
||||
utils::{
|
||||
convert_amount, AccessTokenRequestInfo, ForeignTryFrom, PaymentsAuthorizeRequestData,
|
||||
RefundsRequestData,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -154,8 +160,8 @@ impl ConnectorCommon for Airwallex {
|
||||
impl ConnectorValidation for Airwallex {}
|
||||
|
||||
impl api::Payment for Airwallex {}
|
||||
impl api::PaymentsPreProcessing for Airwallex {}
|
||||
impl api::PaymentsCompleteAuthorize for Airwallex {}
|
||||
impl api::PaymentsCreateOrder for Airwallex {}
|
||||
impl api::MandateSetup for Airwallex {}
|
||||
impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
|
||||
for Airwallex
|
||||
@ -262,12 +268,10 @@ impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken>
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResponseData>
|
||||
for Airwallex
|
||||
{
|
||||
impl ConnectorIntegration<CreateOrder, CreateOrderRequestData, PaymentsResponseData> for Airwallex {
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &PaymentsPreProcessingRouterData,
|
||||
req: &CreateOrderRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
@ -279,7 +283,7 @@ impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResp
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &PaymentsPreProcessingRouterData,
|
||||
_req: &CreateOrderRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
@ -291,22 +295,13 @@ impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResp
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &PaymentsPreProcessingRouterData,
|
||||
req: &CreateOrderRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let amount_in_minor_unit = MinorUnit::new(req.request.amount.ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "amount",
|
||||
},
|
||||
)?);
|
||||
let amount = convert_amount(
|
||||
self.amount_converter,
|
||||
amount_in_minor_unit,
|
||||
req.request
|
||||
.currency
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "currency",
|
||||
})?,
|
||||
req.request.minor_amount,
|
||||
req.request.currency,
|
||||
)?;
|
||||
let connector_router_data = airwallex::AirwallexRouterData::try_from((amount, req))?;
|
||||
let connector_req = airwallex::AirwallexIntentRequest::try_from(&connector_router_data)?;
|
||||
@ -315,35 +310,29 @@ impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResp
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &PaymentsPreProcessingRouterData,
|
||||
req: &CreateOrderRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
RequestBuilder::new()
|
||||
.method(Method::Post)
|
||||
.url(&types::PaymentsPreProcessingType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.url(&CreateOrderType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsPreProcessingType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.set_body(types::PaymentsPreProcessingType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.headers(CreateOrderType::get_headers(self, req, connectors)?)
|
||||
.set_body(CreateOrderType::get_request_body(self, req, connectors)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &PaymentsPreProcessingRouterData,
|
||||
data: &CreateOrderRouterData,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: airwallex::AirwallexPaymentsResponse = res
|
||||
) -> CustomResult<CreateOrderRouterData, errors::ConnectorError> {
|
||||
let response: airwallex::AirwallexOrderResponse = res
|
||||
.response
|
||||
.parse_struct("airwallex AirwallexPaymentsResponse")
|
||||
.parse_struct("airwallex AirwallexOrderResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
@ -391,9 +380,7 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
"{}{}{}{}",
|
||||
self.base_url(connectors),
|
||||
"api/v1/pa/payment_intents/",
|
||||
req.reference_id
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::MissingConnectorTransactionID)?,
|
||||
req.request.get_order_id()?,
|
||||
"/confirm"
|
||||
))
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
types::{RefundsResponseRouterData, ResponseRouterData},
|
||||
types::{CreateOrderResponseRouterData, RefundsResponseRouterData, ResponseRouterData},
|
||||
utils::{
|
||||
self, BrowserInformationData, CardData as _, ForeignTryFrom, PaymentsAuthorizeRequestData,
|
||||
PhoneDetailsData, RouterData as _,
|
||||
@ -74,23 +74,17 @@ pub struct AirwallexIntentRequest {
|
||||
order: Option<AirwallexOrderData>,
|
||||
}
|
||||
|
||||
impl TryFrom<&AirwallexRouterData<&types::PaymentsPreProcessingRouterData>>
|
||||
for AirwallexIntentRequest
|
||||
{
|
||||
impl TryFrom<&AirwallexRouterData<&types::CreateOrderRouterData>> for AirwallexIntentRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &AirwallexRouterData<&types::PaymentsPreProcessingRouterData>,
|
||||
item: &AirwallexRouterData<&types::CreateOrderRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let referrer_data = ReferrerData {
|
||||
r_type: "hyperswitch".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
};
|
||||
let amount = item.amount.clone();
|
||||
let currency = item.router_data.request.currency.ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "currency",
|
||||
},
|
||||
)?;
|
||||
let currency = item.router_data.request.currency;
|
||||
|
||||
let order = match item.router_data.request.payment_method_data {
|
||||
Some(PaymentMethodData::PayLater(_)) => Some(
|
||||
@ -137,6 +131,30 @@ impl TryFrom<&AirwallexRouterData<&types::PaymentsPreProcessingRouterData>>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AirwallexOrderResponse {
|
||||
pub status: AirwallexPaymentStatus,
|
||||
pub id: String,
|
||||
pub payment_consent_id: Option<Secret<String>>,
|
||||
pub next_action: Option<AirwallexPaymentsNextAction>,
|
||||
}
|
||||
|
||||
impl TryFrom<CreateOrderResponseRouterData<AirwallexOrderResponse>>
|
||||
for types::CreateOrderRouterData
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: CreateOrderResponseRouterData<AirwallexOrderResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(PaymentsResponseData::PaymentsCreateOrderResponse {
|
||||
order_id: item.response.id.clone(),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AirwallexRouterData<T> {
|
||||
pub amount: StringMajorUnit,
|
||||
|
||||
@ -768,7 +768,6 @@ default_imp_for_create_order!(
|
||||
connectors::Adyen,
|
||||
connectors::Adyenplatform,
|
||||
connectors::Affirm,
|
||||
connectors::Airwallex,
|
||||
connectors::Amazonpay,
|
||||
connectors::Archipel,
|
||||
connectors::Authipay,
|
||||
@ -2359,6 +2358,7 @@ default_imp_for_pre_processing_steps!(
|
||||
connectors::Aci,
|
||||
connectors::Adyenplatform,
|
||||
connectors::Affirm,
|
||||
connectors::Airwallex,
|
||||
connectors::Amazonpay,
|
||||
connectors::Archipel,
|
||||
connectors::Authipay,
|
||||
|
||||
@ -517,6 +517,8 @@ impl TryFrom<ExternalVaultProxyPaymentsData> for PaymentMethodTokenizationData {
|
||||
pub struct CreateOrderRequestData {
|
||||
pub minor_amount: MinorUnit,
|
||||
pub currency: storage_enums::Currency,
|
||||
pub payment_method_data: Option<PaymentMethodData>,
|
||||
pub order_details: Option<Vec<OrderDetailsWithAmount>>,
|
||||
}
|
||||
|
||||
impl TryFrom<PaymentsAuthorizeData> for CreateOrderRequestData {
|
||||
@ -526,6 +528,8 @@ impl TryFrom<PaymentsAuthorizeData> for CreateOrderRequestData {
|
||||
Ok(Self {
|
||||
minor_amount: data.minor_amount,
|
||||
currency: data.currency,
|
||||
payment_method_data: Some(data.payment_method_data),
|
||||
order_details: data.order_details,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -537,6 +541,8 @@ impl TryFrom<ExternalVaultProxyPaymentsData> for CreateOrderRequestData {
|
||||
Ok(Self {
|
||||
minor_amount: data.minor_amount,
|
||||
currency: data.currency,
|
||||
payment_method_data: None,
|
||||
order_details: data.order_details,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -6655,14 +6655,6 @@ where
|
||||
dyn api::Connector:
|
||||
services::api::ConnectorIntegration<F, Req, router_types::PaymentsResponseData>,
|
||||
{
|
||||
if !is_operation_complete_authorize(&operation)
|
||||
&& connector
|
||||
.connector_name
|
||||
.is_pre_processing_required_before_authorize()
|
||||
{
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
return Ok((router_data, should_continue_payment));
|
||||
}
|
||||
//TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check
|
||||
let router_data_and_should_continue_payment = match payment_data.get_payment_method_data() {
|
||||
Some(domain::PaymentMethodData::BankTransfer(_)) => (router_data, should_continue_payment),
|
||||
|
||||
@ -711,9 +711,7 @@ pub async fn authorize_preprocessing_steps<F: Clone>(
|
||||
router_data.request.to_owned(),
|
||||
resp.response.clone(),
|
||||
);
|
||||
if connector.connector_name == api_models::enums::Connector::Airwallex {
|
||||
authorize_router_data.reference_id = resp.reference_id;
|
||||
} else if connector.connector_name == api_models::enums::Connector::Nuvei {
|
||||
if connector.connector_name == api_models::enums::Connector::Nuvei {
|
||||
let (enrolled_for_3ds, related_transaction_id) = match &authorize_router_data.response {
|
||||
Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse {
|
||||
enrolled_v2,
|
||||
|
||||
Reference in New Issue
Block a user