feat(connector): [Zen] add apple pay redirect flow support for zen connector (#1383)

This commit is contained in:
Arjun Karthik
2023-06-08 02:25:28 +05:30
committed by GitHub
parent bce01ced11
commit b3b16fcf95
3 changed files with 408 additions and 119 deletions

View File

@ -804,6 +804,8 @@ pub enum WalletData {
AliPay(AliPayRedirection),
/// The wallet data for Apple pay
ApplePay(ApplePayWalletData),
/// Wallet data for apple pay redirect flow
ApplePayRedirect(Box<ApplePayRedirectData>),
/// The wallet data for Google pay
GooglePay(GooglePayWalletData),
MbWay(Box<MbWayRedirection>),
@ -831,6 +833,9 @@ pub struct GooglePayWalletData {
pub tokenization_data: GpayTokenizationData,
}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct ApplePayRedirectData {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct WeChatPayRedirection {}

View File

@ -47,6 +47,12 @@ impl api::Refund for Zen {}
impl api::RefundExecute for Zen {}
impl api::RefundSync for Zen {}
impl Zen {
fn get_default_header() -> (String, request::Maskable<String>) {
("request-id".to_string(), Uuid::new_v4().to_string().into())
}
}
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Zen
where
Self: ConnectorIntegration<Flow, Request, Response>,
@ -56,13 +62,10 @@ where
req: &types::RouterData<Flow, Request, Response>,
_connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
let mut headers = vec![
(
headers::CONTENT_TYPE.to_string(),
self.get_content_type().to_string().into(),
),
("request-id".to_string(), Uuid::new_v4().to_string().into()),
];
let mut headers = vec![(
headers::CONTENT_TYPE.to_string(),
self.get_content_type().to_string().into(),
)];
let mut auth_header = self.get_auth_header(&req.connector_auth_type)?;
headers.append(&mut auth_header);
@ -147,7 +150,17 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
req: &types::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
let mut headers = self.build_headers(req, connectors)?;
let api_headers = match req.request.payment_method_data {
api_models::payments::PaymentMethodData::Wallet(
api_models::payments::WalletData::ApplePayRedirect(_),
) => None,
_ => Some(Self::get_default_header()),
};
if let Some(api_header) = api_headers {
headers.push(api_header)
}
Ok(headers)
}
fn get_content_type(&self) -> &'static str {
@ -156,10 +169,16 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
fn get_url(
&self,
_req: &types::PaymentsAuthorizeRouterData,
req: &types::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!("{}v1/transactions", self.base_url(connectors),))
let endpoint = match &req.request.payment_method_data {
api_models::payments::PaymentMethodData::Wallet(
api_models::payments::WalletData::ApplePayRedirect(_),
) => "api/checkouts",
_ => "v1/transactions",
};
Ok(format!("{}{}", self.base_url(connectors), endpoint))
}
fn get_request_body(
@ -224,7 +243,9 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
let mut headers = self.build_headers(req, connectors)?;
headers.push(Self::get_default_header());
Ok(headers)
}
fn get_content_type(&self) -> &'static str {
@ -290,11 +311,37 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
for Zen
{
fn build_request(
&self,
_req: &types::RouterData<
api::Capture,
types::PaymentsCaptureData,
types::PaymentsResponseData,
>,
_connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Err(errors::ConnectorError::FlowNotSupported {
flow: "Capture".to_owned(),
connector: "Zen".to_owned(),
}
.into())
}
}
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
for Zen
{
fn build_request(
&self,
_req: &types::RouterData<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>,
_connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Err(errors::ConnectorError::FlowNotSupported {
flow: "Void".to_owned(),
connector: "Zen".to_owned(),
}
.into())
}
}
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Zen {
@ -303,7 +350,9 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
req: &types::RefundsRouterData<api::Execute>,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
let mut headers = self.build_headers(req, connectors)?;
headers.push(Self::get_default_header());
Ok(headers)
}
fn get_content_type(&self) -> &'static str {
@ -378,7 +427,9 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
req: &types::RefundSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
let mut headers = self.build_headers(req, connectors)?;
headers.push(Self::get_default_header());
Ok(headers)
}
fn get_content_type(&self) -> &'static str {
@ -525,8 +576,8 @@ impl api::IncomingWebhook for Zen {
.change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
Ok(match &webhook_body.transaction_type {
ZenWebhookTxnType::TrtPurchase => api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(
webhook_body.transaction_id,
api_models::payments::PaymentIdType::PaymentAttemptId(
webhook_body.merchant_transaction_id,
),
),
ZenWebhookTxnType::TrtRefund => api_models::webhooks::ObjectReferenceId::RefundId(

View File

@ -1,9 +1,13 @@
use std::net::IpAddr;
use api_models::payments::{ApplePayRedirectData, Card, GooglePayWalletData};
use cards::CardNumber;
use common_utils::pii::Email;
use masking::Secret;
use common_utils::{ext_traits::ValueExt, pii::Email};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use ring::digest;
use serde::{Deserialize, Serialize};
use strum::Display;
use crate::{
connector::utils::{
@ -11,7 +15,7 @@ use crate::{
},
core::errors,
services::{self, Method},
types::{self, api, storage::enums, transformers::ForeignTryFrom},
types::{self, api, storage::enums, transformers::ForeignTryFrom, BrowserInformation},
};
// Auth Struct
@ -34,7 +38,7 @@ impl TryFrom<&types::ConnectorAuthType> for ZenAuthType {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenPaymentsRequest {
pub struct ApiRequest {
merchant_transaction_id: String,
payment_channel: ZenPaymentChannels,
amount: String,
@ -46,6 +50,27 @@ pub struct ZenPaymentsRequest {
}
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum ZenPaymentsRequest {
ApiRequest(Box<ApiRequest>),
CheckoutRequest(Box<CheckoutRequest>),
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckoutRequest {
amount: String,
currency: enums::Currency,
custom_ipn_url: String,
items: Vec<ZenItemObject>,
merchant_transaction_id: String,
signature: Option<Secret<String>>,
specified_payment_channel: ZenPaymentChannels,
terminal_uuid: Secret<String>,
url_redirect: String,
}
#[derive(Clone, Debug, Display, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(clippy::enum_variant_names)]
pub enum ZenPaymentChannels {
@ -113,109 +138,250 @@ pub struct ZenItemObject {
line_amount_total: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SessionObject {
pub terminal_uuid: Option<String>,
pub pay_wall_secret: Option<String>,
}
impl TryFrom<(&types::PaymentsAuthorizeRouterData, &Card)> for ZenPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(value: (&types::PaymentsAuthorizeRouterData, &Card)) -> Result<Self, Self::Error> {
let (item, ccard) = value;
let browser_info = item.request.get_browser_info()?;
let ip = browser_info.get_ip_address()?;
let browser_details = get_browser_details(&browser_info)?;
let amount = utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
let payment_specific_data = ZenPaymentData {
browser_details,
//Connector Specific for cards
payment_type: ZenPaymentTypes::Onetime,
token: None,
card: Some(ZenCardDetails {
number: ccard.card_number.clone(),
expiry_date: ccard.get_card_expiry_month_year_2_digit_with_delimiter("".to_owned()),
cvv: ccard.card_cvc.clone(),
}),
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
};
Ok(Self::ApiRequest(Box::new(ApiRequest {
merchant_transaction_id: item.attempt_id.clone(),
payment_channel: ZenPaymentChannels::PclCard,
currency: item.request.currency,
payment_specific_data,
customer: get_customer(item, ip)?,
custom_ipn_url: item.request.get_webhook_url()?,
items: get_item_object(item, amount.clone())?,
amount,
})))
}
}
impl TryFrom<(&types::PaymentsAuthorizeRouterData, &GooglePayWalletData)> for ZenPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
(item, gpay_pay_redirect_data): (&types::PaymentsAuthorizeRouterData, &GooglePayWalletData),
) -> Result<Self, Self::Error> {
let amount = utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
let browser_info = item.request.get_browser_info()?;
let browser_details = get_browser_details(&browser_info)?;
let ip = browser_info.get_ip_address()?;
let payment_specific_data = ZenPaymentData {
browser_details,
//Connector Specific for wallet
payment_type: ZenPaymentTypes::ExternalPaymentToken,
token: Some(gpay_pay_redirect_data.tokenization_data.token.clone()),
card: None,
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
};
Ok(Self::ApiRequest(Box::new(ApiRequest {
merchant_transaction_id: item.attempt_id.clone(),
payment_channel: ZenPaymentChannels::PclGooglepay,
currency: item.request.currency,
payment_specific_data,
customer: get_customer(item, ip)?,
custom_ipn_url: item.request.get_webhook_url()?,
items: get_item_object(item, amount.clone())?,
amount,
})))
}
}
impl
TryFrom<(
&types::PaymentsAuthorizeRouterData,
&Box<ApplePayRedirectData>,
)> for ZenPaymentsRequest
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
(item, _apple_pay_redirect_data): (
&types::PaymentsAuthorizeRouterData,
&Box<ApplePayRedirectData>,
),
) -> Result<Self, Self::Error> {
let amount = utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
let connector_meta = item.get_connector_meta()?;
let session: SessionObject = connector_meta
.parse_value("SessionObject")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
let terminal_uuid = session
.terminal_uuid
.clone()
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let mut checkout_request = CheckoutRequest {
merchant_transaction_id: item.attempt_id.clone(),
specified_payment_channel: ZenPaymentChannels::PclApplepay,
currency: item.request.currency,
custom_ipn_url: item.request.get_webhook_url()?,
items: get_item_object(item, amount.clone())?,
amount,
terminal_uuid: Secret::new(terminal_uuid),
signature: None,
url_redirect: item.request.get_return_url()?,
};
checkout_request.signature = Some(get_checkout_signature(&checkout_request, &session)?);
Ok(Self::CheckoutRequest(Box::new(checkout_request)))
}
}
fn get_checkout_signature(
checkout_request: &CheckoutRequest,
session: &SessionObject,
) -> Result<Secret<String>, error_stack::Report<errors::ConnectorError>> {
let pay_wall_secret = session
.pay_wall_secret
.clone()
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let mut signature_data = get_signature_data(checkout_request);
signature_data.push_str(&pay_wall_secret);
let payload_digest = digest::digest(&digest::SHA256, signature_data.as_bytes());
let mut signature = hex::encode(payload_digest);
signature.push_str(";sha256");
Ok(Secret::new(signature))
}
/// Fields should be in alphabetical order
fn get_signature_data(checkout_request: &CheckoutRequest) -> String {
let specified_payment_channel = match checkout_request.specified_payment_channel {
ZenPaymentChannels::PclCard => "pcl_card",
ZenPaymentChannels::PclGooglepay => "pcl_googlepay",
ZenPaymentChannels::PclApplepay => "pcl_applepay",
};
let mut signature_data = vec![
format!("amount={}", checkout_request.amount),
format!("currency={}", checkout_request.currency),
format!("customipnurl={}", checkout_request.custom_ipn_url),
];
for index in 0..checkout_request.items.len() {
let prefix = format!("items[{index}].");
signature_data.push(format!(
"{prefix}lineamounttotal={}",
checkout_request.items[index].line_amount_total
));
signature_data.push(format!(
"{prefix}name={}",
checkout_request.items[index].name
));
signature_data.push(format!(
"{prefix}price={}",
checkout_request.items[index].price
));
signature_data.push(format!(
"{prefix}quantity={}",
checkout_request.items[index].quantity
));
}
signature_data.push(format!(
"merchanttransactionid={}",
checkout_request.merchant_transaction_id
));
signature_data.push(format!(
"specifiedpaymentchannel={specified_payment_channel}"
));
signature_data.push(format!(
"terminaluuid={}",
checkout_request.terminal_uuid.peek()
));
signature_data.push(format!("urlredirect={}", checkout_request.url_redirect));
let signature = signature_data.join("&");
signature.to_lowercase()
}
fn get_customer(
item: &types::PaymentsAuthorizeRouterData,
ip: IpAddr,
) -> Result<ZenCustomerDetails, error_stack::Report<errors::ConnectorError>> {
Ok(ZenCustomerDetails {
email: item.request.get_email()?,
ip,
})
}
fn get_item_object(
item: &types::PaymentsAuthorizeRouterData,
amount: String,
) -> Result<Vec<ZenItemObject>, error_stack::Report<errors::ConnectorError>> {
let order_details = item.request.get_order_details()?;
Ok(vec![ZenItemObject {
name: order_details.product_name,
price: amount.clone(),
quantity: 1,
line_amount_total: amount,
}])
}
fn get_browser_details(
browser_info: &BrowserInformation,
) -> Result<ZenBrowserDetails, error_stack::Report<errors::ConnectorError>> {
let window_size = match (browser_info.screen_height, browser_info.screen_width) {
(250, 400) => "01",
(390, 400) => "02",
(500, 600) => "03",
(600, 400) => "04",
_ => "05",
}
.to_string();
Ok(ZenBrowserDetails {
color_depth: browser_info.color_depth.to_string(),
java_enabled: browser_info.java_enabled,
lang: browser_info.language.clone(),
screen_height: browser_info.screen_height.to_string(),
screen_width: browser_info.screen_width.to_string(),
timezone: browser_info.time_zone.to_string(),
accept_header: browser_info.accept_header.clone(),
window_size,
user_agent: browser_info.user_agent.clone(),
})
}
impl TryFrom<&types::PaymentsAuthorizeRouterData> for ZenPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
let browser_info = item.request.get_browser_info()?;
let order_details = item.request.get_order_details()?;
let ip = browser_info.get_ip_address()?;
let window_size = match (browser_info.screen_height, browser_info.screen_width) {
(250, 400) => "01",
(390, 400) => "02",
(500, 600) => "03",
(600, 400) => "04",
_ => "05",
}
.to_string();
let browser_details = ZenBrowserDetails {
color_depth: browser_info.color_depth.to_string(),
java_enabled: browser_info.java_enabled,
lang: browser_info.language,
screen_height: browser_info.screen_height.to_string(),
screen_width: browser_info.screen_width.to_string(),
timezone: browser_info.time_zone.to_string(),
accept_header: browser_info.accept_header,
window_size,
user_agent: browser_info.user_agent,
};
let (payment_specific_data, payment_channel) = match &item.request.payment_method_data {
api::PaymentMethodData::Card(ccard) => Ok((
ZenPaymentData {
browser_details,
//Connector Specific for cards
payment_type: ZenPaymentTypes::Onetime,
token: None,
card: Some(ZenCardDetails {
number: ccard.card_number.clone(),
expiry_date: ccard
.get_card_expiry_month_year_2_digit_with_delimiter("".to_owned()),
cvv: ccard.card_cvc.clone(),
}),
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
},
//Connector Specific for cards
ZenPaymentChannels::PclCard,
)),
api::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
api_models::payments::WalletData::GooglePay(data) => Ok((
ZenPaymentData {
browser_details,
//Connector Specific for wallet
payment_type: ZenPaymentTypes::ExternalPaymentToken,
token: Some(data.tokenization_data.token.clone()),
card: None,
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
},
ZenPaymentChannels::PclGooglepay,
)),
api_models::payments::WalletData::ApplePay(data) => Ok((
ZenPaymentData {
browser_details,
//Connector Specific for wallet
payment_type: ZenPaymentTypes::ExternalPaymentToken,
token: Some(data.payment_data.clone()),
card: None,
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
},
ZenPaymentChannels::PclApplepay,
)),
match &item.request.payment_method_data {
api_models::payments::PaymentMethodData::Card(card) => Self::try_from((item, card)),
api_models::payments::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
api_models::payments::WalletData::ApplePayRedirect(apple_pay_redirect_data) => {
Self::try_from((item, apple_pay_redirect_data))
}
api_models::payments::WalletData::GooglePay(gpay_redirect_data) => {
Self::try_from((item, gpay_redirect_data))
}
_ => Err(errors::ConnectorError::NotImplemented(
"payment method".to_string(),
)),
))?,
},
_ => Err(errors::ConnectorError::NotImplemented(
"payment method".to_string(),
)),
}?;
let order_amount =
utils::to_currency_base_unit(item.request.amount, item.request.currency)?;
Ok(Self {
merchant_transaction_id: item.payment_id.clone(),
payment_channel,
amount: order_amount.clone(),
currency: item.request.currency,
payment_specific_data,
customer: ZenCustomerDetails {
email: item.request.get_email()?,
ip,
},
custom_ipn_url: item.request.get_webhook_url()?,
items: vec![ZenItemObject {
name: order_details.product_name,
price: order_amount.clone(),
quantity: 1,
line_amount_total: order_amount,
}],
})
))?,
}
}
}
// PaymentsResponse
#[derive(Debug, Default, Deserialize, Clone, PartialEq, strum::Display)]
#[derive(Debug, Default, Deserialize, Clone, strum::Display)]
#[serde(rename_all = "UPPERCASE")]
pub enum ZenPaymentStatus {
Authorized,
@ -247,12 +413,25 @@ impl ForeignTryFrom<(ZenPaymentStatus, Option<ZenActions>)> for enums::AttemptSt
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenPaymentsResponse {
pub struct ApiResponse {
status: ZenPaymentStatus,
id: String,
merchant_action: Option<ZenMerchantAction>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum ZenPaymentsResponse {
ApiResponse(ApiResponse),
CheckoutResponse(CheckoutResponse),
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckoutResponse {
redirect_url: url::Url,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenMerchantAction {
@ -278,7 +457,33 @@ impl<F, T>
fn try_from(
item: types::ResponseRouterData<F, ZenPaymentsResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data_action = item.response.merchant_action.map(|merchant_action| {
match item.response {
ZenPaymentsResponse::ApiResponse(response) => {
Self::try_from(types::ResponseRouterData {
response,
data: item.data,
http_code: item.http_code,
})
}
ZenPaymentsResponse::CheckoutResponse(response) => {
Self::try_from(types::ResponseRouterData {
response,
data: item.data,
http_code: item.http_code,
})
}
}
}
}
impl<F, T> TryFrom<types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
value: types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data_action = value.response.merchant_action.map(|merchant_action| {
(
services::RedirectForm::from((merchant_action.data.redirect_url, Method::Get)),
merchant_action.action,
@ -290,15 +495,40 @@ impl<F, T>
};
Ok(Self {
status: enums::AttemptStatus::foreign_try_from((item.response.status, action))?,
status: enums::AttemptStatus::foreign_try_from((value.response.status, action))?,
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
resource_id: types::ResponseId::ConnectorTransactionId(value.response.id),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
}),
..item.data
..value.data
})
}
}
impl<F, T> TryFrom<types::ResponseRouterData<F, CheckoutResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
value: types::ResponseRouterData<F, CheckoutResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data = Some(services::RedirectForm::from((
value.response.redirect_url,
Method::Get,
)));
Ok(Self {
status: enums::AttemptStatus::AuthenticationPending,
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::NoResponseId,
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
}),
..value.data
})
}
}
@ -389,9 +619,11 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
}
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenWebhookBody {
#[serde(rename = "transactionId")]
pub id: String,
pub merchant_transaction_id: String,
pub amount: String,
pub currency: String,
@ -403,15 +635,16 @@ pub struct ZenWebhookSignature {
pub hash: String,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenWebhookObjectReference {
#[serde(rename = "type")]
pub transaction_type: ZenWebhookTxnType,
pub transaction_id: String,
pub merchant_transaction_id: String,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenWebhookEventType {
#[serde(rename = "type")]
@ -420,7 +653,7 @@ pub struct ZenWebhookEventType {
pub status: ZenPaymentStatus,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ZenWebhookTxnType {
TrtPurchase,