mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(connector): [payload] add webhook support (#8558)
This commit is contained in:
@ -10,9 +10,9 @@ use crate::{disputes, enums as api_enums, mandates, payments, refunds};
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum IncomingWebhookEvent {
|
||||
/// Authorization + Capture success
|
||||
PaymentIntentFailure,
|
||||
/// Authorization + Capture failure
|
||||
PaymentIntentFailure,
|
||||
/// Authorization + Capture success
|
||||
PaymentIntentSuccess,
|
||||
PaymentIntentProcessing,
|
||||
PaymentIntentPartiallyFunded,
|
||||
|
||||
@ -6328,8 +6328,7 @@ type="Text"
|
||||
payment_method_type = "Visa"
|
||||
[payload.connector_auth.HeaderKey]
|
||||
api_key="API Key"
|
||||
[payload.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
|
||||
[silverflow]
|
||||
[silverflow.connector_auth.BodyKey]
|
||||
api_key="API Key"
|
||||
|
||||
@ -4935,10 +4935,8 @@ type="Text"
|
||||
payment_method_type = "Visa"
|
||||
[payload.connector_auth.HeaderKey]
|
||||
api_key="API Key"
|
||||
[payload.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
|
||||
[silverflow]
|
||||
[silverflow.connector_auth.BodyKey]
|
||||
api_key="API Key"
|
||||
key1="Secret Key"
|
||||
key1="Secret Key"
|
||||
|
||||
@ -6302,8 +6302,7 @@ type="Text"
|
||||
payment_method_type = "Visa"
|
||||
[payload.connector_auth.HeaderKey]
|
||||
api_key="API Key"
|
||||
[payload.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
|
||||
[silverflow]
|
||||
[silverflow.connector_auth.BodyKey]
|
||||
api_key="API Key"
|
||||
|
||||
@ -8,12 +8,12 @@ use base64::Engine;
|
||||
use common_enums::enums;
|
||||
use common_utils::{
|
||||
consts::BASE64_ENGINE,
|
||||
errors::CustomResult,
|
||||
ext_traits::BytesExt,
|
||||
errors::{CustomResult, ReportSwitchExt},
|
||||
ext_traits::{ByteSliceExt, BytesExt},
|
||||
request::{Method, Request, RequestBuilder, RequestContent},
|
||||
types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
|
||||
};
|
||||
use error_stack::{report, ResultExt};
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::{
|
||||
payment_method_data::PaymentMethodData,
|
||||
router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
|
||||
@ -47,7 +47,7 @@ use hyperswitch_interfaces::{
|
||||
types::{self, PaymentsVoidType, Response},
|
||||
webhooks,
|
||||
};
|
||||
use masking::{ExposeInterface, Mask};
|
||||
use masking::{ExposeInterface, Mask, Secret};
|
||||
use transformers as payload;
|
||||
|
||||
use crate::{constants::headers, types::ResponseRouterData, utils};
|
||||
@ -195,7 +195,18 @@ impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> fo
|
||||
|
||||
impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Payload {}
|
||||
|
||||
impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Payload {}
|
||||
impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Payload {
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
Err(
|
||||
errors::ConnectorError::NotImplemented("Setup Mandate flow for Payload".to_string())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Payload {
|
||||
fn get_headers(
|
||||
@ -701,25 +712,95 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Payload {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl webhooks::IncomingWebhook for Payload {
|
||||
fn get_webhook_object_reference_id(
|
||||
async fn verify_webhook_source(
|
||||
&self,
|
||||
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||
_merchant_id: &common_utils::id_type::MerchantId,
|
||||
_connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
|
||||
_connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>,
|
||||
_connector_label: &str,
|
||||
) -> CustomResult<bool, errors::ConnectorError> {
|
||||
// Payload does not support source verification
|
||||
// It does, but the client id and client secret generation is not possible at present
|
||||
// It requires OAuth connect which falls under Access Token flow and it also requires multiple calls to be made
|
||||
// We return false just so that a PSync call is triggered internally
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
|
||||
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
|
||||
let webhook_body: responses::PayloadWebhookEvent = request
|
||||
.body
|
||||
.parse_struct("PayloadWebhookEvent")
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
|
||||
let reference_id = match webhook_body.trigger {
|
||||
responses::PayloadWebhooksTrigger::Payment
|
||||
| responses::PayloadWebhooksTrigger::Processed
|
||||
| responses::PayloadWebhooksTrigger::Authorized
|
||||
| responses::PayloadWebhooksTrigger::Credit
|
||||
| responses::PayloadWebhooksTrigger::Reversal
|
||||
| responses::PayloadWebhooksTrigger::Void
|
||||
| responses::PayloadWebhooksTrigger::AutomaticPayment
|
||||
| responses::PayloadWebhooksTrigger::Decline
|
||||
| responses::PayloadWebhooksTrigger::Deposit
|
||||
| responses::PayloadWebhooksTrigger::Reject
|
||||
| responses::PayloadWebhooksTrigger::PaymentActivationStatus
|
||||
| responses::PayloadWebhooksTrigger::PaymentLinkStatus
|
||||
| responses::PayloadWebhooksTrigger::ProcessingStatus
|
||||
| responses::PayloadWebhooksTrigger::BankAccountReject
|
||||
| responses::PayloadWebhooksTrigger::Chargeback
|
||||
| responses::PayloadWebhooksTrigger::ChargebackReversal
|
||||
| responses::PayloadWebhooksTrigger::TransactionOperation
|
||||
| responses::PayloadWebhooksTrigger::TransactionOperationClear => {
|
||||
api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api_models::payments::PaymentIdType::ConnectorTransactionId(
|
||||
webhook_body
|
||||
.triggered_on
|
||||
.transaction_id
|
||||
.ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
responses::PayloadWebhooksTrigger::Refund => {
|
||||
api_models::webhooks::ObjectReferenceId::RefundId(
|
||||
api_models::webhooks::RefundIdType::ConnectorRefundId(
|
||||
webhook_body
|
||||
.triggered_on
|
||||
.transaction_id
|
||||
.ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(reference_id)
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||
request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
|
||||
let webhook_body: responses::PayloadWebhookEvent =
|
||||
request.body.parse_struct("PayloadWebhookEvent").switch()?;
|
||||
|
||||
Ok(api_models::webhooks::IncomingWebhookEvent::from(
|
||||
webhook_body.trigger,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||
request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
|
||||
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
|
||||
let webhook_body: responses::PayloadWebhookEvent = request
|
||||
.body
|
||||
.parse_struct("PayloadWebhookEvent")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
Ok(Box::new(webhook_body))
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,7 +863,11 @@ static PAYLOAD_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
|
||||
connector_type: enums::PaymentConnectorCategory::PaymentGateway,
|
||||
};
|
||||
|
||||
static PAYLOAD_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];
|
||||
static PAYLOAD_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 3] = [
|
||||
enums::EventClass::Disputes,
|
||||
enums::EventClass::Payments,
|
||||
enums::EventClass::Refunds,
|
||||
];
|
||||
|
||||
impl ConnectorSpecifications for Payload {
|
||||
fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
|
||||
|
||||
@ -38,6 +38,7 @@ pub struct PayloadCardsResponseData {
|
||||
#[serde(rename = "id")]
|
||||
pub transaction_id: String,
|
||||
pub payment_method_id: Option<Secret<String>>,
|
||||
// Connector customer id
|
||||
pub processing_id: Option<String>,
|
||||
pub processing_method_id: Option<String>,
|
||||
pub ref_number: Option<String>,
|
||||
@ -84,6 +85,7 @@ pub struct PayloadRefundResponse {
|
||||
pub transaction_id: String,
|
||||
pub ledger: Vec<RefundsLedger>,
|
||||
pub payment_method_id: Option<Secret<String>>,
|
||||
// Connector customer id
|
||||
pub processing_id: Option<String>,
|
||||
pub ref_number: Option<String>,
|
||||
pub status: RefundStatus,
|
||||
@ -99,3 +101,51 @@ pub struct PayloadErrorResponse {
|
||||
/// Payload returns arbitrary details in JSON format
|
||||
pub details: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PayloadWebhooksTrigger {
|
||||
Payment,
|
||||
Processed,
|
||||
Authorized,
|
||||
Credit,
|
||||
Refund,
|
||||
Reversal,
|
||||
Void,
|
||||
AutomaticPayment,
|
||||
Decline,
|
||||
Deposit,
|
||||
Reject,
|
||||
#[serde(rename = "payment_activation:status")]
|
||||
PaymentActivationStatus,
|
||||
#[serde(rename = "payment_link:status")]
|
||||
PaymentLinkStatus,
|
||||
ProcessingStatus,
|
||||
BankAccountReject,
|
||||
Chargeback,
|
||||
ChargebackReversal,
|
||||
#[serde(rename = "transaction:operation")]
|
||||
TransactionOperation,
|
||||
#[serde(rename = "transaction:operation:clear")]
|
||||
TransactionOperationClear,
|
||||
}
|
||||
|
||||
// Webhook response structures
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PayloadWebhookEvent {
|
||||
pub object: String, // Added to match actual webhook structure
|
||||
pub trigger: PayloadWebhooksTrigger,
|
||||
pub webhook_id: String,
|
||||
pub triggered_at: String, // Added to match actual webhook structure
|
||||
// Webhooks Payload
|
||||
pub triggered_on: PayloadEventDetails,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PayloadEventDetails {
|
||||
#[serde(rename = "id")]
|
||||
pub transaction_id: Option<String>,
|
||||
pub object: String,
|
||||
pub value: Option<serde_json::Value>, // Changed to handle any value type including null
|
||||
}
|
||||
|
||||
@ -1,20 +1,24 @@
|
||||
use api_models::webhooks::IncomingWebhookEvent;
|
||||
use common_enums::enums;
|
||||
use common_utils::types::StringMajorUnit;
|
||||
use hyperswitch_domain_models::{
|
||||
payment_method_data::PaymentMethodData,
|
||||
router_data::{ConnectorAuthType, RouterData},
|
||||
router_data::{ConnectorAuthType, ErrorResponse, RouterData},
|
||||
router_flow_types::refunds::{Execute, RSync},
|
||||
router_request_types::ResponseId,
|
||||
router_response_types::{PaymentsResponseData, RefundsResponseData},
|
||||
types::{PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, RefundsRouterData},
|
||||
};
|
||||
use hyperswitch_interfaces::errors;
|
||||
use hyperswitch_interfaces::{
|
||||
consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
|
||||
errors,
|
||||
};
|
||||
use masking::Secret;
|
||||
|
||||
use super::{requests, responses};
|
||||
use crate::{
|
||||
types::{RefundsResponseRouterData, ResponseRouterData},
|
||||
utils::{is_manual_capture, CardData, RouterData as OtherRouterData},
|
||||
utils::{is_manual_capture, AddressDetailsData, CardData, RouterData as OtherRouterData},
|
||||
};
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
@ -56,12 +60,20 @@ impl TryFrom<&PayloadRouterData<&PaymentsAuthorizeRouterData>>
|
||||
cvc: req_card.card_cvc,
|
||||
};
|
||||
let address = item.router_data.get_billing_address()?;
|
||||
|
||||
// Check for required fields and fail if they're missing
|
||||
let city = address.get_city()?.to_owned();
|
||||
let country = address.get_country()?.to_owned();
|
||||
let postal_code = address.get_zip()?.to_owned();
|
||||
let state_province = address.get_state()?.to_owned();
|
||||
let street_address = address.get_line1()?.to_owned();
|
||||
|
||||
let billing_address = requests::BillingAddress {
|
||||
city: address.city.clone().unwrap_or_default(),
|
||||
country: address.country.unwrap_or_default(),
|
||||
postal_code: address.zip.clone().unwrap_or_default(),
|
||||
state_province: address.state.clone().unwrap_or_default(),
|
||||
street_address: address.line1.clone().unwrap_or_default(),
|
||||
city,
|
||||
country,
|
||||
postal_code,
|
||||
state_province,
|
||||
street_address,
|
||||
};
|
||||
|
||||
// For manual capture, set status to "authorized"
|
||||
@ -127,13 +139,29 @@ impl<F, T>
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response.clone() {
|
||||
responses::PayloadPaymentsResponse::PayloadCardsResponse(response) => {
|
||||
let payment_status = response.status;
|
||||
let transaction_id = response.transaction_id;
|
||||
|
||||
Ok(Self {
|
||||
status: common_enums::AttemptStatus::from(payment_status),
|
||||
response: Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: ResponseId::ConnectorTransactionId(transaction_id),
|
||||
let status = enums::AttemptStatus::from(response.status);
|
||||
let connector_customer = response.processing_id.clone();
|
||||
let response_result = if status == enums::AttemptStatus::Failure {
|
||||
Err(ErrorResponse {
|
||||
attempt_status: None,
|
||||
code: response
|
||||
.status_code
|
||||
.clone()
|
||||
.unwrap_or_else(|| NO_ERROR_CODE.to_string()),
|
||||
message: response
|
||||
.status_message
|
||||
.clone()
|
||||
.unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
|
||||
reason: response.status_message,
|
||||
status_code: item.http_code,
|
||||
connector_transaction_id: Some(response.transaction_id.clone()),
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
})
|
||||
} else {
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: ResponseId::ConnectorTransactionId(response.transaction_id),
|
||||
redirection_data: Box::new(None),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
@ -141,7 +169,12 @@ impl<F, T>
|
||||
connector_response_reference_id: response.ref_number,
|
||||
incremental_authorization_allowed: None,
|
||||
charges: None,
|
||||
}),
|
||||
})
|
||||
};
|
||||
Ok(Self {
|
||||
status,
|
||||
response: response_result,
|
||||
connector_customer,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
@ -225,3 +258,41 @@ impl TryFrom<RefundsResponseRouterData<RSync, responses::PayloadRefundResponse>>
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Webhook transformations
|
||||
impl From<responses::PayloadWebhooksTrigger> for IncomingWebhookEvent {
|
||||
fn from(trigger: responses::PayloadWebhooksTrigger) -> Self {
|
||||
match trigger {
|
||||
// Payment Success Events
|
||||
responses::PayloadWebhooksTrigger::Processed => Self::PaymentIntentSuccess,
|
||||
responses::PayloadWebhooksTrigger::Authorized => {
|
||||
Self::PaymentIntentAuthorizationSuccess
|
||||
}
|
||||
// Payment Processing Events
|
||||
responses::PayloadWebhooksTrigger::Payment
|
||||
| responses::PayloadWebhooksTrigger::AutomaticPayment => Self::PaymentIntentProcessing,
|
||||
// Payment Failure Events
|
||||
responses::PayloadWebhooksTrigger::Decline
|
||||
| responses::PayloadWebhooksTrigger::Reject
|
||||
| responses::PayloadWebhooksTrigger::BankAccountReject => Self::PaymentIntentFailure,
|
||||
responses::PayloadWebhooksTrigger::Void
|
||||
| responses::PayloadWebhooksTrigger::Reversal => Self::PaymentIntentCancelled,
|
||||
// Refund Events
|
||||
responses::PayloadWebhooksTrigger::Refund => Self::RefundSuccess,
|
||||
// Dispute Events
|
||||
responses::PayloadWebhooksTrigger::Chargeback => Self::DisputeOpened,
|
||||
responses::PayloadWebhooksTrigger::ChargebackReversal => Self::DisputeWon,
|
||||
// Other payment-related events
|
||||
// Events not supported by our standard flows
|
||||
responses::PayloadWebhooksTrigger::PaymentActivationStatus
|
||||
| responses::PayloadWebhooksTrigger::Credit
|
||||
| responses::PayloadWebhooksTrigger::Deposit
|
||||
| responses::PayloadWebhooksTrigger::PaymentLinkStatus
|
||||
| responses::PayloadWebhooksTrigger::ProcessingStatus
|
||||
| responses::PayloadWebhooksTrigger::TransactionOperation
|
||||
| responses::PayloadWebhooksTrigger::TransactionOperationClear => {
|
||||
Self::EventNotSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import { customerAcceptance } from "./Commons";
|
||||
import {
|
||||
customerAcceptance,
|
||||
singleUseMandateData,
|
||||
multiUseMandateData,
|
||||
} from "./Commons";
|
||||
|
||||
const successfulNo3DSCardDetails = {
|
||||
card_number: "4242424242424242",
|
||||
@ -8,43 +12,18 @@ const successfulNo3DSCardDetails = {
|
||||
card_cvc: "123",
|
||||
};
|
||||
|
||||
// Note: Payload may not support 3DS authentication - using same card for consistency
|
||||
const successfulThreeDSTestCardDetails = {
|
||||
card_number: "4242424242424242",
|
||||
card_exp_month: "12",
|
||||
card_exp_year: "25",
|
||||
card_holder_name: "John Doe",
|
||||
card_cvc: "123",
|
||||
...successfulNo3DSCardDetails,
|
||||
};
|
||||
|
||||
const failedNo3DSCardDetails = {
|
||||
card_number: "4000000000000002",
|
||||
card_number: "4111111111119903",
|
||||
card_exp_month: "01",
|
||||
card_exp_year: "25",
|
||||
card_holder_name: "John Doe",
|
||||
card_cvc: "123",
|
||||
};
|
||||
|
||||
const singleUseMandateData = {
|
||||
customer_acceptance: customerAcceptance,
|
||||
mandate_type: {
|
||||
single_use: {
|
||||
amount: 8000,
|
||||
currency: "USD",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const multiUseMandateData = {
|
||||
customer_acceptance: customerAcceptance,
|
||||
mandate_type: {
|
||||
multi_use: {
|
||||
amount: 8000,
|
||||
currency: "USD",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const payment_method_data_no3ds = {
|
||||
card: {
|
||||
last4: "4242",
|
||||
@ -95,14 +74,6 @@ export const connectorDetails = {
|
||||
},
|
||||
},
|
||||
},
|
||||
SessionToken: {
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
session_token: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
PaymentIntentWithShippingCost: {
|
||||
Request: {
|
||||
currency: "USD",
|
||||
@ -151,7 +122,7 @@ export const connectorDetails = {
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 501,
|
||||
status: 400,
|
||||
body: {
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
@ -175,7 +146,7 @@ export const connectorDetails = {
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 501,
|
||||
status: 400,
|
||||
body: {
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
@ -433,24 +404,25 @@ export const connectorDetails = {
|
||||
},
|
||||
SaveCardUse3DSAutoCaptureOffSession: {
|
||||
Configs: {
|
||||
DELAY: {
|
||||
STATUS: true,
|
||||
TIMEOUT: 15000,
|
||||
},
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_type: "debit",
|
||||
payment_method_data: {
|
||||
card: successfulThreeDSTestCardDetails,
|
||||
},
|
||||
setup_future_usage: "off_session",
|
||||
customer_acceptance: customerAcceptance,
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
status: 400,
|
||||
body: {
|
||||
status: "requires_customer_action",
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
message: "3DS authentication is not supported by Payload",
|
||||
code: "IR_00",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -693,33 +665,11 @@ export const connectorDetails = {
|
||||
mandate_data: singleUseMandateData,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
status: 501,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
ZeroAuthConfirmPayment: {
|
||||
Configs: {
|
||||
DELAY: {
|
||||
STATUS: true,
|
||||
TIMEOUT: 15000,
|
||||
},
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Request: {
|
||||
payment_type: "setup_mandate",
|
||||
payment_method: "card",
|
||||
payment_method_type: "credit",
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
setup_future_usage: "off_session",
|
||||
code: "IR_00",
|
||||
message: "Setup Mandate flow for Payload is not implemented",
|
||||
type: "invalid_request",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user