feat: add support for 3ds exemption rules in euclid crate (#8013)

This commit is contained in:
Sai Harsha Vardhan
2025-05-16 19:24:13 +05:30
committed by GitHub
parent e5cf6698da
commit 34dd99d805
9 changed files with 371 additions and 34 deletions

View File

@ -10,3 +10,5 @@ pub mod payments;
/// types that are wrappers around primitive types
pub mod primitive_wrappers;
pub mod refunds;
/// types for three ds decision rule engine
pub mod three_ds_decision_rule_engine;

View File

@ -0,0 +1,64 @@
use common_utils::impl_to_sql_from_sql_json;
use diesel::{sql_types::Jsonb, AsExpression, FromSqlRow};
use euclid::frontend::dir::{DirKeyKind, EuclidDirFilter};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
/// Enum representing the possible outcomes of the 3DS Decision Rule Engine.
#[derive(
Serialize,
Deserialize,
Debug,
Clone,
Copy,
PartialEq,
Eq,
FromSqlRow,
AsExpression,
ToSchema,
Default,
)]
#[diesel(sql_type = Jsonb)]
#[serde(rename_all = "snake_case")]
pub enum ThreeDSDecision {
/// No 3DS authentication required
#[default]
NoThreeDs,
/// Mandate 3DS Challenge
ChallengeRequested,
/// Prefer 3DS Challenge
ChallengePreferred,
/// Request 3DS Exemption, Type: Transaction Risk Analysis (TRA)
ThreeDsExemptionRequestedTra,
/// Request 3DS Exemption, Type: Low Value Transaction
ThreeDsExemptionRequestedLowValue,
/// No challenge requested by merchant (e.g., delegated authentication)
IssuerThreeDsExemptionRequested,
}
impl_to_sql_from_sql_json!(ThreeDSDecision);
/// Struct representing the output configuration for the 3DS Decision Rule Engine.
#[derive(
Serialize, Default, Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression, ToSchema,
)]
#[diesel(sql_type = Jsonb)]
pub struct ThreeDSDecisionRule {
/// The decided 3DS action based on the rules
pub decision: ThreeDSDecision,
}
impl_to_sql_from_sql_json!(ThreeDSDecisionRule);
impl EuclidDirFilter for ThreeDSDecisionRule {
const ALLOWED: &'static [DirKeyKind] = &[
DirKeyKind::CardNetwork,
DirKeyKind::PaymentAmount,
DirKeyKind::PaymentCurrency,
DirKeyKind::IssuerName,
DirKeyKind::IssuerCountry,
DirKeyKind::CustomerDevicePlatform,
DirKeyKind::CustomerDeviceType,
DirKeyKind::CustomerDeviceDisplaySize,
DirKeyKind::AcquirerCountry,
DirKeyKind::AcquirerFraudRate,
];
}

View File

@ -71,6 +71,17 @@ impl cgraph::NodeViz for dir::DirValue {
Self::RealTimePaymentType(rtpt) => rtpt.to_string(),
Self::OpenBankingType(ob) => ob.to_string(),
Self::MobilePaymentType(mpt) => mpt.to_string(),
Self::IssuerName(issuer_name) => issuer_name.value.clone(),
Self::IssuerCountry(issuer_country) => issuer_country.to_string(),
Self::CustomerDevicePlatform(customer_device_platform) => {
customer_device_platform.to_string()
}
Self::CustomerDeviceType(customer_device_type) => customer_device_type.to_string(),
Self::CustomerDeviceDisplaySize(customer_device_display_size) => {
customer_device_display_size.to_string()
}
Self::AcquirerCountry(acquirer_country) => acquirer_country.to_string(),
Self::AcquirerFraudRate(acquirer_fraud_rate) => acquirer_fraud_rate.number.to_string(),
}
}
}

View File

