mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +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)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum IncomingWebhookEvent {
|
pub enum IncomingWebhookEvent {
|
||||||
/// Authorization + Capture success
|
|
||||||
PaymentIntentFailure,
|
|
||||||
/// Authorization + Capture failure
|
/// Authorization + Capture failure
|
||||||
|
PaymentIntentFailure,
|
||||||
|
/// Authorization + Capture success
|
||||||
PaymentIntentSuccess,
|
PaymentIntentSuccess,
|
||||||
PaymentIntentProcessing,
|
PaymentIntentProcessing,
|
||||||
PaymentIntentPartiallyFunded,
|
PaymentIntentPartiallyFunded,
|
||||||
|
|||||||
@ -6328,8 +6328,7 @@ type="Text"
|
|||||||
payment_method_type = "Visa"
|
payment_method_type = "Visa"
|
||||||
[payload.connector_auth.HeaderKey]
|
[payload.connector_auth.HeaderKey]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
[payload.connector_webhook_details]
|
|
||||||
merchant_secret="Source verification key"
|
|
||||||
[silverflow]
|
[silverflow]
|
||||||
[silverflow.connector_auth.BodyKey]
|
[silverflow.connector_auth.BodyKey]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
|
|||||||
@ -4935,10 +4935,8 @@ type="Text"
|
|||||||
payment_method_type = "Visa"
|
payment_method_type = "Visa"
|
||||||
[payload.connector_auth.HeaderKey]
|
[payload.connector_auth.HeaderKey]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
[payload.connector_webhook_details]
|
|
||||||
merchant_secret="Source verification key"
|
|
||||||
|
|
||||||
[silverflow]
|
[silverflow]
|
||||||
[silverflow.connector_auth.BodyKey]
|
[silverflow.connector_auth.BodyKey]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
key1="Secret Key"
|
key1="Secret Key"
|
||||||
|
|||||||
@ -6302,8 +6302,7 @@ type="Text"
|
|||||||
payment_method_type = "Visa"
|
payment_method_type = "Visa"
|
||||||
[payload.connector_auth.HeaderKey]
|
[payload.connector_auth.HeaderKey]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
[payload.connector_webhook_details]
|
|
||||||
merchant_secret="Source verification key"
|
|
||||||
[silverflow]
|
[silverflow]
|
||||||
[silverflow.connector_auth.BodyKey]
|
[silverflow.connector_auth.BodyKey]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
|
|||||||
@ -8,12 +8,12 @@ use base64::Engine;
|
|||||||
use common_enums::enums;
|
use common_enums::enums;
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
consts::BASE64_ENGINE,
|
consts::BASE64_ENGINE,
|
||||||
errors::CustomResult,
|
errors::{CustomResult, ReportSwitchExt},
|
||||||
ext_traits::BytesExt,
|
ext_traits::{ByteSliceExt, BytesExt},
|
||||||
request::{Method, Request, RequestBuilder, RequestContent},
|
request::{Method, Request, RequestBuilder, RequestContent},
|
||||||
types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
|
types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
|
||||||
};
|
};
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::ResultExt;
|
||||||
use hyperswitch_domain_models::{
|
use hyperswitch_domain_models::{
|
||||||
payment_method_data::PaymentMethodData,
|
payment_method_data::PaymentMethodData,
|
||||||
router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
|
router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
|
||||||
@ -47,7 +47,7 @@ use hyperswitch_interfaces::{
|
|||||||
types::{self, PaymentsVoidType, Response},
|
types::{self, PaymentsVoidType, Response},
|
||||||
webhooks,
|
webhooks,
|
||||||
};
|
};
|
||||||
use masking::{ExposeInterface, Mask};
|
use masking::{ExposeInterface, Mask, Secret};
|
||||||
use transformers as payload;
|
use transformers as payload;
|
||||||
|
|
||||||
use crate::{constants::headers, types::ResponseRouterData, utils};
|
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<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 {
|
impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Payload {
|
||||||
fn get_headers(
|
fn get_headers(
|
||||||
@ -701,25 +712,95 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Payload {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl webhooks::IncomingWebhook for Payload {
|
impl webhooks::IncomingWebhook for Payload {
|
||||||
fn get_webhook_object_reference_id(
|
async fn verify_webhook_source(
|
||||||
&self,
|
&self,
|
||||||
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
_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> {
|
) -> 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(
|
fn get_webhook_event_type(
|
||||||
&self,
|
&self,
|
||||||
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||||
) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
|
) -> 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(
|
fn get_webhook_resource_object(
|
||||||
&self,
|
&self,
|
||||||
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
request: &webhooks::IncomingWebhookRequestDetails<'_>,
|
||||||
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
|
) -> 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,
|
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 {
|
impl ConnectorSpecifications for Payload {
|
||||||
fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
|
fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ pub struct PayloadCardsResponseData {
|
|||||||
#[serde(rename = "id")]
|
#[serde(rename = "id")]
|
||||||
pub transaction_id: String,
|
pub transaction_id: String,
|
||||||
pub payment_method_id: Option<Secret<String>>,
|
pub payment_method_id: Option<Secret<String>>,
|
||||||
|
// Connector customer id
|
||||||
pub processing_id: Option<String>,
|
pub processing_id: Option<String>,
|
||||||
pub processing_method_id: Option<String>,
|
pub processing_method_id: Option<String>,
|
||||||
pub ref_number: Option<String>,
|
pub ref_number: Option<String>,
|
||||||
@ -84,6 +85,7 @@ pub struct PayloadRefundResponse {
|
|||||||
pub transaction_id: String,
|
pub transaction_id: String,
|
||||||
pub ledger: Vec<RefundsLedger>,
|
pub ledger: Vec<RefundsLedger>,
|
||||||
pub payment_method_id: Option<Secret<String>>,
|
pub payment_method_id: Option<Secret<String>>,
|
||||||
|
// Connector customer id
|
||||||
pub processing_id: Option<String>,
|
pub processing_id: Option<String>,
|
||||||
pub ref_number: Option<String>,
|
pub ref_number: Option<String>,
|
||||||
pub status: RefundStatus,
|
pub status: RefundStatus,
|
||||||
@ -99,3 +101,51 @@ pub struct PayloadErrorResponse {
|
|||||||
/// Payload returns arbitrary details in JSON format
|
/// Payload returns arbitrary details in JSON format
|
||||||
pub details: Option<serde_json::Value>,
|
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_enums::enums;
|
||||||
use common_utils::types::StringMajorUnit;
|
use common_utils::types::StringMajorUnit;
|
||||||
use hyperswitch_domain_models::{
|
use hyperswitch_domain_models::{
|
||||||
payment_method_data::PaymentMethodData,
|
payment_method_data::PaymentMethodData,
|
||||||
router_data::{ConnectorAuthType, RouterData},
|
router_data::{ConnectorAuthType, ErrorResponse, RouterData},
|
||||||
router_flow_types::refunds::{Execute, RSync},
|
router_flow_types::refunds::{Execute, RSync},
|
||||||
router_request_types::ResponseId,
|
router_request_types::ResponseId,
|
||||||
router_response_types::{PaymentsResponseData, RefundsResponseData},
|
router_response_types::{PaymentsResponseData, RefundsResponseData},
|
||||||
types::{PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, RefundsRouterData},
|
types::{PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, RefundsRouterData},
|
||||||
};
|
};
|
||||||
use hyperswitch_interfaces::errors;
|
use hyperswitch_interfaces::{
|
||||||
|
consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
|
||||||
|
errors,
|
||||||
|
};
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
|
|
||||||
use super::{requests, responses};
|
use super::{requests, responses};
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{RefundsResponseRouterData, ResponseRouterData},
|
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
|
//TODO: Fill the struct with respective fields
|
||||||
@ -56,12 +60,20 @@ impl TryFrom<&PayloadRouterData<&PaymentsAuthorizeRouterData>>
|
|||||||
cvc: req_card.card_cvc,
|
cvc: req_card.card_cvc,
|
||||||
};
|
};
|
||||||
let address = item.router_data.get_billing_address()?;
|
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 {
|
let billing_address = requests::BillingAddress {
|
||||||
city: address.city.clone().unwrap_or_default(),
|
city,
|
||||||
country: address.country.unwrap_or_default(),
|
country,
|
||||||
postal_code: address.zip.clone().unwrap_or_default(),
|
postal_code,
|
||||||
state_province: address.state.clone().unwrap_or_default(),
|
state_province,
|
||||||
street_address: address.line1.clone().unwrap_or_default(),
|
street_address,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For manual capture, set status to "authorized"
|
// For manual capture, set status to "authorized"
|
||||||
@ -127,13 +139,29 @@ impl<F, T>
|
|||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
match item.response.clone() {
|
match item.response.clone() {
|
||||||
responses::PayloadPaymentsResponse::PayloadCardsResponse(response) => {
|
responses::PayloadPaymentsResponse::PayloadCardsResponse(response) => {
|
||||||
let payment_status = response.status;
|
let status = enums::AttemptStatus::from(response.status);
|
||||||
let transaction_id = response.transaction_id;
|
let connector_customer = response.processing_id.clone();
|
||||||
|
let response_result = if status == enums::AttemptStatus::Failure {
|
||||||
Ok(Self {
|
Err(ErrorResponse {
|
||||||
status: common_enums::AttemptStatus::from(payment_status),
|
attempt_status: None,
|
||||||
response: Ok(PaymentsResponseData::TransactionResponse {
|
code: response
|
||||||
resource_id: ResponseId::ConnectorTransactionId(transaction_id),
|
.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),
|
redirection_data: Box::new(None),
|
||||||
mandate_reference: Box::new(None),
|
mandate_reference: Box::new(None),
|
||||||
connector_metadata: None,
|
connector_metadata: None,
|
||||||
@ -141,7 +169,12 @@ impl<F, T>
|
|||||||
connector_response_reference_id: response.ref_number,
|
connector_response_reference_id: response.ref_number,
|
||||||
incremental_authorization_allowed: None,
|
incremental_authorization_allowed: None,
|
||||||
charges: None,
|
charges: None,
|
||||||
}),
|
})
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
status,
|
||||||
|
response: response_result,
|
||||||
|
connector_customer,
|
||||||
..item.data
|
..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 = {
|
const successfulNo3DSCardDetails = {
|
||||||
card_number: "4242424242424242",
|
card_number: "4242424242424242",
|
||||||
@ -8,43 +12,18 @@ const successfulNo3DSCardDetails = {
|
|||||||
card_cvc: "123",
|
card_cvc: "123",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: Payload may not support 3DS authentication - using same card for consistency
|
|
||||||
const successfulThreeDSTestCardDetails = {
|
const successfulThreeDSTestCardDetails = {
|
||||||
card_number: "4242424242424242",
|
...successfulNo3DSCardDetails,
|
||||||
card_exp_month: "12",
|
|
||||||
card_exp_year: "25",
|
|
||||||
card_holder_name: "John Doe",
|
|
||||||
card_cvc: "123",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const failedNo3DSCardDetails = {
|
const failedNo3DSCardDetails = {
|
||||||
card_number: "4000000000000002",
|
card_number: "4111111111119903",
|
||||||
card_exp_month: "01",
|
card_exp_month: "01",
|
||||||
card_exp_year: "25",
|
card_exp_year: "25",
|
||||||
card_holder_name: "John Doe",
|
card_holder_name: "John Doe",
|
||||||
card_cvc: "123",
|
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 = {
|
const payment_method_data_no3ds = {
|
||||||
card: {
|
card: {
|
||||||
last4: "4242",
|
last4: "4242",
|
||||||
@ -95,14 +74,6 @@ export const connectorDetails = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SessionToken: {
|
|
||||||
Response: {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
session_token: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PaymentIntentWithShippingCost: {
|
PaymentIntentWithShippingCost: {
|
||||||
Request: {
|
Request: {
|
||||||
currency: "USD",
|
currency: "USD",
|
||||||
@ -151,7 +122,7 @@ export const connectorDetails = {
|
|||||||
setup_future_usage: "on_session",
|
setup_future_usage: "on_session",
|
||||||
},
|
},
|
||||||
Response: {
|
Response: {
|
||||||
status: 501,
|
status: 400,
|
||||||
body: {
|
body: {
|
||||||
error: {
|
error: {
|
||||||
type: "invalid_request",
|
type: "invalid_request",
|
||||||
@ -175,7 +146,7 @@ export const connectorDetails = {
|
|||||||
setup_future_usage: "on_session",
|
setup_future_usage: "on_session",
|
||||||
},
|
},
|
||||||
Response: {
|
Response: {
|
||||||
status: 501,
|
status: 400,
|
||||||
body: {
|
body: {
|
||||||
error: {
|
error: {
|
||||||
type: "invalid_request",
|
type: "invalid_request",
|
||||||
@ -433,24 +404,25 @@ export const connectorDetails = {
|
|||||||
},
|
},
|
||||||
SaveCardUse3DSAutoCaptureOffSession: {
|
SaveCardUse3DSAutoCaptureOffSession: {
|
||||||
Configs: {
|
Configs: {
|
||||||
DELAY: {
|
TRIGGER_SKIP: true,
|
||||||
STATUS: true,
|
|
||||||
TIMEOUT: 15000,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Request: {
|
Request: {
|
||||||
payment_method: "card",
|
payment_method: "card",
|
||||||
payment_method_type: "debit",
|
|
||||||
payment_method_data: {
|
payment_method_data: {
|
||||||
card: successfulThreeDSTestCardDetails,
|
card: successfulThreeDSTestCardDetails,
|
||||||
},
|
},
|
||||||
setup_future_usage: "off_session",
|
currency: "USD",
|
||||||
customer_acceptance: customerAcceptance,
|
customer_acceptance: null,
|
||||||
|
setup_future_usage: "on_session",
|
||||||
},
|
},
|
||||||
Response: {
|
Response: {
|
||||||
status: 200,
|
status: 400,
|
||||||
body: {
|
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,
|
mandate_data: singleUseMandateData,
|
||||||
},
|
},
|
||||||
Response: {
|
Response: {
|
||||||
status: 200,
|
status: 501,
|
||||||
body: {
|
body: {
|
||||||
status: "succeeded",
|
code: "IR_00",
|
||||||
},
|
message: "Setup Mandate flow for Payload is not implemented",
|
||||||
},
|
type: "invalid_request",
|
||||||
},
|
|
||||||
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",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user