mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
refactor(connector): [WorldPay] migrate from modular to standard payment APIs (#6317)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6438,6 +6438,7 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
"unidecode",
|
||||
"url",
|
||||
"urlencoding",
|
||||
"utoipa",
|
||||
"uuid",
|
||||
"validator",
|
||||
|
||||
@ -3416,6 +3416,12 @@ key1="Password"
|
||||
api_secret="Merchant Identifier"
|
||||
[worldpay.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
[worldpay.metadata.merchant_name]
|
||||
name="merchant_name"
|
||||
label="Name of the merchant to de displayed during 3DS challenge"
|
||||
placeholder="Enter Name of the merchant"
|
||||
required=true
|
||||
type="Text"
|
||||
|
||||
[[worldpay.metadata.apple_pay]]
|
||||
name="certificate"
|
||||
|
||||
@ -2473,6 +2473,12 @@ merchant_secret="Source verification key"
|
||||
api_key="Username"
|
||||
key1="Password"
|
||||
api_secret="Merchant Identifier"
|
||||
[worldpay.metadata.merchant_name]
|
||||
name="merchant_name"
|
||||
label="Name of the merchant to de displayed during 3DS challenge"
|
||||
placeholder="Enter Name of the merchant"
|
||||
required=true
|
||||
type="Text"
|
||||
|
||||
[[worldpay.metadata.apple_pay]]
|
||||
name="certificate"
|
||||
|
||||
@ -3406,6 +3406,12 @@ key1="Password"
|
||||
api_secret="Merchant Identifier"
|
||||
[worldpay.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
[worldpay.metadata.merchant_name]
|
||||
name="merchant_name"
|
||||
label="Name of the merchant to de displayed during 3DS challenge"
|
||||
placeholder="Enter Name of the merchant"
|
||||
required=true
|
||||
type="Text"
|
||||
|
||||
[[worldpay.metadata.apple_pay]]
|
||||
name="certificate"
|
||||
|
||||
@ -115,6 +115,7 @@ tracing-futures = { version = "0.2.5", features = ["tokio"] }
|
||||
unicode-segmentation = "1.11.0"
|
||||
unidecode = "0.3.0"
|
||||
url = { version = "2.5.0", features = ["serde"] }
|
||||
urlencoding = "2.1.3"
|
||||
utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] }
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
validator = "0.17.0"
|
||||
|
||||
@ -725,6 +725,7 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
|
||||
pub trait PaymentsCaptureRequestData {
|
||||
fn is_multiple_capture(&self) -> bool;
|
||||
fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
|
||||
fn get_capture_method(&self) -> Option<enums::CaptureMethod>;
|
||||
}
|
||||
|
||||
impl PaymentsCaptureRequestData for types::PaymentsCaptureData {
|
||||
@ -736,6 +737,9 @@ impl PaymentsCaptureRequestData for types::PaymentsCaptureData {
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("browser_info"))
|
||||
}
|
||||
fn get_capture_method(&self) -> Option<enums::CaptureMethod> {
|
||||
self.capture_method.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RevokeMandateRequestData {
|
||||
|
||||
@ -16,6 +16,7 @@ use self::{requests::*, response::*};
|
||||
use super::utils::{self as connector_utils, RefundsRequestData};
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
core::errors::{self, CustomResult},
|
||||
events::connector_api_logs::ConnectorEvent,
|
||||
headers,
|
||||
@ -64,6 +65,7 @@ where
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
self.get_content_type().to_string().into(),
|
||||
),
|
||||
(headers::X_WP_API_VERSION.to_string(), "2024-06-01".into()),
|
||||
];
|
||||
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||
headers.append(&mut api_key);
|
||||
@ -81,7 +83,7 @@ impl ConnectorCommon for Worldpay {
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
"application/vnd.worldpay.payments-v7+json"
|
||||
"application/json"
|
||||
}
|
||||
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
@ -205,8 +207,9 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}payments/authorizations/cancellations/{connector_payment_id}",
|
||||
"{}api/payments/{}/cancellations",
|
||||
self.base_url(connectors),
|
||||
urlencoding::encode(&connector_payment_id),
|
||||
))
|
||||
}
|
||||
|
||||
@ -244,15 +247,24 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
let optional_correlation_id = res.headers.and_then(|headers| {
|
||||
headers
|
||||
.get(consts::WP_CORRELATION_ID)
|
||||
.and_then(|header_value| header_value.to_str().ok())
|
||||
.map(|id| id.to_string())
|
||||
});
|
||||
Ok(types::PaymentsCancelRouterData {
|
||||
status: enums::AttemptStatus::Voided,
|
||||
status: enums::AttemptStatus::from(response.outcome.clone()),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::foreign_try_from(response.links)?,
|
||||
resource_id: types::ResponseId::foreign_try_from((
|
||||
response,
|
||||
Some(data.request.connector_transaction_id.clone()),
|
||||
))?,
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
connector_response_reference_id: optional_correlation_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}),
|
||||
@ -306,9 +318,9 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||
Ok(format!(
|
||||
"{}payments/events/{}",
|
||||
"{}api/payments/{}",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
urlencoding::encode(&connector_payment_id),
|
||||
))
|
||||
}
|
||||
|
||||
@ -349,6 +361,12 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
|
||||
let optional_correlation_id = res.headers.and_then(|headers| {
|
||||
headers
|
||||
.get(consts::WP_CORRELATION_ID)
|
||||
.and_then(|header_value| header_value.to_str().ok())
|
||||
.map(|id| id.to_string())
|
||||
});
|
||||
let attempt_status = data.status;
|
||||
let worldpay_status = response.last_event;
|
||||
let status = match (attempt_status, worldpay_status.clone()) {
|
||||
@ -371,7 +389,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
connector_response_reference_id: optional_correlation_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}),
|
||||
@ -403,9 +421,9 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}payments/settlements/partials/{}",
|
||||
"{}api/payments/{}/partialSettlements",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
urlencoding::encode(&connector_payment_id),
|
||||
))
|
||||
}
|
||||
|
||||
@ -457,15 +475,24 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
let optional_correlation_id = res.headers.and_then(|headers| {
|
||||
headers
|
||||
.get(consts::WP_CORRELATION_ID)
|
||||
.and_then(|header_value| header_value.to_str().ok())
|
||||
.map(|id| id.to_string())
|
||||
});
|
||||
Ok(types::PaymentsCaptureRouterData {
|
||||
status: enums::AttemptStatus::Pending,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::foreign_try_from(response.links)?,
|
||||
resource_id: types::ResponseId::foreign_try_from((
|
||||
response,
|
||||
Some(data.request.connector_transaction_id.clone()),
|
||||
))?,
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
connector_response_reference_id: optional_correlation_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}),
|
||||
@ -514,10 +541,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}cardPayments/customerInitiatedTransactions",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
Ok(format!("{}api/payments", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -573,12 +597,21 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
let optional_correlation_id = res.headers.and_then(|headers| {
|
||||
headers
|
||||
.get(consts::WP_CORRELATION_ID)
|
||||
.and_then(|header_value| header_value.to_str().ok())
|
||||
.map(|id| id.to_string())
|
||||
});
|
||||
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
types::RouterData::foreign_try_from((
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
},
|
||||
optional_correlation_id,
|
||||
))
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
@ -631,9 +664,9 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}payments/settlements/refunds/partials/{}",
|
||||
"{}api/payments/{}/partialRefunds",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
urlencoding::encode(&connector_payment_id),
|
||||
))
|
||||
}
|
||||
|
||||
@ -670,9 +703,19 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
let optional_correlation_id = res.headers.and_then(|headers| {
|
||||
headers
|
||||
.get(consts::WP_CORRELATION_ID)
|
||||
.and_then(|header_value| header_value.to_str().ok())
|
||||
.map(|id| id.to_string())
|
||||
});
|
||||
Ok(types::RefundExecuteRouterData {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: ResponseIdStr::try_from(response.links)?.id,
|
||||
connector_refund_id: ResponseIdStr::foreign_try_from((
|
||||
response,
|
||||
optional_correlation_id,
|
||||
))?
|
||||
.id,
|
||||
refund_status: enums::RefundStatus::Pending,
|
||||
}),
|
||||
..data.clone()
|
||||
@ -710,9 +753,9 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}payments/events/{}",
|
||||
"{}api/payments/{}",
|
||||
self.base_url(connectors),
|
||||
req.request.get_connector_refund_id()?
|
||||
urlencoding::encode(&req.request.get_connector_refund_id()?),
|
||||
))
|
||||
}
|
||||
|
||||
@ -813,7 +856,7 @@ impl api::IncomingWebhook for Worldpay {
|
||||
.parse_struct("WorldpayWebhookTransactionId")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api::PaymentIdType::ConnectorTransactionId(body.event_details.transaction_reference),
|
||||
api::PaymentIdType::PaymentAttemptId(body.event_details.transaction_reference),
|
||||
))
|
||||
}
|
||||
|
||||
@ -829,13 +872,14 @@ impl api::IncomingWebhook for Worldpay {
|
||||
EventType::Authorized => {
|
||||
Ok(api::IncomingWebhookEvent::PaymentIntentAuthorizationSuccess)
|
||||
}
|
||||
EventType::SentForSettlement => Ok(api::IncomingWebhookEvent::PaymentIntentProcessing),
|
||||
EventType::Settled => Ok(api::IncomingWebhookEvent::PaymentIntentSuccess),
|
||||
EventType::SentForSettlement | EventType::SentForAuthorization => {
|
||||
Ok(api::IncomingWebhookEvent::PaymentIntentProcessing)
|
||||
}
|
||||
EventType::Error | EventType::Expired | EventType::SettlementFailed => {
|
||||
Ok(api::IncomingWebhookEvent::PaymentIntentFailure)
|
||||
}
|
||||
EventType::Unknown
|
||||
| EventType::SentForAuthorization
|
||||
| EventType::Cancelled
|
||||
| EventType::Refused
|
||||
| EventType::Refunded
|
||||
|
||||
@ -1,5 +1,99 @@
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WorldpayPaymentsRequest {
|
||||
pub transaction_reference: String,
|
||||
pub merchant: Merchant,
|
||||
pub instruction: Instruction,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub customer: Option<Customer>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Merchant {
|
||||
pub entity: Secret<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mcc: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub payment_facilitator: Option<PaymentFacilitator>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Instruction {
|
||||
pub settlement: Option<AutoSettlement>,
|
||||
pub method: PaymentMethod,
|
||||
pub payment_instrument: PaymentInstrument,
|
||||
pub narrative: InstructionNarrative,
|
||||
pub value: PaymentValue,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub debt_repayment: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum PaymentInstrument {
|
||||
Card(CardPayment),
|
||||
CardToken(CardToken),
|
||||
Googlepay(WalletPayment),
|
||||
Applepay(WalletPayment),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CardPayment {
|
||||
#[serde(rename = "type")]
|
||||
pub payment_type: PaymentType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub card_holder_name: Option<Secret<String>>,
|
||||
pub card_number: cards::CardNumber,
|
||||
pub expiry_date: ExpiryDate,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub billing_address: Option<BillingAddress>,
|
||||
pub cvc: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CardToken {
|
||||
#[serde(rename = "type")]
|
||||
pub payment_type: PaymentType,
|
||||
pub href: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cvc: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WalletPayment {
|
||||
#[serde(rename = "type")]
|
||||
pub payment_type: PaymentType,
|
||||
pub wallet_token: Secret<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub billing_address: Option<BillingAddress>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, Default, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PaymentType {
|
||||
#[default]
|
||||
Plain,
|
||||
Token,
|
||||
Encrypted,
|
||||
Checkout,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
pub struct ExpiryDate {
|
||||
pub month: Secret<i8>,
|
||||
pub year: Secret<i32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillingAddress {
|
||||
@ -17,17 +111,6 @@ pub struct BillingAddress {
|
||||
pub country_code: common_enums::CountryAlpha2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WorldpayPaymentsRequest {
|
||||
pub transaction_reference: String,
|
||||
pub merchant: Merchant,
|
||||
pub instruction: Instruction,
|
||||
pub channel: Channel,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub customer: Option<Customer>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
|
||||
)]
|
||||
@ -100,89 +183,23 @@ pub struct NetworkToken {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Instruction {
|
||||
pub request_auto_settlement: RequestAutoSettlement,
|
||||
pub narrative: InstructionNarrative,
|
||||
pub value: PaymentValue,
|
||||
pub payment_instrument: PaymentInstrument,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub debt_repayment: Option<bool>,
|
||||
pub struct AutoSettlement {
|
||||
pub auto: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RequestAutoSettlement {
|
||||
pub enabled: bool,
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PaymentMethod {
|
||||
#[default]
|
||||
Card,
|
||||
ApplePay,
|
||||
GooglePay,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InstructionNarrative {
|
||||
pub line1: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub line2: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum PaymentInstrument {
|
||||
Card(CardPayment),
|
||||
CardToken(CardToken),
|
||||
Googlepay(WalletPayment),
|
||||
Applepay(WalletPayment),
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, Default, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
|
||||
)]
|
||||
pub enum PaymentType {
|
||||
#[default]
|
||||
#[serde(rename = "card/plain")]
|
||||
Card,
|
||||
#[serde(rename = "card/token")]
|
||||
CardToken,
|
||||
#[serde(rename = "card/wallet+googlepay")]
|
||||
Googlepay,
|
||||
#[serde(rename = "card/wallet+applepay")]
|
||||
Applepay,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CardPayment {
|
||||
#[serde(rename = "type")]
|
||||
pub payment_type: PaymentType,
|
||||
pub card_number: cards::CardNumber,
|
||||
pub expiry_date: ExpiryDate,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub card_holder_name: Option<Secret<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub billing_address: Option<BillingAddress>,
|
||||
pub cvc: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CardToken {
|
||||
#[serde(rename = "type")]
|
||||
pub payment_type: PaymentType,
|
||||
pub href: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WalletPayment {
|
||||
#[serde(rename = "type")]
|
||||
pub payment_type: PaymentType,
|
||||
pub wallet_token: Secret<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub billing_address: Option<BillingAddress>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
pub struct ExpiryDate {
|
||||
pub month: Secret<i8>,
|
||||
pub year: Secret<i32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
@ -191,16 +208,6 @@ pub struct PaymentValue {
|
||||
pub currency: api_models::enums::Currency,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Merchant {
|
||||
pub entity: Secret<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mcc: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub payment_facilitator: Option<PaymentFacilitator>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PaymentFacilitator {
|
||||
|
||||
@ -1,25 +1,97 @@
|
||||
use error_stack::ResultExt;
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::requests::*;
|
||||
use crate::{core::errors, types, types::transformers::ForeignTryFrom};
|
||||
use crate::core::errors;
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WorldpayPaymentsResponse {
|
||||
pub outcome: Option<PaymentOutcome>,
|
||||
/// Any risk factors which have been identified for the authorization. This section will not appear if no risks are identified.
|
||||
pub outcome: PaymentOutcome,
|
||||
pub transaction_reference: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub other_fields: WorldpayPaymentResponseFields,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum WorldpayPaymentResponseFields {
|
||||
AuthorizedResponse(Box<AuthorizedResponse>),
|
||||
DDCResponse(DDCResponse),
|
||||
FraudHighRisk(FraudHighRiskResponse),
|
||||
RefusedResponse(RefusedResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizedResponse {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub risk_factors: Option<Vec<RiskFactorsInner>>,
|
||||
pub payment_instrument: Option<PaymentsResPaymentInstrument>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub issuer: Option<Issuer>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub scheme: Option<PaymentsResponseScheme>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub payment_instrument: Option<PaymentsResPaymentInstrument>,
|
||||
#[serde(rename = "_links", skip_serializing_if = "Option::is_none")]
|
||||
pub links: Option<PaymentLinks>,
|
||||
pub links: Option<SelfLink>,
|
||||
#[serde(rename = "_actions")]
|
||||
pub actions: Option<ActionLinks>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub risk_factors: Option<Vec<RiskFactorsInner>>,
|
||||
pub fraud: Option<Fraud>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FraudHighRiskResponse {
|
||||
pub score: f32,
|
||||
pub reason: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RefusedResponse {
|
||||
pub refusal_description: String,
|
||||
pub refusal_code: String,
|
||||
pub risk_factors: Vec<RiskFactorsInner>,
|
||||
pub fraud: Fraud,
|
||||
#[serde(rename = "threeDS")]
|
||||
pub three_ds: Option<ThreeDsResponse>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ThreeDsResponse {
|
||||
pub outcome: String,
|
||||
pub issuer_response: IssuerResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum IssuerResponse {
|
||||
Challenged,
|
||||
Frictionless,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DDCResponse {
|
||||
pub device_data_collection: DDCToken,
|
||||
#[serde(rename = "_actions")]
|
||||
pub actions: DDCActionLink,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DDCToken {
|
||||
pub jwt: String,
|
||||
pub url: String,
|
||||
pub bin: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DDCActionLink {
|
||||
#[serde(rename = "supply3dsDeviceData")]
|
||||
supply_ddc_data: ActionLink,
|
||||
method: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -28,16 +100,57 @@ pub enum PaymentOutcome {
|
||||
#[serde(alias = "authorized", alias = "Authorized")]
|
||||
Authorized,
|
||||
Refused,
|
||||
#[serde(alias = "Sent for Settlement")]
|
||||
SentForSettlement,
|
||||
#[serde(alias = "Sent for Refund")]
|
||||
SentForRefund,
|
||||
FraudHighRisk,
|
||||
#[serde(alias = "3dsDeviceDataRequired")]
|
||||
ThreeDsDeviceDataRequired,
|
||||
ThreeDsChallenged,
|
||||
SentForCancellation,
|
||||
#[serde(alias = "3dsAuthenticationFailed")]
|
||||
ThreeDsAuthenticationFailed,
|
||||
SentForPartialRefund,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum RefundOutcome {
|
||||
#[serde(alias = "Sent for Refund")]
|
||||
SentForRefund,
|
||||
pub struct SelfLink {
|
||||
#[serde(rename = "self")]
|
||||
pub self_link: SelfLinkInner,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SelfLinkInner {
|
||||
pub href: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActionLinks {
|
||||
supply_3ds_device_data: Option<ActionLink>,
|
||||
settle_payment: Option<ActionLink>,
|
||||
partially_settle_payment: Option<ActionLink>,
|
||||
refund_payment: Option<ActionLink>,
|
||||
partiall_refund_payment: Option<ActionLink>,
|
||||
cancel_payment: Option<ActionLink>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ActionLink {
|
||||
pub href: String,
|
||||
pub method: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Fraud {
|
||||
pub outcome: FraudOutcome,
|
||||
pub score: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum FraudOutcome {
|
||||
LowRisk,
|
||||
HighRisk,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -70,40 +183,6 @@ pub enum EventType {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
pub struct PaymentLinks {
|
||||
#[serde(
|
||||
rename = "cardPayments:events",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub events: Option<PaymentLink>,
|
||||
#[serde(
|
||||
rename = "cardPayments:settle",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub settle_event: Option<PaymentLink>,
|
||||
#[serde(
|
||||
rename = "cardPayments:partialSettle",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub partial_settle_event: Option<PaymentLink>,
|
||||
#[serde(
|
||||
rename = "cardPayments:refund",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub refund_event: Option<PaymentLink>,
|
||||
#[serde(
|
||||
rename = "cardPayments:partialRefund",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub partial_refund_event: Option<PaymentLink>,
|
||||
#[serde(
|
||||
rename = "cardPayments:reverse",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub reverse_event: Option<PaymentLink>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
pub struct EventLinks {
|
||||
#[serde(rename = "payments:events", skip_serializing_if = "Option::is_none")]
|
||||
@ -115,20 +194,42 @@ pub struct PaymentLink {
|
||||
pub href: String,
|
||||
}
|
||||
|
||||
fn get_resource_id<T, F>(
|
||||
links: Option<PaymentLinks>,
|
||||
pub fn get_resource_id<T, F>(
|
||||
response: WorldpayPaymentsResponse,
|
||||
connector_transaction_id: Option<String>,
|
||||
transform_fn: F,
|
||||
) -> Result<T, error_stack::Report<errors::ConnectorError>>
|
||||
where
|
||||
F: Fn(String) -> T,
|
||||
{
|
||||
let reference_id = links
|
||||
.and_then(|l| l.events)
|
||||
.and_then(|e| e.href.rsplit_once('/').map(|h| h.1.to_string()))
|
||||
.map(transform_fn);
|
||||
reference_id.ok_or_else(|| {
|
||||
let reference_id = match response.other_fields {
|
||||
WorldpayPaymentResponseFields::AuthorizedResponse(res) => res
|
||||
.links
|
||||
.as_ref()
|
||||
.and_then(|link| link.self_link.href.rsplit_once('/'))
|
||||
.map(|(_, h)| urlencoding::decode(h))
|
||||
.transpose()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?
|
||||
.map(|s| transform_fn(s.into_owned())),
|
||||
WorldpayPaymentResponseFields::DDCResponse(res) => res
|
||||
.actions
|
||||
.supply_ddc_data
|
||||
.href
|
||||
.split('/')
|
||||
.rev()
|
||||
.nth(1)
|
||||
.map(urlencoding::decode)
|
||||
.transpose()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?
|
||||
.map(|s| transform_fn(s.into_owned())),
|
||||
WorldpayPaymentResponseFields::FraudHighRisk(_) => None,
|
||||
WorldpayPaymentResponseFields::RefusedResponse(_) => None,
|
||||
};
|
||||
reference_id
|
||||
.or_else(|| connector_transaction_id.map(transform_fn))
|
||||
.ok_or_else(|| {
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "links.events",
|
||||
field_name: "_links.self.href",
|
||||
}
|
||||
.into()
|
||||
})
|
||||
@ -138,20 +239,6 @@ pub struct ResponseIdStr {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<Option<PaymentLinks>> for ResponseIdStr {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(links: Option<PaymentLinks>) -> Result<Self, Self::Error> {
|
||||
get_resource_id(links, |id| Self { id })
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<Option<PaymentLinks>> for types::ResponseId {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn foreign_try_from(links: Option<PaymentLinks>) -> Result<Self, Self::Error> {
|
||||
get_resource_id(links, Self::ConnectorTransactionId)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Issuer {
|
||||
@ -173,10 +260,10 @@ pub struct PaymentsResPaymentInstrument {
|
||||
pub payment_instrument_type: Option<String>,
|
||||
pub card_bin: Option<String>,
|
||||
pub last_four: Option<String>,
|
||||
pub category: Option<String>,
|
||||
pub expiry_date: Option<ExpiryDate>,
|
||||
pub card_brand: Option<String>,
|
||||
pub funding_type: Option<String>,
|
||||
pub category: Option<String>,
|
||||
pub issuer_name: Option<String>,
|
||||
pub payment_account_reference: Option<String>,
|
||||
}
|
||||
@ -231,7 +318,7 @@ pub enum RiskType {
|
||||
#[derive(
|
||||
Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Detail {
|
||||
#[default]
|
||||
Address,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use api_models::payments::Address;
|
||||
use base64::Engine;
|
||||
use common_utils::{errors::CustomResult, ext_traits::OptionExt, types::MinorUnit};
|
||||
use common_utils::{errors::CustomResult, ext_traits::OptionExt, pii, types::MinorUnit};
|
||||
use diesel_models::enums;
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_connectors::utils::RouterData;
|
||||
use masking::{PeekInterface, Secret};
|
||||
use serde::Serialize;
|
||||
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{requests::*, response::*};
|
||||
use crate::{
|
||||
@ -45,13 +45,38 @@ impl<T>
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct WorldpayConnectorMetadataObject {
|
||||
pub merchant_name: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
impl TryFrom<&Option<pii::SecretSerdeValue>> for WorldpayConnectorMetadataObject {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(meta_data: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
|
||||
let metadata: Self = utils::to_connector_meta_from_secret::<Self>(meta_data.clone())
|
||||
.change_context(errors::ConnectorError::InvalidConnectorConfig {
|
||||
config: "metadata",
|
||||
})?;
|
||||
Ok(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_payment_instrument(
|
||||
payment_method: domain::PaymentMethodData,
|
||||
billing_address: Option<&Address>,
|
||||
auth_type: enums::AuthenticationType,
|
||||
) -> CustomResult<PaymentInstrument, errors::ConnectorError> {
|
||||
match payment_method {
|
||||
domain::PaymentMethodData::Card(card) => Ok(PaymentInstrument::Card(CardPayment {
|
||||
payment_type: PaymentType::Card,
|
||||
domain::PaymentMethodData::Card(card) => {
|
||||
if auth_type == enums::AuthenticationType::ThreeDs {
|
||||
return Err(errors::ConnectorError::NotImplemented(
|
||||
"ThreeDS flow through worldpay".to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
Ok(PaymentInstrument::Card(CardPayment {
|
||||
payment_type: PaymentType::Plain,
|
||||
expiry_date: ExpiryDate {
|
||||
month: utils::CardData::get_expiry_month_as_i8(&card)?,
|
||||
year: utils::CardData::get_expiry_year_as_i32(&card)?,
|
||||
@ -81,17 +106,18 @@ fn fetch_payment_instrument(
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})),
|
||||
}))
|
||||
}
|
||||
domain::PaymentMethodData::Wallet(wallet) => match wallet {
|
||||
domain::WalletData::GooglePay(data) => {
|
||||
Ok(PaymentInstrument::Googlepay(WalletPayment {
|
||||
payment_type: PaymentType::Googlepay,
|
||||
payment_type: PaymentType::Encrypted,
|
||||
wallet_token: Secret::new(data.tokenization_data.token),
|
||||
..WalletPayment::default()
|
||||
}))
|
||||
}
|
||||
domain::WalletData::ApplePay(data) => Ok(PaymentInstrument::Applepay(WalletPayment {
|
||||
payment_type: PaymentType::Applepay,
|
||||
payment_type: PaymentType::Encrypted,
|
||||
wallet_token: Secret::new(data.payment_data),
|
||||
..WalletPayment::default()
|
||||
})),
|
||||
@ -149,6 +175,27 @@ fn fetch_payment_instrument(
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(enums::PaymentMethod, enums::PaymentMethodType)> for PaymentMethod {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
src: (enums::PaymentMethod, enums::PaymentMethodType),
|
||||
) -> Result<Self, Self::Error> {
|
||||
match (src.0, src.1) {
|
||||
(enums::PaymentMethod::Card, _) => Ok(Self::Card),
|
||||
(enums::PaymentMethod::Wallet, enums::PaymentMethodType::ApplePay) => {
|
||||
Ok(Self::ApplePay)
|
||||
}
|
||||
(enums::PaymentMethod::Wallet, enums::PaymentMethodType::GooglePay) => {
|
||||
Ok(Self::GooglePay)
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("worldpay"),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<(
|
||||
&WorldpayRouterData<
|
||||
@ -176,28 +223,44 @@ impl
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let (item, entity_id) = req;
|
||||
let worldpay_connector_metadata_object: WorldpayConnectorMetadataObject =
|
||||
WorldpayConnectorMetadataObject::try_from(&item.router_data.connector_meta_data)?;
|
||||
let merchant_name = worldpay_connector_metadata_object.merchant_name.ok_or(
|
||||
errors::ConnectorError::InvalidConnectorConfig {
|
||||
config: "metadata.merchant_name",
|
||||
},
|
||||
)?;
|
||||
Ok(Self {
|
||||
instruction: Instruction {
|
||||
request_auto_settlement: RequestAutoSettlement {
|
||||
enabled: item.router_data.request.capture_method
|
||||
== Some(enums::CaptureMethod::Automatic),
|
||||
settlement: item
|
||||
.router_data
|
||||
.request
|
||||
.capture_method
|
||||
.map(|capture_method| AutoSettlement {
|
||||
auto: capture_method == enums::CaptureMethod::Automatic,
|
||||
}),
|
||||
method: item
|
||||
.router_data
|
||||
.request
|
||||
.payment_method_type
|
||||
.map(|pmt| PaymentMethod::try_from((item.router_data.payment_method, pmt)))
|
||||
.transpose()?
|
||||
.get_required_value("payment_method")
|
||||
.change_context(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "payment_method",
|
||||
})?,
|
||||
payment_instrument: fetch_payment_instrument(
|
||||
item.router_data.request.payment_method_data.clone(),
|
||||
item.router_data.get_optional_billing(),
|
||||
item.router_data.auth_type,
|
||||
)?,
|
||||
narrative: InstructionNarrative {
|
||||
line1: merchant_name.expose(),
|
||||
},
|
||||
value: PaymentValue {
|
||||
amount: item.amount,
|
||||
currency: item.router_data.request.currency,
|
||||
},
|
||||
narrative: InstructionNarrative {
|
||||
line1: item
|
||||
.router_data
|
||||
.merchant_id
|
||||
.get_string_repr()
|
||||
.replace('_', "-"),
|
||||
..Default::default()
|
||||
},
|
||||
payment_instrument: fetch_payment_instrument(
|
||||
item.router_data.request.payment_method_data.clone(),
|
||||
item.router_data.get_optional_billing(),
|
||||
)?,
|
||||
debt_repayment: None,
|
||||
},
|
||||
merchant: Merchant {
|
||||
@ -205,7 +268,6 @@ impl
|
||||
..Default::default()
|
||||
},
|
||||
transaction_reference: item.router_data.connector_request_reference_id.clone(),
|
||||
channel: Channel::Ecom,
|
||||
customer: None,
|
||||
})
|
||||
}
|
||||
@ -250,9 +312,15 @@ impl From<PaymentOutcome> for enums::AttemptStatus {
|
||||
fn from(item: PaymentOutcome) -> Self {
|
||||
match item {
|
||||
PaymentOutcome::Authorized => Self::Authorized,
|
||||
PaymentOutcome::Refused => Self::Failure,
|
||||
PaymentOutcome::SentForSettlement => Self::CaptureInitiated,
|
||||
PaymentOutcome::SentForRefund => Self::AutoRefunded,
|
||||
PaymentOutcome::ThreeDsDeviceDataRequired => Self::DeviceDataCollectionPending,
|
||||
PaymentOutcome::ThreeDsAuthenticationFailed => Self::AuthenticationFailed,
|
||||
PaymentOutcome::ThreeDsChallenged => Self::AuthenticationPending,
|
||||
PaymentOutcome::SentForCancellation => Self::VoidInitiated,
|
||||
PaymentOutcome::SentForPartialRefund | PaymentOutcome::SentForRefund => {
|
||||
Self::AutoRefunded
|
||||
}
|
||||
PaymentOutcome::Refused | PaymentOutcome::FraudHighRisk => Self::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -295,32 +363,43 @@ impl From<EventType> for enums::RefundStatus {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::PaymentsResponseRouterData<WorldpayPaymentsResponse>>
|
||||
for types::PaymentsAuthorizeRouterData
|
||||
impl
|
||||
ForeignTryFrom<(
|
||||
types::PaymentsResponseRouterData<WorldpayPaymentsResponse>,
|
||||
Option<String>,
|
||||
)> for types::PaymentsAuthorizeRouterData
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::PaymentsResponseRouterData<WorldpayPaymentsResponse>,
|
||||
fn foreign_try_from(
|
||||
item: (
|
||||
types::PaymentsResponseRouterData<WorldpayPaymentsResponse>,
|
||||
Option<String>,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let (router_data, optional_correlation_id) = item;
|
||||
let description = match router_data.response.other_fields {
|
||||
WorldpayPaymentResponseFields::AuthorizedResponse(ref res) => res.description.clone(),
|
||||
WorldpayPaymentResponseFields::DDCResponse(_)
|
||||
| WorldpayPaymentResponseFields::FraudHighRisk(_)
|
||||
| WorldpayPaymentResponseFields::RefusedResponse(_) => None,
|
||||
};
|
||||
Ok(Self {
|
||||
status: match item.response.outcome {
|
||||
Some(outcome) => enums::AttemptStatus::from(outcome),
|
||||
None => Err(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "outcome",
|
||||
})?,
|
||||
},
|
||||
description: item.response.description,
|
||||
status: enums::AttemptStatus::from(router_data.response.outcome.clone()),
|
||||
description,
|
||||
response: Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::foreign_try_from(item.response.links)?,
|
||||
resource_id: types::ResponseId::foreign_try_from((
|
||||
router_data.response,
|
||||
optional_correlation_id.clone(),
|
||||
))?,
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
connector_response_reference_id: optional_correlation_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}),
|
||||
..item.data
|
||||
..router_data.data
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -362,3 +441,21 @@ impl TryFrom<WorldpayWebhookEventType> for WorldpayEventResponse {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<(WorldpayPaymentsResponse, Option<String>)> for ResponseIdStr {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn foreign_try_from(
|
||||
item: (WorldpayPaymentsResponse, Option<String>),
|
||||
) -> Result<Self, Self::Error> {
|
||||
get_resource_id(item.0, item.1, |id| Self { id })
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<(WorldpayPaymentsResponse, Option<String>)> for types::ResponseId {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn foreign_try_from(
|
||||
item: (WorldpayPaymentsResponse, Option<String>),
|
||||
) -> Result<Self, Self::Error> {
|
||||
get_resource_id(item.0, item.1, Self::ConnectorTransactionId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,3 +177,6 @@ pub const VAULT_DELETE_FLOW_TYPE: &str = "delete_from_vault";
|
||||
/// Vault Fingerprint fetch flow type
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
pub const VAULT_GET_FINGERPRINT_FLOW_TYPE: &str = "get_fingerprint_vault";
|
||||
|
||||
/// Worldpay's unique reference ID for a request TODO: Move to hyperswitch_connectors/constants once Worldpay is moved to connectors crate
|
||||
pub const WP_CORRELATION_ID: &str = "WP-CorrelationId";
|
||||
|
||||
@ -87,6 +87,7 @@ pub mod headers {
|
||||
pub const X_APP_ID: &str = "x-app-id";
|
||||
pub const X_REDIRECT_URI: &str = "x-redirect-uri";
|
||||
pub const X_TENANT_ID: &str = "x-tenant-id";
|
||||
pub const X_WP_API_VERSION: &str = "WP-Api-Version";
|
||||
}
|
||||
|
||||
pub mod pii {
|
||||
|
||||
Reference in New Issue
Block a user