@ -223,57 +223,31 @@ fn lower_comparison_inner<O: EuclidDirFilter>(
match key_enum {
dir::DirKeyKind::PaymentMethod => lower_enum!(PaymentMethod, value),
dir::DirKeyKind::CardType => lower_enum!(CardType, value),
dir::DirKeyKind::CardNetwork => lower_enum!(CardNetwork, value),
dir::DirKeyKind::PayLaterType => lower_enum!(PayLaterType, value),
dir::DirKeyKind::WalletType => lower_enum!(WalletType, value),
dir::DirKeyKind::BankDebitType => lower_enum!(BankDebitType, value),
dir::DirKeyKind::BankRedirectType => lower_enum!(BankRedirectType, value),
dir::DirKeyKind::CryptoType => lower_enum!(CryptoType, value),
dir::DirKeyKind::PaymentType => lower_enum!(PaymentType, value),
dir::DirKeyKind::MandateType => lower_enum!(MandateType, value),
dir::DirKeyKind::MandateAcceptanceType => lower_enum!(MandateAcceptanceType, value),
dir::DirKeyKind::RewardType => lower_enum!(RewardType, value),
dir::DirKeyKind::PaymentCurrency => lower_enum!(PaymentCurrency, value),
dir::DirKeyKind::AuthenticationType => lower_enum!(AuthenticationType, value),
dir::DirKeyKind::CaptureMethod => lower_enum!(CaptureMethod, value),
dir::DirKeyKind::BusinessCountry => lower_enum!(BusinessCountry, value),
dir::DirKeyKind::BillingCountry => lower_enum!(BillingCountry, value),
dir::DirKeyKind::SetupFutureUsage => lower_enum!(SetupFutureUsage, value),
dir::DirKeyKind::UpiType => lower_enum!(UpiType, value),
dir::DirKeyKind::OpenBankingType => lower_enum!(OpenBankingType, value),
dir::DirKeyKind::VoucherType => lower_enum!(VoucherType, value),
dir::DirKeyKind::GiftCardType => lower_enum!(GiftCardType, value),
dir::DirKeyKind::BankTransferType => lower_enum!(BankTransferType, value),
dir::DirKeyKind::CardRedirectType => lower_enum!(CardRedirectType, value),
dir::DirKeyKind::MobilePaymentType => lower_enum!(MobilePaymentType, value),
dir::DirKeyKind::RealTimePaymentType => lower_enum!(RealTimePaymentType, value),
dir::DirKeyKind::CardBin => {
let validation_closure = |st: &String| -> Result<(), AnalysisErrorType> {
if st.len() == 6 && st.chars().all(|x| x.is_ascii_digit()) {
@ -288,16 +262,19 @@ fn lower_comparison_inner<O: EuclidDirFilter>(
};
lower_str!(CardBin, value, validation_closure)
}
dir::DirKeyKind::BusinessLabel => lower_str!(BusinessLabel, value),
dir::DirKeyKind::MetaData => lower_metadata!(MetaData, value),
dir::DirKeyKind::PaymentAmount => lower_number!(PaymentAmount, value, comparison),
dir::DirKeyKind::Connector => Err(AnalysisErrorType::InvalidKey(
dir::DirKeyKind::Connector.to_string(),
)),
dir::DirKeyKind::IssuerName => lower_str!(IssuerName, value),
dir::DirKeyKind::IssuerCountry => lower_enum!(IssuerCountry, value),
dir::DirKeyKind::CustomerDevicePlatform => lower_enum!(CustomerDevicePlatform, value),
dir::DirKeyKind::CustomerDeviceType => lower_enum!(CustomerDeviceType, value),
dir::DirKeyKind::CustomerDeviceDisplaySize => lower_enum!(CustomerDeviceDisplaySize, value),
dir::DirKeyKind::AcquirerCountry => lower_enum!(AcquirerCountry, value),
dir::DirKeyKind::AcquirerFraudRate => lower_number!(AcquirerFraudRate, value, comparison),
}
}

View File

@ -285,6 +285,55 @@ pub enum DirKeyKind {
props(Category = "Payment Method Types")
)]
MobilePaymentType,
#[strum(
serialize = "issuer_name",
detailed_message = "Name of the card issuing bank",
props(Category = "3DS Decision")
)]
#[serde(rename = "issuer_name")]
IssuerName,
#[strum(
serialize = "issuer_country",
detailed_message = "Country of the card issuing bank",
props(Category = "3DS Decision")
)]
#[serde(rename = "issuer_country")]
IssuerCountry,
#[strum(
serialize = "customer_device_platform",
detailed_message = "Platform of the customer's device (Web, Android, iOS)",
props(Category = "3DS Decision")
)]
#[serde(rename = "customer_device_platform")]
CustomerDevicePlatform,
#[strum(
serialize = "customer_device_type",
detailed_message = "Type of the customer's device (Mobile, Tablet, Desktop, Gaming Console)",
props(Category = "3DS Decision")
)]
#[serde(rename = "customer_device_type")]
CustomerDeviceType,
#[strum(
serialize = "customer_device_display_size",
detailed_message = "Display size of the customer's device (e.g., 500x600)",
props(Category = "3DS Decision")
)]
#[serde(rename = "customer_device_display_size")]
CustomerDeviceDisplaySize,
#[strum(
serialize = "acquirer_country",
detailed_message = "Country of the acquiring bank",
props(Category = "3DS Decision")
)]
#[serde(rename = "acquirer_country")]
AcquirerCountry,
#[strum(
serialize = "acquirer_fraud_rate",
detailed_message = "Fraud rate of the acquiring bank",
props(Category = "3DS Decision")
)]
#[serde(rename = "acquirer_fraud_rate")]
AcquirerFraudRate,
}
pub trait EuclidDirFilter: Sized
@ -335,6 +384,13 @@ impl DirKeyKind {
Self::RealTimePaymentType => types::DataType::EnumVariant,
Self::OpenBankingType => types::DataType::EnumVariant,
Self::MobilePaymentType => types::DataType::EnumVariant,
Self::IssuerName => types::DataType::StrValue,
Self::IssuerCountry => types::DataType::EnumVariant,
Self::CustomerDevicePlatform => types::DataType::EnumVariant,
Self::CustomerDeviceType => types::DataType::EnumVariant,
Self::CustomerDeviceDisplaySize => types::DataType::EnumVariant,
Self::AcquirerCountry => types::DataType::EnumVariant,
Self::AcquirerFraudRate => types::DataType::Number,
}
}
pub fn get_value_set(&self) -> Option<Vec<DirValue>> {
@ -472,6 +528,33 @@ impl DirKeyKind {
.map(DirValue::MobilePaymentType)
.collect(),
),
Self::IssuerName => None,
Self::IssuerCountry => Some(
enums::Country::iter()
.map(DirValue::IssuerCountry)
.collect(),
),
Self::CustomerDevicePlatform => Some(
enums::CustomerDevicePlatform::iter()
.map(DirValue::CustomerDevicePlatform)
.collect(),
),
Self::CustomerDeviceType => Some(
enums::CustomerDeviceType::iter()
.map(DirValue::CustomerDeviceType)
.collect(),
),
Self::CustomerDeviceDisplaySize => Some(
enums::CustomerDeviceDisplaySize::iter()
.map(DirValue::CustomerDeviceDisplaySize)
.collect(),
),
Self::AcquirerCountry => Some(
enums::Country::iter()
.map(DirValue::AcquirerCountry)
.collect(),
),
Self::AcquirerFraudRate => None,
}
}
}
@ -543,6 +626,20 @@ pub enum DirValue {
OpenBankingType(enums::OpenBankingType),
#[serde(rename = "mobile_payment")]
MobilePaymentType(enums::MobilePaymentType),
#[serde(rename = "issuer_name")]
IssuerName(types::StrValue),
#[serde(rename = "issuer_country")]
IssuerCountry(enums::Country),
#[serde(rename = "customer_device_platform")]
CustomerDevicePlatform(enums::CustomerDevicePlatform),
#[serde(rename = "customer_device_type")]
CustomerDeviceType(enums::CustomerDeviceType),
#[serde(rename = "customer_device_display_size")]
CustomerDeviceDisplaySize(enums::CustomerDeviceDisplaySize),
#[serde(rename = "acquirer_country")]
AcquirerCountry(enums::Country),
#[serde(rename = "acquirer_fraud_rate")]
AcquirerFraudRate(types::NumValue),
}
impl DirValue {
@ -579,6 +676,13 @@ impl DirValue {
Self::RealTimePaymentType(_) => (DirKeyKind::RealTimePaymentType, None),
Self::OpenBankingType(_) => (DirKeyKind::OpenBankingType, None),
Self::MobilePaymentType(_) => (DirKeyKind::MobilePaymentType, None),
Self::IssuerName(_) => (DirKeyKind::IssuerName, None),
Self::IssuerCountry(_) => (DirKeyKind::IssuerCountry, None),
Self::CustomerDevicePlatform(_) => (DirKeyKind::CustomerDevicePlatform, None),
Self::CustomerDeviceType(_) => (DirKeyKind::CustomerDeviceType, None),
Self::CustomerDeviceDisplaySize(_) => (DirKeyKind::CustomerDeviceDisplaySize, None),
Self::AcquirerCountry(_) => (DirKeyKind::AcquirerCountry, None),
Self::AcquirerFraudRate(_) => (DirKeyKind::AcquirerFraudRate, None),
};
DirKey::new(kind, data)
@ -616,12 +720,20 @@ impl DirValue {
Self::RealTimePaymentType(_) => None,
Self::OpenBankingType(_) => None,
Self::MobilePaymentType(_) => None,
Self::IssuerName(_) => None,
Self::IssuerCountry(_) => None,
Self::CustomerDevicePlatform(_) => None,
Self::CustomerDeviceType(_) => None,
Self::CustomerDeviceDisplaySize(_) => None,
Self::AcquirerCountry(_) => None,
Self::AcquirerFraudRate(_) => None,
}
}
pub fn get_str_val(&self) -> Option<types::StrValue> {
match self {
Self::CardBin(val) => Some(val.clone()),
Self::IssuerName(val) => Some(val.clone()),
_ => None,
}
}
@ -629,6 +741,7 @@ impl DirValue {
pub fn get_num_value(&self) -> Option<types::NumValue> {
match self {
Self::PaymentAmount(val) => Some(val.clone()),
Self::AcquirerFraudRate(val) => Some(val.clone()),
_ => None,
}
}
@ -662,6 +775,13 @@ impl DirValue {
(Self::UpiType(ut1), Self::UpiType(ut2)) => ut1 == ut2,
(Self::VoucherType(vt1), Self::VoucherType(vt2)) => vt1 == vt2,
(Self::CardRedirectType(crt1), Self::CardRedirectType(crt2)) => crt1 == crt2,
(Self::IssuerName(n1), Self::IssuerName(n2)) => n1 == n2,
(Self::IssuerCountry(c1), Self::IssuerCountry(c2)) => c1 == c2,
(Self::CustomerDevicePlatform(p1), Self::CustomerDevicePlatform(p2)) => p1 == p2,
(Self::CustomerDeviceType(t1), Self::CustomerDeviceType(t2)) => t1 == t2,
(Self::CustomerDeviceDisplaySize(s1), Self::CustomerDeviceDisplaySize(s2)) => s1 == s2,
(Self::AcquirerCountry(c1), Self::AcquirerCountry(c2)) => c1 == c2,
(Self::AcquirerFraudRate(r1), Self::AcquirerFraudRate(r2)) => r1 == r2,
_ => false,
}
}

