mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(api): add browser information in payments response (#3963)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -3186,6 +3186,10 @@ pub struct PaymentsResponse {
|
||||
/// Payment Fingerprint
|
||||
pub fingerprint: Option<String>,
|
||||
|
||||
#[schema(value_type = Option<BrowserInformation>)]
|
||||
/// The browser information used for this payment
|
||||
pub browser_info: Option<serde_json::Value>,
|
||||
|
||||
/// Payment Method Id
|
||||
pub payment_method_id: Option<String>,
|
||||
|
||||
|
||||
@ -475,14 +475,12 @@ where
|
||||
|
||||
let cloned_payment_data = payment_data.clone();
|
||||
let cloned_customer = customer.clone();
|
||||
let cloned_request = req.clone();
|
||||
|
||||
crate::utils::trigger_payments_webhook(
|
||||
merchant_account,
|
||||
business_profile,
|
||||
&key_store,
|
||||
cloned_payment_data,
|
||||
Some(cloned_request),
|
||||
cloned_customer,
|
||||
state,
|
||||
operation,
|
||||
@ -687,7 +685,7 @@ where
|
||||
FData: Send + Sync,
|
||||
Op: Operation<F, Req, Ctx> + Send + Sync + Clone,
|
||||
Req: Debug + Authenticate + Clone,
|
||||
Res: transformers::ToResponse<Req, PaymentData<F>, Op>,
|
||||
Res: transformers::ToResponse<PaymentData<F>, Op>,
|
||||
// To create connector flow specific interface data
|
||||
PaymentData<F>: ConstructFlowSpecificData<F, FData, router_types::PaymentsResponseData>,
|
||||
router_types::RouterData<F, FData, router_types::PaymentsResponseData>: Feature<F, FData>,
|
||||
@ -706,7 +704,7 @@ where
|
||||
.flat_map(|c| c.foreign_try_into())
|
||||
.collect()
|
||||
});
|
||||
let (payment_data, req, customer, connector_http_status_code, external_latency) =
|
||||
let (payment_data, _req, customer, connector_http_status_code, external_latency) =
|
||||
payments_operation_core::<_, _, _, _, Ctx>(
|
||||
&state,
|
||||
merchant_account,
|
||||
@ -721,7 +719,6 @@ where
|
||||
.await?;
|
||||
|
||||
Res::generate_response(
|
||||
Some(req),
|
||||
payment_data,
|
||||
customer,
|
||||
auth_flow,
|
||||
|
||||
@ -185,14 +185,13 @@ where
|
||||
Ok(router_data)
|
||||
}
|
||||
|
||||
pub trait ToResponse<Req, D, Op>
|
||||
pub trait ToResponse<D, Op>
|
||||
where
|
||||
Self: Sized,
|
||||
Op: Debug,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn generate_response(
|
||||
req: Option<Req>,
|
||||
data: D,
|
||||
customer: Option<domain::Customer>,
|
||||
auth_flow: services::AuthFlow,
|
||||
@ -205,14 +204,13 @@ where
|
||||
) -> RouterResponse<Self>;
|
||||
}
|
||||
|
||||
impl<F, Req, Op> ToResponse<Req, PaymentData<F>, Op> for api::PaymentsResponse
|
||||
impl<F, Op> ToResponse<PaymentData<F>, Op> for api::PaymentsResponse
|
||||
where
|
||||
F: Clone,
|
||||
Op: Debug,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn generate_response(
|
||||
req: Option<Req>,
|
||||
payment_data: PaymentData<F>,
|
||||
customer: Option<domain::Customer>,
|
||||
auth_flow: services::AuthFlow,
|
||||
@ -242,7 +240,6 @@ where
|
||||
});
|
||||
|
||||
payments_to_payments_response(
|
||||
req,
|
||||
payment_data,
|
||||
captures,
|
||||
customer,
|
||||
@ -257,15 +254,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Req, Op> ToResponse<Req, PaymentData<F>, Op> for api::PaymentsSessionResponse
|
||||
impl<F, Op> ToResponse<PaymentData<F>, Op> for api::PaymentsSessionResponse
|
||||
where
|
||||
Self: From<Req>,
|
||||
F: Clone,
|
||||
Op: Debug,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn generate_response(
|
||||
_req: Option<Req>,
|
||||
payment_data: PaymentData<F>,
|
||||
_customer: Option<domain::Customer>,
|
||||
_auth_flow: services::AuthFlow,
|
||||
@ -291,15 +286,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Req, Op> ToResponse<Req, PaymentData<F>, Op> for api::VerifyResponse
|
||||
impl<F, Op> ToResponse<PaymentData<F>, Op> for api::VerifyResponse
|
||||
where
|
||||
Self: From<Req>,
|
||||
F: Clone,
|
||||
Op: Debug,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn generate_response(
|
||||
_req: Option<Req>,
|
||||
data: PaymentData<F>,
|
||||
customer: Option<domain::Customer>,
|
||||
_auth_flow: services::AuthFlow,
|
||||
@ -354,8 +347,7 @@ where
|
||||
// try to use router data here so that already validated things , we don't want to repeat the validations.
|
||||
// Add internal value not found and external value not found so that we can give 500 / Internal server error for internal value not found
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn payments_to_payments_response<R, Op, F: Clone>(
|
||||
payment_request: Option<R>,
|
||||
pub fn payments_to_payments_response<Op, F: Clone>(
|
||||
payment_data: PaymentData<F>,
|
||||
captures: Option<Vec<storage::Capture>>,
|
||||
customer: Option<domain::Customer>,
|
||||
@ -510,44 +502,42 @@ where
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
let output = Ok(match payment_request {
|
||||
Some(_request) => {
|
||||
if payments::is_start_pay(&operation) && payment_attempt.authentication_data.is_some() {
|
||||
let redirection_data = payment_attempt
|
||||
.authentication_data
|
||||
.get_required_value("redirection_data")?;
|
||||
let output = if payments::is_start_pay(&operation)
|
||||
&& payment_attempt.authentication_data.is_some()
|
||||
{
|
||||
let redirection_data = payment_attempt
|
||||
.authentication_data
|
||||
.get_required_value("redirection_data")?;
|
||||
|
||||
let form: RedirectForm = serde_json::from_value(redirection_data)
|
||||
.map_err(|_| errors::ApiErrorResponse::InternalServerError)?;
|
||||
let form: RedirectForm = serde_json::from_value(redirection_data)
|
||||
.map_err(|_| errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
services::ApplicationResponse::Form(Box::new(services::RedirectionFormData {
|
||||
redirect_form: form,
|
||||
payment_method_data: payment_data.payment_method_data,
|
||||
amount,
|
||||
currency: currency.to_string(),
|
||||
}))
|
||||
} else {
|
||||
let mut next_action_response = None;
|
||||
services::ApplicationResponse::Form(Box::new(services::RedirectionFormData {
|
||||
redirect_form: form,
|
||||
payment_method_data: payment_data.payment_method_data,
|
||||
amount,
|
||||
currency: currency.to_string(),
|
||||
}))
|
||||
} else {
|
||||
let mut next_action_response = None;
|
||||
|
||||
let bank_transfer_next_steps =
|
||||
bank_transfer_next_steps_check(payment_attempt.clone())?;
|
||||
let bank_transfer_next_steps = bank_transfer_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;
|
||||
let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
let next_action_containing_qr_code_url =
|
||||
qr_code_next_steps_check(payment_attempt.clone())?;
|
||||
let next_action_containing_qr_code_url = qr_code_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
let next_action_containing_wait_screen =
|
||||
wait_screen_next_steps_check(payment_attempt.clone())?;
|
||||
let next_action_containing_wait_screen =
|
||||
wait_screen_next_steps_check(payment_attempt.clone())?;
|
||||
|
||||
if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
|
||||
|| bank_transfer_next_steps.is_some()
|
||||
|| next_action_voucher.is_some()
|
||||
|| next_action_containing_qr_code_url.is_some()
|
||||
|| next_action_containing_wait_screen.is_some()
|
||||
|| payment_data.authentication.is_some()
|
||||
{
|
||||
next_action_response = bank_transfer_next_steps
|
||||
if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
|
||||
|| bank_transfer_next_steps.is_some()
|
||||
|| next_action_voucher.is_some()
|
||||
|| next_action_containing_qr_code_url.is_some()
|
||||
|| next_action_containing_wait_screen.is_some()
|
||||
|| payment_data.authentication.is_some()
|
||||
{
|
||||
next_action_response = bank_transfer_next_steps
|
||||
.map(|bank_transfer| {
|
||||
api_models::payments::NextActionData::DisplayBankTransferInformation {
|
||||
bank_transfer_steps_and_charges_details: bank_transfer,
|
||||
@ -610,261 +600,183 @@ where
|
||||
},
|
||||
None => None
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response)
|
||||
if third_party_sdk_session_next_action(&payment_attempt, operation) {
|
||||
next_action_response = Some(
|
||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken {
|
||||
session_token: payment_data.sessions_token.first().cloned(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
let mut response: api::PaymentsResponse = Default::default();
|
||||
let routed_through = payment_attempt.connector.clone();
|
||||
|
||||
let connector_label = routed_through.as_ref().and_then(|connector_name| {
|
||||
core_utils::get_connector_label(
|
||||
payment_intent.business_country,
|
||||
payment_intent.business_label.as_ref(),
|
||||
payment_attempt.business_sub_label.as_ref(),
|
||||
connector_name,
|
||||
)
|
||||
});
|
||||
services::ApplicationResponse::JsonWithHeaders((
|
||||
response
|
||||
.set_net_amount(payment_attempt.net_amount)
|
||||
.set_payment_id(Some(payment_attempt.payment_id))
|
||||
.set_merchant_id(Some(payment_attempt.merchant_id))
|
||||
.set_status(payment_intent.status)
|
||||
.set_amount(payment_attempt.amount)
|
||||
.set_amount_capturable(Some(payment_attempt.amount_capturable))
|
||||
.set_amount_received(payment_intent.amount_captured)
|
||||
.set_surcharge_details(surcharge_details)
|
||||
.set_connector(routed_through)
|
||||
.set_client_secret(payment_intent.client_secret.map(masking::Secret::new))
|
||||
.set_created(Some(payment_intent.created_at))
|
||||
.set_currency(currency.to_string())
|
||||
.set_customer_id(customer.as_ref().map(|cus| cus.clone().customer_id))
|
||||
.set_email(
|
||||
customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.email.as_ref().map(|s| s.to_owned())),
|
||||
)
|
||||
.set_name(
|
||||
customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.name.as_ref().map(|s| s.to_owned())),
|
||||
)
|
||||
.set_phone(
|
||||
customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.phone.as_ref().map(|s| s.to_owned())),
|
||||
)
|
||||
.set_mandate_id(mandate_id)
|
||||
.set_mandate_data(
|
||||
payment_data.setup_mandate.map(|d| api::MandateData {
|
||||
customer_acceptance: d.customer_acceptance.map(|d| {
|
||||
api::CustomerAcceptance {
|
||||
acceptance_type: match d.acceptance_type {
|
||||
data_models::mandates::AcceptanceType::Online => {
|
||||
api::AcceptanceType::Online
|
||||
}
|
||||
data_models::mandates::AcceptanceType::Offline => {
|
||||
api::AcceptanceType::Offline
|
||||
}
|
||||
},
|
||||
accepted_at: d.accepted_at,
|
||||
online: d.online.map(|d| api::OnlineMandate {
|
||||
ip_address: d.ip_address,
|
||||
user_agent: d.user_agent,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
mandate_type: d.mandate_type.map(|d| match d {
|
||||
data_models::mandates::MandateDataType::MultiUse(Some(i)) => {
|
||||
api::MandateType::MultiUse(Some(api::MandateAmountData {
|
||||
amount: i.amount,
|
||||
currency: i.currency,
|
||||
start_date: i.start_date,
|
||||
end_date: i.end_date,
|
||||
metadata: i.metadata,
|
||||
}))
|
||||
}
|
||||
data_models::mandates::MandateDataType::SingleUse(i) => {
|
||||
api::MandateType::SingleUse(
|
||||
api::payments::MandateAmountData {
|
||||
amount: i.amount,
|
||||
currency: i.currency,
|
||||
start_date: i.start_date,
|
||||
end_date: i.end_date,
|
||||
metadata: i.metadata,
|
||||
},
|
||||
)
|
||||
}
|
||||
data_models::mandates::MandateDataType::MultiUse(None) => {
|
||||
api::MandateType::MultiUse(None)
|
||||
}
|
||||
}),
|
||||
update_mandate_id: d.update_mandate_id,
|
||||
}),
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_description(payment_intent.description)
|
||||
.set_refunds(refunds_response) // refunds.iter().map(refund_to_refund_response),
|
||||
.set_disputes(disputes_response)
|
||||
.set_attempts(attempts_response)
|
||||
.set_captures(captures_response)
|
||||
.set_payment_method(
|
||||
payment_attempt.payment_method,
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_payment_method_data(
|
||||
payment_method_data_response,
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_payment_token(payment_attempt.payment_token)
|
||||
.set_error_message(
|
||||
payment_attempt
|
||||
.error_reason
|
||||
.or(payment_attempt.error_message),
|
||||
)
|
||||
.set_error_code(payment_attempt.error_code)
|
||||
.set_shipping(payment_data.address.get_shipping().cloned())
|
||||
.set_billing(payment_data.address.get_payment_billing().cloned())
|
||||
.set_next_action(next_action_response)
|
||||
.set_return_url(payment_intent.return_url)
|
||||
.set_cancellation_reason(payment_attempt.cancellation_reason)
|
||||
.set_authentication_type(payment_attempt.authentication_type)
|
||||
.set_statement_descriptor_name(payment_intent.statement_descriptor_name)
|
||||
.set_statement_descriptor_suffix(payment_intent.statement_descriptor_suffix)
|
||||
.set_setup_future_usage(payment_intent.setup_future_usage)
|
||||
.set_capture_method(payment_attempt.capture_method)
|
||||
.set_payment_experience(payment_attempt.payment_experience)
|
||||
.set_payment_method_type(payment_attempt.payment_method_type)
|
||||
.set_metadata(payment_intent.metadata)
|
||||
.set_order_details(payment_intent.order_details)
|
||||
.set_connector_label(connector_label)
|
||||
.set_business_country(payment_intent.business_country)
|
||||
.set_business_label(payment_intent.business_label)
|
||||
.set_business_sub_label(payment_attempt.business_sub_label)
|
||||
.set_allowed_payment_method_types(
|
||||
payment_intent.allowed_payment_method_types,
|
||||
)
|
||||
.set_ephemeral_key(
|
||||
payment_data.ephemeral_key.map(ForeignFrom::foreign_from),
|
||||
)
|
||||
.set_frm_message(frm_message)
|
||||
.set_merchant_decision(merchant_decision)
|
||||
.set_manual_retry_allowed(helpers::is_manual_retry_allowed(
|
||||
&payment_intent.status,
|
||||
&payment_attempt.status,
|
||||
connector_request_reference_id_config,
|
||||
&merchant_id,
|
||||
))
|
||||
.set_connector_transaction_id(payment_attempt.connector_transaction_id)
|
||||
.set_feature_metadata(payment_intent.feature_metadata)
|
||||
.set_connector_metadata(payment_intent.connector_metadata)
|
||||
.set_reference_id(payment_attempt.connector_response_reference_id)
|
||||
.set_payment_link(payment_link_data)
|
||||
.set_profile_id(payment_intent.profile_id)
|
||||
.set_attempt_count(payment_intent.attempt_count)
|
||||
.set_merchant_connector_id(payment_attempt.merchant_connector_id)
|
||||
.set_unified_code(payment_attempt.unified_code)
|
||||
.set_unified_message(payment_attempt.unified_message)
|
||||
.set_incremental_authorization_allowed(
|
||||
payment_intent.incremental_authorization_allowed,
|
||||
)
|
||||
.set_external_authentication_details(external_authentication_details)
|
||||
.set_fingerprint(payment_intent.fingerprint_id)
|
||||
.set_authorization_count(payment_intent.authorization_count)
|
||||
.set_incremental_authorizations(incremental_authorizations_response)
|
||||
.set_expires_on(payment_intent.session_expiry)
|
||||
.set_external_3ds_authentication_attempted(
|
||||
payment_attempt.external_three_ds_authentication_attempted,
|
||||
)
|
||||
.set_payment_method_id(payment_attempt.payment_method_id)
|
||||
.set_payment_method_status(
|
||||
payment_data.payment_method_info.map(|info| info.status),
|
||||
)
|
||||
.set_customer(customer_details_response.clone())
|
||||
.to_owned(),
|
||||
headers,
|
||||
))
|
||||
}
|
||||
// next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response)
|
||||
if third_party_sdk_session_next_action(&payment_attempt, operation) {
|
||||
next_action_response = Some(
|
||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken {
|
||||
session_token: payment_data.sessions_token.first().cloned(),
|
||||
},
|
||||
)
|
||||
}
|
||||
None => services::ApplicationResponse::JsonWithHeaders((
|
||||
api::PaymentsResponse {
|
||||
net_amount: payment_attempt.net_amount,
|
||||
payment_id: Some(payment_attempt.payment_id),
|
||||
merchant_id: Some(payment_attempt.merchant_id),
|
||||
status: payment_intent.status,
|
||||
amount: payment_attempt.amount,
|
||||
amount_capturable: None,
|
||||
amount_received: payment_intent.amount_captured,
|
||||
client_secret: payment_intent.client_secret.map(masking::Secret::new),
|
||||
created: Some(payment_intent.created_at),
|
||||
currency: currency.to_string(),
|
||||
customer_id: payment_intent.customer_id,
|
||||
description: payment_intent.description,
|
||||
refunds: refunds_response,
|
||||
disputes: disputes_response,
|
||||
attempts: attempts_response,
|
||||
captures: captures_response,
|
||||
payment_method: payment_attempt.payment_method,
|
||||
capture_method: payment_attempt.capture_method,
|
||||
error_message: payment_attempt
|
||||
.error_reason
|
||||
.or(payment_attempt.error_message),
|
||||
error_code: payment_attempt.error_code,
|
||||
payment_method_data: payment_method_data_response,
|
||||
email: customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.email.as_ref().map(|s| s.to_owned())),
|
||||
name: customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.name.as_ref().map(|s| s.to_owned())),
|
||||
phone: customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.phone.as_ref().map(|s| s.to_owned())),
|
||||
mandate_id,
|
||||
shipping: payment_data.address.get_shipping().cloned(),
|
||||
billing: payment_data.address.get_payment_billing().cloned(),
|
||||
cancellation_reason: payment_attempt.cancellation_reason,
|
||||
payment_token: payment_attempt.payment_token,
|
||||
metadata: payment_intent.metadata,
|
||||
manual_retry_allowed: helpers::is_manual_retry_allowed(
|
||||
|
||||
let mut response: api::PaymentsResponse = Default::default();
|
||||
let routed_through = payment_attempt.connector.clone();
|
||||
|
||||
let connector_label = routed_through.as_ref().and_then(|connector_name| {
|
||||
core_utils::get_connector_label(
|
||||
payment_intent.business_country,
|
||||
payment_intent.business_label.as_ref(),
|
||||
payment_attempt.business_sub_label.as_ref(),
|
||||
connector_name,
|
||||
)
|
||||
});
|
||||
|
||||
services::ApplicationResponse::JsonWithHeaders((
|
||||
response
|
||||
.set_net_amount(payment_attempt.net_amount)
|
||||
.set_payment_id(Some(payment_attempt.payment_id))
|
||||
.set_merchant_id(Some(payment_attempt.merchant_id))
|
||||
.set_status(payment_intent.status)
|
||||
.set_amount(payment_attempt.amount)
|
||||
.set_amount_capturable(Some(payment_attempt.amount_capturable))
|
||||
.set_amount_received(payment_intent.amount_captured)
|
||||
.set_surcharge_details(surcharge_details)
|
||||
.set_connector(routed_through)
|
||||
.set_client_secret(payment_intent.client_secret.map(masking::Secret::new))
|
||||
.set_created(Some(payment_intent.created_at))
|
||||
.set_currency(currency.to_string())
|
||||
.set_customer_id(customer.as_ref().map(|cus| cus.clone().customer_id))
|
||||
.set_email(
|
||||
customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.email.as_ref().map(|s| s.to_owned())),
|
||||
)
|
||||
.set_name(
|
||||
customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.name.as_ref().map(|s| s.to_owned())),
|
||||
)
|
||||
.set_phone(
|
||||
customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.phone.as_ref().map(|s| s.to_owned())),
|
||||
)
|
||||
.set_mandate_id(mandate_id)
|
||||
.set_mandate_data(
|
||||
payment_data.setup_mandate.map(|d| api::MandateData {
|
||||
customer_acceptance: d.customer_acceptance.map(|d| {
|
||||
api::CustomerAcceptance {
|
||||
acceptance_type: match d.acceptance_type {
|
||||
data_models::mandates::AcceptanceType::Online => {
|
||||
api::AcceptanceType::Online
|
||||
}
|
||||
data_models::mandates::AcceptanceType::Offline => {
|
||||
api::AcceptanceType::Offline
|
||||
}
|
||||
},
|
||||
accepted_at: d.accepted_at,
|
||||
online: d.online.map(|d| api::OnlineMandate {
|
||||
ip_address: d.ip_address,
|
||||
user_agent: d.user_agent,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
mandate_type: d.mandate_type.map(|d| match d {
|
||||
data_models::mandates::MandateDataType::MultiUse(Some(i)) => {
|
||||
api::MandateType::MultiUse(Some(api::MandateAmountData {
|
||||
amount: i.amount,
|
||||
currency: i.currency,
|
||||
start_date: i.start_date,
|
||||
end_date: i.end_date,
|
||||
metadata: i.metadata,
|
||||
}))
|
||||
}
|
||||
data_models::mandates::MandateDataType::SingleUse(i) => {
|
||||
api::MandateType::SingleUse(api::payments::MandateAmountData {
|
||||
amount: i.amount,
|
||||
currency: i.currency,
|
||||
start_date: i.start_date,
|
||||
end_date: i.end_date,
|
||||
metadata: i.metadata,
|
||||
})
|
||||
}
|
||||
data_models::mandates::MandateDataType::MultiUse(None) => {
|
||||
api::MandateType::MultiUse(None)
|
||||
}
|
||||
}),
|
||||
update_mandate_id: d.update_mandate_id,
|
||||
}),
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_description(payment_intent.description)
|
||||
.set_refunds(refunds_response) // refunds.iter().map(refund_to_refund_response),
|
||||
.set_disputes(disputes_response)
|
||||
.set_attempts(attempts_response)
|
||||
.set_captures(captures_response)
|
||||
.set_payment_method(
|
||||
payment_attempt.payment_method,
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_payment_method_data(
|
||||
payment_method_data_response,
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_payment_token(payment_attempt.payment_token)
|
||||
.set_error_message(
|
||||
payment_attempt
|
||||
.error_reason
|
||||
.or(payment_attempt.error_message),
|
||||
)
|
||||
.set_error_code(payment_attempt.error_code)
|
||||
.set_shipping(payment_data.address.get_shipping().cloned())
|
||||
.set_billing(payment_data.address.get_payment_billing().cloned())
|
||||
.set_next_action(next_action_response)
|
||||
.set_return_url(payment_intent.return_url)
|
||||
.set_cancellation_reason(payment_attempt.cancellation_reason)
|
||||
.set_authentication_type(payment_attempt.authentication_type)
|
||||
.set_statement_descriptor_name(payment_intent.statement_descriptor_name)
|
||||
.set_statement_descriptor_suffix(payment_intent.statement_descriptor_suffix)
|
||||
.set_setup_future_usage(payment_intent.setup_future_usage)
|
||||
.set_capture_method(payment_attempt.capture_method)
|
||||
.set_payment_experience(payment_attempt.payment_experience)
|
||||
.set_payment_method_type(payment_attempt.payment_method_type)
|
||||
.set_metadata(payment_intent.metadata)
|
||||
.set_order_details(payment_intent.order_details)
|
||||
.set_connector_label(connector_label)
|
||||
.set_business_country(payment_intent.business_country)
|
||||
.set_business_label(payment_intent.business_label)
|
||||
.set_business_sub_label(payment_attempt.business_sub_label)
|
||||
.set_allowed_payment_method_types(payment_intent.allowed_payment_method_types)
|
||||
.set_ephemeral_key(payment_data.ephemeral_key.map(ForeignFrom::foreign_from))
|
||||
.set_frm_message(frm_message)
|
||||
.set_merchant_decision(merchant_decision)
|
||||
.set_manual_retry_allowed(helpers::is_manual_retry_allowed(
|
||||
&payment_intent.status,
|
||||
&payment_attempt.status,
|
||||
connector_request_reference_id_config,
|
||||
&merchant_id,
|
||||
),
|
||||
order_details: payment_intent.order_details,
|
||||
frm_message,
|
||||
connector_transaction_id: payment_attempt.connector_transaction_id,
|
||||
feature_metadata: payment_intent.feature_metadata,
|
||||
connector_metadata: payment_intent.connector_metadata,
|
||||
allowed_payment_method_types: payment_intent.allowed_payment_method_types,
|
||||
reference_id: payment_attempt.connector_response_reference_id,
|
||||
attempt_count: payment_intent.attempt_count,
|
||||
payment_link: payment_link_data,
|
||||
surcharge_details,
|
||||
unified_code: payment_attempt.unified_code,
|
||||
unified_message: payment_attempt.unified_message,
|
||||
incremental_authorization_allowed: payment_intent.incremental_authorization_allowed,
|
||||
authorization_count: payment_intent.authorization_count,
|
||||
incremental_authorizations: incremental_authorizations_response,
|
||||
external_authentication_details,
|
||||
expires_on: payment_intent.session_expiry,
|
||||
external_3ds_authentication_attempted: payment_attempt
|
||||
.external_three_ds_authentication_attempted,
|
||||
customer: customer_details_response,
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
.set_connector_transaction_id(payment_attempt.connector_transaction_id)
|
||||
.set_feature_metadata(payment_intent.feature_metadata)
|
||||
.set_connector_metadata(payment_intent.connector_metadata)
|
||||
.set_reference_id(payment_attempt.connector_response_reference_id)
|
||||
.set_payment_link(payment_link_data)
|
||||
.set_profile_id(payment_intent.profile_id)
|
||||
.set_attempt_count(payment_intent.attempt_count)
|
||||
.set_merchant_connector_id(payment_attempt.merchant_connector_id)
|
||||
.set_unified_code(payment_attempt.unified_code)
|
||||
.set_unified_message(payment_attempt.unified_message)
|
||||
.set_incremental_authorization_allowed(
|
||||
payment_intent.incremental_authorization_allowed,
|
||||
)
|
||||
.set_external_authentication_details(external_authentication_details)
|
||||
.set_fingerprint(payment_intent.fingerprint_id)
|
||||
.set_authorization_count(payment_intent.authorization_count)
|
||||
.set_incremental_authorizations(incremental_authorizations_response)
|
||||
.set_expires_on(payment_intent.session_expiry)
|
||||
.set_external_3ds_authentication_attempted(
|
||||
payment_attempt.external_three_ds_authentication_attempted,
|
||||
)
|
||||
.set_payment_method_id(payment_attempt.payment_method_id)
|
||||
.set_payment_method_status(payment_data.payment_method_info.map(|info| info.status))
|
||||
.set_customer(customer_details_response.clone())
|
||||
.set_browser_info(payment_attempt.browser_info)
|
||||
.to_owned(),
|
||||
headers,
|
||||
)),
|
||||
});
|
||||
))
|
||||
};
|
||||
|
||||
metrics::PAYMENT_OPS_COUNT.add(
|
||||
&metrics::CONTEXT,
|
||||
@ -877,7 +789,7 @@ where
|
||||
],
|
||||
);
|
||||
|
||||
output
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn third_party_sdk_session_next_action<Op>(
|
||||
|
||||
@ -736,12 +736,11 @@ pub fn add_apple_pay_payment_status_metrics(
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn trigger_payments_webhook<F, Req, Op>(
|
||||
pub async fn trigger_payments_webhook<F, Op>(
|
||||
merchant_account: domain::MerchantAccount,
|
||||
business_profile: diesel_models::business_profile::BusinessProfile,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
payment_data: crate::core::payments::PaymentData<F>,
|
||||
req: Option<Req>,
|
||||
customer: Option<domain::Customer>,
|
||||
state: &crate::routes::AppState,
|
||||
operation: Op,
|
||||
@ -770,7 +769,6 @@ where
|
||||
| enums::IntentStatus::PartiallyCaptured
|
||||
) {
|
||||
let payments_response = crate::core::payments::transformers::payments_to_payments_response(
|
||||
req,
|
||||
payment_data,
|
||||
captures,
|
||||
customer,
|
||||
|
||||
@ -176,16 +176,11 @@ impl ProcessTrackerWorkflow<AppState> for PaymentsSyncWorkflow {
|
||||
|
||||
// Trigger the outgoing webhook to notify the merchant about failed payment
|
||||
let operation = operations::PaymentStatus;
|
||||
Box::pin(utils::trigger_payments_webhook::<
|
||||
_,
|
||||
api_models::payments::PaymentsRequest,
|
||||
_,
|
||||
>(
|
||||
Box::pin(utils::trigger_payments_webhook(
|
||||
merchant_account,
|
||||
business_profile,
|
||||
&key_store,
|
||||
payment_data,
|
||||
None,
|
||||
customer,
|
||||
state,
|
||||
operation,
|
||||
|
||||
@ -14761,6 +14761,14 @@
|
||||
"description": "Payment Fingerprint",
|
||||
"nullable": true
|
||||
},
|
||||
"browser_info": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/BrowserInformation"
|
||||
}
|
||||
],
|
||||
"nullable": true
|
||||
},
|
||||
"payment_method_id": {
|
||||
"type": "string",
|
||||
"description": "Payment Method Id",
|
||||
|
||||
@ -19,7 +19,7 @@ pm.test("[POST]::/payments - Response has JSON Body", function () {
|
||||
let jsonData = {};
|
||||
try {
|
||||
jsonData = pm.response.json();
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
|
||||
// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id
|
||||
if (jsonData?.payment_id) {
|
||||
@ -78,3 +78,12 @@ pm.test(
|
||||
.true;
|
||||
},
|
||||
);
|
||||
|
||||
// Response body should have "browser_info"
|
||||
pm.test(
|
||||
"[POST]::/payments - Content check if 'browser_info' exists",
|
||||
function () {
|
||||
pm.expect(typeof jsonData.browser_info !== "undefined").to.be
|
||||
.true;
|
||||
},
|
||||
);
|
||||
|
||||
@ -54,6 +54,18 @@
|
||||
"card_cvc": "123"
|
||||
}
|
||||
},
|
||||
"browser_info": {
|
||||
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
||||
"accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"language": "nl-NL",
|
||||
"color_depth": 24,
|
||||
"screen_height": 723,
|
||||
"screen_width": 1536,
|
||||
"time_zone": 0,
|
||||
"java_enabled": true,
|
||||
"java_script_enabled": true,
|
||||
"ip_address": "127.0.0.1"
|
||||
},
|
||||
"billing": {
|
||||
"address": {
|
||||
"line1": "1467",
|
||||
|
||||
@ -7085,7 +7085,7 @@
|
||||
"let jsonData = {};",
|
||||
"try {",
|
||||
" jsonData = pm.response.json();",
|
||||
"} catch (e) {}",
|
||||
"} catch (e) { }",
|
||||
"",
|
||||
"// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id",
|
||||
"if (jsonData?.payment_id) {",
|
||||
@ -7144,6 +7144,15 @@
|
||||
" .true;",
|
||||
" },",
|
||||
");",
|
||||
"",
|
||||
"// Response body should have \"browser_info\"",
|
||||
"pm.test(",
|
||||
" \"[POST]::/payments - Content check if 'browser_info' exists\",",
|
||||
" function () {",
|
||||
" pm.expect(typeof jsonData.browser_info !== \"undefined\").to.be",
|
||||
" .true;",
|
||||
" },",
|
||||
");",
|
||||
""
|
||||
],
|
||||
"type": "text/javascript"
|
||||
@ -7169,7 +7178,7 @@
|
||||
"language": "json"
|
||||
}
|
||||
},
|
||||
"raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}"
|
||||
"raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/payments",
|
||||
|
||||
Reference in New Issue
Block a user