From cd818a228477bffd438b54fd18a5aeb658460dab Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Wed, 25 Oct 2023 16:48:57 +0530 Subject: [PATCH 01/33] feat(router): added merchant custom name support for payment link --- crates/api_models/src/payments.rs | 8 +++--- crates/diesel_models/src/payment_link.rs | 3 ++- crates/diesel_models/src/schema.rs | 2 ++ crates/router/src/core/payment_link.rs | 26 +++++++++---------- .../payments/operations/payment_create.rs | 1 + .../down.sql | 2 ++ .../up.sql | 2 ++ 7 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/down.sql create mode 100644 migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/up.sql diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index f61f6f9a44ac..00a736760f67 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3095,6 +3095,8 @@ pub struct PaymentLinkObject { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub link_expiry: Option, pub merchant_custom_domain_name: Option, + /// Custom merchant name for payment link + pub custom_merchant_name: Option, } #[derive(Default, Debug, serde::Deserialize, Clone, ToSchema)] @@ -3138,11 +3140,11 @@ pub struct PaymentLinkDetails { pub pub_key: String, pub client_secret: String, pub payment_id: String, - #[serde(with = "common_utils::custom_serde::iso8601")] - pub expiry: PrimitiveDateTime, + #[serde(with = "common_utils::custom_serde::iso8601::option")] + pub expiry: Option, pub merchant_logo: String, pub return_url: String, - pub merchant_name: crypto::OptionalEncryptableName, + pub merchant_name: String, pub order_details: Vec, pub max_items_visible_after_collapse: i8, } diff --git a/crates/diesel_models/src/payment_link.rs b/crates/diesel_models/src/payment_link.rs index 4b182a8155a5..50cc5e89cee9 100644 --- a/crates/diesel_models/src/payment_link.rs +++ b/crates/diesel_models/src/payment_link.rs @@ -20,8 +20,8 @@ pub struct PaymentLink { pub last_modified_at: PrimitiveDateTime, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub custom_merchant_name: Option, } - #[derive( Clone, Debug, @@ -47,4 +47,5 @@ pub struct PaymentLinkNew { pub last_modified_at: Option, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub custom_merchant_name: Option, } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 2f3e7d345a75..5c3c9f3e6958 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -643,6 +643,8 @@ diesel::table! { created_at -> Timestamp, last_modified_at -> Timestamp, fulfilment_time -> Nullable, + #[max_length = 64] + custom_merchant_name -> Nullable, } } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 2c51fa0c3cbb..624f073da3bf 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -1,6 +1,7 @@ use api_models::admin as admin_types; use common_utils::ext_traits::AsyncExt; use error_stack::{IntoReport, ResultExt}; +use masking::PeekInterface; use super::errors::{self, RouterResult, StorageErrorExt}; use crate::{ @@ -55,20 +56,14 @@ pub async fn intiate_payment_link_flow( "create payment link", )?; - let fulfillment_time = payment_intent + let payment_link = payment_intent .payment_link_id .as_ref() - .async_and_then(|pli| async move { - db.find_payment_link_by_payment_link_id(pli) - .await - .ok()? - .fulfilment_time - .ok_or(errors::ApiErrorResponse::PaymentNotFound) - .ok() - }) + .async_and_then( + |pli| async move { db.find_payment_link_by_payment_link_id(pli).await.ok() }, + ) .await - .get_required_value("fulfillment_time") - .change_context(errors::ApiErrorResponse::PaymentNotFound)?; + .ok_or(errors::ApiErrorResponse::PaymentNotFound)?; let payment_link_config = merchant_account .payment_link_config @@ -108,10 +103,15 @@ pub async fn intiate_payment_link_flow( amount: payment_intent.amount, currency, payment_id: payment_intent.payment_id, - merchant_name: merchant_account.merchant_name, + merchant_name: payment_link.custom_merchant_name.unwrap_or( + merchant_account + .merchant_name + .map(|merchant_name| merchant_name.into_inner().peek().to_owned()) + .unwrap_or_default(), + ), order_details, return_url, - expiry: fulfillment_time, + expiry: payment_link.fulfilment_time, pub_key, client_secret, merchant_logo: payment_link_config diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 87195510fc68..094717794383 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -811,6 +811,7 @@ async fn create_payment_link( created_at, last_modified_at, fulfilment_time: payment_link_object.link_expiry, + custom_merchant_name: payment_link_object.custom_merchant_name, }; let payment_link_db = db .insert_payment_link(payment_link_req) diff --git a/migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/down.sql b/migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/down.sql new file mode 100644 index 000000000000..84f009021df8 --- /dev/null +++ b/migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_link DROP COLUMN custom_merchant_name; diff --git a/migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/up.sql b/migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/up.sql new file mode 100644 index 000000000000..c4fa756e57a0 --- /dev/null +++ b/migrations/2023-10-25-070909_add_merchant_custom_name_payment_link/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE payment_link ADD COLUMN custom_merchant_name VARCHAR(64); \ No newline at end of file From 30590d6b18d68118652a98ca974cf9388daba4ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:33:18 +0000 Subject: [PATCH 02/33] docs(openapi): re-generate OpenAPI specification --- openapi/openapi_spec.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 7649a4e98211..7d4cac059328 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -7865,6 +7865,11 @@ "merchant_custom_domain_name": { "type": "string", "nullable": true + }, + "custom_merchant_name": { + "type": "string", + "description": "Custom merchant name for payment link", + "nullable": true } } }, From 865741deaf80e46f386c4d7dafdfb18d142221b4 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 27 Oct 2023 18:29:09 +0530 Subject: [PATCH 03/33] refactor(router): addressed pr comments --- crates/router/src/core/payment_link.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 624f073da3bf..3a37abfe317d 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -59,11 +59,14 @@ pub async fn intiate_payment_link_flow( let payment_link = payment_intent .payment_link_id .as_ref() - .async_and_then( - |pli| async move { db.find_payment_link_by_payment_link_id(pli).await.ok() }, - ) + .async_and_then(|pli| async { + db.find_payment_link_by_payment_link_id(pli) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentLinkNotFound) + .ok() + }) .await - .ok_or(errors::ApiErrorResponse::PaymentNotFound)?; + .ok_or(errors::ApiErrorResponse::PaymentLinkNotFound)?; let payment_link_config = merchant_account .payment_link_config From 214675ec44ad8b441f2764c17731f21cb96fd233 Mon Sep 17 00:00:00 2001 From: Kashif <46213975+kashif-m@users.noreply.github.com> Date: Sat, 28 Oct 2023 14:42:59 +0530 Subject: [PATCH 04/33] feat: add currency symbols (#2709) Co-authored-by: Kashif --- crates/api_models/src/payments.rs | 1 + crates/common_enums/Cargo.toml | 4 +- crates/common_enums/src/enums.rs | 369 +++++ crates/router/src/core/payment_link.rs | 1 + .../src/core/payment_link/payment_link.html | 1266 +++++++++-------- 5 files changed, 1021 insertions(+), 620 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index f9cb21dae5f2..9662fe809713 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3138,6 +3138,7 @@ pub struct PaymentLinkInitiateRequest { pub struct PaymentLinkDetails { pub amount: i64, pub currency: api_enums::Currency, + pub currency_symbol: api_enums::CurrencySymbol, pub pub_key: String, pub client_secret: String, pub payment_id: String, diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index 10b4fb509e88..f5219d9833db 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -12,9 +12,9 @@ dummy_connector = [] [dependencies] diesel = { version = "2.1.0", features = ["postgres"] } -serde = { version = "1.0.160", features = [ "derive" ] } +serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0.96" -strum = { version = "0.25", features = [ "derive" ] } +strum = { version = "0.25", features = ["derive"] } time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } utoipa = { version = "3.3.0", features = ["preserve_order"] } diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index f0386fc2f42e..0822d1c0432c 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -773,6 +773,375 @@ impl Currency { | Self::ZAR => false, } } + + pub fn to_currency_symbol(&self) -> CurrencySymbol { + match self { + Self::AED => CurrencySymbol::AED, + Self::ALL => CurrencySymbol::ALL, + Self::AMD => CurrencySymbol::AMD, + Self::ANG => CurrencySymbol::ANG, + Self::ARS => CurrencySymbol::ARS, + Self::AUD => CurrencySymbol::AUD, + Self::AWG => CurrencySymbol::AWG, + Self::AZN => CurrencySymbol::AZN, + Self::BBD => CurrencySymbol::BBD, + Self::BDT => CurrencySymbol::BDT, + Self::BHD => CurrencySymbol::BHD, + Self::BIF => CurrencySymbol::BIF, + Self::BMD => CurrencySymbol::BMD, + Self::BND => CurrencySymbol::BND, + Self::BOB => CurrencySymbol::BOB, + Self::BRL => CurrencySymbol::BRL, + Self::BSD => CurrencySymbol::BSD, + Self::BWP => CurrencySymbol::BWP, + Self::BZD => CurrencySymbol::BZD, + Self::CAD => CurrencySymbol::CAD, + Self::CHF => CurrencySymbol::CHF, + Self::CLP => CurrencySymbol::CLP, + Self::COP => CurrencySymbol::COP, + Self::CRC => CurrencySymbol::CRC, + Self::CUP => CurrencySymbol::CUP, + Self::CZK => CurrencySymbol::CZK, + Self::DJF => CurrencySymbol::DJF, + Self::DKK => CurrencySymbol::DKK, + Self::DOP => CurrencySymbol::DOP, + Self::DZD => CurrencySymbol::DZD, + Self::EGP => CurrencySymbol::EGP, + Self::ETB => CurrencySymbol::ETB, + Self::EUR => CurrencySymbol::EUR, + Self::FJD => CurrencySymbol::FJD, + Self::GBP => CurrencySymbol::GBP, + Self::GHS => CurrencySymbol::GHS, + Self::GIP => CurrencySymbol::GIP, + Self::GMD => CurrencySymbol::GMD, + Self::GNF => CurrencySymbol::GNF, + Self::GTQ => CurrencySymbol::GTQ, + Self::GYD => CurrencySymbol::GYD, + Self::HKD => CurrencySymbol::HKD, + Self::HNL => CurrencySymbol::HNL, + Self::HTG => CurrencySymbol::HTG, + Self::HUF => CurrencySymbol::HUF, + Self::HRK => CurrencySymbol::HRK, + Self::IDR => CurrencySymbol::IDR, + Self::ILS => CurrencySymbol::ILS, + Self::INR => CurrencySymbol::INR, + Self::JMD => CurrencySymbol::JMD, + Self::JOD => CurrencySymbol::JOD, + Self::JPY => CurrencySymbol::JPY, + Self::KES => CurrencySymbol::KES, + Self::KGS => CurrencySymbol::KGS, + Self::KHR => CurrencySymbol::KHR, + Self::KMF => CurrencySymbol::KMF, + Self::KRW => CurrencySymbol::KRW, + Self::KWD => CurrencySymbol::KWD, + Self::KYD => CurrencySymbol::KYD, + Self::KZT => CurrencySymbol::KZT, + Self::LAK => CurrencySymbol::LAK, + Self::LBP => CurrencySymbol::LBP, + Self::LKR => CurrencySymbol::LKR, + Self::LRD => CurrencySymbol::LRD, + Self::LSL => CurrencySymbol::LSL, + Self::MAD => CurrencySymbol::MAD, + Self::MDL => CurrencySymbol::MDL, + Self::MGA => CurrencySymbol::MGA, + Self::MKD => CurrencySymbol::MKD, + Self::MMK => CurrencySymbol::MMK, + Self::MNT => CurrencySymbol::MNT, + Self::MOP => CurrencySymbol::MOP, + Self::MUR => CurrencySymbol::MUR, + Self::MVR => CurrencySymbol::MVR, + Self::MWK => CurrencySymbol::MWK, + Self::MXN => CurrencySymbol::MXN, + Self::MYR => CurrencySymbol::MYR, + Self::NAD => CurrencySymbol::NAD, + Self::NGN => CurrencySymbol::NGN, + Self::NIO => CurrencySymbol::NIO, + Self::NOK => CurrencySymbol::NOK, + Self::NPR => CurrencySymbol::NPR, + Self::NZD => CurrencySymbol::NZD, + Self::OMR => CurrencySymbol::OMR, + Self::PEN => CurrencySymbol::PEN, + Self::PGK => CurrencySymbol::PGK, + Self::PHP => CurrencySymbol::PHP, + Self::PKR => CurrencySymbol::PKR, + Self::PLN => CurrencySymbol::PLN, + Self::PYG => CurrencySymbol::PYG, + Self::QAR => CurrencySymbol::QAR, + Self::RON => CurrencySymbol::RON, + Self::CNY => CurrencySymbol::CNY, + Self::RUB => CurrencySymbol::RUB, + Self::RWF => CurrencySymbol::RWF, + Self::SAR => CurrencySymbol::SAR, + Self::SCR => CurrencySymbol::SCR, + Self::SEK => CurrencySymbol::SEK, + Self::SGD => CurrencySymbol::SGD, + Self::SLL => CurrencySymbol::SLL, + Self::SOS => CurrencySymbol::SOS, + Self::SSP => CurrencySymbol::SSP, + Self::SVC => CurrencySymbol::SVC, + Self::SZL => CurrencySymbol::SZL, + Self::THB => CurrencySymbol::THB, + Self::TRY => CurrencySymbol::TRY, + Self::TTD => CurrencySymbol::TTD, + Self::TWD => CurrencySymbol::TWD, + Self::TZS => CurrencySymbol::TZS, + Self::UGX => CurrencySymbol::UGX, + Self::USD => CurrencySymbol::USD, + Self::UYU => CurrencySymbol::UYU, + Self::UZS => CurrencySymbol::UZS, + Self::VND => CurrencySymbol::VND, + Self::VUV => CurrencySymbol::VUV, + Self::XAF => CurrencySymbol::XAF, + Self::XOF => CurrencySymbol::XOF, + Self::XPF => CurrencySymbol::XPF, + Self::YER => CurrencySymbol::YER, + Self::ZAR => CurrencySymbol::ZAR, + } + } +} + +#[derive(Clone, Debug, serde::Serialize)] +pub enum CurrencySymbol { + #[serde(rename = "د.إ")] + AED, + #[serde(rename = "L")] + ALL, + #[serde(rename = "֏")] + AMD, + #[serde(rename = "ƒ")] + ANG, + #[serde(rename = "$")] + ARS, + #[serde(rename = "$")] + AUD, + #[serde(rename = "ƒ")] + AWG, + #[serde(rename = "₼")] + AZN, + #[serde(rename = "$")] + BBD, + #[serde(rename = "৳")] + BDT, + #[serde(rename = ".د.ب")] + BHD, + #[serde(rename = "FBu")] + BIF, + #[serde(rename = "$")] + BMD, + #[serde(rename = "$")] + BND, + #[serde(rename = "Bs.")] + BOB, + #[serde(rename = "R$")] + BRL, + #[serde(rename = "$")] + BSD, + #[serde(rename = "P")] + BWP, + #[serde(rename = "BZ$")] + BZD, + #[serde(rename = "$")] + CAD, + #[serde(rename = "CHF")] + CHF, + #[serde(rename = "$")] + CLP, + #[serde(rename = "¥")] + CNY, + #[serde(rename = "$")] + COP, + #[serde(rename = "₡")] + CRC, + #[serde(rename = "$")] + CUP, + #[serde(rename = "Kč")] + CZK, + #[serde(rename = "Fdj")] + DJF, + #[serde(rename = "kr")] + DKK, + #[serde(rename = "$")] + DOP, + #[serde(rename = "DA")] + DZD, + #[serde(rename = "E£")] + EGP, + #[serde(rename = "Br")] + ETB, + #[serde(rename = "€")] + EUR, + #[serde(rename = "$")] + FJD, + #[serde(rename = "£")] + GBP, + #[serde(rename = "₵")] + GHS, + #[serde(rename = "£")] + GIP, + #[serde(rename = "D")] + GMD, + #[serde(rename = "GNF")] + GNF, + #[serde(rename = "Q")] + GTQ, + #[serde(rename = "$")] + GYD, + #[serde(rename = "$")] + HKD, + #[serde(rename = "L")] + HNL, + #[serde(rename = "kn")] + HRK, + #[serde(rename = "G")] + HTG, + #[serde(rename = "Ft")] + HUF, + #[serde(rename = "Rp")] + IDR, + #[serde(rename = "₪")] + ILS, + #[serde(rename = "₹")] + INR, + #[serde(rename = "$")] + JMD, + #[serde(rename = "JD")] + JOD, + #[serde(rename = "¥")] + JPY, + #[serde(rename = "KSh")] + KES, + #[serde(rename = "som")] + KGS, + #[serde(rename = "៛")] + KHR, + #[serde(rename = "CF")] + KMF, + #[serde(rename = "₩")] + KRW, + #[serde(rename = "KD")] + KWD, + #[serde(rename = "$")] + KYD, + #[serde(rename = "₸")] + KZT, + #[serde(rename = "₭")] + LAK, + #[serde(rename = "£")] + LBP, + #[serde(rename = "Rs")] + LKR, + #[serde(rename = "$")] + LRD, + #[serde(rename = "L")] + LSL, + #[serde(rename = "DH")] + MAD, + #[serde(rename = "MDL")] + MDL, + #[serde(rename = "Ar")] + MGA, + #[serde(rename = "ден")] + MKD, + #[serde(rename = "Ks")] + MMK, + #[serde(rename = "₮")] + MNT, + #[serde(rename = "P")] + MOP, + #[serde(rename = "Rs")] + MUR, + #[serde(rename = "Rf")] + MVR, + #[serde(rename = "MK")] + MWK, + #[serde(rename = "$")] + MXN, + #[serde(rename = "RM")] + MYR, + #[serde(rename = "$")] + NAD, + #[serde(rename = "₦")] + NGN, + #[serde(rename = "C$")] + NIO, + #[serde(rename = "kr")] + NOK, + #[serde(rename = "रू")] + NPR, + #[serde(rename = "$")] + NZD, + #[serde(rename = "ر.ع.")] + OMR, + #[serde(rename = "S/")] + PEN, + #[serde(rename = "K")] + PGK, + #[serde(rename = "₱")] + PHP, + #[serde(rename = "Rs")] + PKR, + #[serde(rename = "zł")] + PLN, + #[serde(rename = "₲")] + PYG, + #[serde(rename = "ر.ق")] + QAR, + #[serde(rename = "lei")] + RON, + #[serde(rename = "₽")] + RUB, + #[serde(rename = "RWF")] + RWF, + #[serde(rename = "ر.س")] + SAR, + #[serde(rename = "Rs")] + SCR, + #[serde(rename = "kr")] + SEK, + #[serde(rename = "$")] + SGD, + #[serde(rename = "Le")] + SLL, + #[serde(rename = "Sh")] + SOS, + #[serde(rename = "£")] + SSP, + #[serde(rename = "$")] + SVC, + #[serde(rename = "L")] // L or E + SZL, + #[serde(rename = "฿")] + THB, + #[serde(rename = "₺")] + TRY, + #[serde(rename = "$")] + TTD, + #[serde(rename = "$")] + TWD, + #[serde(rename = "TSh")] + TZS, + #[serde(rename = "USh")] + UGX, + #[serde(rename = "$")] + USD, + #[serde(rename = "$")] + UYU, + #[serde(rename = "лв")] + UZS, + #[serde(rename = "₫")] + VND, + #[serde(rename = "VT")] + VUV, + #[serde(rename = "FCFA")] + XAF, + #[serde(rename = "CFA")] + XOF, + #[serde(rename = "₣")] + XPF, + #[serde(rename = "ر.ي")] + YER, + #[serde(rename = "R")] + ZAR, } #[derive( diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 2c51fa0c3cbb..687065b88dcd 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -107,6 +107,7 @@ pub async fn intiate_payment_link_flow( let payment_details = api_models::payments::PaymentLinkDetails { amount: payment_intent.amount, currency, + currency_symbol: currency.to_currency_symbol(), payment_id: payment_intent.payment_id, merchant_name: merchant_account.merchant_name, order_details, diff --git a/crates/router/src/core/payment_link/payment_link.html b/crates/router/src/core/payment_link/payment_link.html index 462a11d2567e..65a4215a0a2f 100644 --- a/crates/router/src/core/payment_link/payment_link.html +++ b/crates/router/src/core/payment_link/payment_link.html @@ -234,13 +234,21 @@ background-color: var(--primary-color); box-shadow: 0px 1px 10px #f2f2f2; display: flex; + flex-flow: column; align-items: center; justify-content: center; } #payment-form-wrap { - min-width: 584px; - padding: 50px; + min-width: 300px; + width: 30vw; + padding: 20px; + background-color: white; + border-radius: 3px; + } + + .powered-by-hyper { + margin-top: 20px; } #hyper-checkout-sdk-header { @@ -295,28 +303,13 @@ margin-top: 10px; } - .checkoutButton { - height: 48px; - border-radius: 25px; - width: 100%; - border: transparent; - background: var(--secondary-color); - color: #ffffff; - font-weight: 600; - cursor: pointer; - } - .page-spinner, .page-spinner::before, - .page-spinner::after, - .spinner, - .spinner:before, - .spinner:after { + .page-spinner::after { border-radius: 50%; } - .page-spinner, - .spinner { + .page-spinner { color: #ffffff; font-size: 22px; text-indent: -99999px; @@ -331,9 +324,7 @@ } .page-spinner::before, - .page-spinner::after, - .spinner:before, - .spinner:after { + .page-spinner::after { position: absolute; content: ""; } @@ -405,19 +396,6 @@ } } - .spinner:before { - width: 10.4px; - height: 20.4px; - background: var(--primary-color); - border-radius: 20.4px 0 0 20.4px; - top: -0.2px; - left: -0.2px; - -webkit-transform-origin: 10.4px 10.2px; - transform-origin: 10.4px 10.2px; - -webkit-animation: loading 2s infinite ease 1.5s; - animation: loading 2s infinite ease 1.5s; - } - #payment-message { font-size: 12px; font-weight: 500; @@ -426,19 +404,6 @@ font-family: "Montserrat"; } - .spinner:after { - width: 10.4px; - height: 10.2px; - background: var(--primary-color); - border-radius: 0 10.2px 10.2px 0; - top: -0.1px; - left: 10.2px; - -webkit-transform-origin: 0px 10.2px; - transform-origin: 0px 10.2px; - -webkit-animation: loading 2s infinite ease; - animation: loading 2s infinite ease; - } - #payment-form { max-width: 560px; width: 100%; @@ -447,11 +412,6 @@ } @media only screen and (max-width: 1200px) { - .checkoutButton { - width: 95%; - background-color: var(--primary-color); - } - .hyper-checkout { flex-flow: column; margin: 0; @@ -627,16 +587,16 @@ @@ -700,7 +660,7 @@
-
+
-
+ +
+ + + + + + + + + + + + + + + + +
- - + function showSDK(e) { + if (window.state.isMobileView) { + hide("#hyper-checkout-cart"); + } else { + show("#hyper-checkout-cart"); + } + setPageLoading(true); + checkStatus() + .then((res) => { + if (res.showSdk) { + renderPaymentDetails(); + renderCart(); + renderSDKHeader(); + show("#hyper-checkout-sdk"); + show("#hyper-checkout-details"); + } else { + show("#hyper-checkout-status"); + show("#hyper-footer"); + } + }) + .catch((err) => {}) + .finally(() => { + setPageLoading(false); + }); + } + + window.addEventListener("resize", (event) => { + const currentHeight = window.innerHeight; + const currentWidth = window.innerWidth; + if (currentWidth <= 1200 && window.state.prevWidth > 1200) { + hide("#hyper-checkout-cart"); + } else if (currentWidth > 1200 && window.state.prevWidth <= 1200) { + show("#hyper-checkout-cart"); + } + + window.state.prevHeight = currentHeight; + window.state.prevWidth = currentWidth; + window.state.isMobileView = currentWidth <= 1200; + }); + + From 80996c262992706b218e39350e01e793c44569af Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Mon, 30 Oct 2023 14:59:49 +0530 Subject: [PATCH 05/33] feat(router): added new design along with sdk theme for payment link --- crates/api_models/src/admin.rs | 5 +- crates/api_models/src/payments.rs | 3 +- crates/router/src/core/payment_link.rs | 90 ++++++++++++------- .../src/core/payment_link/payment_link.html | 10 +-- 4 files changed, 65 insertions(+), 43 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index b1a258e6b26c..0b4bbb4859b3 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -529,9 +529,8 @@ pub struct PaymentLinkConfig { #[serde(deny_unknown_fields)] pub struct PaymentLinkColorSchema { - pub primary_color: Option, - pub primary_accent_color: Option, - pub secondary_color: Option, + pub background_primary_color: Option, + pub sdk_theme: Option, } #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 9662fe809713..4772c923d498 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3147,6 +3147,7 @@ pub struct PaymentLinkDetails { pub merchant_logo: String, pub return_url: String, pub merchant_name: crypto::OptionalEncryptableName, - pub order_details: Vec, + pub order_details: Vec, pub max_items_visible_after_collapse: i8, + pub sdk_theme: Option, } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 687065b88dcd..ace62f7bf515 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -1,6 +1,7 @@ -use api_models::admin as admin_types; -use common_utils::ext_traits::AsyncExt; +use api_models::{admin as admin_types, payments::OrderDetailsWithAmount}; +use common_utils::ext_traits::{AsyncExt, ValueExt}; use error_stack::{IntoReport, ResultExt}; +use masking::Secret; use super::errors::{self, RouterResult, StorageErrorExt}; use crate::{ @@ -81,12 +82,7 @@ pub async fn intiate_payment_link_flow( }) .transpose()?; - let order_details = payment_intent - .order_details - .get_required_value("order_details") - .change_context(errors::ApiErrorResponse::MissingRequiredField { - field_name: "order_details", - })?; + let order_details = validate_order_details(payment_intent.order_details)?; let return_url = if let Some(payment_create_return_url) = payment_intent.return_url { payment_create_return_url @@ -104,6 +100,9 @@ pub async fn intiate_payment_link_flow( payment_intent.client_secret, )?; + let (default_sdk_theme, default_background_color) = + ("#7EA8F6".to_string(), "#E5E5E5".to_string()); + let payment_details = api_models::payments::PaymentLinkDetails { amount: payment_intent.amount, currency, @@ -117,13 +116,22 @@ pub async fn intiate_payment_link_flow( client_secret, merchant_logo: payment_link_config .clone() - .map(|pl_metadata| pl_metadata.merchant_logo.unwrap_or_default()) + .map(|pl_config| { + pl_config + .merchant_logo + .unwrap_or("https://i.imgur.com/RfxPFQo.png".to_string()) + }) .unwrap_or_default(), max_items_visible_after_collapse: 3, + sdk_theme: payment_link_config.clone().and_then(|pl_config| { + pl_config + .color_scheme + .map(|color| color.sdk_theme.unwrap_or(default_sdk_theme.clone())) + }), }; let js_script = get_js_script(payment_details)?; - let css_script = get_color_scheme_css(payment_link_config.clone()); + let css_script = get_color_scheme_css(payment_link_config.clone(), default_background_color); let payment_link_data = services::PaymentLinkFormData { js_script, sdk_url: state.conf.payment_link.sdk_url.clone(), @@ -150,38 +158,21 @@ fn get_js_script( fn get_color_scheme_css( payment_link_config: Option, + default_primary_color: String, ) -> String { - let (default_primary_color, default_accent_color, default_secondary_color) = ( - "#C6C7C8".to_string(), - "#6A8EF5".to_string(), - "#0C48F6".to_string(), - ); - - let (primary_color, primary_accent_color, secondary_color) = payment_link_config + let background_primary_color = payment_link_config .and_then(|pl_config| { pl_config.color_scheme.map(|color| { - ( - color.primary_color.unwrap_or(default_primary_color.clone()), - color - .primary_accent_color - .unwrap_or(default_accent_color.clone()), - color - .secondary_color - .unwrap_or(default_secondary_color.clone()), - ) + color + .background_primary_color + .unwrap_or(default_primary_color.clone()) }) }) - .unwrap_or(( - default_primary_color, - default_accent_color, - default_secondary_color, - )); + .unwrap_or(default_primary_color); format!( ":root {{ - --primary-color: {primary_color}; - --primary-accent-color: {primary_accent_color}; - --secondary-color: {secondary_color}; + --primary-color: {background_primary_color}; }}" ) } @@ -204,3 +195,34 @@ fn validate_sdk_requirements( })?; Ok((pub_key, currency, client_secret)) } + +fn validate_order_details( + order_details: Option>>, +) -> Result, error_stack::Report> { + let order_details = order_details + .map(|order_details| { + order_details + .iter() + .map(|data| { + data.to_owned() + .parse_value("OrderDetailsWithAmount") + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "OrderDetailsWithAmount", + }) + .attach_printable("Unable to parse OrderDetailsWithAmount") + }) + .collect::, _>>() + }) + .transpose()?; + + if let Some(mut order_details) = order_details.clone() { + for order in order_details.iter_mut() { + if order.product_img_link.is_none() { + order.product_img_link = Some("https://i.imgur.com/On3VtKF.png".to_string()); + } + } + return Ok(order_details); + } + + Ok(Vec::new()) +} diff --git a/crates/router/src/core/payment_link/payment_link.html b/crates/router/src/core/payment_link/payment_link.html index 65a4215a0a2f..97810d698e8a 100644 --- a/crates/router/src/core/payment_link/payment_link.html +++ b/crates/router/src/core/payment_link/payment_link.html @@ -112,8 +112,8 @@ } #hyper-checkout-merchant-image > img { - height: 48px; - width: 48px; + height: 40px; + width: 40px; } #hyper-checkout-cart-image { @@ -175,8 +175,8 @@ } .hyper-checkout-cart-product-image { - height: 72px; - width: 72px; + height: 56px; + width: 56px; } .hyper-checkout-card-item-name { @@ -825,7 +825,7 @@ var client_secret = paymentDetails.client_secret; const appearance = { variables: { - colorPrimary: "rgb(0, 109, 249)", + colorPrimary: paymentDetails.sdk_theme, fontFamily: "Work Sans, sans-serif", fontSizeBase: "16px", colorText: "rgb(51, 65, 85)", From d507ed0ab0fa6eed3399fde28bce7c66a7e5c7a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 09:47:03 +0000 Subject: [PATCH 06/33] docs(openapi): re-generate OpenAPI specification --- openapi/openapi_spec.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 8b5988dcb47e..bbe3754d32c7 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -7808,15 +7808,11 @@ "PaymentLinkColorSchema": { "type": "object", "properties": { - "primary_color": { + "background_primary_color": { "type": "string", "nullable": true }, - "primary_accent_color": { - "type": "string", - "nullable": true - }, - "secondary_color": { + "sdk_theme": { "type": "string", "nullable": true } From e05614d3e71c67ff150444886ed7e5e80f72c954 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 31 Oct 2023 14:17:56 +0530 Subject: [PATCH 07/33] feat(router): added payment_link_config support for each payment --- crates/api_models/src/payments.rs | 5 ++- crates/diesel_models/src/payment_link.rs | 18 +++------ crates/diesel_models/src/schema.rs | 1 + crates/router/src/core/payment_link.rs | 37 +++++++++++-------- .../payments/operations/payment_create.rs | 6 +++ .../down.sql | 2 + .../up.sql | 2 + 7 files changed, 41 insertions(+), 30 deletions(-) create mode 100644 migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/down.sql create mode 100644 migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/up.sql diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 4772c923d498..a63f61c49a26 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3098,6 +3098,7 @@ pub struct PaymentLinkObject { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub link_expiry: Option, pub merchant_custom_domain_name: Option, + pub payment_link_config: Option, } #[derive(Default, Debug, serde::Deserialize, Clone, ToSchema, serde::Serialize)] @@ -3142,8 +3143,8 @@ pub struct PaymentLinkDetails { pub pub_key: String, pub client_secret: String, pub payment_id: String, - #[serde(with = "common_utils::custom_serde::iso8601")] - pub expiry: PrimitiveDateTime, + #[serde(with = "common_utils::custom_serde::iso8601::option")] + pub expiry: Option, pub merchant_logo: String, pub return_url: String, pub merchant_name: crypto::OptionalEncryptableName, diff --git a/crates/diesel_models/src/payment_link.rs b/crates/diesel_models/src/payment_link.rs index 4b182a8155a5..2fb634c9904c 100644 --- a/crates/diesel_models/src/payment_link.rs +++ b/crates/diesel_models/src/payment_link.rs @@ -4,7 +4,7 @@ use time::PrimitiveDateTime; use crate::{enums as storage_enums, schema::payment_link}; -#[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize)] +#[derive(Clone, Debug, Identifiable, Queryable, Serialize, Deserialize)] #[diesel(table_name = payment_link)] #[diesel(primary_key(payment_link_id))] pub struct PaymentLink { @@ -20,20 +20,11 @@ pub struct PaymentLink { pub last_modified_at: PrimitiveDateTime, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub payment_link_config: Option, } -#[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - Insertable, - serde::Serialize, - serde::Deserialize, - router_derive::DebugAsDisplay, -)] -#[diesel(table_name = payment_link)] +#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize)] +#[diesel(table_name = payment_link, primary_key(payment_link_id))] pub struct PaymentLinkNew { pub payment_link_id: String, pub payment_id: String, @@ -47,4 +38,5 @@ pub struct PaymentLinkNew { pub last_modified_at: Option, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub payment_link_config: Option, } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 6c1b13bacf9d..c9fc904ab8b8 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -651,6 +651,7 @@ diesel::table! { created_at -> Timestamp, last_modified_at -> Timestamp, fulfilment_time -> Nullable, + payment_link_config -> Nullable, } } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index ace62f7bf515..af6fce5763be 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -10,7 +10,6 @@ use crate::{ routes::AppState, services, types::{domain, storage::enums as storage_enums, transformers::ForeignFrom}, - utils::OptionExt, }; pub async fn retrieve_payment_link( @@ -56,31 +55,39 @@ pub async fn intiate_payment_link_flow( "create payment link", )?; - let fulfillment_time = payment_intent + let payment_link = payment_intent .payment_link_id .as_ref() - .async_and_then(|pli| async move { + .async_and_then(|pli| async { db.find_payment_link_by_payment_link_id(pli) .await - .ok()? - .fulfilment_time - .ok_or(errors::ApiErrorResponse::PaymentNotFound) + .to_not_found_response(errors::ApiErrorResponse::PaymentLinkNotFound) .ok() }) .await - .get_required_value("fulfillment_time") - .change_context(errors::ApiErrorResponse::PaymentNotFound)?; + .ok_or(errors::ApiErrorResponse::PaymentLinkNotFound)?; - let payment_link_config = merchant_account - .payment_link_config - .map(|pl_config| { + let payment_link_config = if let Some(pl_config) = payment_link.payment_link_config.clone() { + Some( serde_json::from_value::(pl_config) .into_report() .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "payment_link_config", - }) - }) - .transpose()?; + }), + ) + .transpose()? + } else { + merchant_account + .payment_link_config + .map(|pl_config| { + serde_json::from_value::(pl_config) + .into_report() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "payment_link_config", + }) + }) + .transpose()? + }; let order_details = validate_order_details(payment_intent.order_details)?; @@ -111,7 +118,7 @@ pub async fn intiate_payment_link_flow( merchant_name: merchant_account.merchant_name, order_details, return_url, - expiry: fulfillment_time, + expiry: payment_link.fulfilment_time, pub_key, client_secret, merchant_logo: payment_link_config diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 2d31f82aeb00..71941dfe16ff 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -803,6 +803,11 @@ async fn create_payment_link( merchant_id.clone(), payment_id.clone() ); + + let payment_link_config = payment_link_object.payment_link_config.map(|pl_config|{ + common_utils::ext_traits::Encode::::encode_to_value(&pl_config) + }).transpose().change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "payment_link_config" })?; + let payment_link_req = storage::PaymentLinkNew { payment_link_id: payment_link_id.clone(), payment_id: payment_id.clone(), @@ -813,6 +818,7 @@ async fn create_payment_link( created_at, last_modified_at, fulfilment_time: payment_link_object.link_expiry, + payment_link_config, }; let payment_link_db = db .insert_payment_link(payment_link_req) diff --git a/migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/down.sql b/migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/down.sql new file mode 100644 index 000000000000..a96b1df98606 --- /dev/null +++ b/migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_link DROP COLUMN payment_link_config; \ No newline at end of file diff --git a/migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/up.sql b/migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/up.sql new file mode 100644 index 000000000000..31efd6ae4557 --- /dev/null +++ b/migrations/2023-10-31-070509_add_payment_link_config_in_payment_link_db/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE payment_link ADD COLUMN payment_link_config JSONB NULL; From 4f501aa6c232cd33672071630177570c1a9e4127 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 31 Oct 2023 16:33:36 +0530 Subject: [PATCH 08/33] refactor(router): addressed pr comments --- crates/router/src/core/payment_link.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 3a37abfe317d..0012efc86c9f 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -1,5 +1,4 @@ use api_models::admin as admin_types; -use common_utils::ext_traits::AsyncExt; use error_stack::{IntoReport, ResultExt}; use masking::PeekInterface; @@ -44,6 +43,11 @@ pub async fn intiate_payment_link_flow( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + let payment_link_id = payment_intent + .payment_link_id + .get_required_value("payment_link_id") + .change_context(errors::ApiErrorResponse::PaymentLinkNotFound)?; + helpers::validate_payment_status_against_not_allowed_statuses( &payment_intent.status, &[ @@ -56,17 +60,10 @@ pub async fn intiate_payment_link_flow( "create payment link", )?; - let payment_link = payment_intent - .payment_link_id - .as_ref() - .async_and_then(|pli| async { - db.find_payment_link_by_payment_link_id(pli) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentLinkNotFound) - .ok() - }) + let payment_link = db + .find_payment_link_by_payment_link_id(&payment_link_id) .await - .ok_or(errors::ApiErrorResponse::PaymentLinkNotFound)?; + .to_not_found_response(errors::ApiErrorResponse::PaymentLinkNotFound)?; let payment_link_config = merchant_account .payment_link_config From 4c37414bd8c6b91527bb2684f3c48ebbaa913550 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Mon, 6 Nov 2023 12:21:15 +0530 Subject: [PATCH 09/33] feat(payment_link): added payment link list support --- crates/api_models/src/mandates.rs | 2 +- crates/api_models/src/payments.rs | 67 +++++++++++++++++++ crates/common_utils/src/consts.rs | 3 + .../diesel_models/src/query/payment_link.rs | 2 +- crates/router/src/core/payment_link.rs | 19 +++++- crates/router/src/core/payments/helpers.rs | 21 ++++++ crates/router/src/db/payment_link.rs | 31 ++++++++- crates/router/src/routes/app.rs | 14 +++- crates/router/src/routes/lock_utils.rs | 6 +- crates/router/src/routes/payment_link.rs | 26 +++++++ crates/router/src/types/api.rs | 3 +- crates/router/src/types/api/payment_link.rs | 21 ++++++ .../router/src/types/storage/payment_link.rs | 64 +++++++++++++++++- crates/router_env/src/logger/types.rs | 2 + 14 files changed, 271 insertions(+), 10 deletions(-) create mode 100644 crates/router/src/types/api/payment_link.rs diff --git a/crates/api_models/src/mandates.rs b/crates/api_models/src/mandates.rs index 035f7adec9f7..34f37c0d62a1 100644 --- a/crates/api_models/src/mandates.rs +++ b/crates/api_models/src/mandates.rs @@ -90,4 +90,4 @@ pub struct MandateListConstraints { #[schema(example = "2022-09-10T10:11:12Z")] #[serde(rename = "created_time.gte")] pub created_time_gte: Option, -} +} \ No newline at end of file diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index f9cb21dae5f2..ceadd356ed5b 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3149,3 +3149,70 @@ pub struct PaymentLinkDetails { pub order_details: Vec, pub max_items_visible_after_collapse: i8, } + + +#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] +#[serde(deny_unknown_fields)] + +pub struct PaymentLinkListConstraints { + /// The identifier for customer + #[schema(example = "cus_meowuwunwiuwiwqw")] + pub customer_id: Option, + + /// A cursor for use in pagination, fetch the next list after some object + #[schema(example = "pay_fafa124123")] + pub starting_after: Option, + + /// A cursor for use in pagination, fetch the previous list before some object + #[schema(example = "pay_fafa124123")] + pub ending_before: Option, + + /// limit on the number of objects to return + pub limit: Option, + + /// The time at which payment is created + #[schema(example = "2022-09-10T10:11:12Z")] + #[serde(default, with = "common_utils::custom_serde::iso8601::option")] + pub created: Option, + + /// Time less than the payment created time + #[schema(example = "2022-09-10T10:11:12Z")] + #[serde( + default, + with = "common_utils::custom_serde::iso8601::option", + rename = "created.lt" + )] + pub created_lt: Option, + + /// Time greater than the payment created time + #[schema(example = "2022-09-10T10:11:12Z")] + #[serde( + default, + with = "common_utils::custom_serde::iso8601::option", + rename = "created.gt" + )] + pub created_gt: Option, + + /// Time less than or equals to the payment created time + #[schema(example = "2022-09-10T10:11:12Z")] + #[serde( + default, + with = "common_utils::custom_serde::iso8601::option", + rename = "created.lte" + )] + pub created_lte: Option, + + /// Time greater than or equals to the payment created time + #[schema(example = "2022-09-10T10:11:12Z")] + #[serde(default, with = "common_utils::custom_serde::iso8601::option")] + #[serde(rename = "created.gte")] + pub created_gte: Option, +} + +#[derive(Clone, Debug, serde::Serialize, ToSchema)] +pub struct PaymentLinkListResponse { + /// The number of payment links included in the list + pub size: usize, + // The list of payment link response objects + pub data: Vec +} \ No newline at end of file diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 2f517295ae48..eb476f805739 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -24,6 +24,9 @@ pub const PAYMENTS_LIST_MAX_LIMIT_V1: u32 = 100; /// Maximum limit for payments list post api with filters pub const PAYMENTS_LIST_MAX_LIMIT_V2: u32 = 20; +/// Maximum limit for payment link list get api +pub const PAYMENTS_LINK_LIST_LIMIT: u32 = 100; + /// surcharge percentage maximum precision length pub const SURCHARGE_PERCENTAGE_PRECISION_LENGTH: u8 = 2; diff --git a/crates/diesel_models/src/query/payment_link.rs b/crates/diesel_models/src/query/payment_link.rs index 085257633c65..1276a653401e 100644 --- a/crates/diesel_models/src/query/payment_link.rs +++ b/crates/diesel_models/src/query/payment_link.rs @@ -1,6 +1,5 @@ use diesel::{associations::HasTable, ExpressionMethods}; use router_env::{instrument, tracing}; - use super::generics; use crate::{ payment_link::{PaymentLink, PaymentLinkNew}, @@ -26,4 +25,5 @@ impl PaymentLink { ) .await } + } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 2c51fa0c3cbb..2fda7dd8beec 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -1,6 +1,7 @@ use api_models::admin as admin_types; use common_utils::ext_traits::AsyncExt; use error_stack::{IntoReport, ResultExt}; +use futures::future; use super::errors::{self, RouterResult, StorageErrorExt}; use crate::{ @@ -8,7 +9,7 @@ use crate::{ errors::RouterResponse, routes::AppState, services, - types::{domain, storage::enums as storage_enums, transformers::ForeignFrom}, + types::{domain, transformers::ForeignFrom, api::payment_link::PaymentLinkResponseExt, storage::{enums as storage_enums}}, utils::OptionExt, }; @@ -203,3 +204,19 @@ fn validate_sdk_requirements( })?; Ok((pub_key, currency, client_secret)) } + +pub async fn list_payment_link( + state: AppState, + merchant: domain::MerchantAccount, + constraints: api_models::payments::PaymentLinkListConstraints, +) -> RouterResponse> { + // helpers::validate_payment_link_list_request(&constraints)?; + let db = state.store.as_ref(); + let payment_link = db.find_payment_link_by_merchant_id(&merchant.merchant_id, constraints) + .await.change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to retrieve payment link")?; + + let payment_link_list = future::try_join_all(payment_link.into_iter().map(|payment_link| api_models::payments::PaymentLinkResponse::from_db_payment_link(payment_link))).await?; + Ok(services::ApplicationResponse::Json(payment_link_list)) + +} diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 4deac54c7010..639ab44adc4d 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3476,3 +3476,24 @@ pub fn validate_payment_link_request( } Ok(()) } + + +// #[cfg(feature = "olap")] +// pub fn validate_payment_link_list_request( +// req: &api_models::payments::PaymentLinkListConstraints, +// ) -> CustomResult<(), errors::ApiErrorResponse> { +// use common_utils::consts::PAYMENTS_LINK_LIST_LIMIT; + +// utils::when( +// req.limit > PAYMENTS_LINK_LIST_LIMIT || req.limit < 1, +// || { +// Err(errors::ApiErrorResponse::InvalidRequestData { +// message: format!( +// "limit should be in between 1 and {}", +// PAYMENTS_LINK_LIST_LIMIT +// ), +// }) +// }, +// )?; +// Ok(()) +// } \ No newline at end of file diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index 38b59b1d60de..ccd18b45d5d5 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -1,10 +1,11 @@ use error_stack::IntoReport; +#[cfg(feature = "olap")] use super::{MockDb, Store}; use crate::{ connection, core::errors::{self, CustomResult}, - types::storage, + types::storage::{self, PaymentLinkDbExt}, }; #[async_trait::async_trait] @@ -18,6 +19,13 @@ pub trait PaymentLinkInterface { &self, _payment_link: storage::PaymentLinkNew, ) -> CustomResult; + + #[cfg(feature = "olap")] + async fn find_payment_link_by_merchant_id( + &self, + merchant_id: &str, + payment_link_contraints: api_models::payments::PaymentLinkListConstraints + ) -> CustomResult, errors::StorageError>; } #[async_trait::async_trait] @@ -44,6 +52,18 @@ impl PaymentLinkInterface for Store { .map_err(Into::into) .into_report() } + + #[cfg(feature = "olap")] + async fn find_payment_link_by_merchant_id( + &self, + merchant_id: &str, + payment_link_contraints: api_models::payments::PaymentLinkListConstraints, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + storage::PaymentLink::filter_by_constraints(&conn, merchant_id, payment_link_contraints) + .await.map_err(Into::into) + .into_report() + } } #[async_trait::async_trait] @@ -63,4 +83,13 @@ impl PaymentLinkInterface for MockDb { // TODO: Implement function for `MockDb`x Err(errors::StorageError::MockDbError)? } + + async fn find_payment_link_by_merchant_id( + &self, + _merchant_id: &str, + _payment_link_contraints: api_models::payments::PaymentLinkListConstraints, + ) -> CustomResult, errors::StorageError>{ + // TODO: Implement function for `MockDb`x + Err(errors::StorageError::MockDbError)? + } } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 5b16e93404ae..8cee18fbf441 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -592,8 +592,18 @@ pub struct PaymentLink; impl PaymentLink { pub fn server(state: AppState) -> Scope { - web::scope("/payment_link") - .app_data(web::Data::new(state)) + let mut route = web::scope("/payment_link") + .app_data(web::Data::new(state)); + #[cfg(feature = "olap")] + { + route = route + .service( + web::resource("/list") + .route(web::get().to(payments_link_list)) + ); + // .service(web::resource("/filter").route(web::post().to(get_filters_for_payment_link))) + } + route .service( web::resource("/{payment_link_id}").route(web::get().to(payment_link_retrieve)), ) diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 5be361098bcc..020c2946cd8d 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -114,9 +114,11 @@ impl From for ApiIdentifier { | Flow::BusinessProfileDelete | Flow::BusinessProfileList => Self::Business, - Flow::Verification => Self::Verification, + Flow::PaymentLinkRetrieve + | Flow::PaymentLinkInitiate + | Flow::PaymentLinkList => Self::PaymentLink, - Flow::PaymentLinkInitiate | Flow::PaymentLinkRetrieve => Self::PaymentLink, + Flow::Verification => Self::Verification, } } } diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index b664ee4429d4..13d4c9d2ccfb 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -80,3 +80,29 @@ pub async fn initiate_payment_link( ) .await } + + +pub async fn payments_link_list( + state: web::Data, + req: actix_web::HttpRequest, + payload: web::Query +) -> impl Responder { + let flow = Flow::PaymentLinkList; + let payload = payload.into_inner(); + api::server_wrap( + flow, + state, + &req, + payload, + |state, auth, req| { + list_payment_link( + state, + auth.merchant_account, + req, + ) + }, + auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), + api_locking::LockAction::NotApplicable, + ) + .await +} \ No newline at end of file diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 00e40cc908b3..a9995ea64b9f 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -12,6 +12,7 @@ pub mod payments; pub mod payouts; pub mod refunds; pub mod webhooks; +pub mod payment_link; use std::{fmt::Debug, str::FromStr}; @@ -19,7 +20,7 @@ use error_stack::{report, IntoReport, ResultExt}; pub use self::{ admin::*, api_keys::*, configs::*, customers::*, disputes::*, files::*, payment_methods::*, - payments::*, payouts::*, refunds::*, webhooks::*, + payments::*, payouts::*, refunds::*, webhooks::*, payment_link::*, }; use super::ErrorResponse; use crate::{ diff --git a/crates/router/src/types/api/payment_link.rs b/crates/router/src/types/api/payment_link.rs new file mode 100644 index 000000000000..5c786501bc6f --- /dev/null +++ b/crates/router/src/types/api/payment_link.rs @@ -0,0 +1,21 @@ +pub use api_models::payments::PaymentLinkResponse; + +use crate::{ + core::errors::RouterResult, + types::storage::{self}, +}; + +#[async_trait::async_trait] +pub(crate) trait PaymentLinkResponseExt: Sized { + async fn from_db_payment_link(payment_link: storage::PaymentLink) -> RouterResult; +} + +#[async_trait::async_trait] +impl PaymentLinkResponseExt for PaymentLinkResponse { + async fn from_db_payment_link(payment_link: storage::PaymentLink) -> RouterResult { + Ok(Self { + link: payment_link.link_to_pay, + payment_link_id: payment_link.payment_link_id, + }) + } +} \ No newline at end of file diff --git a/crates/router/src/types/storage/payment_link.rs b/crates/router/src/types/storage/payment_link.rs index 1fa2465e5131..b2626b152b74 100644 --- a/crates/router/src/types/storage/payment_link.rs +++ b/crates/router/src/types/storage/payment_link.rs @@ -1 +1,63 @@ -pub use diesel_models::payment_link::{PaymentLink, PaymentLinkNew}; +use async_bb8_diesel::AsyncRunQueryDsl; +pub use diesel_models::{ + payment_link::{PaymentLink, PaymentLinkNew}, + schema::payment_link::dsl, +}; + +use diesel::{associations::HasTable, ExpressionMethods, QueryDsl}; + +use crate::{connection::PgPooledConn, logger, core::errors::{self, CustomResult}}; +use error_stack::{IntoReport, ResultExt}; +#[async_trait::async_trait] + +pub trait PaymentLinkDbExt: Sized { + async fn filter_by_constraints( + conn: &PgPooledConn, + merchant_id: &str, + payment_link_list_constraints: api_models::payments::PaymentLinkListConstraints, + ) -> CustomResult, errors::DatabaseError>; +} + +#[async_trait::async_trait] +impl PaymentLinkDbExt for PaymentLink { + async fn filter_by_constraints( + conn: &PgPooledConn, + merchant_id: &str, + payment_link_list_constraints: api_models::payments::PaymentLinkListConstraints, + ) -> CustomResult, errors::DatabaseError> { + let mut filter = ::table() + .filter(dsl::merchant_id.eq(merchant_id.to_owned())) + .order(dsl::created_at.desc()) + .into_boxed(); + + if let Some(created_time) = payment_link_list_constraints.created { + filter = filter.filter(dsl::created_at.eq(created_time)); + } + if let Some(created_time_lt) = payment_link_list_constraints.created_lt { + filter = filter.filter(dsl::created_at.lt(created_time_lt)); + } + if let Some(created_time_gt) = payment_link_list_constraints.created_gt { + filter = filter.filter(dsl::created_at.gt(created_time_gt)); + } + if let Some(created_time_lte) = payment_link_list_constraints.created_lte { + filter = filter.filter(dsl::created_at.le(created_time_lte)); + } + if let Some(created_time_gte) = payment_link_list_constraints.created_gte { + filter = filter.filter(dsl::created_at.ge(created_time_gte)); + } + if let Some(limit) = payment_link_list_constraints.limit { + filter = filter.limit(limit); + } + + logger::debug!(query = %diesel::debug_query::(&filter).to_string()); + + filter + .get_results_async(conn) + .await + .into_report() + // The query built here returns an empty Vec when no records are found, and if any error does occur, + // it would be an internal database error, due to which we are raising a DatabaseError::Unknown error + .change_context(errors::DatabaseError::Others) + .attach_printable("Error filtering payment link by specified constraints") + } +} \ No newline at end of file diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index d63ddce58f30..527403890b11 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -203,6 +203,8 @@ pub enum Flow { PaymentLinkRetrieve, /// payment Link Initiate flow PaymentLinkInitiate, + /// Payment Link List flow + PaymentLinkList, /// Create a business profile BusinessProfileCreate, /// Update a business profile From 77cd091140377f20d4be21e0b0b8e6f9fedaf9d2 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Mon, 6 Nov 2023 13:22:17 +0530 Subject: [PATCH 10/33] feat(router): added status support to list payment link --- crates/api_models/src/mandates.rs | 2 +- crates/api_models/src/payments.rs | 13 +++--- crates/diesel_models/src/payment_link.rs | 2 + .../diesel_models/src/query/payment_link.rs | 2 +- crates/diesel_models/src/schema.rs | 2 + crates/router/src/core/payment_link.rs | 42 ++++++++++++++----- crates/router/src/core/payments/helpers.rs | 3 +- .../payments/operations/payment_create.rs | 3 ++ crates/router/src/db/payment_link.rs | 11 ++--- crates/router/src/routes/app.rs | 18 +++----- crates/router/src/routes/lock_utils.rs | 6 +-- crates/router/src/routes/payment_link.rs | 13 ++---- crates/router/src/types/api.rs | 6 +-- crates/router/src/types/api/payment_link.rs | 16 +++++-- .../router/src/types/storage/payment_link.rs | 13 +++--- crates/router/src/types/transformers.rs | 10 ++--- .../down.sql | 2 + .../up.sql | 2 + 18 files changed, 97 insertions(+), 69 deletions(-) create mode 100644 migrations/2023-11-06-065213_add_description_to_payment_link/down.sql create mode 100644 migrations/2023-11-06-065213_add_description_to_payment_link/up.sql diff --git a/crates/api_models/src/mandates.rs b/crates/api_models/src/mandates.rs index 34f37c0d62a1..035f7adec9f7 100644 --- a/crates/api_models/src/mandates.rs +++ b/crates/api_models/src/mandates.rs @@ -90,4 +90,4 @@ pub struct MandateListConstraints { #[schema(example = "2022-09-10T10:11:12Z")] #[serde(rename = "created_time.gte")] pub created_time_gte: Option, -} \ No newline at end of file +} diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ceadd356ed5b..1d99d18a69e5 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3111,21 +3111,19 @@ pub struct PaymentLinkResponse { pub payment_link_id: String, } + #[derive(Clone, Debug, serde::Serialize, ToSchema)] pub struct RetrievePaymentLinkResponse { pub payment_link_id: String, - pub payment_id: String, pub merchant_id: String, pub link_to_pay: String, pub amount: i64, - #[schema(value_type = Option, example = "USD")] - pub currency: Option, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, - #[serde(with = "common_utils::custom_serde::iso8601")] - pub last_modified_at: PrimitiveDateTime, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub link_expiry: Option, + pub description: Option, + pub status: String } #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] @@ -3150,7 +3148,6 @@ pub struct PaymentLinkDetails { pub max_items_visible_after_collapse: i8, } - #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] #[serde(deny_unknown_fields)] @@ -3214,5 +3211,5 @@ pub struct PaymentLinkListResponse { /// The number of payment links included in the list pub size: usize, // The list of payment link response objects - pub data: Vec -} \ No newline at end of file + pub data: Vec, +} diff --git a/crates/diesel_models/src/payment_link.rs b/crates/diesel_models/src/payment_link.rs index 4b182a8155a5..0db40c2de559 100644 --- a/crates/diesel_models/src/payment_link.rs +++ b/crates/diesel_models/src/payment_link.rs @@ -20,6 +20,7 @@ pub struct PaymentLink { pub last_modified_at: PrimitiveDateTime, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub description: Option } #[derive( @@ -47,4 +48,5 @@ pub struct PaymentLinkNew { pub last_modified_at: Option, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub description: Option } diff --git a/crates/diesel_models/src/query/payment_link.rs b/crates/diesel_models/src/query/payment_link.rs index 1276a653401e..085257633c65 100644 --- a/crates/diesel_models/src/query/payment_link.rs +++ b/crates/diesel_models/src/query/payment_link.rs @@ -1,5 +1,6 @@ use diesel::{associations::HasTable, ExpressionMethods}; use router_env::{instrument, tracing}; + use super::generics; use crate::{ payment_link::{PaymentLink, PaymentLinkNew}, @@ -25,5 +26,4 @@ impl PaymentLink { ) .await } - } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 6c1b13bacf9d..4d09044e5a7a 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -651,6 +651,8 @@ diesel::table! { created_at -> Timestamp, last_modified_at -> Timestamp, fulfilment_time -> Nullable, + #[max_length = 255] + description -> Nullable, } } diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 2fda7dd8beec..2a998cb3b0c3 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -2,6 +2,7 @@ use api_models::admin as admin_types; use common_utils::ext_traits::AsyncExt; use error_stack::{IntoReport, ResultExt}; use futures::future; +use time::PrimitiveDateTime; use super::errors::{self, RouterResult, StorageErrorExt}; use crate::{ @@ -9,7 +10,10 @@ use crate::{ errors::RouterResponse, routes::AppState, services, - types::{domain, transformers::ForeignFrom, api::payment_link::PaymentLinkResponseExt, storage::{enums as storage_enums}}, + types::{ + api::payment_link::PaymentLinkResponseExt, domain, storage::enums as storage_enums, + transformers::ForeignFrom, + }, utils::OptionExt, }; @@ -17,14 +21,16 @@ pub async fn retrieve_payment_link( state: AppState, payment_link_id: String, ) -> RouterResponse { - let db = &*state.store; + let db = &*state.store; let payment_link_object = db .find_payment_link_by_payment_link_id(&payment_link_id) .await .to_not_found_response(errors::ApiErrorResponse::PaymentLinkNotFound)?; + let status = check_payment_link_status(payment_link_object.fulfilment_time); + let response = - api_models::payments::RetrievePaymentLinkResponse::foreign_from(payment_link_object); + api_models::payments::RetrievePaymentLinkResponse::foreign_from((payment_link_object, status)); Ok(services::ApplicationResponse::Json(response)) } @@ -209,14 +215,30 @@ pub async fn list_payment_link( state: AppState, merchant: domain::MerchantAccount, constraints: api_models::payments::PaymentLinkListConstraints, -) -> RouterResponse> { +) -> RouterResponse> { // helpers::validate_payment_link_list_request(&constraints)?; let db = state.store.as_ref(); - let payment_link = db.find_payment_link_by_merchant_id(&merchant.merchant_id, constraints) - .await.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to retrieve payment link")?; - - let payment_link_list = future::try_join_all(payment_link.into_iter().map(|payment_link| api_models::payments::PaymentLinkResponse::from_db_payment_link(payment_link))).await?; + let payment_link = db + .find_payment_link_by_merchant_id(&merchant.merchant_id, constraints) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to retrieve payment link")?; + let payment_link_list = future::try_join_all(payment_link.into_iter().map(|payment_link| { + api_models::payments::RetrievePaymentLinkResponse::from_db_payment_link(payment_link) + })) + .await?; Ok(services::ApplicationResponse::Json(payment_link_list)) - } + + +pub fn check_payment_link_status( + fulfillment_time: Option +) -> String { + let curr_time = Some(common_utils::date_time::now()); + + if curr_time > fulfillment_time{ + return "expired".to_string() + }else{ + return "active".to_string() + }; +} \ No newline at end of file diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 639ab44adc4d..4a95aa8497de 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3477,7 +3477,6 @@ pub fn validate_payment_link_request( Ok(()) } - // #[cfg(feature = "olap")] // pub fn validate_payment_link_list_request( // req: &api_models::payments::PaymentLinkListConstraints, @@ -3496,4 +3495,4 @@ pub fn validate_payment_link_request( // }, // )?; // Ok(()) -// } \ No newline at end of file +// } diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 2d31f82aeb00..cfc19835caca 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -79,6 +79,7 @@ impl db, state, amount, + request.description.clone(), ) .await? } else { @@ -788,6 +789,7 @@ async fn create_payment_link( db: &dyn StorageInterface, state: &AppState, amount: api::Amount, + description: Option ) -> RouterResult> { let created_at @ last_modified_at = Some(common_utils::date_time::now()); let domain = if let Some(domain_name) = payment_link_object.merchant_custom_domain_name { @@ -813,6 +815,7 @@ async fn create_payment_link( created_at, last_modified_at, fulfilment_time: payment_link_object.link_expiry, + description, }; let payment_link_db = db .insert_payment_link(payment_link_req) diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index ccd18b45d5d5..adaef6bb90b0 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -1,6 +1,6 @@ use error_stack::IntoReport; -#[cfg(feature = "olap")] +#[cfg(feature = "olap")] use super::{MockDb, Store}; use crate::{ connection, @@ -24,7 +24,7 @@ pub trait PaymentLinkInterface { async fn find_payment_link_by_merchant_id( &self, merchant_id: &str, - payment_link_contraints: api_models::payments::PaymentLinkListConstraints + payment_link_contraints: api_models::payments::PaymentLinkListConstraints, ) -> CustomResult, errors::StorageError>; } @@ -61,8 +61,9 @@ impl PaymentLinkInterface for Store { ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage::PaymentLink::filter_by_constraints(&conn, merchant_id, payment_link_contraints) - .await.map_err(Into::into) - .into_report() + .await + .map_err(Into::into) + .into_report() } } @@ -88,7 +89,7 @@ impl PaymentLinkInterface for MockDb { &self, _merchant_id: &str, _payment_link_contraints: api_models::payments::PaymentLinkListConstraints, - ) -> CustomResult, errors::StorageError>{ + ) -> CustomResult, errors::StorageError> { // TODO: Implement function for `MockDb`x Err(errors::StorageError::MockDbError)? } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 8cee18fbf441..091a2d7286fa 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -592,18 +592,12 @@ pub struct PaymentLink; impl PaymentLink { pub fn server(state: AppState) -> Scope { - let mut route = web::scope("/payment_link") - .app_data(web::Data::new(state)); - #[cfg(feature = "olap")] - { - route = route - .service( - web::resource("/list") - .route(web::get().to(payments_link_list)) - ); - // .service(web::resource("/filter").route(web::post().to(get_filters_for_payment_link))) - } - route + let mut route = web::scope("/payment_link").app_data(web::Data::new(state)); + #[cfg(feature = "olap")] + { + route = route.service(web::resource("/list").route(web::get().to(payments_link_list))); + } + route .service( web::resource("/{payment_link_id}").route(web::get().to(payment_link_retrieve)), ) diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 020c2946cd8d..5895c05f4e72 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -114,9 +114,9 @@ impl From for ApiIdentifier { | Flow::BusinessProfileDelete | Flow::BusinessProfileList => Self::Business, - Flow::PaymentLinkRetrieve - | Flow::PaymentLinkInitiate - | Flow::PaymentLinkList => Self::PaymentLink, + Flow::PaymentLinkRetrieve | Flow::PaymentLinkInitiate | Flow::PaymentLinkList => { + Self::PaymentLink + } Flow::Verification => Self::Verification, } diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index 13d4c9d2ccfb..66e175802fe8 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -81,11 +81,10 @@ pub async fn initiate_payment_link( .await } - pub async fn payments_link_list( state: web::Data, req: actix_web::HttpRequest, - payload: web::Query + payload: web::Query, ) -> impl Responder { let flow = Flow::PaymentLinkList; let payload = payload.into_inner(); @@ -94,15 +93,9 @@ pub async fn payments_link_list( state, &req, payload, - |state, auth, req| { - list_payment_link( - state, - auth.merchant_account, - req, - ) - }, + |state, auth, req| list_payment_link(state, auth.merchant_account, req), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) .await -} \ No newline at end of file +} diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index a9995ea64b9f..e8ffc157611e 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -7,20 +7,20 @@ pub mod enums; pub mod ephemeral_key; pub mod files; pub mod mandates; +pub mod payment_link; pub mod payment_methods; pub mod payments; pub mod payouts; pub mod refunds; pub mod webhooks; -pub mod payment_link; use std::{fmt::Debug, str::FromStr}; use error_stack::{report, IntoReport, ResultExt}; pub use self::{ - admin::*, api_keys::*, configs::*, customers::*, disputes::*, files::*, payment_methods::*, - payments::*, payouts::*, refunds::*, webhooks::*, payment_link::*, + admin::*, api_keys::*, configs::*, customers::*, disputes::*, files::*, payment_link::*, + payment_methods::*, payments::*, payouts::*, refunds::*, webhooks::*, }; use super::ErrorResponse; use crate::{ diff --git a/crates/router/src/types/api/payment_link.rs b/crates/router/src/types/api/payment_link.rs index 5c786501bc6f..be186e9fd1b8 100644 --- a/crates/router/src/types/api/payment_link.rs +++ b/crates/router/src/types/api/payment_link.rs @@ -1,8 +1,9 @@ -pub use api_models::payments::PaymentLinkResponse; +pub use api_models::payments::RetrievePaymentLinkResponse; use crate::{ core::errors::RouterResult, types::storage::{self}, + core::payment_link }; #[async_trait::async_trait] @@ -11,11 +12,18 @@ pub(crate) trait PaymentLinkResponseExt: Sized { } #[async_trait::async_trait] -impl PaymentLinkResponseExt for PaymentLinkResponse { +impl PaymentLinkResponseExt for RetrievePaymentLinkResponse { async fn from_db_payment_link(payment_link: storage::PaymentLink) -> RouterResult { + let status = payment_link::check_payment_link_status(payment_link.fulfilment_time); Ok(Self { - link: payment_link.link_to_pay, + link_to_pay: payment_link.link_to_pay, payment_link_id: payment_link.payment_link_id, + amount: payment_link.amount, + description: payment_link.description, + created_at: payment_link.created_at, + merchant_id: payment_link.merchant_id, + link_expiry: payment_link.fulfilment_time, + status }) } -} \ No newline at end of file +} diff --git a/crates/router/src/types/storage/payment_link.rs b/crates/router/src/types/storage/payment_link.rs index b2626b152b74..4dd9e06b4b41 100644 --- a/crates/router/src/types/storage/payment_link.rs +++ b/crates/router/src/types/storage/payment_link.rs @@ -1,13 +1,16 @@ use async_bb8_diesel::AsyncRunQueryDsl; +use diesel::{associations::HasTable, ExpressionMethods, QueryDsl}; pub use diesel_models::{ payment_link::{PaymentLink, PaymentLinkNew}, schema::payment_link::dsl, }; - -use diesel::{associations::HasTable, ExpressionMethods, QueryDsl}; - -use crate::{connection::PgPooledConn, logger, core::errors::{self, CustomResult}}; use error_stack::{IntoReport, ResultExt}; + +use crate::{ + connection::PgPooledConn, + core::errors::{self, CustomResult}, + logger, +}; #[async_trait::async_trait] pub trait PaymentLinkDbExt: Sized { @@ -60,4 +63,4 @@ impl PaymentLinkDbExt for PaymentLink { .change_context(errors::DatabaseError::Others) .attach_printable("Error filtering payment link by specified constraints") } -} \ No newline at end of file +} diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 31ec31cfa988..4a4e70fe5a19 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -830,22 +830,22 @@ impl } } -impl ForeignFrom for api_models::payments::RetrievePaymentLinkResponse { - fn foreign_from(payment_link_object: storage::PaymentLink) -> Self { +impl ForeignFrom<(storage::PaymentLink, String)> for api_models::payments::RetrievePaymentLinkResponse { + fn foreign_from((payment_link_object, status): (storage::PaymentLink, String)) -> Self { Self { payment_link_id: payment_link_object.payment_link_id, - payment_id: payment_link_object.payment_id, merchant_id: payment_link_object.merchant_id, link_to_pay: payment_link_object.link_to_pay, amount: payment_link_object.amount, - currency: payment_link_object.currency, created_at: payment_link_object.created_at, - last_modified_at: payment_link_object.last_modified_at, link_expiry: payment_link_object.fulfilment_time, + description: payment_link_object.description, + status, } } } + impl From for payments::AddressDetails { fn from(addr: domain::Address) -> Self { Self { diff --git a/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql b/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql new file mode 100644 index 000000000000..c36daebdcc3a --- /dev/null +++ b/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_link DROP COLUMN description; \ No newline at end of file diff --git a/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql b/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql new file mode 100644 index 000000000000..76f89ad04a37 --- /dev/null +++ b/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER table payment_link ADD COLUMN description VARCHAR (255); \ No newline at end of file From 5efb34ca779f0632eb02afbc6424be192f39fff7 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Mon, 6 Nov 2023 16:30:32 +0530 Subject: [PATCH 11/33] refactor(router): addressed pr comments --- crates/api_models/src/payments.rs | 1 - crates/common_enums/src/enums.rs | 369 ------------------ crates/router/src/core/payment_link.rs | 1 - .../src/core/payment_link/payment_link.html | 16 +- 4 files changed, 8 insertions(+), 379 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 1d8f7a05c5fa..2f5cd99be445 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3140,7 +3140,6 @@ pub struct PaymentLinkInitiateRequest { pub struct PaymentLinkDetails { pub amount: i64, pub currency: api_enums::Currency, - pub currency_symbol: api_enums::CurrencySymbol, pub pub_key: String, pub client_secret: String, pub payment_id: String, diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 0822d1c0432c..f0386fc2f42e 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -773,375 +773,6 @@ impl Currency { | Self::ZAR => false, } } - - pub fn to_currency_symbol(&self) -> CurrencySymbol { - match self { - Self::AED => CurrencySymbol::AED, - Self::ALL => CurrencySymbol::ALL, - Self::AMD => CurrencySymbol::AMD, - Self::ANG => CurrencySymbol::ANG, - Self::ARS => CurrencySymbol::ARS, - Self::AUD => CurrencySymbol::AUD, - Self::AWG => CurrencySymbol::AWG, - Self::AZN => CurrencySymbol::AZN, - Self::BBD => CurrencySymbol::BBD, - Self::BDT => CurrencySymbol::BDT, - Self::BHD => CurrencySymbol::BHD, - Self::BIF => CurrencySymbol::BIF, - Self::BMD => CurrencySymbol::BMD, - Self::BND => CurrencySymbol::BND, - Self::BOB => CurrencySymbol::BOB, - Self::BRL => CurrencySymbol::BRL, - Self::BSD => CurrencySymbol::BSD, - Self::BWP => CurrencySymbol::BWP, - Self::BZD => CurrencySymbol::BZD, - Self::CAD => CurrencySymbol::CAD, - Self::CHF => CurrencySymbol::CHF, - Self::CLP => CurrencySymbol::CLP, - Self::COP => CurrencySymbol::COP, - Self::CRC => CurrencySymbol::CRC, - Self::CUP => CurrencySymbol::CUP, - Self::CZK => CurrencySymbol::CZK, - Self::DJF => CurrencySymbol::DJF, - Self::DKK => CurrencySymbol::DKK, - Self::DOP => CurrencySymbol::DOP, - Self::DZD => CurrencySymbol::DZD, - Self::EGP => CurrencySymbol::EGP, - Self::ETB => CurrencySymbol::ETB, - Self::EUR => CurrencySymbol::EUR, - Self::FJD => CurrencySymbol::FJD, - Self::GBP => CurrencySymbol::GBP, - Self::GHS => CurrencySymbol::GHS, - Self::GIP => CurrencySymbol::GIP, - Self::GMD => CurrencySymbol::GMD, - Self::GNF => CurrencySymbol::GNF, - Self::GTQ => CurrencySymbol::GTQ, - Self::GYD => CurrencySymbol::GYD, - Self::HKD => CurrencySymbol::HKD, - Self::HNL => CurrencySymbol::HNL, - Self::HTG => CurrencySymbol::HTG, - Self::HUF => CurrencySymbol::HUF, - Self::HRK => CurrencySymbol::HRK, - Self::IDR => CurrencySymbol::IDR, - Self::ILS => CurrencySymbol::ILS, - Self::INR => CurrencySymbol::INR, - Self::JMD => CurrencySymbol::JMD, - Self::JOD => CurrencySymbol::JOD, - Self::JPY => CurrencySymbol::JPY, - Self::KES => CurrencySymbol::KES, - Self::KGS => CurrencySymbol::KGS, - Self::KHR => CurrencySymbol::KHR, - Self::KMF => CurrencySymbol::KMF, - Self::KRW => CurrencySymbol::KRW, - Self::KWD => CurrencySymbol::KWD, - Self::KYD => CurrencySymbol::KYD, - Self::KZT => CurrencySymbol::KZT, - Self::LAK => CurrencySymbol::LAK, - Self::LBP => CurrencySymbol::LBP, - Self::LKR => CurrencySymbol::LKR, - Self::LRD => CurrencySymbol::LRD, - Self::LSL => CurrencySymbol::LSL, - Self::MAD => CurrencySymbol::MAD, - Self::MDL => CurrencySymbol::MDL, - Self::MGA => CurrencySymbol::MGA, - Self::MKD => CurrencySymbol::MKD, - Self::MMK => CurrencySymbol::MMK, - Self::MNT => CurrencySymbol::MNT, - Self::MOP => CurrencySymbol::MOP, - Self::MUR => CurrencySymbol::MUR, - Self::MVR => CurrencySymbol::MVR, - Self::MWK => CurrencySymbol::MWK, - Self::MXN => CurrencySymbol::MXN, - Self::MYR => CurrencySymbol::MYR, - Self::NAD => CurrencySymbol::NAD, - Self::NGN => CurrencySymbol::NGN, - Self::NIO => CurrencySymbol::NIO, - Self::NOK => CurrencySymbol::NOK, - Self::NPR => CurrencySymbol::NPR, - Self::NZD => CurrencySymbol::NZD, - Self::OMR => CurrencySymbol::OMR, - Self::PEN => CurrencySymbol::PEN, - Self::PGK => CurrencySymbol::PGK, - Self::PHP => CurrencySymbol::PHP, - Self::PKR => CurrencySymbol::PKR, - Self::PLN => CurrencySymbol::PLN, - Self::PYG => CurrencySymbol::PYG, - Self::QAR => CurrencySymbol::QAR, - Self::RON => CurrencySymbol::RON, - Self::CNY => CurrencySymbol::CNY, - Self::RUB => CurrencySymbol::RUB, - Self::RWF => CurrencySymbol::RWF, - Self::SAR => CurrencySymbol::SAR, - Self::SCR => CurrencySymbol::SCR, - Self::SEK => CurrencySymbol::SEK, - Self::SGD => CurrencySymbol::SGD, - Self::SLL => CurrencySymbol::SLL, - Self::SOS => CurrencySymbol::SOS, - Self::SSP => CurrencySymbol::SSP, - Self::SVC => CurrencySymbol::SVC, - Self::SZL => CurrencySymbol::SZL, - Self::THB => CurrencySymbol::THB, - Self::TRY => CurrencySymbol::TRY, - Self::TTD => CurrencySymbol::TTD, - Self::TWD => CurrencySymbol::TWD, - Self::TZS => CurrencySymbol::TZS, - Self::UGX => CurrencySymbol::UGX, - Self::USD => CurrencySymbol::USD, - Self::UYU => CurrencySymbol::UYU, - Self::UZS => CurrencySymbol::UZS, - Self::VND => CurrencySymbol::VND, - Self::VUV => CurrencySymbol::VUV, - Self::XAF => CurrencySymbol::XAF, - Self::XOF => CurrencySymbol::XOF, - Self::XPF => CurrencySymbol::XPF, - Self::YER => CurrencySymbol::YER, - Self::ZAR => CurrencySymbol::ZAR, - } - } -} - -#[derive(Clone, Debug, serde::Serialize)] -pub enum CurrencySymbol { - #[serde(rename = "د.إ")] - AED, - #[serde(rename = "L")] - ALL, - #[serde(rename = "֏")] - AMD, - #[serde(rename = "ƒ")] - ANG, - #[serde(rename = "$")] - ARS, - #[serde(rename = "$")] - AUD, - #[serde(rename = "ƒ")] - AWG, - #[serde(rename = "₼")] - AZN, - #[serde(rename = "$")] - BBD, - #[serde(rename = "৳")] - BDT, - #[serde(rename = ".د.ب")] - BHD, - #[serde(rename = "FBu")] - BIF, - #[serde(rename = "$")] - BMD, - #[serde(rename = "$")] - BND, - #[serde(rename = "Bs.")] - BOB, - #[serde(rename = "R$")] - BRL, - #[serde(rename = "$")] - BSD, - #[serde(rename = "P")] - BWP, - #[serde(rename = "BZ$")] - BZD, - #[serde(rename = "$")] - CAD, - #[serde(rename = "CHF")] - CHF, - #[serde(rename = "$")] - CLP, - #[serde(rename = "¥")] - CNY, - #[serde(rename = "$")] - COP, - #[serde(rename = "₡")] - CRC, - #[serde(rename = "$")] - CUP, - #[serde(rename = "Kč")] - CZK, - #[serde(rename = "Fdj")] - DJF, - #[serde(rename = "kr")] - DKK, - #[serde(rename = "$")] - DOP, - #[serde(rename = "DA")] - DZD, - #[serde(rename = "E£")] - EGP, - #[serde(rename = "Br")] - ETB, - #[serde(rename = "€")] - EUR, - #[serde(rename = "$")] - FJD, - #[serde(rename = "£")] - GBP, - #[serde(rename = "₵")] - GHS, - #[serde(rename = "£")] - GIP, - #[serde(rename = "D")] - GMD, - #[serde(rename = "GNF")] - GNF, - #[serde(rename = "Q")] - GTQ, - #[serde(rename = "$")] - GYD, - #[serde(rename = "$")] - HKD, - #[serde(rename = "L")] - HNL, - #[serde(rename = "kn")] - HRK, - #[serde(rename = "G")] - HTG, - #[serde(rename = "Ft")] - HUF, - #[serde(rename = "Rp")] - IDR, - #[serde(rename = "₪")] - ILS, - #[serde(rename = "₹")] - INR, - #[serde(rename = "$")] - JMD, - #[serde(rename = "JD")] - JOD, - #[serde(rename = "¥")] - JPY, - #[serde(rename = "KSh")] - KES, - #[serde(rename = "som")] - KGS, - #[serde(rename = "៛")] - KHR, - #[serde(rename = "CF")] - KMF, - #[serde(rename = "₩")] - KRW, - #[serde(rename = "KD")] - KWD, - #[serde(rename = "$")] - KYD, - #[serde(rename = "₸")] - KZT, - #[serde(rename = "₭")] - LAK, - #[serde(rename = "£")] - LBP, - #[serde(rename = "Rs")] - LKR, - #[serde(rename = "$")] - LRD, - #[serde(rename = "L")] - LSL, - #[serde(rename = "DH")] - MAD, - #[serde(rename = "MDL")] - MDL, - #[serde(rename = "Ar")] - MGA, - #[serde(rename = "ден")] - MKD, - #[serde(rename = "Ks")] - MMK, - #[serde(rename = "₮")] - MNT, - #[serde(rename = "P")] - MOP, - #[serde(rename = "Rs")] - MUR, - #[serde(rename = "Rf")] - MVR, - #[serde(rename = "MK")] - MWK, - #[serde(rename = "$")] - MXN, - #[serde(rename = "RM")] - MYR, - #[serde(rename = "$")] - NAD, - #[serde(rename = "₦")] - NGN, - #[serde(rename = "C$")] - NIO, - #[serde(rename = "kr")] - NOK, - #[serde(rename = "रू")] - NPR, - #[serde(rename = "$")] - NZD, - #[serde(rename = "ر.ع.")] - OMR, - #[serde(rename = "S/")] - PEN, - #[serde(rename = "K")] - PGK, - #[serde(rename = "₱")] - PHP, - #[serde(rename = "Rs")] - PKR, - #[serde(rename = "zł")] - PLN, - #[serde(rename = "₲")] - PYG, - #[serde(rename = "ر.ق")] - QAR, - #[serde(rename = "lei")] - RON, - #[serde(rename = "₽")] - RUB, - #[serde(rename = "RWF")] - RWF, - #[serde(rename = "ر.س")] - SAR, - #[serde(rename = "Rs")] - SCR, - #[serde(rename = "kr")] - SEK, - #[serde(rename = "$")] - SGD, - #[serde(rename = "Le")] - SLL, - #[serde(rename = "Sh")] - SOS, - #[serde(rename = "£")] - SSP, - #[serde(rename = "$")] - SVC, - #[serde(rename = "L")] // L or E - SZL, - #[serde(rename = "฿")] - THB, - #[serde(rename = "₺")] - TRY, - #[serde(rename = "$")] - TTD, - #[serde(rename = "$")] - TWD, - #[serde(rename = "TSh")] - TZS, - #[serde(rename = "USh")] - UGX, - #[serde(rename = "$")] - USD, - #[serde(rename = "$")] - UYU, - #[serde(rename = "лв")] - UZS, - #[serde(rename = "₫")] - VND, - #[serde(rename = "VT")] - VUV, - #[serde(rename = "FCFA")] - XAF, - #[serde(rename = "CFA")] - XOF, - #[serde(rename = "₣")] - XPF, - #[serde(rename = "ر.ي")] - YER, - #[serde(rename = "R")] - ZAR, } #[derive( diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 38b6f2080276..c3486769856d 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -101,7 +101,6 @@ pub async fn intiate_payment_link_flow( let payment_details = api_models::payments::PaymentLinkDetails { amount: payment_intent.amount, currency, - currency_symbol: currency.to_currency_symbol(), payment_id: payment_intent.payment_id, merchant_name: payment_link.custom_merchant_name.unwrap_or( merchant_account diff --git a/crates/router/src/core/payment_link/payment_link.html b/crates/router/src/core/payment_link/payment_link.html index 97810d698e8a..67410cac8418 100644 --- a/crates/router/src/core/payment_link/payment_link.html +++ b/crates/router/src/core/payment_link/payment_link.html @@ -973,7 +973,7 @@ // Payment details var amountNode = createItem( "AMOUNT PAID", - paymentDetails.currency_symbol + " " + paymentDetails.amount + paymentDetails.currency + " " + paymentDetails.amount ); var paymentId = createItem("PAYMENT ID", paymentDetails.payment_id); // @ts-ignore @@ -988,7 +988,7 @@ // Payment details var amountNode = createItem( "AMOUNT PAID", - paymentDetails.currency_symbol + " " + paymentDetails.amount + paymentDetails.currency + " " + paymentDetails.amount ); var paymentId = createItem("PAYMENT ID", paymentDetails.payment_id); // @ts-ignore @@ -1002,7 +1002,7 @@ // Payment details var amountNode = createItem( "AMOUNT PAID", - paymentDetails.currency_symbol + " " + paymentDetails.amount + paymentDetails.currency + " " + paymentDetails.amount ); var paymentId = createItem("PAYMENT ID", paymentDetails.payment_id); // @ts-ignore @@ -1016,7 +1016,7 @@ // Payment details var amountNode = createItem( "AMOUNT PAID", - paymentDetails.currency_symbol + " " + paymentDetails.amount + paymentDetails.currency + " " + paymentDetails.amount ); var paymentId = createItem("PAYMENT ID", paymentDetails.payment_id); // @ts-ignore @@ -1030,7 +1030,7 @@ // Payment details var amountNode = createItem( "AMOUNT PAID", - paymentDetails.currency_symbol + " " + paymentDetails.amount + paymentDetails.currency + " " + paymentDetails.amount ); var paymentId = createItem("PAYMENT ID", paymentDetails.payment_id); var paymentId = createItem( @@ -1125,7 +1125,7 @@ var priceNode = document.createElement("div"); priceNode.className = "hyper-checkout-payment-price"; priceNode.innerText = - paymentDetails.currency_symbol + " " + paymentDetails.amount; + paymentDetails.currency + " " + paymentDetails.amount; // Create merchant name's node var merchantNameNode = document.createElement("div"); @@ -1239,7 +1239,7 @@ var priceNode = document.createElement("div"); priceNode.className = "hyper-checkout-card-item-price"; priceNode.innerText = - paymentDetails.currency_symbol + " " + item.amount; + paymentDetails.currency + " " + item.amount; // Append items nameAndQuantityWrapperNode.append(productNameNode, quantityNode); itemWrapperNode.append( @@ -1359,7 +1359,7 @@ var sdkHeaderAmountNode = document.createElement("div"); sdkHeaderAmountNode.className = "hyper-checkout-sdk-header-amount"; sdkHeaderAmountNode.innerText = - paymentDetails.currency_symbol + " " + paymentDetails.amount; + paymentDetails.currency + " " + paymentDetails.amount; sdkHeaderItemNode.append(sdkHeaderMerchantNameNode); sdkHeaderItemNode.append(sdkHeaderAmountNode); From bd53ef615eab3c20266ac4efc396223eff951060 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 7 Nov 2023 14:55:00 +0530 Subject: [PATCH 12/33] refactor(router): addressed pr comments --- crates/common_utils/src/consts.rs | 6 ++++++ crates/router/src/core/payment_link.rs | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 2f517295ae48..079f80c3eda6 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -29,3 +29,9 @@ pub const SURCHARGE_PERCENTAGE_PRECISION_LENGTH: u8 = 2; /// Header Key for application overhead of a request pub const X_HS_LATENCY: &str = "x-hs-latency"; + +/// SDK Default Theme const +pub const DEFAULT_SDK_THEME: &str = "#7EA8F6"; + +/// Default Payment Link Background color +pub const DEFAULT_BACKGROUND_COLOR: &str = "#E5E5E5"; diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index c3486769856d..fe0e020d1e42 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -1,5 +1,8 @@ use api_models::admin as admin_types; -use common_utils::ext_traits::ValueExt; +use common_utils::{ + consts::{DEFAULT_BACKGROUND_COLOR, DEFAULT_SDK_THEME}, + ext_traits::ValueExt, +}; use error_stack::{IntoReport, ResultExt}; use masking::{PeekInterface, Secret}; @@ -96,7 +99,7 @@ pub async fn intiate_payment_link_flow( )?; let (default_sdk_theme, default_background_color) = - ("#7EA8F6".to_string(), "#E5E5E5".to_string()); + (DEFAULT_SDK_THEME, DEFAULT_BACKGROUND_COLOR); let payment_details = api_models::payments::PaymentLinkDetails { amount: payment_intent.amount, @@ -123,14 +126,19 @@ pub async fn intiate_payment_link_flow( .unwrap_or_default(), max_items_visible_after_collapse: 3, sdk_theme: payment_link_config.clone().and_then(|pl_config| { - pl_config - .color_scheme - .map(|color| color.sdk_theme.unwrap_or(default_sdk_theme.clone())) + pl_config.color_scheme.map(|color| { + color + .sdk_theme + .unwrap_or(default_sdk_theme.clone().to_string()) + }) }), }; let js_script = get_js_script(payment_details)?; - let css_script = get_color_scheme_css(payment_link_config.clone(), default_background_color); + let css_script = get_color_scheme_css( + payment_link_config.clone(), + default_background_color.to_string(), + ); let payment_link_data = services::PaymentLinkFormData { js_script, sdk_url: state.conf.payment_link.sdk_url.clone(), From eec8f33bb5faf9e858104ed5fc108c0cca7a4eb2 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Wed, 8 Nov 2023 13:32:34 +0530 Subject: [PATCH 13/33] refactor(router): fixed spell check issue --- crates/router/src/db/payment_link.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index adaef6bb90b0..dde27eceb609 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -57,10 +57,10 @@ impl PaymentLinkInterface for Store { async fn find_payment_link_by_merchant_id( &self, merchant_id: &str, - payment_link_contraints: api_models::payments::PaymentLinkListConstraints, + payment_link_constraints: api_models::payments::PaymentLinkListConstraints, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; - storage::PaymentLink::filter_by_constraints(&conn, merchant_id, payment_link_contraints) + storage::PaymentLink::filter_by_constraints(&conn, merchant_id, payment_link_constraints) .await .map_err(Into::into) .into_report() From b138667a4729e2828c94b8ce7909a4215b4a9d1b Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 14:24:12 +0530 Subject: [PATCH 14/33] refactor(router): syned with latest changes --- ...heck.yml => conventional-commit-check.yml} | 19 +- .../workflows/postman-collection-runner.yml | 2 +- .github/workflows/pr-title-spell-check.yml | 27 + CHANGELOG.md | 54 ++ Cargo.lock | 2 +- README.md | 32 +- config/development.toml | 2 +- connector-template/mod.rs | 14 +- crates/api_models/src/admin.rs | 2 + crates/api_models/src/api_keys.rs | 6 + crates/api_models/src/events.rs | 76 ++ crates/api_models/src/events/customer.rs | 35 + crates/api_models/src/events/gsm.rs | 33 + crates/api_models/src/events/payment.rs | 151 ++++ crates/api_models/src/events/payouts.rs | 29 + crates/api_models/src/events/refund.rs | 63 ++ crates/api_models/src/events/routing.rs | 58 ++ crates/api_models/src/gsm.rs | 75 ++ crates/api_models/src/lib.rs | 2 + crates/api_models/src/payment_methods.rs | 8 +- crates/api_models/src/payments.rs | 6 +- crates/api_models/src/refunds.rs | 13 +- crates/api_models/src/routing.rs | 5 + crates/common_enums/Cargo.toml | 1 - crates/common_utils/Cargo.toml | 1 + crates/common_utils/src/consts.rs | 6 + crates/common_utils/src/events.rs | 92 +++ crates/common_utils/src/lib.rs | 2 + crates/diesel_models/src/ephemeral_key.rs | 6 + crates/diesel_models/src/gsm.rs | 106 +++ crates/diesel_models/src/lib.rs | 1 + crates/diesel_models/src/query.rs | 1 + crates/diesel_models/src/query/gsm.rs | 100 +++ crates/diesel_models/src/refund.rs | 9 + crates/diesel_models/src/schema.rs | 28 + .../src/compatibility/stripe/refunds.rs | 8 +- .../src/compatibility/stripe/refunds/types.rs | 3 + crates/router/src/compatibility/wrap.rs | 5 +- crates/router/src/connector/aci.rs | 20 +- crates/router/src/connector/adyen.rs | 64 +- .../src/connector/adyen/transformers.rs | 5 + crates/router/src/connector/airwallex.rs | 37 +- .../router/src/connector/authorizedotnet.rs | 36 +- .../connector/authorizedotnet/transformers.rs | 4 + crates/router/src/connector/bambora.rs | 24 +- crates/router/src/connector/bankofamerica.rs | 20 +- crates/router/src/connector/bitpay.rs | 6 +- .../src/connector/bitpay/transformers.rs | 9 +- crates/router/src/connector/bluesnap.rs | 44 +- crates/router/src/connector/boku.rs | 27 +- crates/router/src/connector/braintree.rs | 49 +- .../braintree_graphql_transformers.rs | 1 + crates/router/src/connector/cashtocode.rs | 6 +- .../src/connector/cashtocode/transformers.rs | 1 + crates/router/src/connector/checkout.rs | 39 +- .../src/connector/checkout/transformers.rs | 2 + crates/router/src/connector/coinbase.rs | 6 +- crates/router/src/connector/cryptopay.rs | 8 +- crates/router/src/connector/cybersource.rs | 22 +- .../src/connector/cybersource/transformers.rs | 1 + crates/router/src/connector/dlocal.rs | 20 +- crates/router/src/connector/dummyconnector.rs | 16 +- crates/router/src/connector/fiserv.rs | 36 +- crates/router/src/connector/forte.rs | 21 +- crates/router/src/connector/globalpay.rs | 34 +- crates/router/src/connector/globepay.rs | 11 +- .../src/connector/globepay/transformers.rs | 1 + crates/router/src/connector/gocardless.rs | 26 +- crates/router/src/connector/helcim.rs | 30 +- crates/router/src/connector/iatapay.rs | 21 +- crates/router/src/connector/klarna.rs | 11 +- crates/router/src/connector/mollie.rs | 16 +- .../src/connector/mollie/transformers.rs | 55 +- crates/router/src/connector/multisafepay.rs | 15 +- .../connector/multisafepay/transformers.rs | 3 + crates/router/src/connector/nexinets.rs | 21 +- crates/router/src/connector/nmi.rs | 35 +- .../router/src/connector/nmi/transformers.rs | 1 + crates/router/src/connector/noon.rs | 25 +- .../router/src/connector/noon/transformers.rs | 1 + crates/router/src/connector/nuvei.rs | 36 +- .../src/connector/nuvei/transformers.rs | 1 + crates/router/src/connector/opayo.rs | 20 +- crates/router/src/connector/opennode.rs | 6 +- crates/router/src/connector/payeezy.rs | 25 +- crates/router/src/connector/payme.rs | 37 +- .../src/connector/payme/transformers.rs | 2 + crates/router/src/connector/paypal.rs | 30 +- crates/router/src/connector/payu.rs | 22 +- crates/router/src/connector/powertranz.rs | 24 +- .../src/connector/powertranz/transformers.rs | 2 + crates/router/src/connector/prophetpay.rs | 20 +- crates/router/src/connector/rapyd.rs | 22 +- .../src/connector/rapyd/transformers.rs | 2 + crates/router/src/connector/shift4.rs | 23 +- crates/router/src/connector/square.rs | 16 +- crates/router/src/connector/stax.rs | 26 +- crates/router/src/connector/stripe.rs | 62 +- .../src/connector/stripe/transformers.rs | 1 + crates/router/src/connector/trustpay.rs | 21 +- .../src/connector/trustpay/transformers.rs | 8 + crates/router/src/connector/tsys.rs | 30 +- .../router/src/connector/tsys/transformers.rs | 1 + crates/router/src/connector/utils.rs | 14 +- crates/router/src/connector/volt.rs | 21 +- crates/router/src/connector/wise.rs | 24 +- crates/router/src/connector/worldline.rs | 15 +- crates/router/src/connector/worldpay.rs | 19 +- crates/router/src/connector/zen.rs | 15 +- crates/router/src/consts.rs | 3 + crates/router/src/core.rs | 1 + crates/router/src/core/api_keys.rs | 8 +- crates/router/src/core/gsm.rs | 137 ++++ crates/router/src/core/payment_link.rs | 27 +- crates/router/src/core/payment_methods.rs | 11 +- .../router/src/core/payment_methods/cards.rs | 131 +--- .../router/src/core/payment_methods/vault.rs | 723 +++++++++--------- crates/router/src/core/payments.rs | 25 +- .../router/src/core/payments/access_token.rs | 1 + .../src/core/payments/flows/authorize_flow.rs | 77 +- crates/router/src/core/payments/helpers.rs | 22 +- crates/router/src/core/payments/operations.rs | 7 +- .../payments/operations/payment_approve.rs | 4 +- .../operations/payment_complete_authorize.rs | 4 +- .../payments/operations/payment_confirm.rs | 4 +- .../payments/operations/payment_create.rs | 4 +- .../operations/payment_method_validate.rs | 3 +- .../payments/operations/payment_session.rs | 1 + .../core/payments/operations/payment_start.rs | 3 +- .../payments/operations/payment_status.rs | 3 +- .../payments/operations/payment_update.rs | 4 +- .../router/src/core/payments/tokenization.rs | 2 + .../router/src/core/payments/transformers.rs | 16 +- crates/router/src/core/payouts.rs | 4 +- crates/router/src/core/payouts/helpers.rs | 21 +- crates/router/src/core/payouts/validator.rs | 2 + crates/router/src/core/refunds.rs | 9 +- crates/router/src/core/routing.rs | 10 +- crates/router/src/core/verification.rs | 3 +- crates/router/src/db.rs | 2 + crates/router/src/db/gsm.rs | 180 +++++ crates/router/src/db/refund.rs | 8 +- crates/router/src/events/api_logs.rs | 68 +- crates/router/src/lib.rs | 1 + crates/router/src/openapi.rs | 2 +- crates/router/src/routes.rs | 3 +- crates/router/src/routes/admin.rs | 8 +- crates/router/src/routes/api_keys.rs | 10 +- crates/router/src/routes/app.rs | 16 +- crates/router/src/routes/dummy_connector.rs | 2 +- crates/router/src/routes/gsm.rs | 93 +++ crates/router/src/routes/lock_utils.rs | 5 + crates/router/src/routes/payment_link.rs | 2 +- crates/router/src/routes/refunds.rs | 9 +- crates/router/src/routes/routing.rs | 6 +- crates/router/src/routes/verification.rs | 2 +- crates/router/src/services/api.rs | 32 +- crates/router/src/types.rs | 5 + crates/router/src/types/api.rs | 1 + crates/router/src/types/api/customers.rs | 6 + .../router/src/types/api/payment_methods.rs | 11 +- crates/router/src/types/storage.rs | 9 +- crates/router/src/types/storage/gsm.rs | 4 + crates/router/src/types/storage/refund.rs | 4 +- crates/router/src/types/transformers.rs | 18 +- crates/router/src/utils.rs | 1 + crates/router/src/workflows/tokenized_data.rs | 16 +- crates/router_env/src/logger/types.rs | 8 + crates/test_utils/README.md | 7 + crates/test_utils/src/main.rs | 28 +- crates/test_utils/src/newman_runner.rs | 72 +- docs/imgs/aws_button.png | Bin 0 -> 2427 bytes .../2023-11-07-110139_add_gsm_table/down.sql | 2 + .../2023-11-07-110139_add_gsm_table/up.sql | 16 + openapi/openapi_spec.json | 4 +- 175 files changed, 3395 insertions(+), 994 deletions(-) rename .github/workflows/{pr-title-check.yml => conventional-commit-check.yml} (88%) create mode 100644 .github/workflows/pr-title-spell-check.yml create mode 100644 crates/api_models/src/events.rs create mode 100644 crates/api_models/src/events/customer.rs create mode 100644 crates/api_models/src/events/gsm.rs create mode 100644 crates/api_models/src/events/payment.rs create mode 100644 crates/api_models/src/events/payouts.rs create mode 100644 crates/api_models/src/events/refund.rs create mode 100644 crates/api_models/src/events/routing.rs create mode 100644 crates/api_models/src/gsm.rs create mode 100644 crates/common_utils/src/events.rs create mode 100644 crates/diesel_models/src/gsm.rs create mode 100644 crates/diesel_models/src/query/gsm.rs create mode 100644 crates/router/src/core/gsm.rs create mode 100644 crates/router/src/db/gsm.rs create mode 100644 crates/router/src/routes/gsm.rs create mode 100644 crates/router/src/types/storage/gsm.rs create mode 100644 docs/imgs/aws_button.png create mode 100644 migrations/2023-11-07-110139_add_gsm_table/down.sql create mode 100644 migrations/2023-11-07-110139_add_gsm_table/up.sql diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/conventional-commit-check.yml similarity index 88% rename from .github/workflows/pr-title-check.yml rename to .github/workflows/conventional-commit-check.yml index 8c15246f0e6c..5fd25e9332d1 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/conventional-commit-check.yml @@ -1,4 +1,4 @@ -name: PR Title Checks +name: Conventional Commit Message Check on: # This is a dangerous event trigger as it causes the workflow to run in the @@ -35,19 +35,6 @@ env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse jobs: - typos: - name: Spell check PR title - runs-on: ubuntu-latest - steps: - - name: Store PR title in a file - shell: bash - run: echo '${{ github.event.pull_request.title }}' > pr_title.txt - - - name: Spell check - uses: crate-ci/typos@master - with: - files: ./pr_title.txt - pr_title_check: name: Verify PR title follows conventional commit standards runs-on: ubuntu-latest @@ -66,8 +53,10 @@ jobs: id: pr_title_check if: ${{ github.event_name == 'pull_request_target' }} shell: bash + env: + TITLE: ${{ github.event.pull_request.title }} continue-on-error: true - run: cog verify '${{ github.event.pull_request.title }}' + run: cog verify "$TITLE" - name: Verify commit message follows conventional commit standards id: commit_message_check diff --git a/.github/workflows/postman-collection-runner.yml b/.github/workflows/postman-collection-runner.yml index 6b0911d1b456..3291755b56cf 100644 --- a/.github/workflows/postman-collection-runner.yml +++ b/.github/workflows/postman-collection-runner.yml @@ -143,7 +143,7 @@ jobs: for i in $(echo "$CONNECTORS" | tr "," "\n"); do echo $i - if ! cargo run --bin test_utils -- --connector_name="$i" --base_url="$BASE_URL" --admin_api_key="$ADMIN_API_KEY"; then + if ! cargo run --bin test_utils -- --connector-name="$i" --base-url="$BASE_URL" --admin-api-key="$ADMIN_API_KEY"; then failed_connectors+=("$i") fi done diff --git a/.github/workflows/pr-title-spell-check.yml b/.github/workflows/pr-title-spell-check.yml new file mode 100644 index 000000000000..6ab6f184739d --- /dev/null +++ b/.github/workflows/pr-title-spell-check.yml @@ -0,0 +1,27 @@ +name: PR Title Spell Check + +on: + pull_request: + types: + - opened + - edited + - synchronize + +jobs: + typos: + name: Spell check PR title + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Store PR title in a file + shell: bash + env: + TITLE: ${{ github.event.pull_request.title }} + run: echo $TITLE > pr_title.txt + + - name: Spell check + uses: crate-ci/typos@master + with: + files: ./pr_title.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 207595f42828..0aa00381bf62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,60 @@ All notable changes to HyperSwitch will be documented here. - - - +## 1.74.0 (2023-11-08) + +### Features + +- **core:** Use redis as temp locker instead of basilisk ([#2789](https://github.com/juspay/hyperswitch/pull/2789)) ([`6678689`](https://github.com/juspay/hyperswitch/commit/6678689265ae9a4fbb7a43c1938237d349c5a68e)) +- **events:** Add request details to api events ([#2769](https://github.com/juspay/hyperswitch/pull/2769)) ([`164d1c6`](https://github.com/juspay/hyperswitch/commit/164d1c66fbcb84104db07412496114db2f8c5c0c)) +- **router:** Add `gateway_status_map` interface ([#2804](https://github.com/juspay/hyperswitch/pull/2804)) ([`a429b23`](https://github.com/juspay/hyperswitch/commit/a429b23c7f21c9d08a79895c0b770b35aab725f7)) +- **test_utils:** Add custom-headers and custom delay support to rustman ([#2636](https://github.com/juspay/hyperswitch/pull/2636)) ([`1effddd`](https://github.com/juspay/hyperswitch/commit/1effddd0a0d3985d6df03c4ae9be28712befc05e)) + +### Bug Fixes + +- **connector:** Add attempt_status in field in error_response ([#2794](https://github.com/juspay/hyperswitch/pull/2794)) ([`5642fef`](https://github.com/juspay/hyperswitch/commit/5642fef52a6d591d12c5745ed381f41a1593f183)) + +### Refactors + +- **config:** Update payment method filter of Klarna in Stripe ([#2807](https://github.com/juspay/hyperswitch/pull/2807)) ([`21ce807`](https://github.com/juspay/hyperswitch/commit/21ce8079f4cb11d70c5eaae78f83773141c67d0c)) +- **router:** Add parameter connectors to get_request_body function ([#2708](https://github.com/juspay/hyperswitch/pull/2708)) ([`7623ea9`](https://github.com/juspay/hyperswitch/commit/7623ea93bee61b0bb22b68e86f44de17f04f876b)) + +### Documentation + +- **README:** Update README ([#2800](https://github.com/juspay/hyperswitch/pull/2800)) ([`bef0a04`](https://github.com/juspay/hyperswitch/commit/bef0a04edc6323b3b7a2e0dd7eeb7954915ba7cf)) + +**Full Changelog:** [`v1.73.0...v1.74.0`](https://github.com/juspay/hyperswitch/compare/v1.73.0...v1.74.0) + +- - - + + +## 1.73.0 (2023-11-07) + +### Features + +- **connector:** + - [BANKOFAMERICA] Add Connector Template Code ([#2764](https://github.com/juspay/hyperswitch/pull/2764)) ([`4563935`](https://github.com/juspay/hyperswitch/commit/4563935372d2cdff3f746fa86a47f1166ffd32ac)) + - [Bitpay] Add order id as the reference id ([#2591](https://github.com/juspay/hyperswitch/pull/2591)) ([`d47d4ac`](https://github.com/juspay/hyperswitch/commit/d47d4ac682705d6ac692f9381149bbf08ad71264)) +- **router:** Make webhook events config disabled only and by default enable all the events ([#2770](https://github.com/juspay/hyperswitch/pull/2770)) ([`d335879`](https://github.com/juspay/hyperswitch/commit/d335879f9289b57a90a76c6587a58a0b3e12c9ad)) +- Make drainer logs queryable with request_id and global_id ([#2771](https://github.com/juspay/hyperswitch/pull/2771)) ([`ff73aba`](https://github.com/juspay/hyperswitch/commit/ff73aba8e72d8e072027881760335c0c818df665)) + +### Bug Fixes + +- **connector:** Fix amount conversion incase of minor unit ([#2793](https://github.com/juspay/hyperswitch/pull/2793)) ([`34f5226`](https://github.com/juspay/hyperswitch/commit/34f52260d3fa68b54e5b46207afaf2ad07a8d8ba)) + +### Refactors + +- **payment_methods:** Added support for account subtype in pmd ([#2651](https://github.com/juspay/hyperswitch/pull/2651)) ([`e7375d0`](https://github.com/juspay/hyperswitch/commit/e7375d0e26099a7e0e6efd1b83b8eb9c7b1c5411)) + +### Documentation + +- **README:** Add one-click deployment information using CDK ([#2798](https://github.com/juspay/hyperswitch/pull/2798)) ([`bb39cd4`](https://github.com/juspay/hyperswitch/commit/bb39cd4081fdcaf68b2b5de2234e93493dbd84b6)) + +**Full Changelog:** [`v1.72.0...v1.73.0`](https://github.com/juspay/hyperswitch/compare/v1.72.0...v1.73.0) + +- - - + + ## 1.72.0 (2023-11-05) ### Features diff --git a/Cargo.lock b/Cargo.lock index 886a8b50acc8..ac7fde55d8e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1503,7 +1503,6 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" name = "common_enums" version = "0.1.0" dependencies = [ - "common_utils", "diesel", "router_derive", "serde", @@ -1519,6 +1518,7 @@ version = "0.1.0" dependencies = [ "async-trait", "bytes", + "common_enums", "diesel", "error-stack", "fake", diff --git a/README.md b/README.md index cc19670a8fc3..e6e9baa07f7d 100644 --- a/README.md +++ b/README.md @@ -35,19 +35,11 @@ The single API to access payment ecosystems across 130+ countries

-

🎉 Hacktoberfest is here! 🎉

- -New to Rust? Hyperswitch is the perfect place to start this hacktoberfest! 😁 - -> ⭐️ If you're new to Hacktoberfest, you can learn more and register to participate [here](https://hacktoberfest.com/participation/). Registration is from **September 26th - October 31st**. -
-Hyperswitch is an open source payments switch to make payments fast, reliable, and, affordable. -It lets you connect with multiple payment processors and route traffic effortlessly, all with a single API integration. - +Hyperswitch is a community-led, open payments switch to enable access to the best payments infrastructure for every digital business. Using Hyperswitch, you can: @@ -58,8 +50,6 @@ Using Hyperswitch, you can: - 🎨 **Customize payment flows** with full visibility and control - 🌐 **Increase business reach** with local/alternate payment methods -> Hyperswitch is **wire-compatible** with top processors like Stripe, making it easy to integrate. -
Hyperswitch-Product @@ -67,24 +57,20 @@ Using Hyperswitch, you can:

⚡️ Quick Start Guide

+

One-click deployment on AWS cloud

- - -Ways to get started with Hyperswitch: - -1. Try it in our Sandbox Environment: Fast and easy to - start. - No code or setup is required in your system, [learn more](/docs/try_sandbox.md) +The fastest and easiest way to try hyperswitch is via our CDK scripts +1. Click on the following button for a quick standalone deployment on AWS, suitable for prototyping. + No code or setup is required in your system and the deployment is covered within the AWS free-tier setup. - + -2. A simple demo of integrating Hyperswitch with your React App, Try our React [Demo App](https://github.com/aashu331998/hyperswitch-react-demo-app/archive/refs/heads/main.zip). +2. Sign-in to your AWS console. +3. Follow the instructions provided on the console to successfully deploy Hyperswitch -3. Install in your local system: Configurations and - setup required in your system. - Suitable if you like to customise the core offering, [setup guide](/docs/try_local_system.md) +For an early access to the production-ready setup fill this Early Access Form

🔌 Fast Integration for Stripe Users

diff --git a/config/development.toml b/config/development.toml index 34fbdbc9e078..63c1f045d94f 100644 --- a/config/development.toml +++ b/config/development.toml @@ -248,7 +248,7 @@ ideal = { country = "NL", currency = "EUR" } [pm_filters.stripe] google_pay = { country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" } apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US,KR,VN,MA,ZA,VA,CL,SV,GT,HN,PA" } -klarna = { country = "US", currency = "USD" } +klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,CH,GB,US", currency = "AUD,CAD,CHF,CZK,DKK,EUR,GBP,NOK,NZD,PLN,SEK,USD" } affirm = { country = "US", currency = "USD" } afterpay_clearpay = { country = "US,CA,GB,AU,NZ,FR,ES", currency = "USD,CAD,GBP,AUD,NZD" } giropay = { country = "DE", currency = "EUR" } diff --git a/connector-template/mod.rs b/connector-template/mod.rs index 05f527d24662..7f21962109de 100644 --- a/connector-template/mod.rs +++ b/connector-template/mod.rs @@ -105,6 +105,7 @@ impl ConnectorCommon for {{project-name | downcase | pascal_case}} { code: response.code, message: response.message, reason: response.reason, + attempt_status: None, }) } } @@ -156,7 +157,7 @@ impl Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } - fn get_request_body(&self, req: &types::PaymentsAuthorizeRouterData) -> CustomResult, errors::ConnectorError> { + fn get_request_body(&self, req: &types::PaymentsAuthorizeRouterData, _connectors: &settings::Connectors,) -> CustomResult, errors::ConnectorError> { let connector_router_data = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}RouterData::try_from(( &self.get_currency_unit(), @@ -185,7 +186,7 @@ impl .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body(self, req, connectors)?) .build(), )) } @@ -301,6 +302,7 @@ impl fn get_request_body( &self, _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -318,7 +320,7 @@ impl .headers(types::PaymentsCaptureType::get_headers( self, req, connectors, )?) - .body(types::PaymentsCaptureType::get_request_body(self, req)?) + .body(types::PaymentsCaptureType::get_request_body(self, req, connectors)?) .build(), )) } @@ -373,7 +375,7 @@ impl Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } - fn get_request_body(&self, req: &types::RefundsRouterData) -> CustomResult, errors::ConnectorError> { + fn get_request_body(&self, req: &types::RefundsRouterData, _connectors: &settings::Connectors,) -> CustomResult, errors::ConnectorError> { let connector_router_data = {{project-name | downcase}}::{{project-name | downcase | pascal_case}}RouterData::try_from(( &self.get_currency_unit(), @@ -393,7 +395,7 @@ impl .url(&types::RefundExecuteType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundExecuteType::get_headers(self, req, connectors)?) - .body(types::RefundExecuteType::get_request_body(self, req)?) + .body(types::RefundExecuteType::get_request_body(self, req, connectors)?) .build(); Ok(Some(request)) } @@ -441,7 +443,7 @@ impl .url(&types::RefundSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundSyncType::get_headers(self, req, connectors)?) - .body(types::RefundSyncType::get_request_body(self, req)?) + .body(types::RefundSyncType::get_request_body(self, req, connectors)?) .build(), )) } diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index da3f59e733c4..979214a071a9 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -892,6 +892,8 @@ pub struct ToggleKVResponse { #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ToggleKVRequest { + #[serde(skip_deserializing)] + pub merchant_id: String, /// Status of KV for the specific merchant #[schema(example = true)] pub kv_enabled: bool, diff --git a/crates/api_models/src/api_keys.rs b/crates/api_models/src/api_keys.rs index f0ab403d9c65..805c5616c2a0 100644 --- a/crates/api_models/src/api_keys.rs +++ b/crates/api_models/src/api_keys.rs @@ -129,6 +129,12 @@ pub struct UpdateApiKeyRequest { /// rotating your keys once every 6 months. #[schema(example = "2022-09-10T10:11:12Z")] pub expiration: Option, + + #[serde(skip_deserializing)] + pub key_id: String, + + #[serde(skip_deserializing)] + pub merchant_id: String, } /// The response body for revoking an API Key. diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs new file mode 100644 index 000000000000..f856a9958e1e --- /dev/null +++ b/crates/api_models/src/events.rs @@ -0,0 +1,76 @@ +pub mod customer; +pub mod gsm; +pub mod payment; +#[cfg(feature = "payouts")] +pub mod payouts; +pub mod refund; +pub mod routing; + +use common_utils::{ + events::{ApiEventMetric, ApiEventsType}, + impl_misc_api_event_type, +}; + +use crate::{ + admin::*, api_keys::*, cards_info::*, disputes::*, files::*, mandates::*, payment_methods::*, + payments::*, verifications::*, +}; + +impl ApiEventMetric for TimeRange {} + +impl_misc_api_event_type!( + PaymentMethodId, + PaymentsSessionResponse, + PaymentMethodListResponse, + PaymentMethodCreate, + PaymentLinkInitiateRequest, + RetrievePaymentLinkResponse, + MandateListConstraints, + CreateFileResponse, + DisputeResponse, + SubmitEvidenceRequest, + MerchantConnectorResponse, + MerchantConnectorId, + MandateResponse, + MandateRevokedResponse, + RetrievePaymentLinkRequest, + PaymentLinkListConstraints, + MandateId, + DisputeListConstraints, + RetrieveApiKeyResponse, + BusinessProfileResponse, + BusinessProfileUpdate, + BusinessProfileCreate, + RevokeApiKeyResponse, + ToggleKVResponse, + ToggleKVRequest, + MerchantAccountDeleteResponse, + MerchantAccountUpdate, + CardInfoResponse, + CreateApiKeyResponse, + CreateApiKeyRequest, + MerchantConnectorDeleteResponse, + MerchantConnectorUpdate, + MerchantConnectorCreate, + MerchantId, + CardsInfoRequest, + MerchantAccountResponse, + MerchantAccountListRequest, + MerchantAccountCreate, + PaymentsSessionRequest, + ApplepayMerchantVerificationRequest, + ApplepayMerchantResponse, + ApplepayVerifiedDomainsResponse, + UpdateApiKeyRequest +); + +#[cfg(feature = "stripe")] +impl_misc_api_event_type!( + StripeSetupIntentResponse, + StripeRefundResponse, + StripePaymentIntentListResponse, + StripePaymentIntentResponse, + CustomerDeleteResponse, + CustomerPaymentMethodListResponse, + CreateCustomerResponse +); diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs new file mode 100644 index 000000000000..29f565042181 --- /dev/null +++ b/crates/api_models/src/events/customer.rs @@ -0,0 +1,35 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::customers::{CustomerDeleteResponse, CustomerId, CustomerRequest, CustomerResponse}; + +impl ApiEventMetric for CustomerDeleteResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} + +impl ApiEventMetric for CustomerRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} + +impl ApiEventMetric for CustomerResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} + +impl ApiEventMetric for CustomerId { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} diff --git a/crates/api_models/src/events/gsm.rs b/crates/api_models/src/events/gsm.rs new file mode 100644 index 000000000000..d984ae1ff698 --- /dev/null +++ b/crates/api_models/src/events/gsm.rs @@ -0,0 +1,33 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::gsm; + +impl ApiEventMetric for gsm::GsmCreateRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} + +impl ApiEventMetric for gsm::GsmUpdateRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} + +impl ApiEventMetric for gsm::GsmRetrieveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} + +impl ApiEventMetric for gsm::GsmDeleteRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} + +impl ApiEventMetric for gsm::GsmDeleteResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} diff --git a/crates/api_models/src/events/payment.rs b/crates/api_models/src/events/payment.rs new file mode 100644 index 000000000000..2f3336fc2777 --- /dev/null +++ b/crates/api_models/src/events/payment.rs @@ -0,0 +1,151 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::{ + payment_methods::{ + CustomerPaymentMethodsListResponse, PaymentMethodDeleteResponse, PaymentMethodListRequest, + PaymentMethodResponse, PaymentMethodUpdate, + }, + payments::{ + PaymentIdType, PaymentListConstraints, PaymentListFilterConstraints, PaymentListFilters, + PaymentListResponse, PaymentListResponseV2, PaymentsApproveRequest, PaymentsCancelRequest, + PaymentsCaptureRequest, PaymentsRejectRequest, PaymentsRequest, PaymentsResponse, + PaymentsRetrieveRequest, PaymentsStartRequest, RedirectionResponse, + }, +}; +impl ApiEventMetric for PaymentsRetrieveRequest { + fn get_api_event_type(&self) -> Option { + match self.resource_id { + PaymentIdType::PaymentIntentId(ref id) => Some(ApiEventsType::Payment { + payment_id: id.clone(), + }), + _ => None, + } + } +} + +impl ApiEventMetric for PaymentsStartRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsCaptureRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.to_owned(), + }) + } +} + +impl ApiEventMetric for PaymentsCancelRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsApproveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsRejectRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsRequest { + fn get_api_event_type(&self) -> Option { + match self.payment_id { + Some(PaymentIdType::PaymentIntentId(ref id)) => Some(ApiEventsType::Payment { + payment_id: id.clone(), + }), + _ => None, + } + } +} + +impl ApiEventMetric for PaymentsResponse { + fn get_api_event_type(&self) -> Option { + self.payment_id + .clone() + .map(|payment_id| ApiEventsType::Payment { payment_id }) + } +} + +impl ApiEventMetric for PaymentMethodResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethod { + payment_method_id: self.payment_method_id.clone(), + payment_method: Some(self.payment_method), + payment_method_type: self.payment_method_type, + }) + } +} + +impl ApiEventMetric for PaymentMethodUpdate {} + +impl ApiEventMetric for PaymentMethodDeleteResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethod { + payment_method_id: self.payment_method_id.clone(), + payment_method: None, + payment_method_type: None, + }) + } +} + +impl ApiEventMetric for CustomerPaymentMethodsListResponse {} + +impl ApiEventMetric for PaymentMethodListRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethodList { + payment_id: self + .client_secret + .as_ref() + .and_then(|cs| cs.rsplit_once("_secret_")) + .map(|(pid, _)| pid.to_string()), + }) + } +} + +impl ApiEventMetric for PaymentListFilterConstraints { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListFilters { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListConstraints { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListResponseV2 { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for RedirectionResponse {} diff --git a/crates/api_models/src/events/payouts.rs b/crates/api_models/src/events/payouts.rs new file mode 100644 index 000000000000..303709acc476 --- /dev/null +++ b/crates/api_models/src/events/payouts.rs @@ -0,0 +1,29 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::payouts::{ + PayoutActionRequest, PayoutCreateRequest, PayoutCreateResponse, PayoutRetrieveRequest, +}; + +impl ApiEventMetric for PayoutRetrieveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} + +impl ApiEventMetric for PayoutCreateRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} + +impl ApiEventMetric for PayoutCreateResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} + +impl ApiEventMetric for PayoutActionRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} diff --git a/crates/api_models/src/events/refund.rs b/crates/api_models/src/events/refund.rs new file mode 100644 index 000000000000..424a3191db66 --- /dev/null +++ b/crates/api_models/src/events/refund.rs @@ -0,0 +1,63 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::refunds::{ + RefundListMetaData, RefundListRequest, RefundListResponse, RefundRequest, RefundResponse, + RefundUpdateRequest, RefundsRetrieveRequest, +}; + +impl ApiEventMetric for RefundRequest { + fn get_api_event_type(&self) -> Option { + let payment_id = self.payment_id.clone(); + self.refund_id + .clone() + .map(|refund_id| ApiEventsType::Refund { + payment_id: Some(payment_id), + refund_id, + }) + } +} + +impl ApiEventMetric for RefundResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: Some(self.payment_id.clone()), + refund_id: self.refund_id.clone(), + }) + } +} + +impl ApiEventMetric for RefundsRetrieveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: None, + refund_id: self.refund_id.clone(), + }) + } +} + +impl ApiEventMetric for RefundUpdateRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: None, + refund_id: self.refund_id.clone(), + }) + } +} + +impl ApiEventMetric for RefundListRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for RefundListResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for RefundListMetaData { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs new file mode 100644 index 000000000000..5eca01acc6fb --- /dev/null +++ b/crates/api_models/src/events/routing.rs @@ -0,0 +1,58 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::routing::{ + LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, RoutingAlgorithmId, + RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind, +}; +#[cfg(feature = "business_profile_routing")] +use crate::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; + +impl ApiEventMetric for RoutingKind { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for MerchantRoutingAlgorithm { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingAlgorithmId { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingDictionaryRecord { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for LinkedRoutingConfigRetrieveResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +#[cfg(feature = "business_profile_routing")] +impl ApiEventMetric for RoutingRetrieveQuery { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingConfigRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +#[cfg(feature = "business_profile_routing")] +impl ApiEventMetric for RoutingRetrieveLinkQuery { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} diff --git a/crates/api_models/src/gsm.rs b/crates/api_models/src/gsm.rs new file mode 100644 index 000000000000..6bd8fd99dd93 --- /dev/null +++ b/crates/api_models/src/gsm.rs @@ -0,0 +1,75 @@ +use crate::enums; + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct GsmCreateRequest { + pub connector: enums::Connector, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: String, + pub router_error: Option, + pub decision: GsmDecision, + pub step_up_possible: bool, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct GsmRetrieveRequest { + pub connector: enums::Connector, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, +} + +#[derive( + Default, + Clone, + Copy, + Debug, + strum::Display, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, + strum::EnumString, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum GsmDecision { + Retry, + Requeue, + #[default] + DoDefault, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct GsmUpdateRequest { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: Option, + pub router_error: Option, + pub decision: Option, + pub step_up_possible: Option, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct GsmDeleteRequest { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, +} + +#[derive(Debug, serde::Serialize)] +pub struct GsmDeleteResponse { + pub gsm_rule_delete: bool, + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, +} diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index ec272514e38a..5da916b14817 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -9,7 +9,9 @@ pub mod enums; pub mod ephemeral_key; #[cfg(feature = "errors")] pub mod errors; +pub mod events; pub mod files; +pub mod gsm; pub mod mandates; pub mod organization; pub mod payment_methods; diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index b30590bfd6f2..289f652981eb 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -12,7 +12,9 @@ use utoipa::ToSchema; #[cfg(feature = "payouts")] use crate::payouts; use crate::{ - admin, enums as api_enums, + admin, + customers::CustomerId, + enums as api_enums, payments::{self, BankCodeResponse}, }; @@ -167,6 +169,8 @@ pub struct CardDetailsPaymentMethod { pub struct PaymentMethodDataBankCreds { pub mask: String, pub hash: String, + pub account_type: Option, + pub account_name: Option, pub payment_method_type: api_enums::PaymentMethodType, pub connector_details: Vec, } @@ -474,6 +478,8 @@ pub struct RequestPaymentMethodTypes { #[derive(Debug, Clone, serde::Serialize, Default, ToSchema)] #[serde(deny_unknown_fields)] pub struct PaymentMethodListRequest { + #[serde(skip_deserializing)] + pub customer_id: Option, /// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK #[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893ein2d")] pub client_secret: Option, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index abcc353b5f81..d2a083539c96 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2232,7 +2232,9 @@ pub struct PaymentListFilters { pub authentication_type: Vec, } -#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] +#[derive( + Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema, +)] pub struct TimeRange { /// The start time to filter payments list or to get list of filters. To get list of filters start time is needed to be passed #[serde(with = "common_utils::custom_serde::iso8601")] @@ -3147,7 +3149,7 @@ pub struct PaymentLinkDetails { pub merchant_logo: String, pub return_url: String, pub merchant_name: String, - pub order_details: Vec, + pub order_details: Option>, pub max_items_visible_after_collapse: i8, pub sdk_theme: Option, } diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index 7b4eae4238ac..6fe8be8b5291 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use utoipa::ToSchema; +use super::payments::TimeRange; use crate::{admin, enums}; #[derive(Default, Debug, ToSchema, Clone, Deserialize, Serialize)] @@ -75,6 +76,8 @@ pub struct RefundsRetrieveRequest { #[derive(Default, Debug, ToSchema, Clone, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct RefundUpdateRequest { + #[serde(skip)] + pub refund_id: String, /// An arbitrary string attached to the object. Often useful for displaying to users and your customer support executive #[schema(max_length = 255, example = "Customer returned the product")] pub reason: Option, @@ -152,16 +155,6 @@ pub struct RefundListRequest { pub refund_status: Option>, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)] -pub struct TimeRange { - /// The start time to filter refunds list or to get list of filters. To get list of filters start time is needed to be passed - #[serde(with = "common_utils::custom_serde::iso8601")] - pub start_time: PrimitiveDateTime, - /// The end time to filter refunds list or to get list of filters. If not passed the default time is now - #[serde(default, with = "common_utils::custom_serde::iso8601::option")] - pub end_time: Option, -} - #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)] pub struct RefundListResponse { /// The number of refunds included in the list diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 95d4c5e10ece..425ca364191d 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -592,3 +592,8 @@ pub enum RoutingKind { Config(RoutingDictionary), RoutingAlgorithm(Vec), } + +#[repr(transparent)] +#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[serde(transparent)] +pub struct RoutingAlgorithmId(pub String); diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index f5219d9833db..db37d27ab0f1 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -19,7 +19,6 @@ time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } utoipa = { version = "3.3.0", features = ["preserve_order"] } # First party crates -common_utils = { version = "0.1.0", path = "../common_utils" } router_derive = { version = "0.1.0", path = "../router_derive" } [dev-dependencies] diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index c1fd91a351c7..62bd747da1b0 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -42,6 +42,7 @@ phonenumber = "0.3.3" # First party crates masking = { version = "0.1.0", path = "../masking" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"], optional = true } +common_enums = { version = "0.1.0", path = "../common_enums" } [target.'cfg(not(target_os = "windows"))'.dependencies] signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"], optional = true } diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index ed2287d2abb0..a053074c6f23 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -38,3 +38,9 @@ pub const DEFAULT_SDK_THEME: &str = "#7EA8F6"; /// Default Payment Link Background color pub const DEFAULT_BACKGROUND_COLOR: &str = "#E5E5E5"; + +/// Default product Img Link +pub const DEFAULT_PRODUCT_IMG: &str = "https://i.imgur.com/On3VtKF.png"; + +/// Default Merchant Logo Link +pub const DEFAULT_MERCHANT_LOGO: &str = "https://i.imgur.com/RfxPFQo.png"; diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs new file mode 100644 index 000000000000..8c52f6c36d63 --- /dev/null +++ b/crates/common_utils/src/events.rs @@ -0,0 +1,92 @@ +use common_enums::{PaymentMethod, PaymentMethodType}; +use serde::Serialize; + +pub trait ApiEventMetric { + fn get_api_event_type(&self) -> Option { + None + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(tag = "flow_type")] +pub enum ApiEventsType { + Payout, + Payment { + payment_id: String, + }, + Refund { + payment_id: Option, + refund_id: String, + }, + PaymentMethod { + payment_method_id: String, + payment_method: Option, + payment_method_type: Option, + }, + Customer { + customer_id: String, + }, + User { + //specified merchant_id will overridden on global defined + merchant_id: String, + user_id: String, + }, + PaymentMethodList { + payment_id: Option, + }, + Webhooks { + connector: String, + payment_id: Option, + }, + Routing, + ResourceListAPI, + PaymentRedirectionResponse, + Gsm, + // TODO: This has to be removed once the corresponding apiEventTypes are created + Miscellaneous, +} + +impl ApiEventMetric for serde_json::Value {} +impl ApiEventMetric for () {} + +impl ApiEventMetric for Result { + fn get_api_event_type(&self) -> Option { + match self { + Ok(q) => q.get_api_event_type(), + Err(_) => None, + } + } +} + +// TODO: Ideally all these types should be replaced by newtype responses +impl ApiEventMetric for Vec { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Miscellaneous) + } +} + +#[macro_export] +macro_rules! impl_misc_api_event_type { + ($($type:ty),+) => { + $( + impl ApiEventMetric for $type { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Miscellaneous) + } + } + )+ + }; +} + +impl_misc_api_event_type!( + String, + (&String, &String), + (Option, Option, String), + bool +); + +impl ApiEventMetric for &T { + fn get_api_event_type(&self) -> Option { + T::get_api_event_type(self) + } +} diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 724c3bca0a27..62428dccfb6a 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -6,6 +6,8 @@ pub mod consts; pub mod crypto; pub mod custom_serde; pub mod errors; +#[allow(missing_docs)] // Todo: add docs +pub mod events; pub mod ext_traits; pub mod fp_utils; pub mod pii; diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index 96bd6e497c33..77b9c647e43b 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -14,3 +14,9 @@ pub struct EphemeralKey { pub expires: i64, pub secret: String, } + +impl common_utils::events::ApiEventMetric for EphemeralKey { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::Miscellaneous) + } +} diff --git a/crates/diesel_models/src/gsm.rs b/crates/diesel_models/src/gsm.rs new file mode 100644 index 000000000000..2e824758aa5a --- /dev/null +++ b/crates/diesel_models/src/gsm.rs @@ -0,0 +1,106 @@ +//! Gateway status mapping + +use common_utils::{ + custom_serde, + events::{ApiEventMetric, ApiEventsType}, +}; +use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; +use time::PrimitiveDateTime; + +use crate::schema::gateway_status_map; + +#[derive( + Clone, + Debug, + Eq, + PartialEq, + router_derive::DebugAsDisplay, + Identifiable, + Queryable, + serde::Serialize, +)] +#[diesel(table_name = gateway_status_map, primary_key(connector, flow, sub_flow, code, message))] +pub struct GatewayStatusMap { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: String, + pub router_error: Option, + pub decision: String, + #[serde(with = "custom_serde::iso8601")] + pub created_at: PrimitiveDateTime, + #[serde(with = "custom_serde::iso8601")] + pub last_modified: PrimitiveDateTime, + pub step_up_possible: bool, +} + +#[derive(Clone, Debug, Eq, PartialEq, Insertable)] +#[diesel(table_name = gateway_status_map)] +pub struct GatewayStatusMappingNew { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: String, + pub router_error: Option, + pub decision: String, + pub step_up_possible: bool, +} + +#[derive( + Clone, + Debug, + PartialEq, + Eq, + AsChangeset, + router_derive::DebugAsDisplay, + Default, + serde::Deserialize, +)] +#[diesel(table_name = gateway_status_map)] +pub struct GatewayStatusMapperUpdateInternal { + pub connector: Option, + pub flow: Option, + pub sub_flow: Option, + pub code: Option, + pub message: Option, + pub status: Option, + pub router_error: Option>, + pub decision: Option, + pub step_up_possible: Option, +} + +#[derive(Debug)] +pub struct GatewayStatusMappingUpdate { + pub status: Option, + pub router_error: Option>, + pub decision: Option, + pub step_up_possible: Option, +} + +impl From for GatewayStatusMapperUpdateInternal { + fn from(value: GatewayStatusMappingUpdate) -> Self { + let GatewayStatusMappingUpdate { + decision, + status, + router_error, + step_up_possible, + } = value; + Self { + status, + router_error, + decision, + step_up_possible, + ..Default::default() + } + } +} + +impl ApiEventMetric for GatewayStatusMap { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index 2d459499a1bd..08d74fb8fd37 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -15,6 +15,7 @@ pub mod events; pub mod file; #[allow(unused)] pub mod fraud_check; +pub mod gsm; #[cfg(feature = "kv_store")] pub mod kv; pub mod locker_mock_up; diff --git a/crates/diesel_models/src/query.rs b/crates/diesel_models/src/query.rs index aeb09b969f13..ac3eeba44359 100644 --- a/crates/diesel_models/src/query.rs +++ b/crates/diesel_models/src/query.rs @@ -11,6 +11,7 @@ pub mod events; pub mod file; pub mod fraud_check; pub mod generics; +pub mod gsm; pub mod locker_mock_up; pub mod mandate; pub mod merchant_account; diff --git a/crates/diesel_models/src/query/gsm.rs b/crates/diesel_models/src/query/gsm.rs new file mode 100644 index 000000000000..bd44ce4dc378 --- /dev/null +++ b/crates/diesel_models/src/query/gsm.rs @@ -0,0 +1,100 @@ +use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; +use error_stack::report; + +use crate::{ + errors, gsm::*, query::generics, schema::gateway_status_map::dsl, PgPooledConn, StorageResult, +}; + +impl GatewayStatusMappingNew { + pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { + generics::generic_insert(conn, self).await + } +} + +impl GatewayStatusMap { + pub async fn find( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + dsl::connector + .eq(connector) + .and(dsl::flow.eq(flow)) + .and(dsl::sub_flow.eq(sub_flow)) + .and(dsl::code.eq(code)) + .and(dsl::message.eq(message)), + ) + .await + } + + pub async fn retrieve_decision( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> StorageResult { + Self::find(conn, connector, flow, sub_flow, code, message) + .await + .map(|item| item.decision) + } + + pub async fn update( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + gsm: GatewayStatusMappingUpdate, + ) -> StorageResult { + generics::generic_update_with_results::< + ::Table, + GatewayStatusMapperUpdateInternal, + _, + _, + >( + conn, + dsl::connector + .eq(connector) + .and(dsl::flow.eq(flow)) + .and(dsl::sub_flow.eq(sub_flow)) + .and(dsl::code.eq(code)) + .and(dsl::message.eq(message)), + gsm.into(), + ) + .await? + .first() + .cloned() + .ok_or_else(|| { + report!(errors::DatabaseError::NotFound) + .attach_printable("Error while updating gsm entry") + }) + } + + pub async fn delete( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> StorageResult { + generics::generic_delete::<::Table, _>( + conn, + dsl::connector + .eq(connector) + .and(dsl::flow.eq(flow)) + .and(dsl::sub_flow.eq(sub_flow)) + .and(dsl::code.eq(code)) + .and(dsl::message.eq(message)), + ) + .await + } +} diff --git a/crates/diesel_models/src/refund.rs b/crates/diesel_models/src/refund.rs index 73ff34030f81..62aec3fb27d8 100644 --- a/crates/diesel_models/src/refund.rs +++ b/crates/diesel_models/src/refund.rs @@ -227,3 +227,12 @@ pub struct RefundCoreWorkflow { pub merchant_id: String, pub payment_id: String, } + +impl common_utils::events::ApiEventMetric for Refund { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::Refund { + payment_id: Some(self.payment_id.clone()), + refund_id: self.refund_id.clone(), + }) + } +} diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 6881c2ff5068..5cf228e28180 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -332,6 +332,33 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use crate::enums::diesel_exports::*; + + gateway_status_map (connector, flow, sub_flow, code, message) { + #[max_length = 64] + connector -> Varchar, + #[max_length = 64] + flow -> Varchar, + #[max_length = 64] + sub_flow -> Varchar, + #[max_length = 255] + code -> Varchar, + #[max_length = 1024] + message -> Varchar, + #[max_length = 64] + status -> Varchar, + #[max_length = 64] + router_error -> Nullable, + #[max_length = 64] + decision -> Varchar, + created_at -> Timestamp, + last_modified -> Timestamp, + step_up_possible -> Bool, + } +} + diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; @@ -914,6 +941,7 @@ diesel::allow_tables_to_appear_in_same_query!( events, file_metadata, fraud_check, + gateway_status_map, locker_mock_up, mandate, merchant_account, diff --git a/crates/router/src/compatibility/stripe/refunds.rs b/crates/router/src/compatibility/stripe/refunds.rs index dc147443828c..ad4accf6ca74 100644 --- a/crates/router/src/compatibility/stripe/refunds.rs +++ b/crates/router/src/compatibility/stripe/refunds.rs @@ -149,8 +149,8 @@ pub async fn refund_update( path: web::Path, form_payload: web::Form, ) -> HttpResponse { - let refund_id = path.into_inner(); - let payload = form_payload.into_inner(); + let mut payload = form_payload.into_inner(); + payload.refund_id = path.into_inner(); let create_refund_update_req: refund_types::RefundUpdateRequest = payload.into(); let flow = Flow::RefundsUpdate; @@ -169,9 +169,7 @@ pub async fn refund_update( state.into_inner(), &req, create_refund_update_req, - |state, auth, req| { - refunds::refund_update_core(state, auth.merchant_account, &refund_id, req) - }, + |state, auth, req| refunds::refund_update_core(state, auth.merchant_account, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/compatibility/stripe/refunds/types.rs b/crates/router/src/compatibility/stripe/refunds/types.rs index e1486186491a..8d65a09187d3 100644 --- a/crates/router/src/compatibility/stripe/refunds/types.rs +++ b/crates/router/src/compatibility/stripe/refunds/types.rs @@ -17,6 +17,8 @@ pub struct StripeCreateRefundRequest { #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct StripeUpdateRefundRequest { + #[serde(skip)] + pub refund_id: String, pub metadata: Option, } @@ -58,6 +60,7 @@ impl From for refunds::RefundRequest { impl From for refunds::RefundUpdateRequest { fn from(req: StripeUpdateRefundRequest) -> Self { Self { + refund_id: req.refund_id, metadata: req.metadata, reason: None, } diff --git a/crates/router/src/compatibility/wrap.rs b/crates/router/src/compatibility/wrap.rs index 75cb07de02ba..1ab156d32ad4 100644 --- a/crates/router/src/compatibility/wrap.rs +++ b/crates/router/src/compatibility/wrap.rs @@ -7,6 +7,7 @@ use serde::Serialize; use crate::{ core::{api_locking, errors}, + events::api_logs::ApiEventMetric, routes::{app::AppStateInfo, metrics}, services::{self, api, authentication as auth, logger}, }; @@ -25,12 +26,12 @@ where F: Fn(A, U, T) -> Fut, Fut: Future, E2>>, E2: ErrorSwitch + std::error::Error + Send + Sync + 'static, - Q: Serialize + std::fmt::Debug + 'a, + Q: Serialize + std::fmt::Debug + 'a + ApiEventMetric, S: TryFrom + Serialize, E: Serialize + error_stack::Context + actix_web::ResponseError + Clone, error_stack::Report: services::EmbedError, errors::ApiErrorResponse: ErrorSwitch, - T: std::fmt::Debug + Serialize, + T: std::fmt::Debug + Serialize + ApiEventMetric, A: AppStateInfo + Clone, { let request_method = request.method().as_str(); diff --git a/crates/router/src/connector/aci.rs b/crates/router/src/connector/aci.rs index 0e325a04ddb0..f6389c802f9e 100644 --- a/crates/router/src/connector/aci.rs +++ b/crates/router/src/connector/aci.rs @@ -78,6 +78,7 @@ impl ConnectorCommon for Aci { .collect::>() .join("; ") }), + attempt_status: None, }) } } @@ -200,7 +201,9 @@ impl .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) - .body(types::PaymentsSyncType::get_request_body(self, req)?) + .body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -279,6 +282,7 @@ impl fn get_request_body( &self, req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { // encode only for for urlencoded things. let connector_router_data = aci::AciRouterData::try_from(( @@ -316,7 +320,9 @@ impl .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -385,6 +391,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = aci::AciCancelRequest::try_from(req)?; let aci_req = types::RequestBody::log_and_get_request_body( @@ -405,7 +412,9 @@ impl .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) - .body(types::PaymentsVoidType::get_request_body(self, req)?) + .body(types::PaymentsVoidType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -478,6 +487,7 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = aci::AciRouterData::try_from(( &self.get_currency_unit(), @@ -507,7 +517,9 @@ impl services::ConnectorIntegration CustomResult, errors::ConnectorError> { let authorize_req = types::PaymentsAuthorizeRouterData::from(( req, @@ -202,7 +204,9 @@ impl .url(&types::SetupMandateType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::SetupMandateType::get_headers(self, req, connectors)?) - .body(types::SetupMandateType::get_request_body(self, req)?) + .body(types::SetupMandateType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -251,6 +255,7 @@ impl code: response.error_code, message: response.message, reason: None, + attempt_status: None, }) } } @@ -304,6 +309,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = adyen::AdyenRouterData::try_from(( &self.get_currency_unit(), @@ -332,7 +338,9 @@ impl .headers(types::PaymentsCaptureType::get_headers( self, req, connectors, )?) - .body(types::PaymentsCaptureType::get_request_body(self, req)?) + .body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -366,6 +374,7 @@ impl code: response.error_code, message: response.message, reason: None, + attempt_status: None, }) } } @@ -395,6 +404,7 @@ impl fn get_request_body( &self, req: &types::RouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { // Adyen doesn't support PSync flow. We use PSync flow to fetch payment details, // specifically the redirect URL that takes the user to their Payment page. In non-redirection flows, @@ -479,7 +489,7 @@ impl req: &types::RouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { - let request_body = self.get_request_body(req)?; + let request_body = self.get_request_body(req, connectors)?; match request_body { Some(_) => Ok(Some( services::RequestBuilder::new() @@ -487,7 +497,9 @@ impl .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) - .body(types::PaymentsSyncType::get_request_body(self, req)?) + .body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) .build(), )), None => Ok(None), @@ -533,6 +545,7 @@ impl code: response.error_code, message: response.message, reason: None, + attempt_status: None, }) } @@ -628,6 +641,7 @@ impl fn get_request_body( &self, req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = adyen::AdyenRouterData::try_from(( &self.get_currency_unit(), @@ -660,7 +674,9 @@ impl .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -699,6 +715,7 @@ impl code: response.error_code, message: response.message, reason: None, + attempt_status: None, }) } } @@ -747,6 +764,7 @@ impl fn get_request_body( &self, req: &types::PaymentsBalanceRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = adyen::AdyenBalanceRequest::try_from(req)?; @@ -771,7 +789,9 @@ impl .headers(types::PaymentsBalanceType::get_headers( self, req, connectors, )?) - .body(types::PaymentsBalanceType::get_request_body(self, req)?) + .body(types::PaymentsBalanceType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -840,6 +860,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = adyen::AdyenCancelRequest::try_from(req)?; @@ -861,7 +882,9 @@ impl .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) - .body(types::PaymentsVoidType::get_request_body(self, req)?) + .body(types::PaymentsVoidType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -896,6 +919,7 @@ impl code: response.error_code, message: response.message, reason: None, + attempt_status: None, }) } } @@ -949,6 +973,7 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = adyen::AdyenPayoutCancelRequest::try_from(req)?; let adyen_req = types::RequestBody::log_and_get_request_body( @@ -969,7 +994,9 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = adyen::AdyenRouterData::try_from(( &self.get_currency_unit(), @@ -1060,7 +1088,9 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = adyen::AdyenRouterData::try_from(( &self.get_currency_unit(), @@ -1156,7 +1187,9 @@ impl .headers(types::PayoutEligibilityType::get_headers( self, req, connectors, )?) - .body(types::PayoutEligibilityType::get_request_body(self, req)?) + .body(types::PayoutEligibilityType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) @@ -1235,6 +1268,7 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = adyen::AdyenRouterData::try_from(( &self.get_currency_unit(), @@ -1263,7 +1297,9 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = adyen::AdyenRouterData::try_from(( &self.get_currency_unit(), @@ -1363,7 +1400,9 @@ impl services::ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = airwallex::AirwallexIntentRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -273,7 +277,9 @@ impl .url(&types::PaymentsInitType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsInitType::get_headers(self, req, connectors)?) - .body(types::PaymentsInitType::get_request_body(self, req)?) + .body(types::PaymentsInitType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -372,6 +378,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = airwallex::AirwallexRouterData::try_from(( &self.get_currency_unit(), @@ -403,7 +410,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = airwallex::AirwallexCompleteRequest::try_from(req)?; @@ -571,7 +581,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -634,6 +644,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = airwallex::AirwallexPaymentsCaptureRequest::try_from(req)?; @@ -659,7 +670,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = airwallex::AirwallexPaymentsCancelRequest::try_from(req)?; @@ -772,7 +786,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = airwallex::AirwallexRouterData::try_from(( &self.get_currency_unit(), @@ -847,7 +864,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = authorizedotnet::AuthorizedotnetRouterData::try_from(( &self.get_currency_unit(), @@ -176,7 +177,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = authorizedotnet::AuthorizedotnetCreateSyncRequest::try_from(req)?; let sync_request = types::RequestBody::log_and_get_request_body( @@ -261,7 +265,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = authorizedotnet::AuthorizedotnetRouterData::try_from(( &self.get_currency_unit(), @@ -362,7 +369,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = authorizedotnet::CancelOrCaptureTransactionRequest::try_from(req)?; @@ -445,7 +455,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = authorizedotnet::AuthorizedotnetRouterData::try_from(( &self.get_currency_unit(), @@ -542,7 +555,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = authorizedotnet::AuthorizedotnetRouterData::try_from(( &self.get_currency_unit(), @@ -634,7 +650,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = authorizedotnet::AuthorizedotnetRouterData::try_from(( &self.get_currency_unit(), @@ -735,7 +754,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -893,6 +912,7 @@ fn get_error_response( message: error.error_text.to_owned(), reason: Some(error.error_text), status_code, + attempt_status: None, }) }) .unwrap_or_else(|| types::ErrorResponse { @@ -900,6 +920,7 @@ fn get_error_response( message: consts::NO_ERROR_MESSAGE.to_string(), reason: None, status_code, + attempt_status: None, })), Some(authorizedotnet::TransactionResponse::AuthorizedotnetTransactionResponseError(_)) | None => { @@ -909,6 +930,7 @@ fn get_error_response( message: message.to_string(), reason: Some(message.to_string()), status_code, + attempt_status: None, }) } } diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 561723be46cf..884504154e8f 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -573,6 +573,7 @@ impl message: error.error_text.clone(), reason: None, status_code: item.http_code, + attempt_status: None, }) }); let metadata = transaction_response @@ -647,6 +648,7 @@ impl message: error.error_text.clone(), reason: None, status_code: item.http_code, + attempt_status: None, }) }); let metadata = transaction_response @@ -789,6 +791,7 @@ impl TryFrom types::Error message: message.message[0].text.clone(), reason: None, status_code, + attempt_status: None, } } diff --git a/crates/router/src/connector/bambora.rs b/crates/router/src/connector/bambora.rs index d5e8119b66c8..802be26408df 100644 --- a/crates/router/src/connector/bambora.rs +++ b/crates/router/src/connector/bambora.rs @@ -95,6 +95,7 @@ impl ConnectorCommon for Bambora { code: response.code.to_string(), message: response.message, reason: Some(serde_json::to_string(&response.details).unwrap_or_default()), + attempt_status: None, }) } } @@ -172,6 +173,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let request = bambora::BamboraPaymentsRequest::try_from(req)?; @@ -194,7 +196,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = bambora::BamboraPaymentsCaptureRequest::try_from(req)?; let bambora_req = types::RequestBody::log_and_get_request_body( @@ -367,7 +370,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let request = bambora::BamboraPaymentsRequest::try_from(req)?; @@ -463,7 +467,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = bambora::BamboraRefundRequest::try_from(req)?; let bambora_req = types::RequestBody::log_and_get_request_body( @@ -556,7 +563,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let request = bambora::BamboraThreedsContinueRequest::try_from(&req.request)?; @@ -765,7 +777,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(); Ok(Some(request)) diff --git a/crates/router/src/connector/bankofamerica.rs b/crates/router/src/connector/bankofamerica.rs index e25d99f9af3d..84870f7407fb 100644 --- a/crates/router/src/connector/bankofamerica.rs +++ b/crates/router/src/connector/bankofamerica.rs @@ -111,6 +111,7 @@ impl ConnectorCommon for Bankofamerica { code: response.code, message: response.message, reason: response.reason, + attempt_status: None, }) } } @@ -165,6 +166,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = bankofamerica::BankofamericaRouterData::try_from(( &self.get_currency_unit(), @@ -197,7 +199,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -331,7 +336,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = bankofamerica::BankofamericaRouterData::try_from(( &self.get_currency_unit(), @@ -419,7 +427,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = bitpay::BitpayRouterData::try_from(( &self.get_currency_unit(), @@ -204,7 +206,9 @@ impl ConnectorIntegration, pub current_time: Option, pub id: String, + pub order_id: Option, pub low_fee_detected: Option, pub display_amount_paid: Option, pub exception_status: ExceptionStatus, @@ -162,7 +163,7 @@ impl .data .url .map(|x| services::RedirectForm::from((x, services::Method::Get))); - let connector_id = types::ResponseId::ConnectorTransactionId(item.response.data.id); + let connector_id = types::ResponseId::ConnectorTransactionId(item.response.data.id.clone()); let attempt_status = item.response.data.status; Ok(Self { status: enums::AttemptStatus::from(attempt_status), @@ -172,7 +173,11 @@ impl mandate_reference: None, connector_metadata: None, network_txn_id: None, - connector_response_reference_id: None, + connector_response_reference_id: item + .response + .data + .order_id + .or(Some(item.response.data.id)), }), ..item.data }) diff --git a/crates/router/src/connector/bluesnap.rs b/crates/router/src/connector/bluesnap.rs index 6c39fc41b721..7bd2ce052538 100644 --- a/crates/router/src/connector/bluesnap.rs +++ b/crates/router/src/connector/bluesnap.rs @@ -126,6 +126,7 @@ impl ConnectorCommon for Bluesnap { .map(|error_code_message| error_code_message.error_message) .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), reason: Some(reason), + attempt_status: None, } } bluesnap::BluesnapErrors::Auth(error_res) => ErrorResponse { @@ -133,23 +134,28 @@ impl ConnectorCommon for Bluesnap { code: error_res.error_code.clone(), message: error_res.error_name.clone().unwrap_or(error_res.error_code), reason: Some(error_res.error_description), + attempt_status: None, }, bluesnap::BluesnapErrors::General(error_response) => { - let error_res = if res.status_code == 403 + let (error_res, attempt_status) = if res.status_code == 403 && error_response.contains(BLUESNAP_TRANSACTION_NOT_FOUND) { - format!( - "{} in bluesnap dashboard", - consts::REQUEST_TIMEOUT_PAYMENT_NOT_FOUND + ( + format!( + "{} in bluesnap dashboard", + consts::REQUEST_TIMEOUT_PAYMENT_NOT_FOUND + ), + Some(enums::AttemptStatus::Failure), // when bluesnap throws 403 for payment not found, we update the payment status to failure. ) } else { - error_response.clone() + (error_response.clone(), None) }; ErrorResponse { status_code: res.status_code, code: consts::NO_ERROR_CODE.to_string(), message: error_response, reason: Some(error_res), + attempt_status, } } }; @@ -257,6 +263,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = bluesnap::BluesnapVoidRequest::try_from(req)?; let bluesnap_req = types::RequestBody::log_and_get_request_body( @@ -277,7 +284,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = bluesnap::BluesnapRouterData::try_from(( &self.get_currency_unit(), @@ -459,7 +469,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = bluesnap::BluesnapCreateWalletToken::try_from(req)?; let bluesnap_req = types::RequestBody::log_and_get_request_body( @@ -546,7 +559,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = bluesnap::BluesnapRouterData::try_from(( &self.get_currency_unit(), @@ -662,7 +678,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = bluesnap::BluesnapRouterData::try_from(( &self.get_currency_unit(), @@ -783,7 +802,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -849,6 +868,7 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = bluesnap::BluesnapRouterData::try_from(( &self.get_currency_unit(), @@ -877,7 +897,9 @@ impl ConnectorIntegration get_xml_deserialized(res), } @@ -206,6 +207,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = boku::BokuPaymentsRequest::try_from(req)?; let boku_req = types::RequestBody::log_and_get_request_body( @@ -231,7 +233,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = boku::BokuPsyncRequest::try_from(req)?; let boku_req = types::RequestBody::log_and_get_request_body( @@ -317,7 +322,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -394,7 +402,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = boku::BokuRefundRequest::try_from(req)?; let boku_req = types::RequestBody::log_and_get_request_body( @@ -484,7 +495,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = boku::BokuRsyncRequest::try_from(req)?; let boku_req = types::RequestBody::log_and_get_request_body( @@ -563,7 +577,9 @@ impl ConnectorIntegration CustomResult Ok(ErrorResponse { @@ -139,6 +140,7 @@ impl ConnectorCommon for Braintree { code: consts::NO_ERROR_CODE.to_string(), message: consts::NO_ERROR_MESSAGE.to_string(), reason: Some(response.errors), + attempt_status: None, }), Err(error_msg) => { logger::error!(deserialization_error =? error_msg); @@ -240,7 +242,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = braintree::BraintreeSessionRequest::try_from(req)?; let braintree_session_request = types::RequestBody::log_and_get_request_body( @@ -322,6 +327,7 @@ impl fn get_request_body( &self, req: &types::TokenizationRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = braintree_graphql_transformers::BraintreeTokenRequest::try_from(req)?; @@ -347,7 +353,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )), false => Ok(None), @@ -437,6 +445,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_api_version = &req.connector_api_version.clone(); let connector_router_data = @@ -482,7 +491,9 @@ impl ConnectorIntegration Err(errors::ConnectorError::NotImplemented( @@ -587,6 +598,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_api_version = &req.connector_api_version; match self.is_braintree_graphql_version(connector_api_version) { @@ -618,7 +630,9 @@ impl ConnectorIntegration Ok(Some( @@ -627,7 +641,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_api_version = &req.connector_api_version; let connector_router_data = @@ -918,7 +937,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_api_version = &req.connector_api_version; match self.is_braintree_graphql_version(connector_api_version) { @@ -1055,6 +1077,7 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_api_version = &req.connector_api_version; let connector_router_data = @@ -1101,7 +1124,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_api_version = &req.connector_api_version; match self.is_braintree_graphql_version(connector_api_version) { @@ -1219,7 +1245,9 @@ impl ConnectorIntegration Ok(None), @@ -1584,6 +1612,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCompleteAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = braintree_graphql_transformers::BraintreeRouterData::try_from(( @@ -1629,7 +1658,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )), diff --git a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs index b622e041915d..bf51973237c5 100644 --- a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs +++ b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs @@ -316,6 +316,7 @@ fn get_error_response( message: error_msg.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), reason: error_reason, status_code: http_code, + attempt_status: None, }) } diff --git a/crates/router/src/connector/cashtocode.rs b/crates/router/src/connector/cashtocode.rs index ed994dca31fc..12a52e485396 100644 --- a/crates/router/src/connector/cashtocode.rs +++ b/crates/router/src/connector/cashtocode.rs @@ -119,6 +119,7 @@ impl ConnectorCommon for Cashtocode { code: response.error.to_string(), message: response.error_description, reason: None, + attempt_status: None, }) } } @@ -202,6 +203,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = cashtocode::CashtocodePaymentsRequest::try_from(req)?; let cashtocode_req = types::RequestBody::log_and_get_request_body( @@ -227,7 +229,9 @@ impl ConnectorIntegration status_code: item.http_code, message: error_data.error_description, reason: None, + attempt_status: None, }), ), CashtocodePaymentsResponse::CashtoCodeData(response_data) => { diff --git a/crates/router/src/connector/checkout.rs b/crates/router/src/connector/checkout.rs index f4cc4ac9640e..f24c08233ed7 100644 --- a/crates/router/src/connector/checkout.rs +++ b/crates/router/src/connector/checkout.rs @@ -131,6 +131,7 @@ impl ConnectorCommon for Checkout { .error_codes .map(|errors| errors.join(" & ")) .or(response.error_type), + attempt_status: None, }) } } @@ -207,6 +208,7 @@ impl fn get_request_body( &self, req: &types::TokenizationRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = checkout::TokenRequest::try_from(req)?; let checkout_req = types::RequestBody::log_and_get_request_body( @@ -228,7 +230,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -312,6 +316,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = checkout::CheckoutRouterData::try_from(( &self.get_currency_unit(), @@ -341,7 +346,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = checkout::CheckoutRouterData::try_from(( &self.get_currency_unit(), @@ -531,7 +541,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = checkout::PaymentVoidRequest::try_from(req)?; let checkout_req = types::RequestBody::log_and_get_request_body( @@ -608,7 +621,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = checkout::CheckoutRouterData::try_from(( &self.get_currency_unit(), @@ -703,7 +719,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let checkout_req = checkout::Evidence::try_from(req)?; let checkout_req_string = types::RequestBody::log_and_get_request_body( @@ -1064,7 +1085,9 @@ impl .headers(types::SubmitEvidenceType::get_headers( self, req, connectors, )?) - .body(types::SubmitEvidenceType::get_request_body(self, req)?) + .body(types::SubmitEvidenceType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) } diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index 53182e65ed5b..6ad040da2842 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -576,6 +576,7 @@ impl TryFrom> .clone() .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), reason: item.response.response_summary, + attempt_status: None, }) } else { None @@ -623,6 +624,7 @@ impl TryFrom> .clone() .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), reason: item.response.response_summary, + attempt_status: None, }) } else { None diff --git a/crates/router/src/connector/coinbase.rs b/crates/router/src/connector/coinbase.rs index d50e490cfc30..5704ea15b005 100644 --- a/crates/router/src/connector/coinbase.rs +++ b/crates/router/src/connector/coinbase.rs @@ -108,6 +108,7 @@ impl ConnectorCommon for Coinbase { code: response.error.error_type, message: response.error.message, reason: response.error.code, + attempt_status: None, }) } } @@ -183,6 +184,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = coinbase::CoinbasePaymentsRequest::try_from(req)?; let coinbase_payment_request = types::RequestBody::log_and_get_request_body( @@ -207,7 +209,9 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { let api_method; - let payload = match self.get_request_body(req)? { + let payload = match self.get_request_body(req, connectors)? { Some(val) => { let body = types::RequestBody::get_inner_value(val).peek().to_owned(); api_method = "POST".to_string(); @@ -167,6 +167,7 @@ impl ConnectorCommon for Cryptopay { code: response.error.code, message: response.error.message, reason: response.error.reason, + attempt_status: None, }) } } @@ -216,6 +217,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = cryptopay::CryptopayRouterData::try_from(( &self.get_currency_unit(), @@ -248,7 +250,9 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { let date = OffsetDateTime::now_utc(); - let cybersource_req = self.get_request_body(req)?; + let cybersource_req = self.get_request_body(req, connectors)?; let auth = cybersource::CybersourceAuthType::try_from(&req.connector_auth_type)?; let merchant_account = auth.merchant_account.clone(); let base_url = connectors.cybersource.base_url.as_str(); @@ -297,6 +298,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = cybersource::CybersourcePaymentsRequest::try_from(req)?; let cybersource_payments_request = types::RequestBody::log_and_get_request_body( @@ -319,7 +321,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( types::RequestBody::log_and_get_request_body("{}".to_string(), Ok) @@ -471,6 +476,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = cybersource::CybersourceRouterData::try_from(( &self.get_currency_unit(), @@ -502,7 +508,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( types::RequestBody::log_and_get_request_body("{}".to_string(), Ok) @@ -587,7 +594,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = cybersource::CybersourceRefundRequest::try_from(req)?; let cybersource_refund_request = types::RequestBody::log_and_get_request_body( @@ -678,7 +686,7 @@ impl ConnectorIntegration message: error.message, reason: Some(error.reason), status_code: item.http_code, + attempt_status: None, }), _ => Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId( diff --git a/crates/router/src/connector/dlocal.rs b/crates/router/src/connector/dlocal.rs index b706d694a3d5..64d3e6f1c12f 100644 --- a/crates/router/src/connector/dlocal.rs +++ b/crates/router/src/connector/dlocal.rs @@ -53,10 +53,10 @@ where fn build_headers( &self, req: &types::RouterData, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { - let dlocal_req = match self.get_request_body(req)? { + let dlocal_req = match self.get_request_body(req, connectors)? { Some(val) => val, None => types::RequestBody::log_and_get_request_body("".to_string(), Ok) .change_context(errors::ConnectorError::RequestEncodingFailed)?, @@ -135,6 +135,7 @@ impl ConnectorCommon for Dlocal { code: response.code.to_string(), message: response.message, reason: response.param, + attempt_status: None, }) } } @@ -210,6 +211,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = dlocal::DlocalRouterData::try_from(( &self.get_currency_unit(), @@ -241,7 +243,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = dlocal::DlocalPaymentsCaptureRequest::try_from(req)?; let dlocal_payments_capture_request = types::RequestBody::log_and_get_request_body( @@ -391,7 +396,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = dlocal::DlocalRouterData::try_from(( &self.get_currency_unit(), @@ -545,7 +553,9 @@ impl ConnectorIntegration ConnectorCommon for DummyConnector { code: response.error.code, message: response.error.message, reason: response.error.reason, + attempt_status: None, }) } } @@ -187,6 +188,7 @@ impl fn get_request_body( &self, req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = transformers::DummyConnectorPaymentsRequest::::try_from(req)?; let dummmy_payments_request = types::RequestBody::log_and_get_request_body( @@ -212,7 +214,9 @@ impl .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -347,6 +351,7 @@ impl fn get_request_body( &self, _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -429,6 +434,7 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = transformers::DummyConnectorRefundRequest::try_from(req)?; let dummmy_refund_request = types::RequestBody::log_and_get_request_body( @@ -451,7 +457,9 @@ impl ConnectorIntegration ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { let timestamp = OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000; let auth: fiserv::FiservAuthType = @@ -70,7 +70,7 @@ where let mut auth_header = self.get_auth_header(&req.connector_auth_type)?; let fiserv_req = self - .get_request_body(req)? + .get_request_body(req, connectors)? .ok_or(errors::ConnectorError::RequestEncodingFailed)?; let client_request_id = Uuid::new_v4().to_string(); @@ -151,6 +151,7 @@ impl ConnectorCommon for Fiserv { message: first_error.message.to_owned(), reason: first_error.field.to_owned(), status_code: res.status_code, + attempt_status: None, }) }) .unwrap_or(types::ErrorResponse { @@ -158,6 +159,7 @@ impl ConnectorCommon for Fiserv { message: consts::NO_ERROR_MESSAGE.to_string(), reason: None, status_code: res.status_code, + attempt_status: None, })) } } @@ -244,6 +246,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = fiserv::FiservCancelRequest::try_from(req)?; let fiserv_payments_cancel_request = types::RequestBody::log_and_get_request_body( @@ -265,7 +268,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = fiserv::FiservSyncRequest::try_from(req)?; let fiserv_payments_sync_request = types::RequestBody::log_and_get_request_body( @@ -351,7 +357,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let router_obj = fiserv::FiservRouterData::try_from(( &self.get_currency_unit(), @@ -432,7 +441,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let router_obj = fiserv::FiservRouterData::try_from(( &self.get_currency_unit(), @@ -545,7 +557,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let router_obj = fiserv::FiservRouterData::try_from(( &self.get_currency_unit(), @@ -634,7 +649,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = fiserv::FiservSyncRequest::try_from(req)?; let fiserv_sync_request = types::RequestBody::log_and_get_request_body( @@ -714,7 +732,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = forte::FortePaymentsRequest::try_from(req)?; let forte_req = types::RequestBody::log_and_get_request_body( @@ -225,7 +227,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = forte::ForteCaptureRequest::try_from(req)?; let forte_req = types::RequestBody::log_and_get_request_body( @@ -379,7 +384,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = forte::ForteCancelRequest::try_from(req)?; let forte_req = types::RequestBody::log_and_get_request_body( @@ -460,7 +468,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = forte::ForteRefundRequest::try_from(req)?; let forte_req = types::RequestBody::log_and_get_request_body( @@ -541,7 +552,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let globalpay_req = types::RequestBody::log_and_get_request_body("{}".to_string(), Ok) .change_context(errors::ConnectorError::RequestEncodingFailed)?; @@ -186,7 +188,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -262,7 +264,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = GlobalpayRefreshTokenRequest::try_from(req)?; let globalpay_req = types::RequestBody::log_and_get_request_body( @@ -313,6 +318,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = requests::GlobalpayCancelRequest::try_from(req)?; let globalpay_req = types::RequestBody::log_and_get_request_body( @@ -539,6 +548,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = requests::GlobalpayCaptureRequest::try_from(req)?; let globalpay_req = types::RequestBody::log_and_get_request_body( @@ -562,7 +572,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = GlobalpayPaymentsRequest::try_from(req)?; let globalpay_req = types::RequestBody::log_and_get_request_body( @@ -653,7 +666,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = requests::GlobalpayRefundRequest::try_from(req)?; let globalpay_req = types::RequestBody::log_and_get_request_body( @@ -740,7 +756,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = globepay::GlobepayPaymentsRequest::try_from(req)?; let globepay_req = types::RequestBody::log_and_get_request_body( @@ -212,7 +214,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = globepay::GlobepayRefundRequest::try_from(req)?; let globepay_req = types::RequestBody::log_and_get_request_body( @@ -386,7 +391,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = gocardless::GocardlessCustomerRequest::try_from(req)?; let gocardless_req = types::RequestBody::log_and_get_request_body( @@ -177,7 +179,9 @@ impl .headers(types::ConnectorCustomerType::get_headers( self, req, connectors, )?) - .body(types::ConnectorCustomerType::get_request_body(self, req)?) + .body(types::ConnectorCustomerType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -247,6 +251,7 @@ impl fn get_request_body( &self, req: &types::TokenizationRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = gocardless::GocardlessBankAccountRequest::try_from(req)?; let gocardless_req = types::RequestBody::log_and_get_request_body( @@ -268,7 +273,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -365,6 +372,7 @@ impl fn get_request_body( &self, req: &types::SetupMandateRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = gocardless::GocardlessMandateRequest::try_from(req)?; let gocardless_req = types::RequestBody::log_and_get_request_body( @@ -388,7 +396,9 @@ impl .url(&types::SetupMandateType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::SetupMandateType::get_headers(self, req, connectors)?) - .body(types::SetupMandateType::get_request_body(self, req)?) + .body(types::SetupMandateType::get_request_body( + self, req, connectors, + )?) .build(), )) } else { @@ -446,6 +456,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = gocardless::GocardlessRouterData::try_from(( &self.get_currency_unit(), @@ -477,7 +488,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = gocardless::GocardlessRouterData::try_from(( &self.get_currency_unit(), @@ -639,7 +653,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = helcim::HelcimVerifyRequest::try_from(req)?; @@ -213,7 +215,9 @@ impl .url(&types::SetupMandateType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::SetupMandateType::get_headers(self, req, connectors)?) - .body(types::SetupMandateType::get_request_body(self, req)?) + .body(types::SetupMandateType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -269,6 +273,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = helcim::HelcimRouterData::try_from(( &self.get_currency_unit(), @@ -300,7 +305,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = helcim::HelcimRouterData::try_from(( &self.get_currency_unit(), @@ -469,7 +477,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = helcim::HelcimVoidRequest::try_from(req)?; let helcim_req = types::RequestBody::log_and_get_request_body( @@ -545,7 +556,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = helcim::HelcimRouterData::try_from(( &self.get_currency_unit(), @@ -627,7 +641,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = iatapay::IatapayAuthUpdateRequest::try_from(req)?; let iatapay_req = types::RequestBody::log_and_get_request_body( @@ -198,7 +200,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = iatapay::IatapayRouterData::try_from(( &self.get_currency_unit(), @@ -305,7 +311,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = iatapay::IatapayRouterData::try_from(( &self.get_currency_unit(), @@ -488,7 +497,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = klarna::KlarnaSessionRequest::try_from(req)?; // encode only for for urlencoded things. @@ -177,7 +179,9 @@ impl .headers(types::PaymentsSessionType::get_headers( self, req, connectors, )?) - .body(types::PaymentsSessionType::get_request_body(self, req)?) + .body(types::PaymentsSessionType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -409,6 +413,7 @@ impl fn get_request_body( &self, req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = klarna::KlarnaRouterData::try_from(( &self.get_currency_unit(), @@ -440,7 +445,9 @@ impl .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) .build(), )) } diff --git a/crates/router/src/connector/mollie.rs b/crates/router/src/connector/mollie.rs index 337e1b78c9a9..ef3eb6a3e7b3 100644 --- a/crates/router/src/connector/mollie.rs +++ b/crates/router/src/connector/mollie.rs @@ -98,6 +98,7 @@ impl ConnectorCommon for Mollie { .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()), message: response.detail, reason: response.field, + attempt_status: None, }) } } @@ -149,6 +150,7 @@ impl fn get_request_body( &self, req: &types::TokenizationRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = mollie::MollieCardTokenRequest::try_from(req)?; let mollie_req = types::RequestBody::log_and_get_request_body( @@ -169,7 +171,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -232,6 +236,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let router_obj = mollie::MollieRouterData::try_from(( &self.get_currency_unit(), @@ -263,7 +268,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let router_obj = mollie::MollieRouterData::try_from(( &self.get_currency_unit(), @@ -454,7 +462,9 @@ impl ConnectorIntegration, #[serde(flatten)] payment_method_data: PaymentMethodData, - metadata: Option, + metadata: Option, sequence_type: SequenceType, mandate_id: Option, } @@ -148,8 +148,10 @@ pub struct Address { pub country: api_models::enums::CountryAlpha2, } -pub struct MollieBrowserInfo { - language: String, +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MollieMetadata { + pub order_id: String, } impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MolliePaymentsRequest { @@ -216,7 +218,9 @@ impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MollieP webhook_url: "".to_string(), locale: None, payment_method_data, - metadata: None, + metadata: Some(MollieMetadata { + order_id: item.router_data.connector_request_reference_id.clone(), + }), sequence_type: SequenceType::Oneoff, mandate_id: None, }) @@ -287,12 +291,7 @@ impl TryFrom<&types::TokenizationRouterData> for MollieCardTokenRequest { let card_expiry_date = ccard.get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned()); let card_cvv = ccard.card_cvc; - let browser_info = get_browser_info(item)?; - let locale = browser_info - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "browser_info.language", - })? - .language; + let locale = item.request.get_browser_info()?.get_language()?; let testmode = item.test_mode .ok_or(errors::ConnectorError::MissingRequiredField { @@ -386,24 +385,6 @@ fn get_address_details( Ok(address_details) } -fn get_browser_info( - item: &types::TokenizationRouterData, -) -> Result, error_stack::Report> { - if matches!(item.auth_type, enums::AuthenticationType::ThreeDs) { - item.request - .browser_info - .as_ref() - .map(|info| { - Ok(MollieBrowserInfo { - language: info.get_language()?, - }) - }) - .transpose() - } else { - Ok(None) - } -} - #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MolliePaymentsResponse { @@ -411,7 +392,7 @@ pub struct MolliePaymentsResponse { pub id: String, pub amount: Amount, pub description: Option, - pub metadata: Option, + pub metadata: Option, pub status: MolliePaymentStatus, pub is_cancelable: Option, pub sequence_type: SequenceType, @@ -544,12 +525,12 @@ impl Ok(Self { status: enums::AttemptStatus::from(item.response.status), response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), + resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()), redirection_data: url, mandate_reference: None, connector_metadata: None, network_txn_id: None, - connector_response_reference_id: None, + connector_response_reference_id: Some(item.response.id), }), ..item.data }) @@ -561,6 +542,7 @@ impl pub struct MollieRefundRequest { amount: Amount, description: Option, + metadata: Option, } impl TryFrom<&MollieRouterData<&types::RefundsRouterData>> for MollieRefundRequest { @@ -575,6 +557,9 @@ impl TryFrom<&MollieRouterData<&types::RefundsRouterData>> for MollieRefun Ok(Self { amount, description: item.router_data.request.reason.to_owned(), + metadata: Some(MollieMetadata { + order_id: item.router_data.request.refund_id.clone(), + }), }) } } @@ -589,7 +574,7 @@ pub struct RefundResponse { settlement_amount: Option, status: MollieRefundStatus, description: Option, - metadata: serde_json::Value, + metadata: Option, payment_id: String, #[serde(rename = "_links")] links: Links, @@ -642,6 +627,4 @@ pub struct ErrorResponse { pub title: Option, pub detail: String, pub field: Option, - #[serde(rename = "_links")] - pub links: Option, } diff --git a/crates/router/src/connector/multisafepay.rs b/crates/router/src/connector/multisafepay.rs index e09f242abc8a..9dc54e7b72e3 100644 --- a/crates/router/src/connector/multisafepay.rs +++ b/crates/router/src/connector/multisafepay.rs @@ -83,6 +83,7 @@ impl ConnectorCommon for Multisafepay { code: response.error_code.to_string(), message: response.error_info, reason: None, + attempt_status: None, }) } } @@ -261,6 +262,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = multisafepay::MultisafepayRouterData::try_from(( &self.get_currency_unit(), @@ -292,7 +294,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = multisafepay::MultisafepayRouterData::try_from(( &self.get_currency_unit(), @@ -391,7 +396,9 @@ impl ConnectorIntegration message: error_response.error_info.clone(), reason: Some(error_response.error_info), status_code: item.http_code, + attempt_status: None, }), ..item.data }), @@ -808,6 +809,7 @@ impl TryFrom CustomResult, errors::ConnectorError> { let req_obj = nexinets::NexinetsPaymentsRequest::try_from(req)?; let nexinets_req = types::RequestBody::log_and_get_request_body( @@ -224,7 +226,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = nexinets::NexinetsCaptureOrVoidRequest::try_from(req)?; let nexinets_req = types::RequestBody::log_and_get_request_body( @@ -384,7 +389,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = nexinets::NexinetsCaptureOrVoidRequest::try_from(req)?; let nexinets_req = types::RequestBody::log_and_get_request_body( @@ -465,7 +473,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = nexinets::NexinetsRefundRequest::try_from(req)?; let nexinets_req = types::RequestBody::log_and_get_request_body( @@ -550,7 +561,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = nmi::NmiPaymentsRequest::try_from(req)?; let nmi_req = types::RequestBody::log_and_get_request_body( @@ -163,7 +164,9 @@ impl .method(services::Method::Post) .url(&types::SetupMandateType::get_url(self, req, connectors)?) .headers(types::SetupMandateType::get_headers(self, req, connectors)?) - .body(types::SetupMandateType::get_request_body(self, req)?) + .body(types::SetupMandateType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -213,6 +216,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = nmi::NmiRouterData::try_from(( &self.get_currency_unit(), @@ -243,7 +247,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = nmi::NmiSyncRequest::try_from(req)?; let nmi_req = types::RequestBody::log_and_get_request_body( @@ -313,7 +320,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = nmi::NmiRouterData::try_from(( &self.get_currency_unit(), @@ -388,7 +398,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = nmi::NmiCancelRequest::try_from(req)?; let nmi_req = types::RequestBody::log_and_get_request_body( @@ -458,7 +471,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = nmi::NmiRouterData::try_from(( &self.get_currency_unit(), @@ -534,7 +550,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = nmi::NmiSyncRequest::try_from(req)?; let nmi_req = types::RequestBody::log_and_get_request_body( @@ -602,7 +621,9 @@ impl ConnectorIntegration for types::ErrorResponse { message: response.responsetext, reason: None, status_code: http_code, + attempt_status: None, } } } diff --git a/crates/router/src/connector/noon.rs b/crates/router/src/connector/noon.rs index 156e10928d3e..866f8f4c58fa 100644 --- a/crates/router/src/connector/noon.rs +++ b/crates/router/src/connector/noon.rs @@ -136,6 +136,7 @@ impl ConnectorCommon for Noon { code: response.result_code.to_string(), message: response.class_description, reason: Some(response.message), + attempt_status: None, }) } } @@ -201,6 +202,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = noon::NoonPaymentsRequest::try_from(req)?; let noon_req = types::RequestBody::log_and_get_request_body( @@ -226,7 +228,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = noon::NoonPaymentsActionRequest::try_from(req)?; let noon_req = types::RequestBody::log_and_get_request_body( @@ -370,7 +375,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = noon::NoonPaymentsCancelRequest::try_from(req)?; let noon_req = types::RequestBody::log_and_get_request_body( @@ -445,7 +453,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = noon::NoonPaymentsActionRequest::try_from(req)?; let noon_req = types::RequestBody::log_and_get_request_body( @@ -520,7 +531,9 @@ impl ConnectorIntegration message: error_message.clone(), reason: Some(error_message), status_code: item.http_code, + attempt_status: None, }), _ => { let connector_response_reference_id = diff --git a/crates/router/src/connector/nuvei.rs b/crates/router/src/connector/nuvei.rs index 754ba5ff7443..15702829d378 100644 --- a/crates/router/src/connector/nuvei.rs +++ b/crates/router/src/connector/nuvei.rs @@ -149,6 +149,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCompleteAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let meta: nuvei::NuveiMeta = utils::to_connector_meta(req.request.connector_meta.clone())?; let req_obj = nuvei::NuveiPaymentsRequest::try_from((req, meta.session_token))?; @@ -176,7 +177,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -235,6 +236,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiPaymentFlowRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -255,7 +257,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiPaymentSyncRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -339,7 +344,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiPaymentFlowRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -421,7 +429,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiPaymentsRequest::try_from((req, req.get_session_token()?))?; let req = types::RequestBody::log_and_get_request_body( @@ -581,7 +592,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiSessionRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -670,7 +684,7 @@ impl self, req, connectors, )?) .body(types::PaymentsPreAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -727,6 +741,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiPaymentsRequest::try_from((req, req.get_session_token()?))?; let req = types::RequestBody::log_and_get_request_body( @@ -749,7 +764,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = nuvei::NuveiPaymentFlowRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -828,7 +846,9 @@ impl ConnectorIntegration( .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), reason: None, status_code: http_code, + attempt_status: None, }) } diff --git a/crates/router/src/connector/opayo.rs b/crates/router/src/connector/opayo.rs index 89e16416d27f..cc517ca1f3b8 100644 --- a/crates/router/src/connector/opayo.rs +++ b/crates/router/src/connector/opayo.rs @@ -107,6 +107,7 @@ impl ConnectorCommon for Opayo { code: response.code, message: response.message, reason: response.reason, + attempt_status: None, }) } } @@ -172,6 +173,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = opayo::OpayoPaymentsRequest::try_from(req)?; let opayo_req = types::RequestBody::log_and_get_request_body( @@ -197,7 +199,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -331,7 +336,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = opayo::OpayoRefundRequest::try_from(req)?; @@ -412,7 +420,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = opennode::OpennodeRouterData::try_from(( &self.get_currency_unit(), @@ -202,7 +204,9 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { let auth = payeezy::PayeezyAuthType::try_from(&req.connector_auth_type)?; - let option_request_payload = self.get_request_body(req)?; + let option_request_payload = self.get_request_body(req, connectors)?; let request_payload = option_request_payload.map_or("{}".to_string(), |payload| { types::RequestBody::get_inner_value(payload).expose() }); @@ -123,6 +123,7 @@ impl ConnectorCommon for Payeezy { code: response.transaction_status, message: error_messages.join(", "), reason: None, + attempt_status: None, }) } } @@ -199,6 +200,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = payeezy::PayeezyCaptureOrVoidRequest::try_from(req)?; let payeezy_req = types::RequestBody::log_and_get_request_body( @@ -219,7 +221,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let router_obj = payeezy::PayeezyRouterData::try_from(( &self.get_currency_unit(), @@ -324,7 +329,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let router_obj = payeezy::PayeezyRouterData::try_from(( &self.get_currency_unit(), @@ -421,7 +429,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let router_obj = payeezy::PayeezyRouterData::try_from(( &self.get_currency_unit(), @@ -513,7 +524,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = payme::CaptureBuyerRequest::try_from(req)?; @@ -172,7 +174,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), ), AuthenticationType::NoThreeDs => None, @@ -252,6 +256,7 @@ impl fn get_request_body( &self, req: &types::PaymentsPreProcessingRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let amount = req.request.get_amount()?; let currency = req.request.get_currency()?; @@ -282,7 +287,7 @@ impl self, req, connectors, )?) .body(types::PaymentsPreProcessingType::get_request_body( - self, req, + self, req, connectors, )?) .build(), ); @@ -377,6 +382,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCompleteAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = payme::Pay3dsRequest::try_from(req)?; let payme_req = types::RequestBody::log_and_get_request_body( @@ -402,7 +408,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -479,6 +485,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = payme::PaymeRouterData::try_from(( &self.get_currency_unit(), @@ -510,7 +517,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = payme::PaymeQuerySaleRequest::try_from(req)?; let payme_req = types::RequestBody::log_and_get_request_body( @@ -594,7 +604,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = payme::PaymeRouterData::try_from(( &self.get_currency_unit(), @@ -687,7 +700,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = payme::PaymeRouterData::try_from(( &self.get_currency_unit(), @@ -792,7 +808,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = payme::PaymeQueryTransactionRequest::try_from(req)?; let payme_req = types::RequestBody::log_and_get_request_body( @@ -873,7 +892,9 @@ impl ConnectorIntegration for types::ErrorResponse { .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), reason: pay_sale_response.status_error_details.to_owned(), status_code: http_code, + attempt_status: None, } } } @@ -308,6 +309,7 @@ impl From<(&SaleQuery, u16)> for types::ErrorResponse { .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), reason: sale_query_response.sale_error_text.clone(), status_code: http_code, + attempt_status: None, } } } diff --git a/crates/router/src/connector/paypal.rs b/crates/router/src/connector/paypal.rs index 854d48dcaadc..d4ab481eb9de 100644 --- a/crates/router/src/connector/paypal.rs +++ b/crates/router/src/connector/paypal.rs @@ -91,6 +91,7 @@ impl Paypal { code: response.name, message: response.message.clone(), reason: error_reason.or(Some(response.message)), + attempt_status: None, }) } } @@ -203,6 +204,7 @@ impl ConnectorCommon for Paypal { code: response.name, message: response.message.clone(), reason, + attempt_status: None, }) } } @@ -281,6 +283,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = paypal::PaypalAuthUpdateRequest::try_from(req)?; let paypal_req = types::RequestBody::log_and_get_request_body( @@ -302,7 +305,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = paypal::PaypalRouterData::try_from(( &self.get_currency_unit(), @@ -412,7 +419,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = paypal::PaypalRouterData::try_from(( &self.get_currency_unit(), @@ -714,7 +724,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = paypal::PaypalRouterData::try_from(( &self.get_currency_unit(), @@ -874,7 +887,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = paypal::PaypalSourceVerificationRequest::try_from(&req.request)?; let paypal_req = types::RequestBody::log_and_get_request_body( diff --git a/crates/router/src/connector/payu.rs b/crates/router/src/connector/payu.rs index 6433735f7c53..9a8d4734f837 100644 --- a/crates/router/src/connector/payu.rs +++ b/crates/router/src/connector/payu.rs @@ -96,6 +96,7 @@ impl ConnectorCommon for Payu { code: response.status.status_code, message: response.status.status_desc, reason: response.status.code_literal, + attempt_status: None, }) } } @@ -243,6 +244,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = payu::PayuAuthUpdateRequest::try_from(req)?; let payu_req = types::RequestBody::log_and_get_request_body( @@ -265,7 +267,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = payu::PayuPaymentsCaptureRequest::try_from(req)?; let payu_req = types::RequestBody::log_and_get_request_body( @@ -439,7 +445,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = payu::PayuPaymentsRequest::try_from(req)?; let payu_req = types::RequestBody::log_and_get_request_body( @@ -539,7 +548,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = payu::PayuRefundRequest::try_from(req)?; let payu_req = types::RequestBody::log_and_get_request_body( @@ -626,7 +638,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = powertranz::PowertranzPaymentsRequest::try_from(req)?; let powertranz_req = types::RequestBody::log_and_get_request_body( @@ -218,7 +220,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let redirect_payload: powertranz::RedirectResponsePayload = req .request @@ -308,7 +313,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -370,6 +375,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = powertranz::PowertranzBaseRequest::try_from(&req.request)?; let powertranz_req = types::RequestBody::log_and_get_request_body( @@ -393,7 +399,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = powertranz::PowertranzBaseRequest::try_from(&req.request)?; let powertranz_req = types::RequestBody::log_and_get_request_body( @@ -481,7 +490,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = powertranz::PowertranzBaseRequest::try_from(req)?; let powertranz_req = types::RequestBody::log_and_get_request_body( @@ -542,7 +554,9 @@ impl ConnectorIntegration>() .join(", "), ), + attempt_status: None, } }) } else if !ISO_SUCCESS_CODES.contains(&item.iso_response_code.as_str()) { @@ -452,6 +453,7 @@ fn build_error_response( code: item.iso_response_code.clone(), message: item.response_message.clone(), reason: Some(item.response_message.clone()), + attempt_status: None, }) } else { None diff --git a/crates/router/src/connector/prophetpay.rs b/crates/router/src/connector/prophetpay.rs index 0e8d5100ea35..417c34207e05 100644 --- a/crates/router/src/connector/prophetpay.rs +++ b/crates/router/src/connector/prophetpay.rs @@ -110,6 +110,7 @@ impl ConnectorCommon for Prophetpay { code: response.code, message: response.message, reason: response.reason, + attempt_status: None, }) } } @@ -164,6 +165,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = prophetpay::ProphetpayRouterData::try_from(( &self.get_currency_unit(), @@ -195,7 +197,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -329,7 +334,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = prophetpay::ProphetpayRouterData::try_from(( &self.get_currency_unit(), @@ -417,7 +425,9 @@ impl ConnectorIntegration { logger::error!(deserialization_error =? error_msg); @@ -182,6 +183,7 @@ impl fn get_request_body( &self, req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = rapyd::RapydRouterData::try_from(( &self.get_currency_unit(), @@ -211,7 +213,7 @@ impl let salt = Alphanumeric.sample_string(&mut rand::thread_rng(), 12); let auth: rapyd::RapydAuthType = rapyd::RapydAuthType::try_from(&req.connector_auth_type)?; - let body = types::PaymentsAuthorizeType::get_request_body(self, req)? + let body = types::PaymentsAuthorizeType::get_request_body(self, req, connectors)? .ok_or(errors::ConnectorError::RequestEncodingFailed)?; let req_body = types::RequestBody::get_inner_value(body).expose(); let signature = @@ -232,7 +234,9 @@ impl self, req, connectors, )?) .headers(headers) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) } @@ -492,6 +496,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = rapyd::RapydRouterData::try_from(( &self.get_currency_unit(), @@ -521,7 +526,7 @@ impl "/v1/payments/{}/capture", req.request.connector_transaction_id ); - let body = types::PaymentsCaptureType::get_request_body(self, req)? + let body = types::PaymentsCaptureType::get_request_body(self, req, connectors)? .ok_or(errors::ConnectorError::RequestEncodingFailed)?; let req_body = types::RequestBody::get_inner_value(body).expose(); let signature = @@ -540,7 +545,9 @@ impl self, req, connectors, )?) .headers(headers) - .body(types::PaymentsCaptureType::get_request_body(self, req)?) + .body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) } @@ -630,6 +637,7 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = rapyd::RapydRouterData::try_from(( &self.get_currency_unit(), @@ -655,7 +663,7 @@ impl services::ConnectorIntegration status_code: item.http_code, message: item.response.status.status.unwrap_or_default(), reason: data.failure_message.to_owned(), + attempt_status: None, }), ), _ => { @@ -497,6 +498,7 @@ impl status_code: item.http_code, message: item.response.status.status.unwrap_or_default(), reason: item.response.status.message, + attempt_status: None, }), ), }; diff --git a/crates/router/src/connector/shift4.rs b/crates/router/src/connector/shift4.rs index a17546711f14..98eb895db548 100644 --- a/crates/router/src/connector/shift4.rs +++ b/crates/router/src/connector/shift4.rs @@ -99,6 +99,7 @@ impl ConnectorCommon for Shift4 { .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()), message: response.error.message, reason: None, + attempt_status: None, }) } } @@ -185,6 +186,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = shift4::Shift4PaymentsRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -248,7 +250,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = shift4::Shift4PaymentsRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -495,7 +500,9 @@ impl .content_type(request::ContentType::FormUrlEncoded) .attach_default_headers() .headers(types::PaymentsInitType::get_headers(self, req, connectors)?) - .body(types::PaymentsInitType::get_request_body(self, req)?) + .body(types::PaymentsInitType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -555,6 +562,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCompleteAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = shift4::Shift4PaymentsRequest::try_from(req)?; let req = types::RequestBody::log_and_get_request_body( @@ -580,7 +588,7 @@ impl self, req, connectors, )?) .body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -635,6 +643,7 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = shift4::Shift4RefundRequest::try_from(req)?; let shift4_req = types::RequestBody::log_and_get_request_body( @@ -657,7 +666,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = square::SquareTokenRequest::try_from(req)?; @@ -267,7 +269,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -412,6 +416,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = square::SquarePaymentsRequest::try_from(req)?; @@ -438,7 +443,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = square::SquareRefundRequest::try_from(req)?; let square_req = types::RequestBody::log_and_get_request_body( @@ -724,7 +732,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = stax::StaxCustomerRequest::try_from(req)?; @@ -186,7 +188,9 @@ impl .headers(types::ConnectorCustomerType::get_headers( self, req, connectors, )?) - .body(types::ConnectorCustomerType::get_request_body(self, req)?) + .body(types::ConnectorCustomerType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -249,6 +253,7 @@ impl fn get_request_body( &self, req: &types::TokenizationRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = stax::StaxTokenRequest::try_from(req)?; @@ -271,7 +276,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -350,6 +357,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = stax::StaxRouterData::try_from(( &self.get_currency_unit(), @@ -382,7 +390,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = stax::StaxRouterData::try_from(( &self.get_currency_unit(), @@ -541,7 +552,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = stax::StaxRouterData::try_from(( &self.get_currency_unit(), @@ -700,7 +714,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req = stripe::StripeCreditTransferSourceRequest::try_from(req)?; let pre_processing_request = types::RequestBody::log_and_get_request_body( @@ -171,7 +172,7 @@ impl self, req, connectors, )?) .body(types::PaymentsPreProcessingType::get_request_body( - self, req, + self, req, connectors, )?) .build(), )) @@ -225,6 +226,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -269,6 +271,7 @@ impl fn get_request_body( &self, req: &types::ConnectorCustomerRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = stripe::CustomerRequest::try_from(req)?; let stripe_req = types::RequestBody::log_and_get_request_body( @@ -294,7 +297,9 @@ impl .headers(types::ConnectorCustomerType::get_headers( self, req, connectors, )?) - .body(types::ConnectorCustomerType::get_request_body(self, req)?) + .body(types::ConnectorCustomerType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -351,6 +356,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -395,6 +401,7 @@ impl fn get_request_body( &self, req: &types::TokenizationRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = stripe::TokenRequest::try_from(req)?; let stripe_req = types::RequestBody::log_and_get_request_body( @@ -416,7 +423,9 @@ impl .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) - .body(types::TokenizationType::get_request_body(self, req)?) + .body(types::TokenizationType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -473,6 +482,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -523,6 +533,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = stripe::CaptureRequest::try_from(req)?; let stripe_req = types::RequestBody::log_and_get_request_body( @@ -546,7 +557,9 @@ impl .headers(types::PaymentsCaptureType::get_headers( self, req, connectors, )?) - .body(types::PaymentsCaptureType::get_request_body(self, req)?) + .body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -603,6 +616,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -667,7 +681,9 @@ impl .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) - .body(types::PaymentsSyncType::get_request_body(self, req)?) + .body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -743,6 +759,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -805,6 +822,7 @@ impl fn get_request_body( &self, req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { match &req.request.payment_method_data { api_models::payments::PaymentMethodData::BankTransfer(bank_transfer_data) => { @@ -837,7 +855,9 @@ impl .headers(types::PaymentsAuthorizeType::get_headers( self, req, connectors, )?) - .body(types::PaymentsAuthorizeType::get_request_body(self, req)?) + .body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -897,6 +917,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -944,6 +965,7 @@ impl fn get_request_body( &self, req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = stripe::CancelRequest::try_from(req)?; let stripe_req = types::RequestBody::log_and_get_request_body( @@ -964,7 +986,9 @@ impl .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) - .body(types::PaymentsVoidType::get_request_body(self, req)?) + .body(types::PaymentsVoidType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) } @@ -1016,6 +1040,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -1077,6 +1102,7 @@ impl types::SetupMandateRequestData, types::PaymentsResponseData, >, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req = stripe::SetupIntentRequest::try_from(req)?; let stripe_req = types::RequestBody::log_and_get_request_body( @@ -1102,7 +1128,7 @@ impl .url(&Verify::get_url(self, req, connectors)?) .attach_default_headers() .headers(Verify::get_headers(self, req, connectors)?) - .body(Verify::get_request_body(self, req)?) + .body(Verify::get_request_body(self, req, connectors)?) .build(), )) } @@ -1170,6 +1196,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } @@ -1212,6 +1239,7 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_request = stripe::RefundRequest::try_from(req)?; let stripe_req = types::RequestBody::log_and_get_request_body( @@ -1234,7 +1262,9 @@ impl services::ConnectorIntegration CustomResult, errors::ConnectorError> { let stripe_req = stripe::Evidence::try_from(req)?; let stripe_req_string = types::RequestBody::log_and_get_request_body( @@ -1707,7 +1744,9 @@ impl .headers(types::SubmitEvidenceType::get_headers( self, req, connectors, )?) - .body(types::SubmitEvidenceType::get_request_body(self, req)?) + .body(types::SubmitEvidenceType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) } @@ -1761,6 +1800,7 @@ impl }) .unwrap_or(message) }), + attempt_status: None, }) } } diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index fa9d7d617041..a783fd23fe19 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -2475,6 +2475,7 @@ impl }) .or(Some(error.message.clone())), status_code: item.http_code, + attempt_status: None, }); let connector_metadata = diff --git a/crates/router/src/connector/trustpay.rs b/crates/router/src/connector/trustpay.rs index 912f1575e1e0..7509131afeef 100644 --- a/crates/router/src/connector/trustpay.rs +++ b/crates/router/src/connector/trustpay.rs @@ -138,6 +138,7 @@ impl ConnectorCommon for Trustpay { .map(|error_code_message| error_code_message.error_code) .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), reason: reason.or(response_data.description), + attempt_status: None, }) } Err(error_msg) => { @@ -235,6 +236,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = trustpay::TrustpayAuthUpdateRequest::try_from(req)?; let trustpay_req = types::RequestBody::log_and_get_request_body( @@ -256,7 +258,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let currency = req.request.get_currency()?; let amount = req @@ -473,7 +480,7 @@ impl self, req, connectors, )?) .body(types::PaymentsPreProcessingType::get_request_body( - self, req, + self, req, connectors, )?) .build(), ); @@ -550,6 +557,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let amount = req .request @@ -596,7 +604,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = trustpay::TrustpayRouterData::try_from(( &self.get_currency_unit(), @@ -703,7 +714,9 @@ impl ConnectorIntegration TryFrom CustomResult, errors::ConnectorError> { let req_obj = tsys::TsysPaymentsRequest::try_from(req)?; let tsys_req = types::RequestBody::log_and_get_request_body( @@ -168,7 +169,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = tsys::TsysSyncRequest::try_from(req)?; let tsys_req = types::RequestBody::log_and_get_request_body( @@ -247,7 +251,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_req = tsys::TsysPaymentsCaptureRequest::try_from(req)?; let tsys_req = types::RequestBody::log_and_get_request_body( @@ -328,7 +335,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = tsys::TsysPaymentsCancelRequest::try_from(req)?; let tsys_req = types::RequestBody::log_and_get_request_body( @@ -404,7 +414,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let req_obj = tsys::TsysRefundRequest::try_from(req)?; let tsys_req = types::RequestBody::log_and_get_request_body( @@ -482,7 +495,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = tsys::TsysSyncRequest::try_from(req)?; let tsys_req = types::RequestBody::log_and_get_request_body( @@ -559,7 +575,9 @@ impl ConnectorIntegration Option>; } +pub trait PaymentMethodTokenizationRequestData { + fn get_browser_info(&self) -> Result; +} + +impl PaymentMethodTokenizationRequestData for types::PaymentMethodTokenizationData { + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } +} + impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { fn is_auto_capture(&self) -> Result { match self.capture_method { @@ -1077,7 +1089,7 @@ pub fn get_amount_as_string( currency: diesel_models::enums::Currency, ) -> Result> { let amount = match currency_unit { - types::api::CurrencyUnit::Minor => to_currency_lower_unit(amount.to_string(), currency)?, + types::api::CurrencyUnit::Minor => amount.to_string(), types::api::CurrencyUnit::Base => to_currency_base_unit(amount, currency)?, }; Ok(amount) diff --git a/crates/router/src/connector/volt.rs b/crates/router/src/connector/volt.rs index e1af4c008b12..3697b8c8923f 100644 --- a/crates/router/src/connector/volt.rs +++ b/crates/router/src/connector/volt.rs @@ -129,6 +129,7 @@ impl ConnectorCommon for Volt { code: response.exception.message.to_string(), message: response.exception.message.clone(), reason: Some(reason), + attempt_status: None, }) } } @@ -173,6 +174,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let req_obj = volt::VoltAuthUpdateRequest::try_from(req)?; let volt_req = types::RequestBody::log_and_get_request_body( @@ -195,7 +197,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = volt::VoltRouterData::try_from(( &self.get_currency_unit(), @@ -292,7 +297,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } @@ -434,7 +442,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = volt::VoltRouterData::try_from(( &self.get_currency_unit(), @@ -524,7 +535,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = wise::WisePayoutQuoteRequest::try_from(req)?; let wise_req = types::RequestBody::log_and_get_request_body( @@ -342,7 +347,9 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = wise::WiseRecipientCreateRequest::try_from(req)?; let wise_req = types::RequestBody::log_and_get_request_body( @@ -419,7 +427,9 @@ impl .headers(types::PayoutRecipientType::get_headers( self, req, connectors, )?) - .body(types::PayoutRecipientType::get_request_body(self, req)?) + .body(types::PayoutRecipientType::get_request_body( + self, req, connectors, + )?) .build(); Ok(Some(request)) @@ -512,6 +522,7 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = wise::WisePayoutCreateRequest::try_from(req)?; let wise_req = types::RequestBody::log_and_get_request_body( @@ -532,7 +543,9 @@ impl services::ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = wise::WisePayoutFulfillRequest::try_from(req)?; let wise_req = types::RequestBody::log_and_get_request_body( @@ -630,7 +644,9 @@ impl services::ConnectorIntegration, + _connectors: &Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = worldline::ApproveRequest::try_from(req)?; @@ -406,7 +407,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = worldline::WorldlineRouterData::try_from(( &self.get_currency_unit(), @@ -524,7 +528,9 @@ impl ConnectorIntegration, + _connectors: &Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_req = worldline::WorldlineRefundRequest::try_from(req)?; let refund_req = types::RequestBody::log_and_get_request_body( @@ -610,7 +617,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = worldpay::WorldpayRouterData::try_from(( &self.get_currency_unit(), @@ -462,7 +466,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_request = WorldpayRefundRequest::try_from(req)?; let fiserv_refund_request = types::RequestBody::log_and_get_request_body( @@ -549,7 +556,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { let connector_router_data = zen::ZenRouterData::try_from(( &self.get_currency_unit(), @@ -246,7 +248,9 @@ impl ConnectorIntegration, + _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { let connector_router_data = zen::ZenRouterData::try_from(( &self.get_currency_unit(), @@ -438,7 +443,9 @@ impl ConnectorIntegration RouterResponse { + let merchant_id = api_key.merchant_id.clone(); + let key_id = api_key.key_id.clone(); let store = state.store.as_ref(); let api_key = store @@ -313,7 +313,7 @@ pub async fn update_api_key( { let expiry_reminder_days = state.conf.api_keys.expiry_reminder_days.clone(); - let task_id = generate_task_id_for_api_key_expiry_workflow(key_id); + let task_id = generate_task_id_for_api_key_expiry_workflow(&key_id); // In order to determine how to update the existing process in the process_tracker table, // we need access to the current entry in the table. let existing_process_tracker_task = store @@ -339,7 +339,7 @@ pub async fn update_api_key( // If an expiry is set to 'never' else { // Process exist in process, revoke it - revoke_api_key_expiry_task(store, key_id) + revoke_api_key_expiry_task(store, &key_id) .await .into_report() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/gsm.rs b/crates/router/src/core/gsm.rs new file mode 100644 index 000000000000..d25860674570 --- /dev/null +++ b/crates/router/src/core/gsm.rs @@ -0,0 +1,137 @@ +use api_models::gsm as gsm_api_types; +use diesel_models::gsm as storage; +use error_stack::{IntoReport, ResultExt}; +use router_env::{instrument, tracing}; + +use crate::{ + core::{ + errors, + errors::{RouterResponse, StorageErrorExt}, + }, + db::gsm::GsmInterface, + services, + types::{self, transformers::ForeignInto}, + AppState, +}; + +#[instrument(skip_all)] +pub async fn create_gsm_rule( + state: AppState, + gsm_rule: gsm_api_types::GsmCreateRequest, +) -> RouterResponse { + let db = state.store.as_ref(); + GsmInterface::add_gsm_rule(db, gsm_rule.foreign_into()) + .await + .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { + message: "GSM with given key already exists in our records".to_string(), + }) + .map(services::ApplicationResponse::Json) +} + +#[instrument(skip_all)] +pub async fn retrieve_gsm_rule( + state: AppState, + gsm_request: gsm_api_types::GsmRetrieveRequest, +) -> RouterResponse { + let db = state.store.as_ref(); + let gsm_api_types::GsmRetrieveRequest { + connector, + flow, + sub_flow, + code, + message, + } = gsm_request; + GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message) + .await + .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { + message: "GSM with given key does not exist in our records".to_string(), + }) + .map(services::ApplicationResponse::Json) +} + +#[instrument(skip_all)] +pub async fn update_gsm_rule( + state: AppState, + gsm_request: gsm_api_types::GsmUpdateRequest, +) -> RouterResponse { + let db = state.store.as_ref(); + let gsm_api_types::GsmUpdateRequest { + connector, + flow, + sub_flow, + code, + message, + decision, + status, + router_error, + step_up_possible, + } = gsm_request; + GsmInterface::update_gsm_rule( + db, + connector.to_string(), + flow, + sub_flow, + code, + message, + storage::GatewayStatusMappingUpdate { + decision: decision.map(|d| d.to_string()), + status, + router_error: Some(router_error), + step_up_possible, + }, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { + message: "GSM with given key does not exist in our records".to_string(), + }) + .attach_printable("Failed while updating Gsm rule") + .map(services::ApplicationResponse::Json) +} + +#[instrument(skip_all)] +pub async fn delete_gsm_rule( + state: AppState, + gsm_request: gsm_api_types::GsmDeleteRequest, +) -> RouterResponse { + let db = state.store.as_ref(); + let gsm_api_types::GsmDeleteRequest { + connector, + flow, + sub_flow, + code, + message, + } = gsm_request; + match GsmInterface::delete_gsm_rule( + db, + connector.to_string(), + flow.to_owned(), + sub_flow.to_owned(), + code.to_owned(), + message.to_owned(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { + message: "GSM with given key does not exist in our records".to_string(), + }) + .attach_printable("Failed while Deleting Gsm rule") + { + Ok(is_deleted) => { + if is_deleted { + Ok(services::ApplicationResponse::Json( + gsm_api_types::GsmDeleteResponse { + gsm_rule_delete: true, + connector, + flow, + sub_flow, + code, + }, + )) + } else { + Err(errors::ApiErrorResponse::InternalServerError) + .into_report() + .attach_printable("Failed while Deleting Gsm rule, got response as false") + } + } + Err(err) => Err(err), + } +} diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index aa716d1918c9..6d263ecdb77b 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -1,6 +1,8 @@ use api_models::admin as admin_types; use common_utils::{ - consts::{DEFAULT_BACKGROUND_COLOR, DEFAULT_SDK_THEME}, + consts::{ + DEFAULT_BACKGROUND_COLOR, DEFAULT_MERCHANT_LOGO, DEFAULT_PRODUCT_IMG, DEFAULT_SDK_THEME, + }, ext_traits::ValueExt, }; use error_stack::{IntoReport, ResultExt}; @@ -141,16 +143,14 @@ pub async fn intiate_payment_link_flow( .map(|pl_config| { pl_config .merchant_logo - .unwrap_or("https://i.imgur.com/RfxPFQo.png".to_string()) + .unwrap_or(DEFAULT_MERCHANT_LOGO.to_string()) }) .unwrap_or_default(), max_items_visible_after_collapse: 3, sdk_theme: payment_link_config.clone().and_then(|pl_config| { - pl_config.color_scheme.map(|color| { - color - .sdk_theme - .unwrap_or(default_sdk_theme.clone().to_string()) - }) + pl_config + .color_scheme + .map(|color| color.sdk_theme.unwrap_or(default_sdk_theme.to_string())) }), }; @@ -255,7 +255,7 @@ pub fn check_payment_link_status(fulfillment_time: Option) -> fn validate_order_details( order_details: Option>>, ) -> Result< - Vec, + Option>, error_stack::Report, > { let order_details = order_details @@ -274,14 +274,13 @@ fn validate_order_details( }) .transpose()?; - if let Some(mut order_details) = order_details.clone() { + let updated_order_details = order_details.map(|mut order_details| { for order in order_details.iter_mut() { if order.product_img_link.is_none() { - order.product_img_link = Some("https://i.imgur.com/On3VtKF.png".to_string()); + order.product_img_link = Some(DEFAULT_PRODUCT_IMG.to_string()); } } - return Ok(order_details); - } - - Ok(Vec::new()) + order_details + }); + Ok(updated_order_details) } diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 422c3fa19881..b19b381af507 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -13,7 +13,10 @@ use diesel_models::enums; use crate::{ core::{errors::RouterResult, payments::helpers}, routes::AppState, - types::api::{self, payments}, + types::{ + api::{self, payments}, + domain, + }, }; pub struct Oss; @@ -25,6 +28,7 @@ pub trait PaymentMethodRetrieve { state: &AppState, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, Option)>; } @@ -35,6 +39,7 @@ impl PaymentMethodRetrieve for Oss { state: &AppState, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, Option)> { match pm_data { pm_opt @ Some(pm @ api::PaymentMethodData::Card(_)) => { @@ -44,6 +49,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::Card, pm, + merchant_key_store, ) .await?; @@ -64,6 +70,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::BankTransfer, pm, + merchant_key_store, ) .await?; @@ -76,6 +83,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::Wallet, pm, + merchant_key_store, ) .await?; @@ -88,6 +96,7 @@ impl PaymentMethodRetrieve for Oss { payment_intent, enums::PaymentMethod::BankRedirect, pm, + merchant_key_store, ) .await?; diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 417b030f5494..234323f0179a 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2009,7 +2009,7 @@ pub async fn list_customer_payment_method( let hyperswitch_token = generate_id(consts::ID_LENGTH, "token"); let card = if pm.payment_method == enums::PaymentMethod::Card { - get_card_details(&pm, key, state, &hyperswitch_token).await? + get_card_details(&pm, key, state, &hyperswitch_token, &key_store).await? } else { None }; @@ -2104,6 +2104,7 @@ async fn get_card_details( key: &[u8], state: &routes::AppState, hyperswitch_token: &str, + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { let mut _card_decrypted = decrypt::(pm.payment_method_data.clone(), key) @@ -2120,7 +2121,7 @@ async fn get_card_details( }); Ok(Some( - get_lookup_key_from_locker(state, hyperswitch_token, pm).await?, + get_lookup_key_from_locker(state, hyperswitch_token, pm, key_store).await?, )) } @@ -2128,6 +2129,7 @@ pub async fn get_lookup_key_from_locker( state: &routes::AppState, payment_token: &str, pm: &storage::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { let card = get_card_from_locker( state, @@ -2142,9 +2144,15 @@ pub async fn get_lookup_key_from_locker( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Get Card Details Failed")?; let card = card_detail.clone(); - let resp = - BasiliskCardSupport::create_payment_method_data_in_locker(state, payment_token, card, pm) - .await?; + + let resp = TempLockerCardSupport::create_payment_method_data_in_temp_locker( + state, + payment_token, + card, + pm, + merchant_key_store, + ) + .await?; Ok(resp) } @@ -2177,6 +2185,7 @@ pub async fn get_lookup_key_for_payout_method( Some(payout_token.to_string()), &pm_parsed, Some(pm.customer_id.to_owned()), + key_store, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2190,110 +2199,16 @@ pub async fn get_lookup_key_for_payout_method( } } -pub struct BasiliskCardSupport; +pub struct TempLockerCardSupport; -#[cfg(not(feature = "basilisk"))] -impl BasiliskCardSupport { - async fn create_payment_method_data_in_locker( - state: &routes::AppState, - payment_token: &str, - card: api::CardDetailFromLocker, - pm: &storage::PaymentMethod, - ) -> errors::RouterResult { - let card_number = card.card_number.clone().get_required_value("card_number")?; - let card_exp_month = card - .expiry_month - .clone() - .expose_option() - .get_required_value("expiry_month")?; - let card_exp_year = card - .expiry_year - .clone() - .expose_option() - .get_required_value("expiry_year")?; - let card_holder_name = card - .card_holder_name - .clone() - .expose_option() - .unwrap_or_default(); - let value1 = payment_methods::mk_card_value1( - card_number, - card_exp_year, - card_exp_month, - Some(card_holder_name), - None, - None, - None, - ) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value1 for locker")?; - let value2 = payment_methods::mk_card_value2( - None, - None, - None, - Some(pm.customer_id.to_string()), - Some(pm.payment_method_id.to_string()), - ) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value2 for locker")?; - - let value1 = vault::VaultPaymentMethod::Card(value1); - let value2 = vault::VaultPaymentMethod::Card(value2); - - let value1 = utils::Encode::::encode_to_string_of_json(&value1) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Wrapped value1 construction failed when saving card to locker")?; - - let value2 = utils::Encode::::encode_to_string_of_json(&value2) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Wrapped value2 construction failed when saving card to locker")?; - - let db_value = vault::MockTokenizeDBValue { value1, value2 }; - - let value_string = - utils::Encode::::encode_to_string_of_json(&db_value) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Mock tokenize value construction failed when saving card to locker", - )?; - - let db = &*state.store; - - let already_present = db.find_config_by_key(payment_token).await; - - if already_present.is_err() { - let config = storage::ConfigNew { - key: payment_token.to_string(), - config: value_string, - }; - - db.insert_config(config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed")?; - } else { - let config_update = storage::ConfigUpdate::Update { - config: Some(value_string), - }; - - db.update_config_by_key(payment_token, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization db update failed")?; - } - - Ok(card) - } -} - -#[cfg(feature = "basilisk")] -impl BasiliskCardSupport { +impl TempLockerCardSupport { #[instrument(skip_all)] - async fn create_payment_method_data_in_locker( + async fn create_payment_method_data_in_temp_locker( state: &routes::AppState, payment_token: &str, card: api::CardDetailFromLocker, pm: &storage::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { let card_number = card.card_number.clone().get_required_value("card_number")?; let card_exp_month = card @@ -2343,8 +2258,14 @@ impl BasiliskCardSupport { .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Wrapped value2 construction failed when saving card to locker")?; - let lookup_key = - vault::create_tokenize(state, value1, Some(value2), payment_token.to_string()).await?; + let lookup_key = vault::create_tokenize( + state, + value1, + Some(value2), + payment_token.to_string(), + merchant_key_store.key.get_inner(), + ) + .await?; vault::add_delete_tokenized_data_task( &*state.store, &lookup_key, diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index d16269deb9b2..5ad78c9d730e 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -1,34 +1,30 @@ -use common_utils::generate_id_with_default_len; -#[cfg(feature = "basilisk")] -use error_stack::report; -use error_stack::{IntoReport, ResultExt}; +use common_utils::{ + crypto::{DecodeMessage, EncodeMessage, GcmAes256}, + ext_traits::BytesExt, + generate_id_with_default_len, +}; +use error_stack::{report, IntoReport, ResultExt}; #[cfg(feature = "basilisk")] use josekit::jwe; use masking::PeekInterface; use router_env::{instrument, tracing}; -#[cfg(feature = "basilisk")] use scheduler::{types::process_data, utils as process_tracker_utils}; -#[cfg(feature = "basilisk")] -use crate::routes::metrics; #[cfg(feature = "payouts")] use crate::types::api::payouts; use crate::{ - configs::settings, + consts, core::errors::{self, CustomResult, RouterResult}, - logger, routes, + db, logger, routes, + routes::metrics, types::{ - api, - storage::{self, enums}, + api, domain, + storage::{self, enums, ProcessTrackerExt}, }, utils::{self, StringExt}, }; #[cfg(feature = "basilisk")] -use crate::{core::payment_methods::transformers as payment_methods, services, utils::BytesExt}; -#[cfg(feature = "basilisk")] -use crate::{db, types::storage::ProcessTrackerExt}; - -#[cfg(feature = "basilisk")] +use crate::{core::payment_methods::transformers as payment_methods, services, settings}; const VAULT_SERVICE_NAME: &str = "CARD"; #[cfg(feature = "basilisk")] const VAULT_VERSION: &str = "0"; @@ -622,196 +618,15 @@ pub struct MockTokenizeDBValue { pub struct Vault; -#[cfg(not(feature = "basilisk"))] impl Vault { #[instrument(skip_all)] pub async fn get_payment_method_data_from_locker( state: &routes::AppState, lookup_key: &str, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, SupplementaryVaultData)> { - let config = state - .store - .find_config_by_key(lookup_key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Could not find payment method in vault")?; - - let tokenize_value: MockTokenizeDBValue = config - .config - .parse_struct("MockTokenizeDBValue") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to deserialize Mock tokenize db value")?; - - let (payment_method, supp_data) = - api::PaymentMethodData::from_values(tokenize_value.value1, tokenize_value.value2) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error parsing Payment Method from Values")?; - - Ok((Some(payment_method), supp_data)) - } - - #[cfg(feature = "payouts")] - #[instrument(skip_all)] - pub async fn get_payout_method_data_from_temporary_locker( - state: &routes::AppState, - lookup_key: &str, - ) -> RouterResult<(Option, SupplementaryVaultData)> { - let config = state - .store - .find_config_by_key(lookup_key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Could not find payment method in vault")?; - - let tokenize_value: MockTokenizeDBValue = config - .config - .parse_struct("MockTokenizeDBValue") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to deserialize Mock tokenize db value")?; - - let (payout_method, supp_data) = - api::PayoutMethodData::from_values(tokenize_value.value1, tokenize_value.value2) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error parsing Payout Method from Values")?; - - Ok((Some(payout_method), supp_data)) - } - - #[cfg(feature = "payouts")] - #[instrument(skip_all)] - pub async fn store_payout_method_data_in_locker( - state: &routes::AppState, - token_id: Option, - payout_method: &api::PayoutMethodData, - customer_id: Option, - ) -> RouterResult { - let value1 = payout_method - .get_value1(customer_id.clone()) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value1 for locker")?; - - let value2 = payout_method - .get_value2(customer_id) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value2 for locker")?; - - let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - - let db_value = MockTokenizeDBValue { value1, value2 }; - - let value_string = - utils::Encode::::encode_to_string_of_json(&db_value) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to encode payout method as mock tokenize db value")?; - - let already_present = state.store.find_config_by_key(&lookup_key).await; - - if already_present.is_err() { - let config = storage::ConfigNew { - key: lookup_key.clone(), - config: value_string, - }; - - state - .store - .insert_config(config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed insert")?; - } else { - let config_update = storage::ConfigUpdate::Update { - config: Some(value_string), - }; - state - .store - .update_config_by_key(&lookup_key, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed update")?; - } - - Ok(lookup_key) - } - - #[instrument(skip_all)] - pub async fn store_payment_method_data_in_locker( - state: &routes::AppState, - token_id: Option, - payment_method: &api::PaymentMethodData, - customer_id: Option, - _pm: enums::PaymentMethod, - ) -> RouterResult { - let value1 = payment_method - .get_value1(customer_id.clone()) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value1 for locker")?; - - let value2 = payment_method - .get_value2(customer_id) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting Value12 for locker")?; - - let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - - let db_value = MockTokenizeDBValue { value1, value2 }; - - let value_string = - utils::Encode::::encode_to_string_of_json(&db_value) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to encode payment method as mock tokenize db value")?; - - let already_present = state.store.find_config_by_key(&lookup_key).await; - - if already_present.is_err() { - let config = storage::ConfigNew { - key: lookup_key.clone(), - config: value_string, - }; - - state - .store - .insert_config(config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed insert")?; - } else { - let config_update = storage::ConfigUpdate::Update { - config: Some(value_string), - }; - state - .store - .update_config_by_key(&lookup_key, config_update) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Mock tokenization save to db failed update")?; - } - - Ok(lookup_key) - } - - #[instrument(skip_all)] - pub async fn delete_locker_payment_method_by_lookup_key( - state: &routes::AppState, - lookup_key: &Option, - ) { - let db = &*state.store; - if let Some(id) = lookup_key { - match db.delete_config_by_key(id).await { - Ok(_) => logger::info!("Card Deleted from locker mock up"), - Err(err) => logger::error!("Err: Card Delete from locker Failed : {}", err), - } - } - } -} - -#[cfg(feature = "basilisk")] -impl Vault { - #[instrument(skip_all)] - pub async fn get_payment_method_data_from_locker( - state: &routes::AppState, - lookup_key: &str, - ) -> RouterResult<(Option, SupplementaryVaultData)> { - let de_tokenize = get_tokenized_data(state, lookup_key, true).await?; + let de_tokenize = + get_tokenized_data(state, lookup_key, true, merchant_key_store.key.get_inner()).await?; let (payment_method, customer_id) = api::PaymentMethodData::from_values(de_tokenize.value1, de_tokenize.value2) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -827,6 +642,7 @@ impl Vault { payment_method: &api::PaymentMethodData, customer_id: Option, pm: enums::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let value1 = payment_method .get_value1(customer_id.clone()) @@ -840,7 +656,14 @@ impl Vault { let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - let lookup_key = create_tokenize(state, value1, Some(value2), lookup_key).await?; + let lookup_key = create_tokenize( + state, + value1, + Some(value2), + lookup_key, + merchant_key_store.key.get_inner(), + ) + .await?; add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?; metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); Ok(lookup_key) @@ -851,8 +674,10 @@ impl Vault { pub async fn get_payout_method_data_from_temporary_locker( state: &routes::AppState, lookup_key: &str, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, SupplementaryVaultData)> { - let de_tokenize = get_tokenized_data(state, lookup_key, true).await?; + let de_tokenize = + get_tokenized_data(state, lookup_key, true, merchant_key_store.key.get_inner()).await?; let (payout_method, supp_data) = api::PayoutMethodData::from_values(de_tokenize.value1, de_tokenize.value2) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -868,6 +693,7 @@ impl Vault { token_id: Option, payout_method: &api::PayoutMethodData, customer_id: Option, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let value1 = payout_method .get_value1(customer_id.clone()) @@ -881,7 +707,14 @@ impl Vault { let lookup_key = token_id.unwrap_or_else(|| generate_id_with_default_len("token")); - let lookup_key = create_tokenize(state, value1, Some(value2), lookup_key).await?; + let lookup_key = create_tokenize( + state, + value1, + Some(value2), + lookup_key, + merchant_key_store.key.get_inner(), + ) + .await?; // add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?; // scheduler_metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); Ok(lookup_key) @@ -893,31 +726,334 @@ impl Vault { lookup_key: &Option, ) { if let Some(lookup_key) = lookup_key { - let delete_resp = delete_tokenized_data(state, lookup_key).await; - match delete_resp { - Ok(resp) => { - if resp == "Ok" { - logger::info!("Card From locker deleted Successfully") - } else { - logger::error!("Error: Deleting Card From Locker : {:?}", resp) - } - } - Err(err) => logger::error!("Err: Deleting Card From Locker : {:?}", err), - } + delete_tokenized_data(state, lookup_key) + .await + .map(|_| logger::info!("Card From locker deleted Successfully")) + .map_err(|err| logger::error!("Error: Deleting Card From Redis Locker : {:?}", err)) + .ok(); } } } //------------------------------------------------TokenizeService------------------------------------------------ -pub fn get_key_id(keys: &settings::Jwekey) -> &str { - let key_identifier = "1"; // [#46]: Fetch this value from redis or external sources - if key_identifier == "1" { - &keys.locker_key_identifier1 - } else { - &keys.locker_key_identifier2 + +#[inline(always)] +fn get_redis_locker_key(lookup_key: &str) -> String { + format!("{}_{}", consts::LOCKER_REDIS_PREFIX, lookup_key) +} + +#[instrument(skip(state, value1, value2))] +pub async fn create_tokenize( + state: &routes::AppState, + value1: String, + value2: Option, + lookup_key: String, + encryption_key: &masking::Secret>, +) -> RouterResult { + let redis_key = get_redis_locker_key(lookup_key.as_str()); + let func = || async { + metrics::CREATED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); + + let payload_to_be_encrypted = api::TokenizePayloadRequest { + value1: value1.clone(), + value2: value2.clone().unwrap_or_default(), + lookup_key: lookup_key.clone(), + service_name: VAULT_SERVICE_NAME.to_string(), + }; + + let payload = utils::Encode::::encode_to_string_of_json( + &payload_to_be_encrypted, + ) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let encrypted_payload = GcmAes256 + .encode_message(encryption_key.peek().as_ref(), payload.as_bytes()) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode redis temp locker data")?; + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + redis_conn + .set_key_if_not_exists_with_expiry( + redis_key.as_str(), + bytes::Bytes::from(encrypted_payload), + Some(i64::from(consts::LOCKER_REDIS_EXPIRY_SECONDS)), + ) + .await + .map(|_| lookup_key.clone()) + .map_err(|err| { + metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + err + }) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error from redis locker") + }; + + match func().await { + Ok(s) => { + logger::info!( + "Insert payload in redis locker successful with lookup key: {:?}", + redis_key + ); + Ok(s) + } + Err(err) => { + logger::error!("Redis Temp locker Failed: {:?}", err); + + #[cfg(feature = "basilisk")] + return old_create_tokenize(state, value1, value2, lookup_key).await; + + #[cfg(not(feature = "basilisk"))] + Err(err) + } + } +} + +#[instrument(skip(state))] +pub async fn get_tokenized_data( + state: &routes::AppState, + lookup_key: &str, + _should_get_value2: bool, + encryption_key: &masking::Secret>, +) -> RouterResult { + let redis_key = get_redis_locker_key(lookup_key); + let func = || async { + metrics::GET_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + let response = redis_conn.get_key::(redis_key.as_str()).await; + + match response { + Ok(resp) => { + let decrypted_payload = GcmAes256 + .decode_message( + encryption_key.peek().as_ref(), + masking::Secret::new(resp.into()), + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to decode redis temp locker data")?; + + let get_response: api::TokenizePayloadRequest = + bytes::Bytes::from(decrypted_payload) + .parse_struct("TokenizePayloadRequest") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Error getting TokenizePayloadRequest from tokenize response", + )?; + + Ok(get_response) + } + Err(err) => { + metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + Err(err).change_context(errors::ApiErrorResponse::UnprocessableEntity { + message: "Token is invalid or expired".into(), + }) + } + } + }; + + match func().await { + Ok(s) => { + logger::info!( + "Fetch payload in redis locker successful with lookup key: {:?}", + redis_key + ); + Ok(s) + } + Err(err) => { + logger::error!("Redis Temp locker Failed: {:?}", err); + + #[cfg(feature = "basilisk")] + return old_get_tokenized_data(state, lookup_key, _should_get_value2).await; + + #[cfg(not(feature = "basilisk"))] + Err(err) + } + } +} + +#[instrument(skip(state))] +pub async fn delete_tokenized_data(state: &routes::AppState, lookup_key: &str) -> RouterResult<()> { + let redis_key = get_redis_locker_key(lookup_key); + let func = || async { + metrics::DELETED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + let response = redis_conn.delete_key(redis_key.as_str()).await; + + match response { + Ok(redis_interface::DelReply::KeyDeleted) => Ok(()), + Ok(redis_interface::DelReply::KeyNotDeleted) => { + Err(errors::ApiErrorResponse::InternalServerError) + .into_report() + .attach_printable("Token invalid or expired") + } + Err(err) => { + metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + Err(errors::ApiErrorResponse::InternalServerError) + .into_report() + .attach_printable_lazy(|| { + format!("Failed to delete from redis locker: {err:?}") + }) + } + } + }; + match func().await { + Ok(s) => { + logger::info!( + "Delete payload in redis locker successful with lookup key: {:?}", + redis_key + ); + Ok(s) + } + Err(err) => { + logger::error!("Redis Temp locker Failed: {:?}", err); + + #[cfg(feature = "basilisk")] + return old_delete_tokenized_data(state, lookup_key).await; + + #[cfg(not(feature = "basilisk"))] + Err(err) + } + } +} + +// ********************************************** PROCESS TRACKER ********************************************** + +pub async fn add_delete_tokenized_data_task( + db: &dyn db::StorageInterface, + lookup_key: &str, + pm: enums::PaymentMethod, +) -> RouterResult<()> { + let runner = "DELETE_TOKENIZE_DATA_WORKFLOW"; + let current_time = common_utils::date_time::now(); + let tracking_data = serde_json::to_value(storage::TokenizeCoreWorkflow { + lookup_key: lookup_key.to_owned(), + pm, + }) + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| format!("unable to convert into value {lookup_key:?}"))?; + + let schedule_time = get_delete_tokenize_schedule_time(db, &pm, 0).await; + + let process_tracker_entry = storage::ProcessTrackerNew { + id: format!("{runner}_{lookup_key}"), + name: Some(String::from(runner)), + tag: vec![String::from("BASILISK-V3")], + runner: Some(String::from(runner)), + retry_count: 0, + schedule_time, + rule: String::new(), + tracking_data, + business_status: String::from("Pending"), + status: enums::ProcessTrackerStatus::New, + event: vec![], + created_at: current_time, + updated_at: current_time, + }; + let response = db.insert_process(process_tracker_entry).await; + response.map(|_| ()).or_else(|err| { + if err.current_context().is_db_unique_violation() { + Ok(()) + } else { + Err(report!(errors::ApiErrorResponse::InternalServerError)) + } + }) +} + +pub async fn start_tokenize_data_workflow( + state: &routes::AppState, + tokenize_tracker: &storage::ProcessTracker, +) -> Result<(), errors::ProcessTrackerError> { + let db = &*state.store; + let delete_tokenize_data = serde_json::from_value::( + tokenize_tracker.tracking_data.clone(), + ) + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| { + format!( + "unable to convert into DeleteTokenizeByTokenRequest {:?}", + tokenize_tracker.tracking_data + ) + })?; + + match delete_tokenized_data(state, &delete_tokenize_data.lookup_key).await { + Ok(()) => { + logger::info!("Card From locker deleted Successfully"); + //mark task as finished + let id = tokenize_tracker.id.clone(); + tokenize_tracker + .clone() + .finish_with_status(db.as_scheduler(), format!("COMPLETED_BY_PT_{id}")) + .await?; + } + Err(err) => { + logger::error!("Err: Deleting Card From Locker : {:?}", err); + retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned()) + .await?; + metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); + } } + Ok(()) +} + +pub async fn get_delete_tokenize_schedule_time( + db: &dyn db::StorageInterface, + pm: &enums::PaymentMethod, + retry_count: i32, +) -> Option { + let redis_mapping = db::get_and_deserialize_key( + db, + &format!("pt_mapping_delete_{pm}_tokenize_data"), + "PaymentMethodsPTMapping", + ) + .await; + let mapping = match redis_mapping { + Ok(x) => x, + Err(err) => { + logger::info!("Redis Mapping Error: {}", err); + process_data::PaymentMethodsPTMapping::default() + } + }; + let time_delta = process_tracker_utils::get_pm_schedule_time(mapping, pm, retry_count + 1); + + process_tracker_utils::get_time_from_delta(time_delta) } +pub async fn retry_delete_tokenize( + db: &dyn db::StorageInterface, + pm: &enums::PaymentMethod, + pt: storage::ProcessTracker, +) -> Result<(), errors::ProcessTrackerError> { + let schedule_time = get_delete_tokenize_schedule_time(db, pm, pt.retry_count).await; + + match schedule_time { + Some(s_time) => pt.retry(db.as_scheduler(), s_time).await, + None => { + pt.finish_with_status(db.as_scheduler(), "RETRIES_EXCEEDED".to_string()) + .await + } + } +} + +// Fallback logic of old temp locker needs to be removed later + #[cfg(feature = "basilisk")] async fn get_locker_jwe_keys( keys: &settings::ActiveKmsSecrets, @@ -936,13 +1072,13 @@ async fn get_locker_jwe_keys( } #[cfg(feature = "basilisk")] -pub async fn create_tokenize( +#[instrument(skip(state, value1, value2))] +pub async fn old_create_tokenize( state: &routes::AppState, value1: String, value2: Option, lookup_key: String, ) -> RouterResult { - metrics::CREATED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); let payload_to_be_encrypted = api::TokenizePayloadRequest { value1, value2: value2.unwrap_or_default(), @@ -1017,7 +1153,7 @@ pub async fn create_tokenize( } #[cfg(feature = "basilisk")] -pub async fn get_tokenized_data( +pub async fn old_get_tokenized_data( state: &routes::AppState, lookup_key: &str, should_get_value2: bool, @@ -1096,10 +1232,10 @@ pub async fn get_tokenized_data( } #[cfg(feature = "basilisk")] -pub async fn delete_tokenized_data( +pub async fn old_delete_tokenized_data( state: &routes::AppState, lookup_key: &str, -) -> RouterResult { +) -> RouterResult<()> { metrics::DELETED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); let payload_to_be_encrypted = api::DeleteTokenizeByTokenRequest { lookup_key: lookup_key.to_string(), @@ -1136,11 +1272,11 @@ pub async fn delete_tokenized_data( .attach_printable("Error while making /tokenize/delete/token call to the locker")?; match response { Ok(r) => { - let delete_response = std::str::from_utf8(&r.response) + let _delete_response = std::str::from_utf8(&r.response) .into_report() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Decoding Failed for basilisk delete response")?; - Ok(delete_response.to_string()) + Ok(()) } Err(err) => { metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); @@ -1151,133 +1287,12 @@ pub async fn delete_tokenized_data( } } -// ********************************************** PROCESS TRACKER ********************************************** #[cfg(feature = "basilisk")] -pub async fn add_delete_tokenized_data_task( - db: &dyn db::StorageInterface, - lookup_key: &str, - pm: enums::PaymentMethod, -) -> RouterResult<()> { - let runner = "DELETE_TOKENIZE_DATA_WORKFLOW"; - let current_time = common_utils::date_time::now(); - let tracking_data = serde_json::to_value(storage::TokenizeCoreWorkflow { - lookup_key: lookup_key.to_owned(), - pm, - }) - .into_report() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("unable to convert into value {lookup_key:?}"))?; - - let schedule_time = get_delete_tokenize_schedule_time(db, &pm, 0).await; - - let process_tracker_entry = storage::ProcessTrackerNew { - id: format!("{runner}_{lookup_key}"), - name: Some(String::from(runner)), - tag: vec![String::from("BASILISK-V3")], - runner: Some(String::from(runner)), - retry_count: 0, - schedule_time, - rule: String::new(), - tracking_data, - business_status: String::from("Pending"), - status: enums::ProcessTrackerStatus::New, - event: vec![], - created_at: current_time, - updated_at: current_time, - }; - let response = db.insert_process(process_tracker_entry).await; - response.map(|_| ()).or_else(|err| { - if err.current_context().is_db_unique_violation() { - Ok(()) - } else { - Err(report!(errors::ApiErrorResponse::InternalServerError)) - } - }) -} - -#[cfg(feature = "basilisk")] -pub async fn start_tokenize_data_workflow( - state: &routes::AppState, - tokenize_tracker: &storage::ProcessTracker, -) -> Result<(), errors::ProcessTrackerError> { - let db = &*state.store; - let delete_tokenize_data = serde_json::from_value::( - tokenize_tracker.tracking_data.clone(), - ) - .into_report() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| { - format!( - "unable to convert into DeleteTokenizeByTokenRequest {:?}", - tokenize_tracker.tracking_data - ) - })?; - - let delete_resp = delete_tokenized_data(state, &delete_tokenize_data.lookup_key).await; - match delete_resp { - Ok(resp) => { - if resp == "Ok" { - logger::info!("Card From locker deleted Successfully"); - //mark task as finished - let id = tokenize_tracker.id.clone(); - tokenize_tracker - .clone() - .finish_with_status(db.as_scheduler(), format!("COMPLETED_BY_PT_{id}")) - .await?; - } else { - logger::error!("Error: Deleting Card From Locker : {:?}", resp); - retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned()) - .await?; - metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); - } - } - Err(err) => { - logger::error!("Err: Deleting Card From Locker : {:?}", err); - retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned()) - .await?; - metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]); - } - } - Ok(()) -} - -#[cfg(feature = "basilisk")] -pub async fn get_delete_tokenize_schedule_time( - db: &dyn db::StorageInterface, - pm: &enums::PaymentMethod, - retry_count: i32, -) -> Option { - let redis_mapping = db::get_and_deserialize_key( - db, - &format!("pt_mapping_delete_{pm}_tokenize_data"), - "PaymentMethodsPTMapping", - ) - .await; - let mapping = match redis_mapping { - Ok(x) => x, - Err(err) => { - logger::info!("Redis Mapping Error: {}", err); - process_data::PaymentMethodsPTMapping::default() - } - }; - let time_delta = process_tracker_utils::get_pm_schedule_time(mapping, pm, retry_count + 1); - - process_tracker_utils::get_time_from_delta(time_delta) -} - -#[cfg(feature = "basilisk")] -pub async fn retry_delete_tokenize( - db: &dyn db::StorageInterface, - pm: &enums::PaymentMethod, - pt: storage::ProcessTracker, -) -> Result<(), errors::ProcessTrackerError> { - let schedule_time = get_delete_tokenize_schedule_time(db, pm, pt.retry_count).await; - - match schedule_time { - Some(s_time) => pt.retry(db.as_scheduler(), s_time).await, - None => { - pt.finish_with_status(db.as_scheduler(), "RETRIES_EXCEEDED".to_string()) - .await - } +pub fn get_key_id(keys: &settings::Jwekey) -> &str { + let key_identifier = "1"; // [#46]: Fetch this value from redis or external sources + if key_identifier == "1" { + &keys.locker_key_identifier1 + } else { + &keys.locker_key_identifier2 } } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 586126467e18..98ab158e7935 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -153,6 +153,7 @@ where &operation, &mut payment_data, &validate_result, + &key_store, ) .await?; @@ -717,6 +718,7 @@ where payment_data, validate_result, &merchant_connector_account, + key_store, ) .await?; @@ -1399,6 +1401,7 @@ pub async fn get_connector_tokenization_action_when_confirm_true( payment_data: &mut PaymentData, validate_result: &operations::ValidateResult<'_>, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(PaymentData, TokenizationAction)> where F: Send + Clone, @@ -1461,7 +1464,12 @@ where TokenizationAction::TokenizeInRouter => { let (_operation, payment_method_data) = operation .to_domain()? - .make_pm_data(state, payment_data, validate_result.storage_scheme) + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) .await?; payment_data.payment_method_data = payment_method_data; TokenizationAction::SkipConnectorTokenization @@ -1471,7 +1479,12 @@ where TokenizationAction::TokenizeInConnectorAndRouter => { let (_operation, payment_method_data) = operation .to_domain()? - .make_pm_data(state, payment_data, validate_result.storage_scheme) + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) .await?; payment_data.payment_method_data = payment_method_data; @@ -1507,6 +1520,7 @@ pub async fn tokenize_in_router_when_confirm_false( operation: &BoxedOperation<'_, F, Req, Ctx>, payment_data: &mut PaymentData, validate_result: &operations::ValidateResult<'_>, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult> where F: Send + Clone, @@ -1516,7 +1530,12 @@ where let payment_data = if !is_operation_confirm(operation) { let (_operation, payment_method_data) = operation .to_domain()? - .make_pm_data(state, payment_data, validate_result.storage_scheme) + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) .await?; payment_data.payment_method_data = payment_method_data; payment_data diff --git a/crates/router/src/core/payments/access_token.rs b/crates/router/src/core/payments/access_token.rs index 887b2b8f411e..af10e91b5a08 100644 --- a/crates/router/src/core/payments/access_token.rs +++ b/crates/router/src/core/payments/access_token.rs @@ -173,6 +173,7 @@ pub async fn refresh_connector_auth( message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(), reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()), status_code: 504, + attempt_status: None, }; Ok(Err(error_response)) diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 2c77184b9bd4..e27fe54c0ed0 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -95,37 +95,56 @@ impl Feature for types::PaymentsAu metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics - let save_payment_result = tokenization::save_payment_method( - state, - connector, - resp.to_owned(), - maybe_customer, - merchant_account, - self.request.payment_method_type, - key_store, - ) - .await; - - let pm_id = match save_payment_result { - Ok(payment_method_id) => Ok(payment_method_id), - Err(error) => { - if resp.request.setup_mandate_details.clone().is_some() { - Err(error) - } else { - logger::error!(save_payment_method_error=?error); - Ok(None) + if resp.request.setup_mandate_details.clone().is_some() { + let payment_method_id = tokenization::save_payment_method( + state, + connector, + resp.to_owned(), + maybe_customer, + merchant_account, + self.request.payment_method_type, + key_store, + ) + .await?; + Ok(mandate::mandate_procedure( + state, + resp, + maybe_customer, + payment_method_id, + connector.merchant_connector_id.clone(), + ) + .await?) + } else { + let connector = connector.clone(); + let response = resp.clone(); + let maybe_customer = maybe_customer.clone(); + let merchant_account = merchant_account.clone(); + let key_store = key_store.clone(); + let state = state.clone(); + + logger::info!("Initiating async call to save_payment_method in locker"); + + tokio::spawn(async move { + logger::info!("Starting async call to save_payment_method in locker"); + + let result = tokenization::save_payment_method( + &state, + &connector, + response, + &maybe_customer, + &merchant_account, + self.request.payment_method_type, + &key_store, + ) + .await; + + if let Err(err) = result { + logger::error!("Asynchronously saving card in locker failed : {:?}", err); } - } - }?; + }); - Ok(mandate::mandate_procedure( - state, - resp, - maybe_customer, - pm_id, - connector.merchant_connector_id.clone(), - ) - .await?) + Ok(resp) + } } else { Ok(self.clone()) } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index abf06b2ed0ee..46cb7fde767d 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -399,6 +399,7 @@ pub async fn get_token_pm_type_mandate_details( request: &api::PaymentsRequest, mandate_type: Option, merchant_account: &domain::MerchantAccount, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( Option, Option, @@ -427,7 +428,13 @@ pub async fn get_token_pm_type_mandate_details( recurring_mandate_payment_data, payment_method_type_, mandate_connector, - ) = get_token_for_recurring_mandate(state, request, merchant_account).await?; + ) = get_token_for_recurring_mandate( + state, + request, + merchant_account, + merchant_key_store, + ) + .await?; Ok(( token_, payment_method_, @@ -452,6 +459,7 @@ pub async fn get_token_for_recurring_mandate( state: &AppState, req: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( Option, Option, @@ -501,7 +509,9 @@ pub async fn get_token_for_recurring_mandate( }; if let diesel_models::enums::PaymentMethod::Card = payment_method.payment_method { - let _ = cards::get_lookup_key_from_locker(state, &token, &payment_method).await?; + let _ = + cards::get_lookup_key_from_locker(state, &token, &payment_method, merchant_key_store) + .await?; if let Some(payment_method_from_request) = req.payment_method { let pm: storage_enums::PaymentMethod = payment_method_from_request; if pm != payment_method.payment_method { @@ -1320,6 +1330,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( operation: BoxedOperation<'a, F, R, Ctx>, state: &'a AppState, payment_data: &mut PaymentData, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, R, Ctx>, Option, @@ -1373,6 +1384,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( let (pm, supplementary_data) = vault::Vault::get_payment_method_data_from_locker( state, &hyperswitch_token, + merchant_key_store, ) .await .attach_printable( @@ -1402,6 +1414,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( &updated_pm, payment_data.payment_intent.customer_id.to_owned(), enums::PaymentMethod::Card, + merchant_key_store, ) .await?; Some(updated_pm) @@ -1442,6 +1455,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>( state, &payment_data.payment_intent, &payment_data.payment_attempt, + merchant_key_store, ) .await?; @@ -1461,6 +1475,7 @@ pub async fn store_in_vault_and_generate_ppmt( payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, payment_method: enums::PaymentMethod, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let router_token = vault::Vault::store_payment_method_data_in_locker( state, @@ -1468,6 +1483,7 @@ pub async fn store_in_vault_and_generate_ppmt( payment_method_data, payment_intent.customer_id.to_owned(), payment_method, + merchant_key_store, ) .await?; let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token"); @@ -1491,6 +1507,7 @@ pub async fn store_payment_method_data_in_vault( payment_intent: &PaymentIntent, payment_method: enums::PaymentMethod, payment_method_data: &api::PaymentMethodData, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult> { if should_store_payment_method_data_in_vault( &state.conf.temp_locker_enable_config, @@ -1503,6 +1520,7 @@ pub async fn store_payment_method_data_in_vault( payment_intent, payment_attempt, payment_method, + merchant_key_store, ) .await?; diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index d198cd562a79..ad747ac2792a 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -123,6 +123,7 @@ pub trait Domain: Send + Sync { state: &'a AppState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, R, Ctx>, Option, @@ -233,11 +234,12 @@ where state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } } @@ -282,6 +284,7 @@ where _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCaptureRequest, Ctx>, Option, @@ -343,6 +346,7 @@ where _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCancelRequest, Ctx>, Option, @@ -394,6 +398,7 @@ where _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRejectRequest, Ctx>, Option, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 16bb84f69ddb..4cd1bae04dee 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -88,6 +88,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ) .await?; @@ -299,12 +300,13 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { let (op, payment_method_data) = - helpers::make_pm_data(Box::new(self), state, payment_data).await?; + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await?; utils::when(payment_method_data.is_none(), || { Err(errors::ApiErrorResponse::PaymentMethodNotFound) diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index a2f5292a37f7..4e87b3869431 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -87,6 +87,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ) .await?; @@ -294,12 +295,13 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { let (op, payment_method_data) = - helpers::make_pm_data(Box::new(self), state, payment_data).await?; + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await?; Ok((op, payment_method_data)) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 8842963990b6..5de281a5e63c 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -69,6 +69,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ); let (mut payment_intent, mandate_details) = @@ -423,12 +424,13 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { let (op, payment_method_data) = - helpers::make_pm_data(Box::new(self), state, payment_data).await?; + helpers::make_pm_data(Box::new(self), state, payment_data, key_store).await?; utils::when(payment_method_data.is_none(), || { Err(errors::ApiErrorResponse::PaymentMethodNotFound) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index a9ed1694ae61..df2cd81dfc68 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -108,6 +108,7 @@ impl request, mandate_type, merchant_account, + merchant_key_store, ) .await?; @@ -354,11 +355,12 @@ impl Domain, _storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/operations/payment_method_validate.rs b/crates/router/src/core/payments/operations/payment_method_validate.rs index 6d97f7b66cd1..33f6c23c8363 100644 --- a/crates/router/src/core/payments/operations/payment_method_validate.rs +++ b/crates/router/src/core/payments/operations/payment_method_validate.rs @@ -297,11 +297,12 @@ where state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::VerifyRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } async fn get_connector<'a>( diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index cf16a053592b..354c62648bb3 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -318,6 +318,7 @@ where _state: &'b AppState, _payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsSessionRequest, Ctx>, Option, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 227e7e2f90db..e9fa301bf07b 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -282,6 +282,7 @@ where state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsStartRequest, Ctx>, Option, @@ -293,7 +294,7 @@ where .map(|connector_name| connector_name == *"bluesnap".to_string()) .unwrap_or(false) { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } else { Ok((Box::new(self), None)) } diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index d20830d9bc6b..96aac6c9d79b 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -95,11 +95,12 @@ impl Domain, _storage_scheme: enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index d0b17b5d460d..622d09754396 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -106,6 +106,7 @@ impl request, mandate_type.clone(), merchant_account, + key_store, ) .await?; @@ -394,11 +395,12 @@ impl Domain, _storage_scheme: storage_enums::MerchantStorageScheme, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, Option, )> { - helpers::make_pm_data(Box::new(self), state, payment_data).await + helpers::make_pm_data(Box::new(self), state, payment_data, merchant_key_store).await } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index f7831465e1ce..794180e2112e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -1,6 +1,7 @@ use common_utils::{ext_traits::ValueExt, pii}; use error_stack::{report, ResultExt}; use masking::ExposeInterface; +use router_env::{instrument, tracing}; use super::helpers; use crate::{ @@ -20,6 +21,7 @@ use crate::{ utils::OptionExt, }; +#[instrument(skip_all)] pub async fn save_payment_method( state: &AppState, connector: &api::ConnectorData, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 2fcd792eca83..c62529826387 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -346,7 +346,7 @@ pub fn payments_to_payments_response( connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, connector_http_status_code: Option, external_latency: Option, - is_latency_header_enabled: Option, + _is_latency_header_enabled: Option, ) -> RouterResponse where Op: Debug, @@ -451,13 +451,13 @@ where payment_confirm_source.to_string(), )) } - if Some(true) == is_latency_header_enabled { - headers.extend( - external_latency - .map(|latency| vec![(X_HS_LATENCY.to_string(), latency.to_string())]) - .unwrap_or_default(), - ); - } + + headers.extend( + external_latency + .map(|latency| vec![(X_HS_LATENCY.to_string(), latency.to_string())]) + .unwrap_or_default(), + ); + let output = Ok(match payment_request { Some(_request) => { if payments::is_start_pay(&operation) diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index ddb2a017e35a..f1136a35a65a 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -112,7 +112,7 @@ where // Validate create request let (payout_id, payout_method_data) = - validator::validate_create_request(&state, &merchant_account, &req).await?; + validator::validate_create_request(&state, &merchant_account, &req, &key_store).await?; // Create DB entries let mut payout_data = payout_create_db_entries( @@ -403,6 +403,7 @@ pub async fn payouts_fulfill_core( &payout_attempt.merchant_id, &payout_attempt.payout_id, Some(&payout_data.payouts.payout_type), + &key_store, ) .await? .get_required_value("payout_method_data")?, @@ -458,6 +459,7 @@ pub async fn call_connector_payout( &payout_attempt.merchant_id, &payout_attempt.payout_id, Some(&payouts.payout_type), + key_store, ) .await? .get_required_value("payout_method_data")?, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 9890cd9d5efd..39079ea36cd6 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -28,6 +28,7 @@ use crate::{ utils::{self, OptionExt}, }; +#[allow(clippy::too_many_arguments)] pub async fn make_payout_method_data<'a>( state: &'a AppState, payout_method_data: Option<&api::PayoutMethodData>, @@ -36,6 +37,7 @@ pub async fn make_payout_method_data<'a>( merchant_id: &str, payout_id: &str, payout_type: Option<&api_enums::PayoutType>, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult> { let db = &*state.store; let hyperswitch_token = if let Some(payout_token) = payout_token { @@ -67,14 +69,16 @@ pub async fn make_payout_method_data<'a>( match (payout_method_data.to_owned(), hyperswitch_token) { // Get operation (None, Some(payout_token)) => { - let (pm, supplementary_data) = vault::Vault::get_payout_method_data_from_temporary_locker( - state, - &payout_token, - ) - .await - .attach_printable( - "Payout method for given token not found or there was a problem fetching it", - )?; + let (pm, supplementary_data) = + vault::Vault::get_payout_method_data_from_temporary_locker( + state, + &payout_token, + merchant_key_store, + ) + .await + .attach_printable( + "Payout method for given token not found or there was a problem fetching it", + )?; utils::when( supplementary_data .customer_id @@ -93,6 +97,7 @@ pub async fn make_payout_method_data<'a>( payout_token.to_owned(), payout_method, Some(customer_id.to_owned()), + merchant_key_store, ) .await?; diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index c815d91e41dd..3793ee523dc3 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -57,6 +57,7 @@ pub async fn validate_create_request( state: &AppState, merchant_account: &domain::MerchantAccount, req: &payouts::PayoutCreateRequest, + merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(String, Option)> { let merchant_id = &merchant_account.merchant_id; @@ -103,6 +104,7 @@ pub async fn validate_create_request( &merchant_account.merchant_id, payout_id.as_ref(), req.payout_type.as_ref(), + merchant_key_store, ) .await? } diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index fcda3c8daf03..a42e46ca62d5 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -476,14 +476,13 @@ pub async fn sync_refund_with_gateway( pub async fn refund_update_core( state: AppState, merchant_account: domain::MerchantAccount, - refund_id: &str, req: refunds::RefundUpdateRequest, ) -> RouterResponse { let db = state.store.as_ref(); let refund = db .find_refund_by_merchant_id_refund_id( &merchant_account.merchant_id, - refund_id, + &req.refund_id, merchant_account.storage_scheme, ) .await @@ -501,7 +500,9 @@ pub async fn refund_update_core( ) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("Unable to update refund with refund_id: {refund_id}"))?; + .attach_printable_lazy(|| { + format!("Unable to update refund with refund_id: {}", req.refund_id) + })?; Ok(services::ApplicationResponse::Json(response.foreign_into())) } @@ -698,7 +699,7 @@ pub async fn refund_list( pub async fn refund_filter_list( state: AppState, merchant_account: domain::MerchantAccount, - req: api_models::refunds::TimeRange, + req: api_models::payments::TimeRange, ) -> RouterResponse { let db = state.store; let filter_list = db diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 8033cc792b54..723611ed5009 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -1,7 +1,7 @@ pub mod helpers; pub mod transformers; -use api_models::routing as routing_types; +use api_models::routing::{self as routing_types, RoutingAlgorithmId}; #[cfg(feature = "business_profile_routing")] use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; #[cfg(not(feature = "business_profile_routing"))] @@ -319,14 +319,14 @@ pub async fn link_routing_config( pub async fn retrieve_routing_config( state: AppState, merchant_account: domain::MerchantAccount, - algorithm_id: String, + algorithm_id: RoutingAlgorithmId, ) -> RouterResponse { let db = state.store.as_ref(); #[cfg(feature = "business_profile_routing")] { let routing_algorithm = db .find_routing_algorithm_by_algorithm_id_merchant_id( - &algorithm_id, + &algorithm_id.0, &merchant_account.merchant_id, ) .await @@ -356,13 +356,13 @@ pub async fn retrieve_routing_config( let record = merchant_dictionary .records .into_iter() - .find(|rec| rec.id == algorithm_id) + .find(|rec| rec.id == algorithm_id.0) .ok_or(errors::ApiErrorResponse::ResourceIdNotFound) .into_report() .attach_printable("Algorithm with the given ID not found in the merchant dictionary")?; let algorithm_config = db - .find_config_by_key(&algorithm_id) + .find_config_by_key(&algorithm_id.0) .await .change_context(errors::ApiErrorResponse::ResourceIdNotFound) .attach_printable("Routing config not found in DB")?; diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index fa700b4cd663..e643e0455b8b 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -1,5 +1,4 @@ pub mod utils; -use actix_web::web; use api_models::verifications::{self, ApplepayMerchantResponse}; use common_utils::{errors::CustomResult, ext_traits::Encode}; use error_stack::ResultExt; @@ -18,7 +17,7 @@ const APPLEPAY_INTERNAL_MERCHANT_NAME: &str = "Applepay_merchant"; pub async fn verify_merchant_creds_for_applepay( state: AppState, _req: &actix_web::HttpRequest, - body: web::Json, + body: verifications::ApplepayMerchantVerificationRequest, kms_config: &kms::KmsConfig, merchant_id: String, ) -> CustomResult< diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index b62ffd2c530f..3efef2c40f29 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -12,6 +12,7 @@ pub mod ephemeral_key; pub mod events; pub mod file; pub mod fraud_check; +pub mod gsm; pub mod locker_mock_up; pub mod mandate; pub mod merchant_account; @@ -80,6 +81,7 @@ pub trait StorageInterface: + business_profile::BusinessProfileInterface + organization::OrganizationInterface + routing_algorithm::RoutingAlgorithmInterface + + gsm::GsmInterface + 'static { fn get_scheduler_db(&self) -> Box; diff --git a/crates/router/src/db/gsm.rs b/crates/router/src/db/gsm.rs new file mode 100644 index 000000000000..b623bdc2bcf5 --- /dev/null +++ b/crates/router/src/db/gsm.rs @@ -0,0 +1,180 @@ +use diesel_models::gsm as storage; +use error_stack::IntoReport; + +use super::MockDb; +use crate::{ + connection, + core::errors::{self, CustomResult}, + services::Store, +}; + +#[async_trait::async_trait] +pub trait GsmInterface { + async fn add_gsm_rule( + &self, + rule: storage::GatewayStatusMappingNew, + ) -> CustomResult; + async fn find_gsm_decision( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult; + async fn find_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult; + async fn update_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + data: storage::GatewayStatusMappingUpdate, + ) -> CustomResult; + + async fn delete_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult; +} + +#[async_trait::async_trait] +impl GsmInterface for Store { + async fn add_gsm_rule( + &self, + rule: storage::GatewayStatusMappingNew, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + rule.insert(&conn).await.map_err(Into::into).into_report() + } + + async fn find_gsm_decision( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage::GatewayStatusMap::retrieve_decision( + &conn, connector, flow, sub_flow, code, message, + ) + .await + .map_err(Into::into) + .into_report() + } + + async fn find_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage::GatewayStatusMap::find(&conn, connector, flow, sub_flow, code, message) + .await + .map_err(Into::into) + .into_report() + } + + async fn update_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + data: storage::GatewayStatusMappingUpdate, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::GatewayStatusMap::update(&conn, connector, flow, sub_flow, code, message, data) + .await + .map_err(Into::into) + .into_report() + } + + async fn delete_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::GatewayStatusMap::delete(&conn, connector, flow, sub_flow, code, message) + .await + .map_err(Into::into) + .into_report() + } +} + +#[async_trait::async_trait] +impl GsmInterface for MockDb { + async fn add_gsm_rule( + &self, + _rule: storage::GatewayStatusMappingNew, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn find_gsm_decision( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn find_gsm_rule( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn update_gsm_rule( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + _data: storage::GatewayStatusMappingUpdate, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn delete_gsm_rule( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } +} diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index a6133edad673..c9b9f8ac55f5 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -78,7 +78,7 @@ pub trait RefundInterface { async fn filter_refund_by_meta_constraints( &self, merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult; @@ -232,7 +232,7 @@ mod storage { async fn filter_refund_by_meta_constraints( &self, merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -707,7 +707,7 @@ mod storage { async fn filter_refund_by_meta_constraints( &self, merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -979,7 +979,7 @@ impl RefundInterface for MockDb { async fn filter_refund_by_meta_constraints( &self, _merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let refunds = self.refunds.lock().await; diff --git a/crates/router/src/events/api_logs.rs b/crates/router/src/events/api_logs.rs index 35eaf1edae7f..1a47568e7ad8 100644 --- a/crates/router/src/events/api_logs.rs +++ b/crates/router/src/events/api_logs.rs @@ -1,9 +1,25 @@ +use actix_web::HttpRequest; +pub use common_utils::events::{ApiEventMetric, ApiEventsType}; +use common_utils::impl_misc_api_event_type; use router_env::{tracing_actix_web::RequestId, types::FlowMetric}; use serde::Serialize; use time::OffsetDateTime; use super::{EventType, RawEvent}; -use crate::services::authentication::AuthenticationType; +#[cfg(feature = "dummy_connector")] +use crate::routes::dummy_connector::types::{ + DummyConnectorPaymentCompleteRequest, DummyConnectorPaymentConfirmRequest, + DummyConnectorPaymentRequest, DummyConnectorPaymentResponse, + DummyConnectorPaymentRetrieveRequest, DummyConnectorRefundRequest, + DummyConnectorRefundResponse, DummyConnectorRefundRetrieveRequest, +}; +use crate::{ + core::payments::PaymentsRedirectResponseData, + services::{authentication::AuthenticationType, ApplicationResponse, PaymentLinkFormData}, + types::api::{ + AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId, + }, +}; #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ApiEvent { @@ -15,10 +31,16 @@ pub struct ApiEvent { #[serde(flatten)] auth_type: AuthenticationType, request: serde_json::Value, + user_agent: Option, + ip_addr: Option, + url_path: String, response: Option, + #[serde(flatten)] + event_type: ApiEventsType, } impl ApiEvent { + #[allow(clippy::too_many_arguments)] pub fn new( api_flow: &impl FlowMetric, request_id: &RequestId, @@ -27,6 +49,8 @@ impl ApiEvent { request: serde_json::Value, response: Option, auth_type: AuthenticationType, + event_type: ApiEventsType, + http_req: &HttpRequest, ) -> Self { Self { api_flow: api_flow.to_string(), @@ -37,6 +61,16 @@ impl ApiEvent { request, response, auth_type, + ip_addr: http_req + .connection_info() + .realip_remote_addr() + .map(ToOwned::to_owned), + user_agent: http_req + .headers() + .get("user-agent") + .and_then(|user_agent_value| user_agent_value.to_str().ok().map(ToOwned::to_owned)), + url_path: http_req.path().to_string(), + event_type, } } } @@ -52,3 +86,35 @@ impl TryFrom for RawEvent { }) } } + +impl ApiEventMetric for ApplicationResponse { + fn get_api_event_type(&self) -> Option { + match self { + Self::Json(r) => r.get_api_event_type(), + Self::JsonWithHeaders((r, _)) => r.get_api_event_type(), + _ => None, + } + } +} +impl_misc_api_event_type!( + Config, + CreateFileRequest, + FileId, + AttachEvidenceRequest, + DisputeId, + PaymentLinkFormData, + PaymentsRedirectResponseData, + ConfigUpdate +); + +#[cfg(feature = "dummy_connector")] +impl_misc_api_event_type!( + DummyConnectorPaymentCompleteRequest, + DummyConnectorPaymentRequest, + DummyConnectorPaymentResponse, + DummyConnectorPaymentRetrieveRequest, + DummyConnectorPaymentConfirmRequest, + DummyConnectorRefundRetrieveRequest, + DummyConnectorRefundResponse, + DummyConnectorRefundRequest +); diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 21ebfc06137b..38efe8b75134 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -142,6 +142,7 @@ pub fn mk_app( .service(routes::Files::server(state.clone())) .service(routes::Disputes::server(state.clone())) .service(routes::Routing::server(state.clone())) + .service(routes::Gsm::server(state.clone())) } #[cfg(all(feature = "olap", feature = "kms"))] diff --git a/crates/router/src/openapi.rs b/crates/router/src/openapi.rs index a5bce200889b..dbcd8cbe4ce2 100644 --- a/crates/router/src/openapi.rs +++ b/crates/router/src/openapi.rs @@ -305,7 +305,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payment_methods::RequiredFieldInfo, api_models::refunds::RefundListRequest, api_models::refunds::RefundListResponse, - api_models::refunds::TimeRange, + api_models::payments::TimeRange, api_models::mandates::MandateRevokedResponse, api_models::mandates::MandateResponse, api_models::mandates::MandateCardDetails, diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 38f95c4cdda8..47b9f23cf8cb 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -10,6 +10,7 @@ pub mod disputes; pub mod dummy_connector; pub mod ephemeral_key; pub mod files; +pub mod gsm; pub mod health; pub mod lock_utils; pub mod mandates; @@ -36,7 +37,7 @@ pub use self::app::Routing; pub use self::app::Verify; pub use self::app::{ ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, Customers, Disputes, EphemeralKey, - Files, Health, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, + Files, Gsm, Health, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Refunds, Webhooks, }; #[cfg(feature = "stripe")] diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index a93556202aab..9153e9e747f6 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -388,15 +388,15 @@ pub async fn merchant_account_toggle_kv( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::ConfigKeyUpdate; - let payload = json_payload.into_inner(); - let merchant_id = path.into_inner(); + let mut payload = json_payload.into_inner(); + payload.merchant_id = path.into_inner(); api::server_wrap( flow, state, &req, - (merchant_id, payload), - |state, _, (merchant_id, payload)| kv_for_merchant(state, merchant_id, payload.kv_enabled), + payload, + |state, _, payload| kv_for_merchant(state, payload.merchant_id, payload.kv_enabled), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, ) diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index 6057b4c5db24..c2e289cd0f7e 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -124,16 +124,16 @@ pub async fn api_key_update( ) -> impl Responder { let flow = Flow::ApiKeyUpdate; let (merchant_id, key_id) = path.into_inner(); - let payload = json_payload.into_inner(); + let mut payload = json_payload.into_inner(); + payload.key_id = key_id; + payload.merchant_id = merchant_id; api::server_wrap( flow, state, &req, - (&merchant_id, &key_id, payload), - |state, _, (merchant_id, key_id, payload)| { - api_keys::update_api_key(state, merchant_id, key_id, payload) - }, + payload, + |state, _, payload| api_keys::update_api_key(state, payload), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, ) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 753379dd3e29..2ffa1b72f9a9 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -19,7 +19,7 @@ use super::routing as cloud_routing; #[cfg(all(feature = "olap", feature = "kms"))] use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; #[cfg(feature = "olap")] -use super::{admin::*, api_keys::*, disputes::*, files::*}; +use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*}; use super::{cache::*, health::*, payment_link::*}; #[cfg(any(feature = "olap", feature = "oltp"))] use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; @@ -670,6 +670,20 @@ impl BusinessProfile { } } +pub struct Gsm; + +#[cfg(feature = "olap")] +impl Gsm { + pub fn server(state: AppState) -> Scope { + web::scope("/gsm") + .app_data(web::Data::new(state)) + .service(web::resource("").route(web::post().to(create_gsm_rule))) + .service(web::resource("/get").route(web::post().to(get_gsm_rule))) + .service(web::resource("/update").route(web::post().to(update_gsm_rule))) + .service(web::resource("/delete").route(web::post().to(delete_gsm_rule))) + } +} + #[cfg(all(feature = "olap", feature = "kms"))] pub struct Verify; diff --git a/crates/router/src/routes/dummy_connector.rs b/crates/router/src/routes/dummy_connector.rs index 52a7f7f77c9a..7d2aad7e3482 100644 --- a/crates/router/src/routes/dummy_connector.rs +++ b/crates/router/src/routes/dummy_connector.rs @@ -10,7 +10,7 @@ use crate::{ mod consts; mod core; mod errors; -mod types; +pub mod types; mod utils; #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentCreate))] diff --git a/crates/router/src/routes/gsm.rs b/crates/router/src/routes/gsm.rs new file mode 100644 index 000000000000..02d943792dba --- /dev/null +++ b/crates/router/src/routes/gsm.rs @@ -0,0 +1,93 @@ +use actix_web::{web, HttpRequest, Responder}; +use api_models::gsm as gsm_api_types; +use router_env::{instrument, tracing, Flow}; + +use super::app::AppState; +use crate::{ + core::{api_locking, gsm}, + services::{api, authentication as auth}, +}; + +#[instrument(skip_all, fields(flow = ?Flow::GsmRuleCreate))] +pub async fn create_gsm_rule( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> impl Responder { + let payload = json_payload.into_inner(); + + let flow = Flow::GsmRuleCreate; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload, + |state, _, payload| gsm::create_gsm_rule(state, payload), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::GsmRuleRetrieve))] +pub async fn get_gsm_rule( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> impl Responder { + let gsm_retrieve_req = json_payload.into_inner(); + let flow = Flow::GsmRuleRetrieve; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + gsm_retrieve_req, + |state, _, gsm_retrieve_req| gsm::retrieve_gsm_rule(state, gsm_retrieve_req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::GsmRuleUpdate))] +pub async fn update_gsm_rule( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> impl Responder { + let payload = json_payload.into_inner(); + + let flow = Flow::GsmRuleUpdate; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + payload, + |state, _, payload| gsm::update_gsm_rule(state, payload), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::GsmRuleDelete))] +pub async fn delete_gsm_rule( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> impl Responder { + let payload = json_payload.into_inner(); + + let flow = Flow::GsmRuleDelete; + + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, _, payload| gsm::delete_gsm_rule(state, payload), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 9567868bd9cd..ef6dd766cba2 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -23,6 +23,7 @@ pub enum ApiIdentifier { ApiKeys, PaymentLink, Routing, + Gsm, } impl From for ApiIdentifier { @@ -131,6 +132,10 @@ impl From for ApiIdentifier { } Flow::Verification => Self::Verification, + Flow::GsmRuleCreate + | Flow::GsmRuleRetrieve + | Flow::GsmRuleUpdate + | Flow::GsmRuleDelete => Self::Gsm, } } } diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index 66e175802fe8..6296b760f467 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -93,7 +93,7 @@ pub async fn payments_link_list( state, &req, payload, - |state, auth, req| list_payment_link(state, auth.merchant_account, req), + |state, auth, payload| list_payment_link(state, auth.merchant_account, payload), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, ) diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index 4c4121b5d532..c20f3fbf975d 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -161,13 +161,14 @@ pub async fn refunds_update( path: web::Path, ) -> HttpResponse { let flow = Flow::RefundsUpdate; - let refund_id = path.into_inner(); + let mut refund_update_req = json_payload.into_inner(); + refund_update_req.refund_id = path.into_inner(); api::server_wrap( flow, state, &req, - json_payload.into_inner(), - |state, auth, req| refund_update_core(state, auth.merchant_account, &refund_id, req), + refund_update_req, + |state, auth, req| refund_update_core(state, auth.merchant_account, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, ) @@ -225,7 +226,7 @@ pub async fn refunds_list( pub async fn refunds_filter_list( state: web::Data, req: HttpRequest, - payload: web::Json, + payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsList; api::server_wrap( diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 1d5ccdf502fc..9252c360a9ce 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -47,7 +47,7 @@ pub async fn routing_create_config( pub async fn routing_link_config( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> impl Responder { let flow = Flow::RoutingLinkConfig; Box::pin(oss_api::server_wrap( @@ -61,7 +61,7 @@ pub async fn routing_link_config( auth.merchant_account, #[cfg(not(feature = "business_profile_routing"))] auth.key_store, - algorithm_id, + algorithm_id.0, ) }, #[cfg(not(feature = "release"))] @@ -78,7 +78,7 @@ pub async fn routing_link_config( pub async fn routing_retrieve_config( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> impl Responder { let algorithm_id = path.into_inner(); let flow = Flow::RoutingRetrieveConfig; diff --git a/crates/router/src/routes/verification.rs b/crates/router/src/routes/verification.rs index a0861f2b14d7..2ad061848c92 100644 --- a/crates/router/src/routes/verification.rs +++ b/crates/router/src/routes/verification.rs @@ -22,7 +22,7 @@ pub async fn apple_pay_merchant_registration( flow, state, &req, - json_payload, + json_payload.into_inner(), |state, _, body| { verification::verify_merchant_creds_for_applepay( state.clone(), diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 3d618d047eac..bb0e70b4b27b 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -10,7 +10,7 @@ use std::{ }; use actix_web::{body, web, FromRequest, HttpRequest, HttpResponse, Responder, ResponseError}; -use api_models::enums::{AttemptStatus, CaptureMethod}; +use api_models::enums::CaptureMethod; pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient}; pub use common_utils::request::{ContentType, Method, Request, RequestBuilder}; use common_utils::{ @@ -34,7 +34,7 @@ use crate::{ errors::{self, CustomResult}, payments, }, - events::api_logs::ApiEvent, + events::api_logs::{ApiEvent, ApiEventMetric, ApiEventsType}, logger, routes::{ app::AppStateInfo, @@ -136,6 +136,7 @@ pub trait ConnectorIntegration: ConnectorIntegrationAny, + _connectors: &Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(None) } @@ -226,6 +227,7 @@ pub trait ConnectorIntegration: ConnectorIntegrationAny { let error_res = connector_integration.get_error_response(body)?; - if router_data.connector == "bluesnap" - && error_res.status_code == 403 - && error_res.reason - == Some(format!( - "{} in bluesnap dashboard", - consts::REQUEST_TIMEOUT_PAYMENT_NOT_FOUND - )) - { - router_data.status = AttemptStatus::Failure; + if let Some(status) = error_res.attempt_status { + router_data.status = status; }; error_res } @@ -434,6 +430,7 @@ where message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(), reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()), status_code: 504, + attempt_status: None, }; router_data.response = Err(error_response); router_data.connector_http_status_code = Some(504); @@ -772,8 +769,8 @@ where F: Fn(A, U, T) -> Fut, 'b: 'a, Fut: Future, E>>, - Q: Serialize + Debug + 'a, - T: Debug + Serialize, + Q: Serialize + Debug + 'a + ApiEventMetric, + T: Debug + Serialize + ApiEventMetric, A: AppStateInfo + Clone, E: ErrorSwitch + error_stack::Context, OErr: ResponseError + error_stack::Context, @@ -794,6 +791,8 @@ where .attach_printable("Failed to serialize json request") .change_context(errors::ApiErrorResponse::InternalServerError.switch())?; + let mut event_type = payload.get_api_event_type(); + // Currently auth failures are not recorded as API events let (auth_out, auth_type) = api_auth .authenticate_and_fetch(request.headers(), &request_state) @@ -841,6 +840,7 @@ where .change_context(errors::ApiErrorResponse::InternalServerError.switch())?, ); } + event_type = res.get_api_event_type().or(event_type); metrics::request::track_response_status_code(res) } @@ -855,6 +855,8 @@ where serialized_request, serialized_response, auth_type, + event_type.unwrap_or(ApiEventsType::Miscellaneous), + request, ); match api_event.clone().try_into() { Ok(event) => { @@ -886,8 +888,8 @@ pub async fn server_wrap<'a, A, T, U, Q, F, Fut, E>( where F: Fn(A, U, T) -> Fut, Fut: Future, E>>, - Q: Serialize + Debug + 'a, - T: Debug + Serialize, + Q: Serialize + Debug + ApiEventMetric + 'a, + T: Debug + Serialize + ApiEventMetric, A: AppStateInfo + Clone, ApplicationResponse: Debug, E: ErrorSwitch + error_stack::Context, diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 261195d166cb..f2e86a4bf335 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -923,6 +923,7 @@ pub struct ErrorResponse { pub message: String, pub reason: Option, pub status_code: u16, + pub attempt_status: Option, } impl ErrorResponse { @@ -938,6 +939,7 @@ impl ErrorResponse { .error_message(), reason: None, status_code: http::StatusCode::INTERNAL_SERVER_ERROR.as_u16(), + attempt_status: None, } } } @@ -980,6 +982,7 @@ impl From for ErrorResponse { errors::ApiErrorResponse::ExternalConnectorError { status_code, .. } => status_code, _ => 500, }, + attempt_status: None, } } } @@ -1190,3 +1193,5 @@ impl } } } + +pub type GsmResponse = storage::GatewayStatusMap; diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 75fe327a96d7..5c8c5440e458 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -112,6 +112,7 @@ pub trait ConnectorCommon { code: consts::NO_ERROR_CODE.to_string(), message: consts::NO_ERROR_MESSAGE.to_string(), reason: None, + attempt_status: None, }) } } diff --git a/crates/router/src/types/api/customers.rs b/crates/router/src/types/api/customers.rs index 2050b4149ef8..32430c0918a2 100644 --- a/crates/router/src/types/api/customers.rs +++ b/crates/router/src/types/api/customers.rs @@ -10,6 +10,12 @@ newtype!( derives = (Debug, Clone, Serialize) ); +impl common_utils::events::ApiEventMetric for CustomerResponse { + fn get_api_event_type(&self) -> Option { + self.0.get_api_event_type() + } +} + pub(crate) trait CustomerRequestExt: Sized { fn validate(self) -> RouterResult; } diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs index e5bf1d8dd1bf..5acb66b5068e 100644 --- a/crates/router/src/types/api/payment_methods.rs +++ b/crates/router/src/types/api/payment_methods.rs @@ -1,12 +1,11 @@ use api_models::enums as api_enums; pub use api_models::payment_methods::{ CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CustomerPaymentMethod, - CustomerPaymentMethodsListResponse, DeleteTokenizeByDateRequest, DeleteTokenizeByTokenRequest, - GetTokenizePayloadRequest, GetTokenizePayloadResponse, PaymentMethodCreate, - PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodList, PaymentMethodListRequest, - PaymentMethodListResponse, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData, - TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, - TokenizedWalletValue1, TokenizedWalletValue2, + CustomerPaymentMethodsListResponse, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest, + GetTokenizePayloadResponse, PaymentMethodCreate, PaymentMethodDeleteResponse, PaymentMethodId, + PaymentMethodList, PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodResponse, + PaymentMethodUpdate, PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, + TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, }; use error_stack::report; diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 00a5e07a30e8..1e7c34a420b1 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -11,6 +11,7 @@ pub mod enums; pub mod ephemeral_key; pub mod events; pub mod file; +pub mod gsm; #[cfg(feature = "kv_store")] pub mod kv; pub mod locker_mock_up; @@ -41,10 +42,10 @@ pub use data_models::payments::{ pub use self::{ address::*, api_keys::*, capture::*, cards_info::*, configs::*, connector_response::*, - customers::*, dispute::*, ephemeral_key::*, events::*, file::*, locker_mock_up::*, mandate::*, - merchant_account::*, merchant_connector_account::*, merchant_key_store::*, payment_link::*, - payment_method::*, payout_attempt::*, payouts::*, process_tracker::*, refund::*, - reverse_lookup::*, routing_algorithm::*, + customers::*, dispute::*, ephemeral_key::*, events::*, file::*, gsm::*, locker_mock_up::*, + mandate::*, merchant_account::*, merchant_connector_account::*, merchant_key_store::*, + payment_link::*, payment_method::*, payout_attempt::*, payouts::*, process_tracker::*, + refund::*, reverse_lookup::*, routing_algorithm::*, }; use crate::types::api::routing; diff --git a/crates/router/src/types/storage/gsm.rs b/crates/router/src/types/storage/gsm.rs new file mode 100644 index 000000000000..bcea00e90910 --- /dev/null +++ b/crates/router/src/types/storage/gsm.rs @@ -0,0 +1,4 @@ +pub use diesel_models::gsm::{ + GatewayStatusMap, GatewayStatusMapperUpdateInternal, GatewayStatusMappingNew, + GatewayStatusMappingUpdate, +}; diff --git a/crates/router/src/types/storage/refund.rs b/crates/router/src/types/storage/refund.rs index bdfa8dc5b5ff..4d5667700122 100644 --- a/crates/router/src/types/storage/refund.rs +++ b/crates/router/src/types/storage/refund.rs @@ -27,7 +27,7 @@ pub trait RefundDbExt: Sized { async fn filter_by_meta_constraints( conn: &PgPooledConn, merchant_id: &str, - refund_list_details: &api_models::refunds::TimeRange, + refund_list_details: &api_models::payments::TimeRange, ) -> CustomResult; async fn get_refunds_count( @@ -114,7 +114,7 @@ impl RefundDbExt for Refund { async fn filter_by_meta_constraints( conn: &PgPooledConn, merchant_id: &str, - refund_list_details: &api_models::refunds::TimeRange, + refund_list_details: &api_models::payments::TimeRange, ) -> CustomResult { let start_time = refund_list_details.start_time; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 6be082355a18..0906f2a2836b 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1,6 +1,6 @@ // use actix_web::HttpMessage; use actix_web::http::header::HeaderMap; -use api_models::{enums as api_enums, payments, routing::ConnectorSelection}; +use api_models::{enums as api_enums, gsm as gsm_api_types, payments, routing::ConnectorSelection}; use common_utils::{ consts::X_HS_LATENCY, crypto::Encryptable, @@ -1033,3 +1033,19 @@ impl ForeignFrom } } } + +impl ForeignFrom for storage::GatewayStatusMappingNew { + fn foreign_from(value: gsm_api_types::GsmCreateRequest) -> Self { + Self { + connector: value.connector.to_string(), + flow: value.flow, + sub_flow: value.sub_flow, + code: value.code, + message: value.message, + decision: value.decision.to_string(), + status: value.status, + router_error: value.router_error, + step_up_possible: value.step_up_possible, + } + } +} diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 386bd02ae94b..558044028f7a 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -401,6 +401,7 @@ pub fn handle_json_response_deserialization_failure( code: consts::NO_ERROR_CODE.to_string(), message: consts::UNSUPPORTED_ERROR_MESSAGE.to_string(), reason: Some(response_data), + attempt_status: None, }) } } diff --git a/crates/router/src/workflows/tokenized_data.rs b/crates/router/src/workflows/tokenized_data.rs index 2f5d33173276..0674982f92fe 100644 --- a/crates/router/src/workflows/tokenized_data.rs +++ b/crates/router/src/workflows/tokenized_data.rs @@ -1,14 +1,13 @@ use scheduler::consumer::workflows::ProcessTrackerWorkflow; -#[cfg(feature = "basilisk")] -use crate::core::payment_methods::vault; -use crate::{errors, logger::error, routes::AppState, types::storage}; +use crate::{ + core::payment_methods::vault, errors, logger::error, routes::AppState, types::storage, +}; pub struct DeleteTokenizeDataWorkflow; #[async_trait::async_trait] impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { - #[cfg(feature = "basilisk")] async fn execute_workflow<'a>( &'a self, state: &'a AppState, @@ -17,15 +16,6 @@ impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { Ok(vault::start_tokenize_data_workflow(state, &process).await?) } - #[cfg(not(feature = "basilisk"))] - async fn execute_workflow<'a>( - &'a self, - _state: &'a AppState, - _process: storage::ProcessTracker, - ) -> Result<(), errors::ProcessTrackerError> { - Ok(()) - } - async fn error_handler<'a>( &'a self, _state: &'a AppState, diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 2030bf097ea6..a2f44a1c3fd0 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -237,6 +237,14 @@ pub enum Flow { BusinessProfileList, /// Different verification flows Verification, + /// Gsm Rule Creation flow + GsmRuleCreate, + /// Gsm Rule Retrieve flow + GsmRuleRetrieve, + /// Gsm Rule Update flow + GsmRuleUpdate, + /// Gsm Rule Delete flow + GsmRuleDelete, } /// diff --git a/crates/test_utils/README.md b/crates/test_utils/README.md index 1e92174b3337..2edbc7104c25 100644 --- a/crates/test_utils/README.md +++ b/crates/test_utils/README.md @@ -28,9 +28,16 @@ Required fields: Optional fields: +- `--delay` -- To add a delay between requests in milliseconds. + - Maximum delay is 4294967295 milliseconds or 4294967.295 seconds or 71616 minutes or 1193.6 hours or 49.733 days + - Example: `--delay 1000` (for 1 second delay) - `--folder` -- To run individual folders in the collection - Use double quotes to specify folder name. If you wish to run multiple folders, separate them with a comma (`,`) - Example: `--folder "QuickStart"` or `--folder "Health check,QuickStart"` +- `--header` -- If you wish to add custom headers to the requests, you can pass them as a string + - Example: `--header "key:value"` + - If you want to pass multiple custom headers, you can pass multiple `--header` flags + - Example: `--header "key1:value1" --header "key2:value2"` - `--verbose` -- A boolean to print detailed logs (requests and responses) **Note:** Passing `--verbose` will also print the connector as well as admin API keys in the logs. So, make sure you don't push the commands with `--verbose` to any public repository. diff --git a/crates/test_utils/src/main.rs b/crates/test_utils/src/main.rs index 637122e468e6..22c91e063d8f 100644 --- a/crates/test_utils/src/main.rs +++ b/crates/test_utils/src/main.rs @@ -3,10 +3,10 @@ use std::process::{exit, Command}; use test_utils::newman_runner; fn main() { - let mut newman_command: Command = newman_runner::command_generate(); + let mut runner = newman_runner::generate_newman_command(); // Execute the newman command - let output = newman_command.spawn(); + let output = runner.newman_command.spawn(); let mut child = match output { Ok(child) => child, Err(err) => { @@ -16,6 +16,30 @@ fn main() { }; let status = child.wait(); + if runner.file_modified_flag { + let git_status = Command::new("git") + .args([ + "restore", + format!("{}/event.prerequest.js", runner.collection_path).as_str(), + ]) + .output(); + + match git_status { + Ok(output) => { + if output.status.success() { + let stdout_str = String::from_utf8_lossy(&output.stdout); + println!("Git command executed successfully: {stdout_str}"); + } else { + let stderr_str = String::from_utf8_lossy(&output.stderr); + eprintln!("Git command failed with error: {stderr_str}"); + } + } + Err(e) => { + eprintln!("Error running Git: {e}"); + } + } + } + let exit_code = match status { Ok(exit_status) => { if exit_status.success() { diff --git a/crates/test_utils/src/newman_runner.rs b/crates/test_utils/src/newman_runner.rs index c51556f8f255..a6e0268e2c29 100644 --- a/crates/test_utils/src/newman_runner.rs +++ b/crates/test_utils/src/newman_runner.rs @@ -1,22 +1,28 @@ -use std::{env, process::Command}; +use std::{env, io::Write, path::Path, process::Command}; use clap::{arg, command, Parser}; use masking::PeekInterface; use crate::connector_auth::{ConnectorAuthType, ConnectorAuthenticationMap}; - #[derive(Parser)] #[command(version, about = "Postman collection runner using newman!", long_about = None)] struct Args { /// Admin API Key of the environment - #[arg(short, long = "admin_api_key")] + #[arg(short, long)] admin_api_key: String, /// Base URL of the Hyperswitch environment - #[arg(short, long = "base_url")] + #[arg(short, long)] base_url: String, /// Name of the connector - #[arg(short, long = "connector_name")] + #[arg(short, long)] connector_name: String, + /// Custom headers + #[arg(short = 'H', long = "header")] + custom_headers: Option>, + /// Minimum delay in milliseconds to be added before sending a request + /// By default, 7 milliseconds will be the delay + #[arg(short, long, default_value_t = 7)] + delay_request: u32, /// Folder name of specific tests #[arg(short, long = "folder")] folders: Option, @@ -25,6 +31,12 @@ struct Args { verbose: bool, } +pub struct ReturnArgs { + pub newman_command: Command, + pub file_modified_flag: bool, + pub collection_path: String, +} + // Just by the name of the connector, this function generates the name of the collection dir // Example: CONNECTOR_NAME="stripe" -> OUTPUT: postman/collection-dir/stripe #[inline] @@ -32,7 +44,29 @@ fn get_path(name: impl AsRef) -> String { format!("postman/collection-dir/{}", name.as_ref()) } -pub fn command_generate() -> Command { +// This function currently allows you to add only custom headers. +// In future, as we scale, this can be modified based on the need +fn insert_content(dir: T, content_to_insert: U) -> std::io::Result<()> +where + T: AsRef, + U: AsRef, +{ + let file_name = "event.prerequest.js"; + let file_path = dir.as_ref().join(file_name); + + // Open the file in write mode or create it if it doesn't exist + let mut file = std::fs::OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(file_path)?; + + write!(file, "{}", content_to_insert.as_ref())?; + + Ok(()) +} + +pub fn generate_newman_command() -> ReturnArgs { let args = Args::parse(); let connector_name = args.connector_name; @@ -129,7 +163,10 @@ pub fn command_generate() -> Command { ]); } - newman_command.arg("--delay-request").arg("7"); // 7 milli seconds delay + newman_command.args([ + "--delay-request", + format!("{}", &args.delay_request).as_str(), + ]); newman_command.arg("--color").arg("on"); @@ -151,5 +188,24 @@ pub fn command_generate() -> Command { newman_command.arg("--verbose"); } - newman_command + let mut modified = false; + if let Some(headers) = &args.custom_headers { + for header in headers { + if let Some((key, value)) = header.split_once(':') { + let content_to_insert = + format!(r#"pm.request.headers.add({{key: "{key}", value: "{value}"}});"#); + if insert_content(&collection_path, &content_to_insert).is_ok() { + modified = true; + } + } else { + eprintln!("Invalid header format: {}", header); + } + } + } + + ReturnArgs { + newman_command, + file_modified_flag: modified, + collection_path, + } } diff --git a/docs/imgs/aws_button.png b/docs/imgs/aws_button.png new file mode 100644 index 0000000000000000000000000000000000000000..4c8c2c20e09729f83f528882be4c3082b0264c0c GIT binary patch literal 2427 zcma);=OY^k1I9xsB|(W9MbL^I5}OiftV*s`)QDE?S&e!xW@6N;D_Sv2sWXdmXScSq zcO5;VXb_aNh}uNl_5J*Qc|SbA=lKJk55H$tmN!9sQhWda0ED$LwfWK0KZ4}F_=8V} z(e(hpC1b29#x{&&E5E}5>>x3~+{8VB8B0|t-foPweZyBkF?s7M(&m~GGlIIuyl+-L?4`;=39f;UVrbH zno9op0(zyv4sHiCV@SiZeN2pM&daUAWgeXC`@zb`p);_gbU|moKUT>YYh&w7rybOu z9kwbWDP1APC@ZBH)>4csgXx$2pCI-HrA>CHOQL9RsndkTYE(^b05Wp-SI3;Mnvm&M zeKNaAALyjVPLtd`82kDW)zF%TdYl?{-FtCjF~Vb}AXsNmM9KL3r!edHduFSx?zbEX zRR&RXy+HIz?^?FeU(CLZ%bK&|jh{6RTm7gTVh`-z=GMs&!;5tf!n_EBDzwFCs=ZD^B3jU6W z=uiHcL2|ZX*yYddK?|Mf@kl|r{oMxJsV1M`wl{!+MK8Lk{wc5~qj2dFPk1d(0;c@I ztjmhtKTTluM6Mh+5%rvP%nGXtl5RY6^1rGo((Y(bG@dwuNvy;}Ds8R!c(qX6y`j~g zk1gajzO~gI)~ia$D)-OmY$Cw7_b+kxYD=b%4tae)54pRnmM^gCS;B&np_%YYmU2pu z+}Hz`x83WW5rsUCRjv?brC9s2$fqF-nI-p!wlGYszOl=G2+T-Uf-q(>P z+t8r%ONP$kOpS;uvI0h|o5HLmw(Gf&cTr7f?YIWjF{HpHryu9XVjW3g*5$>jgURGgQ%=`+9?^UojX5Hw+; zUwm0w4Gru?ENJ%XNpZubdu)4=TJ69>^*^QUWzd2&-TS;XoZd}nL0EhaFa zpC0ZkIJkK%yvvm$xo&);6flHS;uctPqSdesg(pn(bk+7{CXenZ+TOT^KahQ4u!D&VsCO4$#!q#q133_4_K_i5{4@E>6v%rMXrPfHFKt07n>VcrSr;9Jo8mKCL9;0{q zRyn1pXXX{>)*%)ACifx1H$_LY)YuynR2>oc?b|2D1V~jW8^K$~2^mFqV3lT+LBr0WIrmKZBOVMJN?A4*2m^^1H^MVa6%~;96 zMkpmfekzrZZeXE6ag?itY2weeR##`fy5tS@L7p`So!t3y(Pn<~RYoIMer)bFi+1NZbLBn3cZoI%PVp2Z zkLQLGH}whxB?EWc5ZUUbV`iW7QKj$Trf6!@gbbtrx&dVDDc9rgem;A-JjS9%|OuM#;KLFdd!9Da$_>Jgyc8`Ws!>dDbs27`6e zlz^I6A(ju(k#85K3PgXl9p1@}tv?GTB_x$GtdR5~Py9P3!M0eN8ie57%*IgK z+$|-=KFaO%FDh!mA~fPhEDFX1W}}J>0|M}-e7Scq%0kAUSIQMDeX_K z=v%%lp?el*ymW@oQr=OtR?Ae`5dy#)$f)@!6*yv4N9DD|L#deJh_F#@NX}Hk_=rW+=MNQmJ}1v|t1` z-*TlKtb(rSc4QgQ7dlzwYE^*5s3zvU5xU-{{_PWUyOaAx$-{VJp!B*rx{f=Uhao03 ztd1M^mHl$P37-XLkZLP->h~LM80nNE;ZyVA+ng~3r4&UV=CT+E3qQdoG`Qf$tEC`2XhqD!Bi8QL+7dnK!3@a_CH#`YEg57T`lGJsA z4(0g5_>LYt`qVyky#)`mZ{I8yyh(<>Yb4(_HiYsKnmP5P0ym$ffOL zY__Wiue)=&IKQggH>1^p>3yHHj_jjYo7QBO!=-KQRZ>ax7>Y`%j~!XU7=^t~&~3(V zb#`q6?6Yf;*o>qdewutlu^bRumPn!ocH1wRkJOEiYt^R4`j?^HiZ%ruUm=p@v3xFU zGK6(9UuAi2KYF|PBPIIo-!FWu=gKQaOa(e@L@$xdu+8s_aR<;M+(E&OYQfYY%H5Md zy?yH@y%V_W&Zzzt~TnOkQ@@a|Wxp3su4!B@o%7tic8qPG6KWzSA+{D|in zq7xE!DrO9!i5eHbjk`mCxd#vHdjIf?-49yehaaM{d7g@$zuy0O?MB@hb|nv50>fQ@ kH?;nH*ukp Date: Fri, 10 Nov 2023 16:22:12 +0530 Subject: [PATCH 15/33] refactor(router): removed commented code --- crates/router/src/core/payments/helpers.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 656990993dd9..4ee2fd4b94d3 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3428,23 +3428,3 @@ pub fn validate_payment_link_request( } Ok(()) } - -// #[cfg(feature = "olap")] -// pub fn validate_payment_link_list_request( -// req: &api_models::payments::PaymentLinkListConstraints, -// ) -> CustomResult<(), errors::ApiErrorResponse> { -// use common_utils::consts::PAYMENTS_LINK_LIST_LIMIT; - -// utils::when( -// req.limit > PAYMENTS_LINK_LIST_LIMIT || req.limit < 1, -// || { -// Err(errors::ApiErrorResponse::InvalidRequestData { -// message: format!( -// "limit should be in between 1 and {}", -// PAYMENTS_LINK_LIST_LIMIT -// ), -// }) -// }, -// )?; -// Ok(()) -// } From f504235e471f9ccb9f5962a471c053a1d08fbc09 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 16:23:53 +0530 Subject: [PATCH 16/33] refactor(router): removed unwanted change --- README.md | 4 ---- crates/router/src/db.rs | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4d6c5d448ccc..93c8312502fa 100644 --- a/README.md +++ b/README.md @@ -68,10 +68,6 @@ The fastest and easiest way to try hyperswitch is via our CDK scripts   
-2. Sign-in to your AWS console. - -3. Follow the instructions provided on the console to successfully deploy Hyperswitch - For an early access to the production-ready setup fill this Early Access Form 2. Sign-in to your AWS console. diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index e2c839839cbd..9687f7f97c92 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -82,6 +82,8 @@ pub trait StorageInterface: + organization::OrganizationInterface + routing_algorithm::RoutingAlgorithmInterface + gsm::GsmInterface + + user::UserInterface + + user_role::UserRoleInterface + 'static { fn get_scheduler_db(&self) -> Box; From 8d7d812cf3130132e3523bc9bdd822fa3d29421a Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 16:24:52 +0530 Subject: [PATCH 17/33] refactor(router): removed extra unwanted data from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 93c8312502fa..f1bc76de04b4 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ The fastest and easiest way to try hyperswitch is via our CDK scripts    -For an early access to the production-ready setup fill this Early Access Form 2. Sign-in to your AWS console. 3. Follow the instructions provided on the console to successfully deploy Hyperswitch From 40a8b3612cd1dab700975b8eb57de8269a8917b4 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 16:42:06 +0530 Subject: [PATCH 18/33] refactor(router): fixed schema.rs issue --- crates/diesel_models/src/schema.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 752105678c1f..74ecd17cb279 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -666,11 +666,11 @@ diesel::table! { created_at -> Timestamp, last_modified_at -> Timestamp, fulfilment_time -> Nullable, + #[max_length = 64] + custom_merchant_name -> Nullable, payment_link_config -> Nullable, #[max_length = 255] description -> Nullable, - #[max_length = 64] - custom_merchant_name -> Nullable, } } From 6529659191ecd50979f13f38c227aedebb2711fe Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 16:49:31 +0530 Subject: [PATCH 19/33] refactor(router): fixed db mismatch issue --- crates/diesel_models/src/payment_link.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/diesel_models/src/payment_link.rs b/crates/diesel_models/src/payment_link.rs index 231756502275..a29e548d8ecc 100644 --- a/crates/diesel_models/src/payment_link.rs +++ b/crates/diesel_models/src/payment_link.rs @@ -20,9 +20,9 @@ pub struct PaymentLink { pub last_modified_at: PrimitiveDateTime, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub custom_merchant_name: Option, pub payment_link_config: Option, pub description: Option, - pub custom_merchant_name: Option, } #[derive( Clone, @@ -49,7 +49,7 @@ pub struct PaymentLinkNew { pub last_modified_at: Option, #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub fulfilment_time: Option, + pub custom_merchant_name: Option, pub payment_link_config: Option, pub description: Option, - pub custom_merchant_name: Option, } From 8f5fb073c6502a6a021baa152e9472845e6d9f65 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 16:51:34 +0530 Subject: [PATCH 20/33] refactor(router): fixed spell check issue --- crates/router/src/db/payment_link.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index dde27eceb609..c8cfc6eab955 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -24,7 +24,7 @@ pub trait PaymentLinkInterface { async fn find_payment_link_by_merchant_id( &self, merchant_id: &str, - payment_link_contraints: api_models::payments::PaymentLinkListConstraints, + payment_link_constraints: api_models::payments::PaymentLinkListConstraints, ) -> CustomResult, errors::StorageError>; } @@ -88,7 +88,7 @@ impl PaymentLinkInterface for MockDb { async fn find_payment_link_by_merchant_id( &self, _merchant_id: &str, - _payment_link_contraints: api_models::payments::PaymentLinkListConstraints, + _payment_link_constraints: api_models::payments::PaymentLinkListConstraints, ) -> CustomResult, errors::StorageError> { // TODO: Implement function for `MockDb`x Err(errors::StorageError::MockDbError)? From 8a3d77f1291bce742a02506bcca9c8ddc3f293c3 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 19:00:29 +0530 Subject: [PATCH 21/33] refactor(router): fixed open-api and clippy issue --- crates/router/src/db/payment_link.rs | 6 ++-- crates/router/src/routes/app.rs | 7 ++-- crates/router/src/routes/payment_link.rs | 24 ++++++++++++++ openapi/openapi_spec.json | 41 ++++++++++++++---------- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index c8cfc6eab955..181510e4cb54 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -1,10 +1,10 @@ use error_stack::IntoReport; -#[cfg(feature = "olap")] -use super::{MockDb, Store}; use crate::{ connection, core::errors::{self, CustomResult}, + db::MockDb, + services::Store, types::storage::{self, PaymentLinkDbExt}, }; @@ -20,7 +20,6 @@ pub trait PaymentLinkInterface { _payment_link: storage::PaymentLinkNew, ) -> CustomResult; - #[cfg(feature = "olap")] async fn find_payment_link_by_merchant_id( &self, merchant_id: &str, @@ -53,7 +52,6 @@ impl PaymentLinkInterface for Store { .into_report() } - #[cfg(feature = "olap")] async fn find_payment_link_by_merchant_id( &self, merchant_id: &str, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 2ffa1b72f9a9..6e1e2ea4fc1a 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -20,9 +20,9 @@ use super::routing as cloud_routing; use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; #[cfg(feature = "olap")] use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*}; -use super::{cache::*, health::*, payment_link::*}; +use super::{cache::*, health::*}; #[cfg(any(feature = "olap", feature = "oltp"))] -use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; +use super::{configs::*, customers::*, mandates::*, payment_link::*, payments::*, refunds::*}; #[cfg(feature = "oltp")] use super::{ephemeral_key::*, payment_methods::*, webhooks::*}; use crate::{ @@ -630,7 +630,7 @@ impl Cache { } pub struct PaymentLink; - +#[cfg(any(feature = "olap", feature = "oltp"))] impl PaymentLink { pub fn server(state: AppState) -> Scope { let mut route = web::scope("/payment_link").app_data(web::Data::new(state)); @@ -638,6 +638,7 @@ impl PaymentLink { { route = route.service(web::resource("/list").route(web::get().to(payments_link_list))); } + #[cfg(feature = "oltp")] route .service( web::resource("/{payment_link_id}").route(web::get().to(payment_link_retrieve)), diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index 6296b760f467..8cff361b3877 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -81,6 +81,30 @@ pub async fn initiate_payment_link( .await } +/// Payment Link - List +/// +/// To list the payment links +#[utoipa::path( + get, + path = "/payment_link/list", + params( + ("limit" = Option, Query, description = "The maximum number of payment_link Objects to include in the response"), + ("connector" = Option, Query, description = "The connector linked to payment_link"), + ("created_time" = Option, Query, description = "The time at which payment_link is created"), + ("created_time.lt" = Option, Query, description = "Time less than the payment_link created time"), + ("created_time.gt" = Option, Query, description = "Time greater than the payment_link created time"), + ("created_time.lte" = Option, Query, description = "Time less than or equals to the payment_link created time"), + ("created_time.gte" = Option, Query, description = "Time greater than or equals to the payment_link created time"), + ), + responses( + (status = 200, description = "The payment link list was retrieved successfully"), + (status = 401, description = "Unauthorized request") + ), + tag = "Payment Link", + operation_id = "List all Payment links", + security(("api_key" = [])) +)] +#[instrument(skip_all, fields(flow = ?Flow::PaymentLinkList))] pub async fn payments_link_list( state: web::Data, req: actix_web::HttpRequest, diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 23f8f1b3628b..3b407deb9a18 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -7863,6 +7863,14 @@ "type": "string", "nullable": true }, + "payment_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/admin.PaymentLinkConfig" + } + ], + "nullable": true + }, "custom_merchant_name": { "type": "string", "description": "Custom merchant name for payment link", @@ -10837,20 +10845,16 @@ "type": "object", "required": [ "payment_link_id", - "payment_id", "merchant_id", "link_to_pay", "amount", "created_at", - "last_modified_at" + "status" ], "properties": { "payment_link_id": { "type": "string" }, - "payment_id": { - "type": "string" - }, "merchant_id": { "type": "string" }, @@ -10861,26 +10865,29 @@ "type": "integer", "format": "int64" }, - "currency": { - "allOf": [ - { - "$ref": "#/components/schemas/Currency" - } - ], - "nullable": true - }, "created_at": { "type": "string", "format": "date-time" }, - "last_modified_at": { - "type": "string", - "format": "date-time" - }, "link_expiry": { "type": "string", "format": "date-time", "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string" + }, + "currency": { + "allOf": [ + { + "$ref": "#/components/schemas/storage_enums.Currency" + } + ], + "nullable": true } } }, From 847b356fa5d535ba37097cd7c0bc27d5f916764c Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 10 Nov 2023 19:12:53 +0530 Subject: [PATCH 22/33] refactor(router): fixed features olap oltp --- crates/router/src/routes/app.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 6e1e2ea4fc1a..c5cd5dc614fd 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -630,7 +630,7 @@ impl Cache { } pub struct PaymentLink; -#[cfg(any(feature = "olap", feature = "oltp"))] +#[cfg(feature = "olap")] impl PaymentLink { pub fn server(state: AppState) -> Scope { let mut route = web::scope("/payment_link").app_data(web::Data::new(state)); @@ -638,7 +638,6 @@ impl PaymentLink { { route = route.service(web::resource("/list").route(web::get().to(payments_link_list))); } - #[cfg(feature = "oltp")] route .service( web::resource("/{payment_link_id}").route(web::get().to(payment_link_retrieve)), From e6ac23d92d8a4f1441412fc24b9995e6e8ce08e2 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 14 Nov 2023 18:30:32 +0530 Subject: [PATCH 23/33] refactor(router): fixed cargo clippy issue --- crates/router/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 38efe8b75134..92af29a0e8c9 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -131,7 +131,6 @@ pub fn mk_app( .service(routes::PaymentMethods::server(state.clone())) .service(routes::EphemeralKey::server(state.clone())) .service(routes::Webhooks::server(state.clone())) - .service(routes::PaymentLink::server(state.clone())); } #[cfg(feature = "olap")] @@ -143,6 +142,7 @@ pub fn mk_app( .service(routes::Disputes::server(state.clone())) .service(routes::Routing::server(state.clone())) .service(routes::Gsm::server(state.clone())) + .service(routes::PaymentLink::server(state.clone())); } #[cfg(all(feature = "olap", feature = "kms"))] From bef803b2b143f61b84f32669e325b26458d89d2e Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Thu, 16 Nov 2023 12:43:07 +0530 Subject: [PATCH 24/33] refactor(router): fixed openapi issue --- crates/api_models/src/payments.rs | 1 + openapi/openapi_spec.json | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index bffb75d002e6..638f7499c6cf 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3176,6 +3176,7 @@ pub struct RetrievePaymentLinkResponse { pub link_expiry: Option, pub description: Option, pub status: String, + #[schema(value_type = Currency)] pub currency: Option, } diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 3b407deb9a18..871e4939fa0d 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -10849,7 +10849,8 @@ "link_to_pay", "amount", "created_at", - "status" + "status", + "currency" ], "properties": { "payment_link_id": { @@ -10882,12 +10883,7 @@ "type": "string" }, "currency": { - "allOf": [ - { - "$ref": "#/components/schemas/storage_enums.Currency" - } - ], - "nullable": true + "$ref": "#/components/schemas/Currency" } } }, From 3ba27b4e21b0e928737f530e98df55af79cab6bb Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Thu, 16 Nov 2023 12:57:33 +0530 Subject: [PATCH 25/33] refactor(router): fixed open api issue --- crates/api_models/src/payments.rs | 1 + openapi/openapi_spec.json | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 638f7499c6cf..2ef81f32dbe6 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3148,6 +3148,7 @@ pub struct PaymentLinkObject { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub link_expiry: Option, pub merchant_custom_domain_name: Option, + #[schema(value_type = PaymentLinkConfig)] pub payment_link_config: Option, /// Custom merchant name for payment link pub custom_merchant_name: Option, diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 871e4939fa0d..1cb1dcb7f556 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -7853,6 +7853,9 @@ }, "PaymentLinkObject": { "type": "object", + "required": [ + "payment_link_config" + ], "properties": { "link_expiry": { "type": "string", @@ -7864,12 +7867,7 @@ "nullable": true }, "payment_link_config": { - "allOf": [ - { - "$ref": "#/components/schemas/admin.PaymentLinkConfig" - } - ], - "nullable": true + "$ref": "#/components/schemas/PaymentLinkConfig" }, "custom_merchant_name": { "type": "string", From 84dd4f997db88639f080f4ac7fabba8917e9ba3e Mon Sep 17 00:00:00 2001 From: Kashif Date: Thu, 16 Nov 2023 15:56:55 +0530 Subject: [PATCH 26/33] fix(payment_link): fix status page UI (cherry picked from commit 8143c9e89fa7ce1f1b3b2e873bd4dbac1fa0fac2) --- crates/router/src/core/payment_link/payment_link.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/router/src/core/payment_link/payment_link.html b/crates/router/src/core/payment_link/payment_link.html index abacf0998f67..097bc630c960 100644 --- a/crates/router/src/core/payment_link/payment_link.html +++ b/crates/router/src/core/payment_link/payment_link.html @@ -418,6 +418,7 @@ margin-top: 20px; border-radius: 3px; border: 1px solid #e6e6e6; + width: 90vw; } .hyper-checkout-status-item { @@ -432,12 +433,15 @@ } .hyper-checkout-item-header { - width: 15ch; + min-width: 13ch; font-size: 12px; } .hyper-checkout-item-value { font-size: 12px; + overflow-x: hidden; + overflow-y: auto; + word-wrap: break-word; } @keyframes loading { From f9a19fcf09eeb6d50e48fea545fc842f7e196484 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 17 Nov 2023 17:18:11 +0530 Subject: [PATCH 27/33] fix(router): added scroll view for ios --- crates/router/src/core/payment_link/payment_link.html | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/core/payment_link/payment_link.html b/crates/router/src/core/payment_link/payment_link.html index 097bc630c960..0ca4abd340d6 100644 --- a/crates/router/src/core/payment_link/payment_link.html +++ b/crates/router/src/core/payment_link/payment_link.html @@ -46,6 +46,7 @@ width: 100%; height: 100%; max-width: 1900px; + overflow: scroll; } #hyper-footer { From ed3e2a3b5c749b2a4d6db8375009dcc2b42a1213 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Sat, 18 Nov 2023 23:20:43 +0530 Subject: [PATCH 28/33] refactor(router): addressed pr comments --- crates/api_models/src/payments.rs | 8 -------- crates/router/src/core/payment_link.rs | 3 +-- crates/router/src/db/payment_link.rs | 6 +++--- .../down.sql | 2 +- .../up.sql | 2 +- 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index b121b1238517..520af6da8513 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3214,14 +3214,6 @@ pub struct PaymentLinkListConstraints { #[schema(example = "cus_meowuwunwiuwiwqw")] pub customer_id: Option, - /// A cursor for use in pagination, fetch the next list after some object - #[schema(example = "pay_fafa124123")] - pub starting_after: Option, - - /// A cursor for use in pagination, fetch the previous list before some object - #[schema(example = "pay_fafa124123")] - pub ending_before: Option, - /// limit on the number of objects to return pub limit: Option, diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index fa89ab27f699..4fdbba30530f 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -211,10 +211,9 @@ pub async fn list_payment_link( merchant: domain::MerchantAccount, constraints: api_models::payments::PaymentLinkListConstraints, ) -> RouterResponse> { - // helpers::validate_payment_link_list_request(&constraints)?; let db = state.store.as_ref(); let payment_link = db - .find_payment_link_by_merchant_id(&merchant.merchant_id, constraints) + .find_payment_link_list_by_merchant_id(&merchant.merchant_id, constraints) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to retrieve payment link")?; diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index 181510e4cb54..746a8caef66d 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -20,7 +20,7 @@ pub trait PaymentLinkInterface { _payment_link: storage::PaymentLinkNew, ) -> CustomResult; - async fn find_payment_link_by_merchant_id( + async fn find_payment_link_list_by_merchant_id( &self, merchant_id: &str, payment_link_constraints: api_models::payments::PaymentLinkListConstraints, @@ -52,7 +52,7 @@ impl PaymentLinkInterface for Store { .into_report() } - async fn find_payment_link_by_merchant_id( + async fn find_payment_link_list_by_merchant_id( &self, merchant_id: &str, payment_link_constraints: api_models::payments::PaymentLinkListConstraints, @@ -83,7 +83,7 @@ impl PaymentLinkInterface for MockDb { Err(errors::StorageError::MockDbError)? } - async fn find_payment_link_by_merchant_id( + async fn find_payment_link_list_by_merchant_id( &self, _merchant_id: &str, _payment_link_constraints: api_models::payments::PaymentLinkListConstraints, diff --git a/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql b/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql index c36daebdcc3a..daf8b261edb3 100644 --- a/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql +++ b/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -ALTER TABLE payment_link DROP COLUMN description; \ No newline at end of file +ALTER table payment_link ADD COLUMN IF NOT EXISTS description VARCHAR (255); \ No newline at end of file diff --git a/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql b/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql index 76f89ad04a37..65a074063ed3 100644 --- a/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql +++ b/migrations/2023-11-06-065213_add_description_to_payment_link/up.sql @@ -1,2 +1,2 @@ -- Your SQL goes here -ALTER table payment_link ADD COLUMN description VARCHAR (255); \ No newline at end of file +ALTER table payment_link ADD COLUMN IF NOT EXISTS description VARCHAR (255); \ No newline at end of file From d16f31b77924998b9460bb43c0e404bbc0da2add Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 21 Nov 2023 12:16:23 +0530 Subject: [PATCH 29/33] refactor(router): addressed pr comments --- crates/api_models/src/payments.rs | 4 ---- .../down.sql | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 520af6da8513..7afee1110e2c 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3210,10 +3210,6 @@ pub struct PaymentLinkDetails { #[serde(deny_unknown_fields)] pub struct PaymentLinkListConstraints { - /// The identifier for customer - #[schema(example = "cus_meowuwunwiuwiwqw")] - pub customer_id: Option, - /// limit on the number of objects to return pub limit: Option, diff --git a/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql b/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql index daf8b261edb3..b184a2ce3dd7 100644 --- a/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql +++ b/migrations/2023-11-06-065213_add_description_to_payment_link/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -ALTER table payment_link ADD COLUMN IF NOT EXISTS description VARCHAR (255); \ No newline at end of file +ALTER TABLE payment_link DROP COLUMN IF EXISTS description; \ No newline at end of file From cd7d1b45ae5e18e243e61a7ceb7963f8a0f43a31 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 21 Nov 2023 15:02:19 +0530 Subject: [PATCH 30/33] refactor(router): fixed error message for payment which already has status --- crates/router/src/core/payment_link.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 4fdbba30530f..41f36e1d64c3 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -71,7 +71,7 @@ pub async fn intiate_payment_link_flow( storage_enums::IntentStatus::RequiresCapture, storage_enums::IntentStatus::RequiresMerchantAction, ], - "create payment link", + "use payment link for", )?; let payment_link = db From 837887d32c2e4a6814fcf5dd916ba7629cc303a2 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 21 Nov 2023 18:13:58 +0530 Subject: [PATCH 31/33] refactor(router): fixed func name for fetching list payment link --- crates/router/src/core/payment_link.rs | 2 +- crates/router/src/db/payment_link.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 41f36e1d64c3..e426d5487932 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -213,7 +213,7 @@ pub async fn list_payment_link( ) -> RouterResponse> { let db = state.store.as_ref(); let payment_link = db - .find_payment_link_list_by_merchant_id(&merchant.merchant_id, constraints) + .find_list_payment_link_by_merchant_id(&merchant.merchant_id, constraints) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to retrieve payment link")?; diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index 746a8caef66d..436683f21315 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -20,7 +20,7 @@ pub trait PaymentLinkInterface { _payment_link: storage::PaymentLinkNew, ) -> CustomResult; - async fn find_payment_link_list_by_merchant_id( + async fn find_list_payment_link_by_merchant_id( &self, merchant_id: &str, payment_link_constraints: api_models::payments::PaymentLinkListConstraints, @@ -52,7 +52,7 @@ impl PaymentLinkInterface for Store { .into_report() } - async fn find_payment_link_list_by_merchant_id( + async fn find_list_payment_link_by_merchant_id( &self, merchant_id: &str, payment_link_constraints: api_models::payments::PaymentLinkListConstraints, @@ -83,7 +83,7 @@ impl PaymentLinkInterface for MockDb { Err(errors::StorageError::MockDbError)? } - async fn find_payment_link_list_by_merchant_id( + async fn find_list_payment_link_by_merchant_id( &self, _merchant_id: &str, _payment_link_constraints: api_models::payments::PaymentLinkListConstraints, From bccedecb7634401059f239cf33afd6b51ed93c75 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 21 Nov 2023 19:15:02 +0530 Subject: [PATCH 32/33] refactor(router): addressed pr comments --- crates/api_models/src/payments.rs | 16 ++++++++-------- crates/router/src/routes/app.rs | 2 +- openapi/openapi_spec.json | 10 +++++++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2e32006ec082..0561ec22faa6 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -14,7 +14,7 @@ use utoipa::ToSchema; use crate::{ admin, disputes, - enums::{self as api_enums, enums as storage_enums}, + enums::{self as api_enums, enums as common_enums}, ephemeral_key::EphemeralKeyCreateResponse, payment_methods::{Surcharge, SurchargeDetailsResponse}, refunds, @@ -3203,8 +3203,8 @@ pub struct RetrievePaymentLinkResponse { pub link_expiry: Option, pub description: Option, pub status: String, - #[schema(value_type = Currency)] - pub currency: Option, + #[schema(value_type = Option)] + pub currency: Option, } #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] @@ -3237,12 +3237,12 @@ pub struct PaymentLinkListConstraints { /// limit on the number of objects to return pub limit: Option, - /// The time at which payment is created + /// The time at which payment link is created #[schema(example = "2022-09-10T10:11:12Z")] #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub created: Option, - /// Time less than the payment created time + /// Time less than the payment link created time #[schema(example = "2022-09-10T10:11:12Z")] #[serde( default, @@ -3251,7 +3251,7 @@ pub struct PaymentLinkListConstraints { )] pub created_lt: Option, - /// Time greater than the payment created time + /// Time greater than the payment link created time #[schema(example = "2022-09-10T10:11:12Z")] #[serde( default, @@ -3260,7 +3260,7 @@ pub struct PaymentLinkListConstraints { )] pub created_gt: Option, - /// Time less than or equals to the payment created time + /// Time less than or equals to the payment link created time #[schema(example = "2022-09-10T10:11:12Z")] #[serde( default, @@ -3269,7 +3269,7 @@ pub struct PaymentLinkListConstraints { )] pub created_lte: Option, - /// Time greater than or equals to the payment created time + /// Time greater than or equals to the payment link created time #[schema(example = "2022-09-10T10:11:12Z")] #[serde(default, with = "common_utils::custom_serde::iso8601::option")] #[serde(rename = "created.gte")] diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 2bff0c657d02..43e88b8077a2 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -670,7 +670,7 @@ impl PaymentLink { let mut route = web::scope("/payment_link").app_data(web::Data::new(state)); #[cfg(feature = "olap")] { - route = route.service(web::resource("/list").route(web::get().to(payments_link_list))); + route = route.service(web::resource("/list").route(web::post().to(payments_link_list))); } route .service( diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 42a80b6753b5..0ba686c16780 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -11310,8 +11310,7 @@ "link_to_pay", "amount", "created_at", - "status", - "currency" + "status" ], "properties": { "payment_link_id": { @@ -11344,7 +11343,12 @@ "type": "string" }, "currency": { - "$ref": "#/components/schemas/Currency" + "allOf": [ + { + "$ref": "#/components/schemas/Currency" + } + ], + "nullable": true } } }, From e49cb6ba56f93ff30ca02f95e60ce64982bd5fa1 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Wed, 22 Nov 2023 13:12:52 +0530 Subject: [PATCH 33/33] refactor(router): addressed pr comments --- crates/api_models/src/payments.rs | 4 ++-- crates/router/src/core/payment_link.rs | 2 +- crates/router/src/db/payment_link.rs | 6 +++--- crates/router/src/routes/app.rs | 9 +++------ 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 0561ec22faa6..508eeb8d7310 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -14,7 +14,7 @@ use utoipa::ToSchema; use crate::{ admin, disputes, - enums::{self as api_enums, enums as common_enums}, + enums::{self as api_enums}, ephemeral_key::EphemeralKeyCreateResponse, payment_methods::{Surcharge, SurchargeDetailsResponse}, refunds, @@ -3204,7 +3204,7 @@ pub struct RetrievePaymentLinkResponse { pub description: Option, pub status: String, #[schema(value_type = Option)] - pub currency: Option, + pub currency: Option, } #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index e426d5487932..07fdf4ae4072 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -213,7 +213,7 @@ pub async fn list_payment_link( ) -> RouterResponse> { let db = state.store.as_ref(); let payment_link = db - .find_list_payment_link_by_merchant_id(&merchant.merchant_id, constraints) + .list_payment_link_by_merchant_id(&merchant.merchant_id, constraints) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to retrieve payment link")?; diff --git a/crates/router/src/db/payment_link.rs b/crates/router/src/db/payment_link.rs index 436683f21315..5dc9871e707e 100644 --- a/crates/router/src/db/payment_link.rs +++ b/crates/router/src/db/payment_link.rs @@ -20,7 +20,7 @@ pub trait PaymentLinkInterface { _payment_link: storage::PaymentLinkNew, ) -> CustomResult; - async fn find_list_payment_link_by_merchant_id( + async fn list_payment_link_by_merchant_id( &self, merchant_id: &str, payment_link_constraints: api_models::payments::PaymentLinkListConstraints, @@ -52,7 +52,7 @@ impl PaymentLinkInterface for Store { .into_report() } - async fn find_list_payment_link_by_merchant_id( + async fn list_payment_link_by_merchant_id( &self, merchant_id: &str, payment_link_constraints: api_models::payments::PaymentLinkListConstraints, @@ -83,7 +83,7 @@ impl PaymentLinkInterface for MockDb { Err(errors::StorageError::MockDbError)? } - async fn find_list_payment_link_by_merchant_id( + async fn list_payment_link_by_merchant_id( &self, _merchant_id: &str, _payment_link_constraints: api_models::payments::PaymentLinkListConstraints, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 43e88b8077a2..a734c1f6e46e 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -667,12 +667,9 @@ pub struct PaymentLink; #[cfg(feature = "olap")] impl PaymentLink { pub fn server(state: AppState) -> Scope { - let mut route = web::scope("/payment_link").app_data(web::Data::new(state)); - #[cfg(feature = "olap")] - { - route = route.service(web::resource("/list").route(web::post().to(payments_link_list))); - } - route + web::scope("/payment_link") + .app_data(web::Data::new(state)) + .service(web::resource("/list").route(web::post().to(payments_link_list))) .service( web::resource("/{payment_link_id}").route(web::get().to(payment_link_retrieve)), )