View File

@ -3,8 +3,9 @@ use strum::VariantNames;
use crate::enums::collect_variants;
pub use crate::enums::{
AuthenticationType, CaptureMethod, CardNetwork, Country, Country as BusinessCountry,
Country as BillingCountry, CountryAlpha2, Currency as PaymentCurrency, MandateAcceptanceType,
MandateType, PaymentMethod, PaymentType, RoutableConnectors, SetupFutureUsage,
Country as BillingCountry, Country as IssuerCountry, Country as AcquirerCountry, CountryAlpha2,
Currency as PaymentCurrency, MandateAcceptanceType, MandateType, PaymentMethod, PaymentType,
RoutableConnectors, SetupFutureUsage,
};
#[cfg(feature = "payouts")]
pub use crate::enums::{PayoutBankTransferType, PayoutType, PayoutWalletType};
@ -382,6 +383,97 @@ pub enum RewardType {
Evoucher,
}
#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
strum::Display,
strum::VariantNames,
strum::EnumIter,
strum::EnumString,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CustomerDevicePlatform {
Web,
Android,
Ios,
}
#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
strum::Display,
strum::VariantNames,
strum::EnumIter,
strum::EnumString,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CustomerDeviceType {
Mobile,
Tablet,
Desktop,
GamingConsole,
}
// Common display sizes for different device types
#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
strum::Display,
strum::VariantNames,
strum::EnumIter,
strum::EnumString,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CustomerDeviceDisplaySize {
// Mobile sizes
Size320x568, // iPhone SE
Size375x667, // iPhone 8
Size390x844, // iPhone 12/13
Size414x896, // iPhone XR/11
Size428x926, // iPhone 12/13 Pro Max
// Tablet sizes
Size768x1024, // iPad
Size834x1112, // iPad Pro 10.5
Size834x1194, // iPad Pro 11
Size1024x1366, // iPad Pro 12.9
// Desktop sizes
Size1280x720, // HD
Size1366x768, // Common laptop
Size1440x900, // MacBook Air
Size1920x1080, // Full HD
Size2560x1440, // QHD
Size3840x2160, // 4K
// Custom sizes
Size500x600,
Size600x400,
// Other common sizes
Size360x640, // Common Android
Size412x915, // Pixel 6
Size800x1280, // Common Android tablet
}
collect_variants!(CardType);
collect_variants!(PayLaterType);
collect_variants!(WalletType);
@ -397,3 +489,6 @@ collect_variants!(BankTransferType);
collect_variants!(CardRedirectType);
collect_variants!(OpenBankingType);
collect_variants!(MobilePaymentType);
collect_variants!(CustomerDeviceType);
collect_variants!(CustomerDevicePlatform);
collect_variants!(CustomerDeviceDisplaySize);

