feat(router): add mertics to apple pay flow (#2235)

Co-authored-by: ItsMeShashank <shashank.attarde@juspay.in>
This commit is contained in:
Shankar Singh C
2023-09-22 16:06:15 +05:30
committed by GitHub
parent 2b8bd03a72
commit b9f25c4a4e
12 changed files with 192 additions and 37 deletions

View File

@ -1796,3 +1796,8 @@ pub enum ReconStatus {
Active, Active,
Disabled, Disabled,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ApplePayFlow {
Simplified,
Manual,
}

View File

@ -9,7 +9,7 @@ pub mod types;
use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant}; use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant};
use api_models::payments::HeaderPayload; use api_models::{enums, payments::HeaderPayload};
use common_utils::{ext_traits::AsyncExt, pii}; use common_utils::{ext_traits::AsyncExt, pii};
use data_models::mandates::MandateData; use data_models::mandates::MandateData;
use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use diesel_models::{ephemeral_key, fraud_check::FraudCheck};
@ -48,7 +48,10 @@ use crate::{
self as router_types, api, domain, self as router_types, api, domain,
storage::{self, enums as storage_enums}, storage::{self, enums as storage_enums},
}, },
utils::{add_connector_http_status_code_metrics, Encode, OptionExt, ValueExt}, utils::{
add_apple_pay_flow_metrics, add_connector_http_status_code_metrics, Encode, OptionExt,
ValueExt,
},
workflows::payment_sync, workflows::payment_sync,
}; };
@ -1105,45 +1108,44 @@ fn is_payment_method_tokenization_enabled_for_connector(
.unwrap_or(false)) .unwrap_or(false))
} }
fn is_apple_pay_predecrypt( fn decide_apple_pay_flow(
payment_method_type: &Option<api_models::enums::PaymentMethodType>, payment_method_type: &Option<api_models::enums::PaymentMethodType>,
merchant_connector_account: &Option<helpers::MerchantConnectorAccountType>, merchant_connector_account: &Option<helpers::MerchantConnectorAccountType>,
) -> RouterResult<bool> { ) -> Option<enums::ApplePayFlow> {
Ok(payment_method_type payment_method_type.and_then(|pmt| match pmt {
.map(|pmt| match pmt {
api_models::enums::PaymentMethodType::ApplePay => { api_models::enums::PaymentMethodType::ApplePay => {
check_apple_pay_metadata(merchant_connector_account) check_apple_pay_metadata(merchant_connector_account)
} }
_ => Ok(false), _ => None,
}) })
.transpose()?
.unwrap_or(false))
} }
fn check_apple_pay_metadata( fn check_apple_pay_metadata(
merchant_connector_account: &Option<helpers::MerchantConnectorAccountType>, merchant_connector_account: &Option<helpers::MerchantConnectorAccountType>,
) -> RouterResult<bool> { ) -> Option<enums::ApplePayFlow> {
let apple_pay_predecrypt = merchant_connector_account merchant_connector_account.clone().and_then(|mca| {
.clone()
.and_then(|mca| {
let metadata = mca.get_metadata(); let metadata = mca.get_metadata();
metadata.and_then(|apple_pay_metadata| { metadata.and_then(|apple_pay_metadata| {
let parsed_metadata: Result<api_models::payments::ApplepaySessionTokenData, _> = let parsed_metadata: Result<api_models::payments::ApplepaySessionTokenData, _> =
apple_pay_metadata.parse_value("ApplepaySessionTokenData"); apple_pay_metadata.parse_value("ApplepaySessionTokenData").map_err(|error| logger::error!(%error, "Failed to Parse Value to ApplepaySessionTokenData"));
parsed_metadata.ok().map(|metadata| match metadata.data { parsed_metadata.ok().map(|metadata| match metadata.data {
api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
apple_pay_combined, apple_pay_combined,
) => match apple_pay_combined { ) => match apple_pay_combined {
api_models::payments::ApplePayCombinedMetadata::Simplified { .. } => true, api_models::payments::ApplePayCombinedMetadata::Simplified { .. } => {
api_models::payments::ApplePayCombinedMetadata::Manual { .. } => false, enums::ApplePayFlow::Simplified
}
api_models::payments::ApplePayCombinedMetadata::Manual { .. } => {
enums::ApplePayFlow::Manual
}
}, },
api_models::payments::ApplepaySessionTokenMetadata::ApplePay(_) => false, api_models::payments::ApplepaySessionTokenMetadata::ApplePay(_) => {
enums::ApplePayFlow::Manual
}
}) })
}) })
}) })
.unwrap_or(false);
Ok(apple_pay_predecrypt)
} }
fn is_payment_method_type_allowed_for_connector( fn is_payment_method_type_allowed_for_connector(
@ -1166,8 +1168,11 @@ async fn decide_payment_method_tokenize_action(
payment_method: &storage::enums::PaymentMethod, payment_method: &storage::enums::PaymentMethod,
pm_parent_token: Option<&String>, pm_parent_token: Option<&String>,
is_connector_tokenization_enabled: bool, is_connector_tokenization_enabled: bool,
is_apple_pay_predecrypt_supported: bool, apple_pay_flow: Option<enums::ApplePayFlow>,
) -> RouterResult<TokenizationAction> { ) -> RouterResult<TokenizationAction> {
let is_apple_pay_predecrypt_supported =
matches!(apple_pay_flow, Some(enums::ApplePayFlow::Simplified));
match pm_parent_token { match pm_parent_token {
None => { None => {
if is_connector_tokenization_enabled && is_apple_pay_predecrypt_supported { if is_connector_tokenization_enabled && is_apple_pay_predecrypt_supported {
@ -1272,10 +1277,16 @@ where
payment_method_type, payment_method_type,
)?; )?;
let is_apple_pay_predecrypt = is_apple_pay_predecrypt( let apple_pay_flow = decide_apple_pay_flow(
payment_method_type, payment_method_type,
&Some(merchant_connector_account.clone()), &Some(merchant_connector_account.clone()),
)?; );
add_apple_pay_flow_metrics(
&apple_pay_flow,
payment_data.payment_attempt.connector.clone(),
payment_data.payment_attempt.merchant_id.clone(),
);
let payment_method_action = decide_payment_method_tokenize_action( let payment_method_action = decide_payment_method_tokenize_action(
state, state,
@ -1283,7 +1294,7 @@ where
payment_method, payment_method,
payment_data.token.as_ref(), payment_data.token.as_ref(),
is_connector_tokenization_enabled, is_connector_tokenization_enabled,
is_apple_pay_predecrypt, apple_pay_flow,
) )
.await?; .await?;

View File

@ -2540,6 +2540,7 @@ pub fn router_data_type_conversion<F1, F2, Req1, Req2, Res1, Res2>(
test_mode: router_data.test_mode, test_mode: router_data.test_mode,
connector_api_version: router_data.connector_api_version, connector_api_version: router_data.connector_api_version,
connector_http_status_code: router_data.connector_http_status_code, connector_http_status_code: router_data.connector_http_status_code,
apple_pay_flow: router_data.apple_pay_flow,
} }
} }

View File

@ -419,6 +419,13 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]); metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
} }
utils::add_apple_pay_payment_status_metrics(
router_data.status,
router_data.apple_pay_flow,
payment_data.payment_attempt.connector.clone(),
payment_data.payment_attempt.merchant_id.clone(),
);
let (capture_updates, payment_attempt_update) = let (capture_updates, payment_attempt_update) =
match payment_data.multiple_capture_data { match payment_data.multiple_capture_data {
Some(multiple_capture_data) => { Some(multiple_capture_data) => {

View File

@ -115,6 +115,11 @@ where
None None
}; };
let apple_pay_flow = payments::decide_apple_pay_flow(
&payment_data.payment_attempt.payment_method_type,
&Some(merchant_connector_account.clone()),
);
router_data = types::RouterData { router_data = types::RouterData {
flow: PhantomData, flow: PhantomData,
merchant_id: merchant_account.merchant_id.clone(), merchant_id: merchant_account.merchant_id.clone(),
@ -157,6 +162,7 @@ where
payment_method_balance: None, payment_method_balance: None,
connector_api_version, connector_api_version,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow,
}; };
Ok(router_data) Ok(router_data)

View File

@ -187,6 +187,7 @@ pub async fn construct_payout_router_data<'a, F>(
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
@ -325,6 +326,7 @@ pub async fn construct_refund_router_data<'a, F>(
payment_method_balance: None, payment_method_balance: None,
connector_api_version, connector_api_version,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
@ -551,6 +553,7 @@ pub async fn construct_accept_dispute_router_data<'a>(
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
} }
@ -635,6 +638,7 @@ pub async fn construct_submit_evidence_router_data<'a>(
test_mode, test_mode,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
} }
@ -724,6 +728,7 @@ pub async fn construct_upload_file_router_data<'a>(
test_mode, test_mode,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
} }
@ -811,6 +816,7 @@ pub async fn construct_defend_dispute_router_data<'a>(
test_mode, test_mode,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
} }
@ -891,6 +897,7 @@ pub async fn construct_retrieve_file_router_data<'a>(
test_mode, test_mode,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
} }

