mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(connector): [Noon] Add Card Mandates and Webhooks Support (#1243)
Co-authored-by: Arjun Karthik <m.arjunkarthik@gmail.com> Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
@ -1559,6 +1559,8 @@ pub struct Metadata {
|
||||
#[schema(value_type = Object, example = r#"{ "city": "NY", "unit": "245" }"#)]
|
||||
#[serde(flatten)]
|
||||
pub data: pii::SecretSerdeValue,
|
||||
/// Information about the order category that merchant wants to specify at connector level. (e.g. In Noon Payments it can take values like "pay", "food", or any other custom string set by the merchant in Noon's Dashboard)
|
||||
pub order_category: Option<String>,
|
||||
|
||||
/// Redirection response coming in request as metadata field only for redirection scenarios
|
||||
pub redirect_response: Option<RedirectResponse>,
|
||||
|
||||
@ -1057,7 +1057,7 @@ impl api::IncomingWebhook for Bluesnap {
|
||||
let details: bluesnap::BluesnapWebhookObjectResource =
|
||||
serde_urlencoded::from_bytes(request.body)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
let res_json =
|
||||
utils::Encode::<transformers::BluesnapWebhookObjectResource>::encode_to_value(&details)
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
|
||||
@ -3,6 +3,7 @@ mod transformers;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use base64::Engine;
|
||||
use common_utils::{crypto, ext_traits::ByteSliceExt};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use transformers as noon;
|
||||
|
||||
@ -14,6 +15,7 @@ use crate::{
|
||||
errors::{self, CustomResult},
|
||||
payments,
|
||||
},
|
||||
db::StorageInterface,
|
||||
headers,
|
||||
services::{
|
||||
self,
|
||||
@ -585,24 +587,112 @@ impl services::ConnectorRedirectResponse for Noon {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl api::IncomingWebhook for Noon {
|
||||
fn get_webhook_object_reference_id(
|
||||
fn get_webhook_source_verification_algorithm(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
|
||||
Ok(Box::new(crypto::HmacSha512))
|
||||
}
|
||||
|
||||
fn get_webhook_source_verification_signature(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let webhook_body: noon::NoonWebhookSignature = request
|
||||
.body
|
||||
.parse_struct("NoonWebhookSignature")
|
||||
.change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
|
||||
let signature = webhook_body.signature;
|
||||
consts::BASE64_ENGINE
|
||||
.decode(signature)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookSignatureNotFound)
|
||||
}
|
||||
|
||||
fn get_webhook_source_verification_message(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
_merchant_id: &str,
|
||||
_secret: &[u8],
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let webhook_body: noon::NoonWebhookBody = request
|
||||
.body
|
||||
.parse_struct("NoonWebhookBody")
|
||||
.change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
|
||||
let message = format!(
|
||||
"{},{},{},{},{}",
|
||||
webhook_body.order_id,
|
||||
webhook_body.order_status,
|
||||
webhook_body.event_id,
|
||||
webhook_body.event_type,
|
||||
webhook_body.time_stamp,
|
||||
);
|
||||
Ok(message.into_bytes())
|
||||
}
|
||||
|
||||
async fn get_webhook_source_verification_merchant_secret(
|
||||
&self,
|
||||
db: &dyn StorageInterface,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let key = format!("whsec_verification_{}_{}", self.id(), merchant_id);
|
||||
let secret = match db.find_config_by_key(&key).await {
|
||||
Ok(config) => Some(config),
|
||||
Err(e) => {
|
||||
crate::logger::warn!("Unable to fetch merchant webhook secret from DB: {:#?}", e);
|
||||
None
|
||||
}
|
||||
};
|
||||
Ok(secret
|
||||
.map(|conf| conf.config.into_bytes())
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::webhooks::ObjectReferenceId, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
let details: noon::NoonWebhookOrderId = request
|
||||
.body
|
||||
.parse_struct("NoonWebhookOrderId")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api_models::payments::PaymentIdType::ConnectorTransactionId(
|
||||
details.order_id.to_string(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
let details: noon::NoonWebhookEvent = request
|
||||
.body
|
||||
.parse_struct("NoonWebhookEvent")
|
||||
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
|
||||
|
||||
Ok(match &details.event_type {
|
||||
noon::NoonWebhookEventTypes::Sale | noon::NoonWebhookEventTypes::Capture => {
|
||||
match &details.order_status {
|
||||
noon::NoonPaymentStatus::Captured => {
|
||||
api::IncomingWebhookEvent::PaymentIntentSuccess
|
||||
}
|
||||
_ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?,
|
||||
}
|
||||
}
|
||||
noon::NoonWebhookEventTypes::Fail => api::IncomingWebhookEvent::PaymentIntentFailure,
|
||||
_ => Err(errors::ConnectorError::WebhookEventTypeNotFound)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
let reference_object: serde_json::Value = serde_json::from_slice(request.body)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
|
||||
Ok(reference_object)
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,13 +16,27 @@ pub enum NoonChannels {
|
||||
Web,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum NoonSubscriptionType {
|
||||
Unscheduled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct NoonSubscriptionData {
|
||||
#[serde(rename = "type")]
|
||||
subscription_type: NoonSubscriptionType,
|
||||
//Short description about the subscription.
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonOrder {
|
||||
amount: String,
|
||||
currency: storage_models::enums::Currency,
|
||||
currency: Option<storage_models::enums::Currency>,
|
||||
channel: NoonChannels,
|
||||
category: String,
|
||||
category: Option<String>,
|
||||
//Short description of the order.
|
||||
name: String,
|
||||
}
|
||||
@ -37,10 +51,17 @@ pub enum NoonPaymentActions {
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonConfiguration {
|
||||
tokenize_c_c: Option<bool>,
|
||||
payment_action: NoonPaymentActions,
|
||||
return_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonSubscription {
|
||||
subscription_identifier: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonCard {
|
||||
@ -55,6 +76,7 @@ pub struct NoonCard {
|
||||
#[serde(tag = "type", content = "data")]
|
||||
pub enum NoonPaymentData {
|
||||
Card(NoonCard),
|
||||
Subscription(NoonSubscription),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@ -72,30 +94,55 @@ pub struct NoonPaymentsRequest {
|
||||
order: NoonOrder,
|
||||
configuration: NoonConfiguration,
|
||||
payment_data: NoonPaymentData,
|
||||
subscription: Option<NoonSubscriptionData>,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
let payment_data = match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => Ok(NoonPaymentData::Card(NoonCard {
|
||||
name_on_card: req_card.card_holder_name,
|
||||
number_plain: req_card.card_number,
|
||||
expiry_month: req_card.card_exp_month,
|
||||
expiry_year: req_card.card_exp_year,
|
||||
cvv: req_card.card_cvc,
|
||||
})),
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment methods".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
let (payment_data, currency, category) = match item.request.connector_mandate_id() {
|
||||
Some(subscription_identifier) => (
|
||||
NoonPaymentData::Subscription(NoonSubscription {
|
||||
subscription_identifier,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
),
|
||||
_ => (
|
||||
match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => Ok(NoonPaymentData::Card(NoonCard {
|
||||
name_on_card: req_card.card_holder_name,
|
||||
number_plain: req_card.card_number,
|
||||
expiry_month: req_card.card_exp_month,
|
||||
expiry_year: req_card.card_exp_year,
|
||||
cvv: req_card.card_cvc,
|
||||
})),
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment methods".to_string(),
|
||||
)),
|
||||
}?,
|
||||
Some(item.request.currency),
|
||||
item.request.order_category.clone(),
|
||||
),
|
||||
};
|
||||
let name = item.get_description()?;
|
||||
let (subscription, tokenize_c_c) =
|
||||
match item.request.setup_future_usage.is_some().then_some((
|
||||
NoonSubscriptionData {
|
||||
subscription_type: NoonSubscriptionType::Unscheduled,
|
||||
name: name.clone(),
|
||||
},
|
||||
true,
|
||||
)) {
|
||||
Some((a, b)) => (Some(a), Some(b)),
|
||||
None => (None, None),
|
||||
};
|
||||
let order = NoonOrder {
|
||||
amount: conn_utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
|
||||
currency: item.request.currency,
|
||||
currency,
|
||||
channel: NoonChannels::Web,
|
||||
category: "pay".to_string(),
|
||||
name: item.get_description()?,
|
||||
category,
|
||||
name,
|
||||
};
|
||||
let payment_action = if item.request.is_auto_capture()? {
|
||||
NoonPaymentActions::Sale
|
||||
@ -108,8 +155,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest {
|
||||
configuration: NoonConfiguration {
|
||||
payment_action,
|
||||
return_url: item.request.router_return_url.clone(),
|
||||
tokenize_c_c,
|
||||
},
|
||||
payment_data,
|
||||
subscription,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -138,13 +187,15 @@ impl TryFrom<&types::ConnectorAuthType> for NoonAuthType {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[derive(Default, Debug, Deserialize, strum::Display)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[strum(serialize_all = "UPPERCASE")]
|
||||
pub enum NoonPaymentStatus {
|
||||
Authorized,
|
||||
Captured,
|
||||
PartiallyCaptured,
|
||||
Reversed,
|
||||
Cancelled,
|
||||
#[serde(rename = "3DS_ENROLL_INITIATED")]
|
||||
ThreeDsEnrollInitiated,
|
||||
Failed,
|
||||
@ -158,6 +209,7 @@ impl From<NoonPaymentStatus> for enums::AttemptStatus {
|
||||
NoonPaymentStatus::Authorized => Self::Authorized,
|
||||
NoonPaymentStatus::Captured | NoonPaymentStatus::PartiallyCaptured => Self::Charged,
|
||||
NoonPaymentStatus::Reversed => Self::Voided,
|
||||
NoonPaymentStatus::Cancelled => Self::AuthenticationFailed,
|
||||
NoonPaymentStatus::ThreeDsEnrollInitiated => Self::AuthenticationPending,
|
||||
NoonPaymentStatus::Failed => Self::Failure,
|
||||
NoonPaymentStatus::Pending => Self::Pending,
|
||||
@ -165,6 +217,11 @@ impl From<NoonPaymentStatus> for enums::AttemptStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct NoonSubscriptionResponse {
|
||||
identifier: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonPaymentsOrderResponse {
|
||||
@ -183,6 +240,7 @@ pub struct NoonCheckoutData {
|
||||
pub struct NoonPaymentsResponseResult {
|
||||
order: NoonPaymentsOrderResponse,
|
||||
checkout_data: Option<NoonCheckoutData>,
|
||||
subscription: Option<NoonSubscriptionResponse>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -205,6 +263,14 @@ impl<F, T>
|
||||
form_fields: std::collections::HashMap::new(),
|
||||
}
|
||||
});
|
||||
let mandate_reference =
|
||||
item.response
|
||||
.result
|
||||
.subscription
|
||||
.map(|subscription_data| types::MandateReference {
|
||||
connector_mandate_id: Some(subscription_data.identifier),
|
||||
payment_method_id: None,
|
||||
});
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.result.order.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
@ -212,7 +278,7 @@ impl<F, T>
|
||||
item.response.result.order.id.to_string(),
|
||||
),
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
mandate_reference,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
}),
|
||||
@ -399,6 +465,44 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundSyncResponse>>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, strum::Display)]
|
||||
pub enum NoonWebhookEventTypes {
|
||||
Authenticate,
|
||||
Authorize,
|
||||
Capture,
|
||||
Fail,
|
||||
Refund,
|
||||
Sale,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonWebhookBody {
|
||||
pub order_id: u64,
|
||||
pub order_status: NoonPaymentStatus,
|
||||
pub event_type: NoonWebhookEventTypes,
|
||||
pub event_id: String,
|
||||
pub time_stamp: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct NoonWebhookSignature {
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonWebhookOrderId {
|
||||
pub order_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonWebhookEvent {
|
||||
pub order_status: NoonPaymentStatus,
|
||||
pub event_type: NoonWebhookEventTypes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NoonErrorResponse {
|
||||
|
||||
@ -364,6 +364,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize {
|
||||
json_payload: Some(req.json_payload.unwrap_or(serde_json::json!({})).into()),
|
||||
}),
|
||||
allowed_payment_method_types: None,
|
||||
order_category: None,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -393,9 +393,13 @@ pub fn validate_request_amount_and_amount_to_capture(
|
||||
pub fn validate_mandate(
|
||||
req: impl Into<api::MandateValidationFields>,
|
||||
is_confirm_operation: bool,
|
||||
) -> RouterResult<Option<api::MandateTxnType>> {
|
||||
) -> CustomResult<Option<api::MandateTxnType>, errors::ApiErrorResponse> {
|
||||
let req: api::MandateValidationFields = req.into();
|
||||
match req.is_mandate() {
|
||||
match req.validate_and_get_mandate_type().change_context(
|
||||
errors::ApiErrorResponse::MandateValidationFailed {
|
||||
reason: "Expected one out of mandate_id and mandate_data but got both".to_string(),
|
||||
},
|
||||
)? {
|
||||
Some(api::MandateTxnType::NewMandateTxn) => {
|
||||
validate_new_mandate_request(req, is_confirm_operation)?;
|
||||
Ok(Some(api::MandateTxnType::NewMandateTxn))
|
||||
|
||||
@ -605,6 +605,9 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
|
||||
.transpose()
|
||||
.unwrap_or_default();
|
||||
|
||||
let order_category = parsed_metadata
|
||||
.as_ref()
|
||||
.and_then(|data| data.order_category.clone());
|
||||
let order_details = parsed_metadata.and_then(|data| data.order_details);
|
||||
let complete_authorize_url = Some(helpers::create_complete_authorize_url(
|
||||
router_base_url,
|
||||
@ -647,6 +650,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
|
||||
email: payment_data.email,
|
||||
payment_experience: payment_data.payment_attempt.payment_experience,
|
||||
order_details,
|
||||
order_category,
|
||||
session_token: None,
|
||||
enrolled_for_3ds: true,
|
||||
related_transaction_id: None,
|
||||
|
||||
@ -226,6 +226,7 @@ pub struct PaymentsAuthorizeData {
|
||||
pub setup_mandate_details: Option<payments::MandateData>,
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
pub order_details: Option<api_models::payments::OrderDetails>,
|
||||
pub order_category: Option<String>,
|
||||
pub session_token: Option<String>,
|
||||
pub enrolled_for_3ds: bool,
|
||||
pub related_transaction_id: Option<String>,
|
||||
@ -729,6 +730,7 @@ impl From<&VerifyRouterData> for PaymentsAuthorizeData {
|
||||
complete_authorize_url: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
session_token: None,
|
||||
enrolled_for_3ds: true,
|
||||
related_transaction_id: None,
|
||||
|
||||
@ -113,15 +113,23 @@ impl PaymentIdTypeExt for PaymentIdType {
|
||||
}
|
||||
|
||||
pub(crate) trait MandateValidationFieldsExt {
|
||||
fn is_mandate(&self) -> Option<MandateTxnType>;
|
||||
fn validate_and_get_mandate_type(
|
||||
&self,
|
||||
) -> errors::CustomResult<Option<MandateTxnType>, errors::ValidationError>;
|
||||
}
|
||||
|
||||
impl MandateValidationFieldsExt for MandateValidationFields {
|
||||
fn is_mandate(&self) -> Option<MandateTxnType> {
|
||||
fn validate_and_get_mandate_type(
|
||||
&self,
|
||||
) -> errors::CustomResult<Option<MandateTxnType>, errors::ValidationError> {
|
||||
match (&self.mandate_data, &self.mandate_id) {
|
||||
(None, None) => None,
|
||||
(_, Some(_)) => Some(MandateTxnType::RecurringMandateTxn),
|
||||
(Some(_), _) => Some(MandateTxnType::NewMandateTxn),
|
||||
(None, None) => Ok(None),
|
||||
(Some(_), Some(_)) => Err(errors::ValidationError::InvalidValue {
|
||||
message: "Expected one out of mandate_id and mandate_data but got both".to_string(),
|
||||
})
|
||||
.into_report(),
|
||||
(_, Some(_)) => Ok(Some(MandateTxnType::RecurringMandateTxn)),
|
||||
(Some(_), _) => Ok(Some(MandateTxnType::NewMandateTxn)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
|
||||
capture_method: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
session_token: None,
|
||||
enrolled_for_3ds: false,
|
||||
|
||||
@ -81,6 +81,7 @@ impl AdyenTest {
|
||||
capture_method: Some(capture_method),
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
payment_experience: None,
|
||||
payment_method_type: None,
|
||||
|
||||
@ -75,6 +75,7 @@ fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
||||
// capture_method: Some(capture_method),
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
payment_experience: None,
|
||||
payment_method_type: None,
|
||||
|
||||
@ -77,6 +77,7 @@ fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
||||
// capture_method: Some(capture_method),
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
payment_experience: None,
|
||||
payment_method_type: None,
|
||||
|
||||
@ -76,6 +76,7 @@ fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
||||
// capture_method: Some(capture_method),
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
payment_experience: None,
|
||||
payment_method_type: None,
|
||||
|
||||
@ -508,6 +508,7 @@ impl Default for PaymentAuthorizeType {
|
||||
setup_mandate_details: None,
|
||||
browser_info: Some(BrowserInfoType::default().0),
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
session_token: None,
|
||||
enrolled_for_3ds: false,
|
||||
|
||||
@ -84,6 +84,7 @@ impl WorldlineTest {
|
||||
capture_method: Some(capture_method),
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
session_token: None,
|
||||
enrolled_for_3ds: false,
|
||||
|
||||
Reference in New Issue
Block a user