View File

@ -262,6 +262,19 @@ fn lower_value(dir_value: dir::DirValue) -> Result<EuclidValue, AnalysisErrorTyp
dir::DirValue::SetupFutureUsage(sfu) => EuclidValue::SetupFutureUsage(sfu),
dir::DirValue::OpenBankingType(ob) => EuclidValue::PaymentMethodType(ob.into()),
dir::DirValue::MobilePaymentType(mp) => EuclidValue::PaymentMethodType(mp.into()),
dir::DirValue::IssuerName(str_value) => EuclidValue::IssuerName(str_value),
dir::DirValue::IssuerCountry(country) => EuclidValue::IssuerCountry(country),
dir::DirValue::CustomerDevicePlatform(customer_device_platform) => {
EuclidValue::CustomerDevicePlatform(customer_device_platform)
}
dir::DirValue::CustomerDeviceType(customer_device_type) => {
EuclidValue::CustomerDeviceType(customer_device_type)
}
dir::DirValue::CustomerDeviceDisplaySize(customer_device_display_size) => {
EuclidValue::CustomerDeviceDisplaySize(customer_device_display_size)
}
dir::DirValue::AcquirerCountry(country) => EuclidValue::AcquirerCountry(country),
dir::DirValue::AcquirerFraudRate(num_value) => EuclidValue::AcquirerFraudRate(num_value),
})
}