View File

@ -115,6 +115,7 @@ pub async fn construct_webhook_router_data<'a>(
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
}; };
Ok(router_data) Ok(router_data)
} }

View File

@ -94,5 +94,13 @@ histogram_metric!(CARD_DELETE_TIME, GLOBAL_METER);
histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER); histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER);
histogram_metric!(DECRYPTION_TIME, GLOBAL_METER); histogram_metric!(DECRYPTION_TIME, GLOBAL_METER);
// Apple Pay Flow Metrics
counter_metric!(APPLE_PAY_MANUAL_FLOW, GLOBAL_METER);
counter_metric!(APPLE_PAY_SIMPLIFIED_FLOW, GLOBAL_METER);
counter_metric!(APPLE_PAY_MANUAL_FLOW_SUCCESSFUL_PAYMENT, GLOBAL_METER);
counter_metric!(APPLE_PAY_SIMPLIFIED_FLOW_SUCCESSFUL_PAYMENT, GLOBAL_METER);
counter_metric!(APPLE_PAY_MANUAL_FLOW_FAILED_PAYMENT, GLOBAL_METER);
counter_metric!(APPLE_PAY_SIMPLIFIED_FLOW_FAILED_PAYMENT, GLOBAL_METER);
pub mod request; pub mod request;
pub mod utils; pub mod utils;

