diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index dbcd8736d4..b75003271b 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -128,6 +128,21 @@ impl PaymentsAuthorizeRouterData { ) .await .change_context(errors::ApiErrorResponse::MandateNotFound)?; + let mandate = match mandate.mandate_type { + storage_enums::MandateType::SingleUse => state + .store + .update_mandate_by_merchant_id_mandate_id( + &resp.merchant_id, + mandate_id, + storage::MandateUpdate::StatusUpdate { + mandate_status: storage_enums::MandateStatus::Revoked, + }, + ) + .await + .change_context(errors::ApiErrorResponse::MandateNotFound), + storage_enums::MandateType::MultiUse => Ok(mandate), + }?; + resp.payment_method_id = Some(mandate.payment_method_id); } None => { @@ -173,19 +188,32 @@ impl PaymentsAuthorizeRouterData { match (self.request.setup_mandate_details.clone(), customer) { (Some(data), Some(cus)) => { let mandate_id = utils::generate_id(consts::ID_LENGTH, "man"); - Some(storage::MandateNew { - mandate_id, - customer_id: cus.customer_id.clone(), - merchant_id: self.merchant_id.clone(), - payment_method_id, - mandate_status: storage_enums::MandateStatus::Active, - mandate_type: storage_enums::MandateType::MultiUse, - customer_ip_address: data.customer_acceptance.get_ip_address().map(Secret::new), - customer_user_agent: data.customer_acceptance.get_user_agent(), - customer_accepted_at: Some(data.customer_acceptance.get_accepted_at()), - ..Default::default() // network_transaction_id: Option, - // previous_transaction_id: Option, - // created_at: Option, + + // The construction of the mandate new must be visible + let mut new_mandate = storage::MandateNew::default(); + + new_mandate + .set_mandate_id(mandate_id) + .set_customer_id(cus.customer_id.clone()) + .set_merchant_id(self.merchant_id.clone()) + .set_payment_method_id(payment_method_id) + .set_mandate_status(storage_enums::MandateStatus::Active) + .set_customer_ip_address( + data.customer_acceptance.get_ip_address().map(Secret::new), + ) + .set_customer_user_agent(data.customer_acceptance.get_user_agent()) + .set_customer_accepted_at(Some(data.customer_acceptance.get_accepted_at())); + + Some(match data.mandate_type { + api::MandateType::SingleUse(data) => new_mandate + .set_single_use_amount(Some(data.amount)) + .set_single_use_currency(Some(data.currency)) + .set_mandate_type(storage_enums::MandateType::SingleUse) + .to_owned(), + + api::MandateType::MultiUse => new_mandate + .set_mandate_type(storage_enums::MandateType::MultiUse) + .to_owned(), }) } (_, _) => None, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e3e84a02bd..beccb70bfa 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -139,6 +139,11 @@ pub async fn get_token_for_recurring_mandate( .await .map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::MandateNotFound))?; + utils::when( + mandate.mandate_status != storage_enums::MandateStatus::Active, + Err(errors::ApiErrorResponse::MandateNotFound), + )?; + let customer = req.customer_id.clone().get_required_value("customer_id")?; let payment_method_id = { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index c1c149bdbd..da9c686301 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -137,7 +137,6 @@ where payment_data.payment_attempt, payment_data.payment_intent, payment_data.refunds, - payment_data.mandate_id, payment_data.payment_method_data, customer, auth_flow, @@ -198,7 +197,6 @@ pub fn payments_to_payments_response( payment_attempt: storage::PaymentAttempt, payment_intent: storage::PaymentIntent, refunds: Vec, - mandate_id: Option, payment_method_data: Option, customer: Option, auth_flow: services::AuthFlow, @@ -216,6 +214,7 @@ where .as_ref() .get_required_value("currency")? .to_string(); + let mandate_id = payment_attempt.mandate_id.clone(); let refunds_response = if refunds.is_empty() { None } else { diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 4ec9763e23..54b91c1d88 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -14,6 +14,7 @@ use super::app::AppState; use crate::{ core::{errors::http_not_implemented, payments}, services::api, + types::api::{ enums as api_enums, payments::{ @@ -21,7 +22,7 @@ use crate::{ PaymentsRequest, PaymentsRetrieveRequest, }, Authorize, Capture, PSync, PaymentRetrieveBody, PaymentsResponse, PaymentsStartRequest, - Verify, VerifyRequest, VerifyResponse, Void, + Verify, VerifyResponse, Void, }, // FIXME imports }; @@ -110,33 +111,6 @@ pub async fn payments_start( .await } -#[allow(dead_code)] -#[instrument(skip(state), fields(flow = ?Flow::ValidatePaymentMethod))] -pub async fn validate_pm( - state: web::Data, - req: HttpRequest, - json_payload: web::Json, -) -> HttpResponse { - let payload = json_payload.into_inner(); - api::server_wrap( - &state, - &req, - payload, - |state, merchant_account, req| { - payments::payments_core::( - state, - merchant_account, - payments::PaymentMethodValidate, - req, - api::AuthFlow::Merchant, - payments::CallConnectorAction::Trigger, - ) - }, - api::MerchantAuthentication::ApiKey, - ) - .await -} - #[instrument(skip(state), fields(flow = ?Flow::PaymentsRetrieve))] // #[get("/{payment_id}")] pub async fn payments_retrieve( diff --git a/crates/router/src/schema.rs b/crates/router/src/schema.rs index 9808bb05de..b98588487a 100644 --- a/crates/router/src/schema.rs +++ b/crates/router/src/schema.rs @@ -127,6 +127,8 @@ diesel::table! { network_transaction_id -> Nullable, previous_transaction_id -> Nullable, created_at -> Timestamp, + single_use_amount -> Nullable, + single_use_currency -> Nullable, } } diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index c459adad3f..698836d2b6 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -7,7 +7,7 @@ use crate::{ core::errors, pii, services::api, - types::{self, api as api_types, api::enums as api_enums}, + types::{self, api as api_types, api::enums as api_enums, storage}, utils::custom_serde, }; @@ -126,6 +126,14 @@ pub enum MandateTxnType { #[serde(deny_unknown_fields)] pub struct MandateData { pub customer_acceptance: CustomerAcceptance, + pub mandate_type: MandateType, +} + +#[derive(Default, Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] +pub enum MandateType { + SingleUse(storage::SingleUseMandate), + #[default] + MultiUse, } #[derive(Default, Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] diff --git a/crates/router/src/types/storage/mandate.rs b/crates/router/src/types/storage/mandate.rs index 257ebce018..d9d251e4da 100644 --- a/crates/router/src/types/storage/mandate.rs +++ b/crates/router/src/types/storage/mandate.rs @@ -24,9 +24,13 @@ pub struct Mandate { pub network_transaction_id: Option, pub previous_transaction_id: Option, pub created_at: PrimitiveDateTime, + pub single_use_amount: Option, + pub single_use_currency: Option, } -#[derive(Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay)] +#[derive( + router_derive::Setter, Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay, +)] #[diesel(table_name = mandate)] pub struct MandateNew { pub mandate_id: String, @@ -41,6 +45,8 @@ pub struct MandateNew { pub network_transaction_id: Option, pub previous_transaction_id: Option, pub created_at: Option, + pub single_use_amount: Option, + pub single_use_currency: Option, } #[derive(Debug)] @@ -50,6 +56,12 @@ pub enum MandateUpdate { }, } +#[derive(Clone, Eq, PartialEq, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] +pub struct SingleUseMandate { + pub amount: i32, + pub currency: storage_enums::Currency, +} + #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = mandate)] pub(super) struct MandateUpdateInternal { diff --git a/migrations/2022-12-05-090521_single_use_mandate_fields/down.sql b/migrations/2022-12-05-090521_single_use_mandate_fields/down.sql new file mode 100644 index 0000000000..177e00e754 --- /dev/null +++ b/migrations/2022-12-05-090521_single_use_mandate_fields/down.sql @@ -0,0 +1,4 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE mandate +DROP COLUMN IF EXISTS single_use_amount, +DROP COLUMN IF EXISTS single_use_currency; \ No newline at end of file diff --git a/migrations/2022-12-05-090521_single_use_mandate_fields/up.sql b/migrations/2022-12-05-090521_single_use_mandate_fields/up.sql new file mode 100644 index 0000000000..393506967f --- /dev/null +++ b/migrations/2022-12-05-090521_single_use_mandate_fields/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +ALTER TABLE mandate +ADD IF NOT EXISTS single_use_amount INTEGER DEFAULT NULL, +ADD IF NOT EXISTS single_use_currency "Currency" DEFAULT NULL;