View File

@ -10,7 +10,10 @@ use crate::{
enums,
frontend::{
ast,
dir::{DirKeyKind, DirValue, EuclidDirFilter},
dir::{
enums::{CustomerDeviceDisplaySize, CustomerDevicePlatform, CustomerDeviceType},
DirKeyKind, DirValue, EuclidDirFilter,
},
},
};
@ -60,7 +63,22 @@ pub enum EuclidKey {
BusinessLabel,
#[strum(serialize = "setup_future_usage")]
SetupFutureUsage,
#[strum(serialize = "issuer_name")]
IssuerName,
#[strum(serialize = "issuer_country")]
IssuerCountry,
#[strum(serialize = "acquirer_country")]
AcquirerCountry,
#[strum(serialize = "acquirer_fraud_rate")]
AcquirerFraudRate,
#[strum(serialize = "customer_device_type")]
CustomerDeviceType,
#[strum(serialize = "customer_device_display_size")]
CustomerDeviceDisplaySize,
#[strum(serialize = "customer_device_platform")]
CustomerDevicePlatform,
}
impl EuclidDirFilter for DummyOutput {
const ALLOWED: &'static [DirKeyKind] = &[
DirKeyKind::AuthenticationType,
@ -138,6 +156,13 @@ impl EuclidKey {
Self::PaymentType => DataType::EnumVariant,
Self::BusinessLabel => DataType::StrValue,
Self::SetupFutureUsage => DataType::EnumVariant,
Self::IssuerName => DataType::StrValue,
Self::IssuerCountry => DataType::EnumVariant,
Self::AcquirerCountry => DataType::EnumVariant,
Self::AcquirerFraudRate => DataType::Number,
Self::CustomerDeviceType => DataType::EnumVariant,
Self::CustomerDeviceDisplaySize => DataType::EnumVariant,
Self::CustomerDevicePlatform => DataType::EnumVariant,
}
}
}
@ -253,6 +278,13 @@ pub enum EuclidValue {
BillingCountry(enums::Country),
BusinessLabel(StrValue),
SetupFutureUsage(enums::SetupFutureUsage),
IssuerName(StrValue),
IssuerCountry(enums::Country),
AcquirerCountry(enums::Country),
AcquirerFraudRate(NumValue),
CustomerDeviceType(CustomerDeviceType),
CustomerDeviceDisplaySize(CustomerDeviceDisplaySize),
CustomerDevicePlatform(CustomerDevicePlatform),
}
impl EuclidValue {
@ -281,6 +313,13 @@ impl EuclidValue {
Self::BillingCountry(_) => EuclidKey::BillingCountry,
Self::BusinessLabel(_) => EuclidKey::BusinessLabel,
Self::SetupFutureUsage(_) => EuclidKey::SetupFutureUsage,
Self::IssuerName(_) => EuclidKey::IssuerName,
Self::IssuerCountry(_) => EuclidKey::IssuerCountry,
Self::AcquirerCountry(_) => EuclidKey::AcquirerCountry,
Self::AcquirerFraudRate(_) => EuclidKey::AcquirerFraudRate,
Self::CustomerDeviceType(_) => EuclidKey::CustomerDeviceType,
Self::CustomerDeviceDisplaySize(_) => EuclidKey::CustomerDeviceDisplaySize,
Self::CustomerDevicePlatform(_) => EuclidKey::CustomerDevicePlatform,
}
}
}