View File

@ -285,6 +285,9 @@ pub struct RouterData<Flow, Request, Response> {
pub test_mode: Option<bool>, pub test_mode: Option<bool>,
pub connector_http_status_code: Option<u16>, pub connector_http_status_code: Option<u16>,
/// Contains apple pay flow type simplified or manual
pub apple_pay_flow: Option<storage_enums::ApplePayFlow>,
} }
#[derive(Debug, Clone, serde::Deserialize)] #[derive(Debug, Clone, serde::Deserialize)]
@ -1108,6 +1111,7 @@ impl<F1, F2, T1, T2> From<(&RouterData<F1, T1, PaymentsResponseData>, T2)>
payment_method_balance: data.payment_method_balance.clone(), payment_method_balance: data.payment_method_balance.clone(),
connector_api_version: data.connector_api_version.clone(), connector_api_version: data.connector_api_version.clone(),
connector_http_status_code: data.connector_http_status_code, connector_http_status_code: data.connector_http_status_code,
apple_pay_flow: data.apple_pay_flow.clone(),
} }
} }
} }
@ -1161,6 +1165,7 @@ impl<F1, F2>
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: data.connector_http_status_code, connector_http_status_code: data.connector_http_status_code,
apple_pay_flow: None,
} }
} }
} }

View File

@ -5,7 +5,7 @@ pub mod ext_traits;
#[cfg(feature = "kv_store")] #[cfg(feature = "kv_store")]
pub mod storage_partitioning; pub mod storage_partitioning;
use api_models::{payments, webhooks}; use api_models::{enums, payments, webhooks};
use base64::Engine; use base64::Engine;
pub use common_utils::{ pub use common_utils::{
crypto, crypto,
@ -530,3 +530,104 @@ impl CustomerAddress for api_models::customers::CustomerRequest {
.await .await
} }
} }
pub fn add_apple_pay_flow_metrics(
apple_pay_flow: &Option<enums::ApplePayFlow>,
connector: Option<String>,
merchant_id: String,
) {
if let Some(flow) = apple_pay_flow {
match flow {
enums::ApplePayFlow::Simplified => metrics::APPLE_PAY_SIMPLIFIED_FLOW.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes(
"connector",
connector.to_owned().unwrap_or("null".to_string()),
),
metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
],
),
enums::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes(
"connector",
connector.to_owned().unwrap_or("null".to_string()),
),
metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
],
),
}
}
}
pub fn add_apple_pay_payment_status_metrics(
payment_attempt_status: enums::AttemptStatus,
apple_pay_flow: Option<enums::ApplePayFlow>,
connector: Option<String>,
merchant_id: String,
) {
if payment_attempt_status == enums::AttemptStatus::Charged {
if let Some(flow) = apple_pay_flow {
match flow {
enums::ApplePayFlow::Simplified => {
metrics::APPLE_PAY_SIMPLIFIED_FLOW_SUCCESSFUL_PAYMENT.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes(
"connector",
connector.to_owned().unwrap_or("null".to_string()),
),
metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
],
)
}
enums::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_SUCCESSFUL_PAYMENT
.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes(
"connector",
connector.to_owned().unwrap_or("null".to_string()),
),
metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
],
),
}
}
} else if payment_attempt_status == enums::AttemptStatus::Failure {
if let Some(flow) = apple_pay_flow {
match flow {
enums::ApplePayFlow::Simplified => {
metrics::APPLE_PAY_SIMPLIFIED_FLOW_FAILED_PAYMENT.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes(
"connector",
connector.to_owned().unwrap_or("null".to_string()),
),
metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
],
)
}
enums::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_FAILED_PAYMENT.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes(
"connector",
connector.to_owned().unwrap_or("null".to_string()),
),
metrics::request::add_attributes("merchant_id", merchant_id.to_owned()),
],
),
}
}
}
}

View File

@ -91,6 +91,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
} }
} }
@ -146,6 +147,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
} }
} }

View File

@ -513,6 +513,7 @@ pub trait ConnectorActions: Connector {
payment_method_balance: None, payment_method_balance: None,
connector_api_version: None, connector_api_version: None,
connector_http_status_code: None, connector_http_status_code: None,
apple_pay_flow: None,
} }
} }