feat(single_use): add extra fields in Mandate table and MandateData [Blocked on #61] (#66)

This commit is contained in:
Nishant Joshi
2022-12-09 17:18:12 +05:30
committed by GitHub
parent 9710af1e56
commit 2aef3bccfb
9 changed files with 81 additions and 45 deletions

View File

@ -128,6 +128,21 @@ impl PaymentsAuthorizeRouterData {
) )
.await .await
.change_context(errors::ApiErrorResponse::MandateNotFound)?; .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); resp.payment_method_id = Some(mandate.payment_method_id);
} }
None => { None => {
@ -173,19 +188,32 @@ impl PaymentsAuthorizeRouterData {
match (self.request.setup_mandate_details.clone(), customer) { match (self.request.setup_mandate_details.clone(), customer) {
(Some(data), Some(cus)) => { (Some(data), Some(cus)) => {
let mandate_id = utils::generate_id(consts::ID_LENGTH, "man"); let mandate_id = utils::generate_id(consts::ID_LENGTH, "man");
Some(storage::MandateNew {
mandate_id, // The construction of the mandate new must be visible
customer_id: cus.customer_id.clone(), let mut new_mandate = storage::MandateNew::default();
merchant_id: self.merchant_id.clone(),
payment_method_id, new_mandate
mandate_status: storage_enums::MandateStatus::Active, .set_mandate_id(mandate_id)
mandate_type: storage_enums::MandateType::MultiUse, .set_customer_id(cus.customer_id.clone())
customer_ip_address: data.customer_acceptance.get_ip_address().map(Secret::new), .set_merchant_id(self.merchant_id.clone())
customer_user_agent: data.customer_acceptance.get_user_agent(), .set_payment_method_id(payment_method_id)
customer_accepted_at: Some(data.customer_acceptance.get_accepted_at()), .set_mandate_status(storage_enums::MandateStatus::Active)
..Default::default() // network_transaction_id: Option<String>, .set_customer_ip_address(
// previous_transaction_id: Option<String>, data.customer_acceptance.get_ip_address().map(Secret::new),
// created_at: Option<PrimitiveDateTime>, )
.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, (_, _) => None,

View File

@ -139,6 +139,11 @@ pub async fn get_token_for_recurring_mandate(
.await .await
.map_err(|error| error.to_not_found_response(errors::ApiErrorResponse::MandateNotFound))?; .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 customer = req.customer_id.clone().get_required_value("customer_id")?;
let payment_method_id = { let payment_method_id = {

View File

@ -137,7 +137,6 @@ where
payment_data.payment_attempt, payment_data.payment_attempt,
payment_data.payment_intent, payment_data.payment_intent,
payment_data.refunds, payment_data.refunds,
payment_data.mandate_id,
payment_data.payment_method_data, payment_data.payment_method_data,
customer, customer,
auth_flow, auth_flow,
@ -198,7 +197,6 @@ pub fn payments_to_payments_response<R, Op>(
payment_attempt: storage::PaymentAttempt, payment_attempt: storage::PaymentAttempt,
payment_intent: storage::PaymentIntent, payment_intent: storage::PaymentIntent,
refunds: Vec<storage::Refund>, refunds: Vec<storage::Refund>,
mandate_id: Option<String>,
payment_method_data: Option<api::PaymentMethod>, payment_method_data: Option<api::PaymentMethod>,
customer: Option<storage::Customer>, customer: Option<storage::Customer>,
auth_flow: services::AuthFlow, auth_flow: services::AuthFlow,
@ -216,6 +214,7 @@ where
.as_ref() .as_ref()
.get_required_value("currency")? .get_required_value("currency")?
.to_string(); .to_string();
let mandate_id = payment_attempt.mandate_id.clone();
let refunds_response = if refunds.is_empty() { let refunds_response = if refunds.is_empty() {
None None
} else { } else {

View File

@ -14,6 +14,7 @@ use super::app::AppState;
use crate::{ use crate::{
core::{errors::http_not_implemented, payments}, core::{errors::http_not_implemented, payments},
services::api, services::api,
types::api::{ types::api::{
enums as api_enums, enums as api_enums,
payments::{ payments::{
@ -21,7 +22,7 @@ use crate::{
PaymentsRequest, PaymentsRetrieveRequest, PaymentsRequest, PaymentsRetrieveRequest,
}, },
Authorize, Capture, PSync, PaymentRetrieveBody, PaymentsResponse, PaymentsStartRequest, Authorize, Capture, PSync, PaymentRetrieveBody, PaymentsResponse, PaymentsStartRequest,
Verify, VerifyRequest, VerifyResponse, Void, Verify, VerifyResponse, Void,
}, // FIXME imports }, // FIXME imports
}; };
@ -110,33 +111,6 @@ pub async fn payments_start(
.await .await
} }
#[allow(dead_code)]
#[instrument(skip(state), fields(flow = ?Flow::ValidatePaymentMethod))]
pub async fn validate_pm(
state: web::Data<AppState>,
req: HttpRequest,
json_payload: web::Json<VerifyRequest>,
) -> HttpResponse {
let payload = json_payload.into_inner();
api::server_wrap(
&state,
&req,
payload,
|state, merchant_account, req| {
payments::payments_core::<Verify, VerifyResponse, _, _, _>(
state,
merchant_account,
payments::PaymentMethodValidate,
req,
api::AuthFlow::Merchant,
payments::CallConnectorAction::Trigger,
)
},
api::MerchantAuthentication::ApiKey,
)
.await
}
#[instrument(skip(state), fields(flow = ?Flow::PaymentsRetrieve))] #[instrument(skip(state), fields(flow = ?Flow::PaymentsRetrieve))]
// #[get("/{payment_id}")] // #[get("/{payment_id}")]
pub async fn payments_retrieve( pub async fn payments_retrieve(

View File

@ -127,6 +127,8 @@ diesel::table! {
network_transaction_id -> Nullable<Varchar>, network_transaction_id -> Nullable<Varchar>,
previous_transaction_id -> Nullable<Varchar>, previous_transaction_id -> Nullable<Varchar>,
created_at -> Timestamp, created_at -> Timestamp,
single_use_amount -> Nullable<Int4>,
single_use_currency -> Nullable<Currency>,
} }
} }

View File

@ -7,7 +7,7 @@ use crate::{
core::errors, core::errors,
pii, pii,
services::api, 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, utils::custom_serde,
}; };
@ -126,6 +126,14 @@ pub enum MandateTxnType {
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct MandateData { pub struct MandateData {
pub customer_acceptance: CustomerAcceptance, 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)] #[derive(Default, Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)]

View File

@ -24,9 +24,13 @@ pub struct Mandate {
pub network_transaction_id: Option<String>, pub network_transaction_id: Option<String>,
pub previous_transaction_id: Option<String>, pub previous_transaction_id: Option<String>,
pub created_at: PrimitiveDateTime, pub created_at: PrimitiveDateTime,
pub single_use_amount: Option<i32>,
pub single_use_currency: Option<storage_enums::Currency>,
} }
#[derive(Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay)] #[derive(
router_derive::Setter, Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay,
)]
#[diesel(table_name = mandate)] #[diesel(table_name = mandate)]
pub struct MandateNew { pub struct MandateNew {
pub mandate_id: String, pub mandate_id: String,
@ -41,6 +45,8 @@ pub struct MandateNew {
pub network_transaction_id: Option<String>, pub network_transaction_id: Option<String>,
pub previous_transaction_id: Option<String>, pub previous_transaction_id: Option<String>,
pub created_at: Option<PrimitiveDateTime>, pub created_at: Option<PrimitiveDateTime>,
pub single_use_amount: Option<i32>,
pub single_use_currency: Option<storage_enums::Currency>,
} }
#[derive(Debug)] #[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)] #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = mandate)] #[diesel(table_name = mandate)]
pub(super) struct MandateUpdateInternal { pub(super) struct MandateUpdateInternal {

View File

@ -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;

View File

@ -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;