View File

@ -11,6 +11,7 @@ use api_models::{
surcharge_decision_configs::SurchargeDecisionConfigs,
};
use common_enums::RoutableConnectors;
use common_types::three_ds_decision_rule_engine::ThreeDSDecisionRule;
use connector_configs::{
common_config::{ConnectorApiIntegrationPayload, DashboardRequestPayload},
connector,
@ -248,6 +249,12 @@ pub fn get_surcharge_keys() -> JsResult {
Ok(serde_wasm_bindgen::to_value(keys)?)
}
#[wasm_bindgen(js_name= getThreeDsDecisionRuleEngineKeys)]
pub fn get_three_ds_decision_rule_engine_keys() -> JsResult {
let keys = <ThreeDSDecisionRule as EuclidDirFilter>::ALLOWED;
Ok(serde_wasm_bindgen::to_value(keys)?)
}
#[wasm_bindgen(js_name=parseToString)]
pub fn parser(val: String) -> String {
ron_parser::my_parse(val)
@ -284,12 +291,21 @@ pub fn get_variant_values(key: &str) -> Result<JsValue, JsValue> {
dir::DirKeyKind::RealTimePaymentType => dir_enums::RealTimePaymentType::VARIANTS,
dir::DirKeyKind::OpenBankingType => dir_enums::OpenBankingType::VARIANTS,
dir::DirKeyKind::MobilePaymentType => dir_enums::MobilePaymentType::VARIANTS,
dir::DirKeyKind::IssuerCountry => dir_enums::Country::VARIANTS,
dir::DirKeyKind::AcquirerCountry => dir_enums::Country::VARIANTS,
dir::DirKeyKind::CustomerDeviceType => dir_enums::CustomerDeviceType::VARIANTS,
dir::DirKeyKind::CustomerDevicePlatform => dir_enums::CustomerDevicePlatform::VARIANTS,
dir::DirKeyKind::CustomerDeviceDisplaySize => {
dir_enums::CustomerDeviceDisplaySize::VARIANTS
}
dir::DirKeyKind::PaymentAmount
| dir::DirKeyKind::Connector
| dir::DirKeyKind::CardBin
| dir::DirKeyKind::BusinessLabel
| dir::DirKeyKind::MetaData => Err("Key does not have variants".to_string())?,
| dir::DirKeyKind::MetaData
| dir::DirKeyKind::IssuerName
| dir::DirKeyKind::AcquirerFraudRate => Err("Key does not have variants".to_string())?,
};
Ok(serde_wasm_bindgen::to_value(variants)?)