-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(mandates): allow off-session payments using payment_method_id
#4132
Conversation
f0a52a9
to
690be75
Compare
req.recurring_details | ||
.check_value_present("recurring_details")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to check mandate_id
also right? One of the two should be given.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recurring_details
is of type RecurringDetails
in which during from
conversion itself, I'm mapping the mandate_id to RecurringDetails::MandateId
or else take the request.recurring_details
impl From<&PaymentsRequest> for MandateValidationFields {
fn from(req: &PaymentsRequest) -> Self {
let recurring_details = req
.mandate_id
.clone()
.map(RecurringDetails::MandateId)
.or(req.recurring_details.clone());
Self {
recurring_details,
confirm: req.confirm,
customer_id: req
.customer
.as_ref()
.map(|customer_details| &customer_details.id)
.or(req.customer_id.as_ref())
.map(ToOwned::to_owned),
mandate_data: req.mandate_data.clone(),
setup_future_usage: req.setup_future_usage,
off_session: req.off_session,
}
}
}
@@ -1949,7 +2010,8 @@ pub(crate) fn validate_payment_method_fields_present( | |||
utils::when( | |||
req.payment_method.is_some() | |||
&& req.payment_method_data.is_none() | |||
&& req.payment_token.is_none(), | |||
&& req.payment_token.is_none() | |||
&& req.recurring_details.is_none(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that i'm getting the payment_method
from payment_method_info
itself instead of relying on the request as suggested by you, we can remove this
@@ -247,6 +249,14 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> | |||
id: profile_id.to_string(), | |||
})?; | |||
|
|||
let recurring_details = request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we ignoring MandateId
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because in PaymentsRequest
, we have both mandate_id: Option<String>
and recurring_details: Option<String>
where recurring_details is mapped to payment method id
crates/router/src/core/payments.rs
Outdated
@@ -2263,6 +2263,7 @@ where | |||
pub authorizations: Vec<diesel_models::authorization::Authorization>, | |||
pub authentication: Option<storage::Authentication>, | |||
pub frm_metadata: Option<serde_json::Value>, | |||
pub recurring_details: Option<String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better if this is the enum itself. With a String
, we won't know later on in the flow whether it's a recurring mandate or a recurring MIT payment.
"RecurringDetails": { | ||
"oneOf": [ | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"type", | ||
"data" | ||
], | ||
"properties": { | ||
"type": { | ||
"type": "string", | ||
"enum": [ | ||
"mandate_id" | ||
] | ||
}, | ||
"data": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"type", | ||
"data" | ||
], | ||
"properties": { | ||
"type": { | ||
"type": "string", | ||
"enum": [ | ||
"payment_method_id" | ||
] | ||
}, | ||
"data": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
], | ||
"discriminator": { | ||
"propertyName": "type" | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"RecurringDetails": { | |
"oneOf": [ | |
{ | |
"type": "object", | |
"required": [ | |
"type", | |
"data" | |
], | |
"properties": { | |
"type": { | |
"type": "string", | |
"enum": [ | |
"mandate_id" | |
] | |
}, | |
"data": { | |
"type": "string" | |
} | |
} | |
}, | |
{ | |
"type": "object", | |
"required": [ | |
"type", | |
"data" | |
], | |
"properties": { | |
"type": { | |
"type": "string", | |
"enum": [ | |
"payment_method_id" | |
] | |
}, | |
"data": { | |
"type": "string" | |
} | |
} | |
} | |
], | |
"discriminator": { | |
"propertyName": "type" | |
} | |
}, | |
"RecurringDetails": { | |
"type": "object", | |
"required": [ | |
"type", | |
"data" | |
], | |
"properties": { | |
"type": { | |
"type": "string", | |
"enum": [ | |
"mandate_id", | |
"payment_method_id" | |
] | |
}, | |
"data": { | |
"type": "string" | |
} | |
}, | |
"discriminator": { | |
"propertyName": "type" | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope the testing for recurring payment with mandate_id
in root level and payment_token
is done already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently we are providing support to pass mandate_id
inside recurring_details
. So it has to be one of mandate_id
or payment_method_id
. Later we'll deprecate the mandate_id
field in root level.
I hope the testing for recurring payment with mandate_id in root level and payment_token is done already
yes
* 'main' of github.com:juspay/hyperswitch: refactor(core): removed the processing status for payment_method_status (#4213) docs(README): remove link to outdated early access form build(deps): bump `error-stack` from version `0.3.1` to `0.4.1` (#4188) chore(version): 2024.04.01.0 feat(mandates): allow off-session payments using `payment_method_id` (#4132) ci(CI-pr): determine modified crates more deterministically (#4233) chore(config): add billwerk base URL in deployment configs (#4243) feat(payment_method): API to list countries and currencies supported by a country and payment method type (#4126) chore(version): 2024.03.28.0 refactor(config): allow wildcard origin for development and Docker Compose setups (#4231) fix(core): Amount capturable remain same for `processing` status in capture (#4229) fix(euclid_wasm): checkout wasm metadata issue (#4198) fix(trustpay): [Trustpay] Add error code mapping '800.100.100' (#4224) feat(connector): [billwerk] add connector template code (#4123) fix(connectors): fix wallet token deserialization error (#4133) fix(log): adding span metadata to `tokio` spawned futures (#4118) chore(version): 2024.03.27.0 fix(connector): [CRYPTOPAY] Skip metadata serialization if none (#4205) fix(connector): [Trustpay] fix deserialization error for incoming webhook response for trustpay and add error code mapping '800.100.203' (#4199) fix(core): make eci in AuthenticationData optional (#4187)
Type of Change
Description
When a mandate is created on connector's end, we store the connector mandate details in our payment methods table. Now in order to make recurring payment with this, we basically have to do a token based payment where set the
setup_future_usage = off_session
and we list the payment methods of a customer and make a payment with payment token. But we should also allow off-session payments using payment method id without having to list customer payment methods. For this, we acceptoff_session = true
and payment method id in the request and make a recurring mandate payment.Additional Changes
Api contract changes -
Added new field in
PaymentsRequest
:Motivation and Context
How did you test it?
db entry -
payment_method_id
Payment succeded -
Also try creating mandate on hyperswitch end by providing mandate_data in setup mandate request and get the
mandate_id
. Do the recurring payment withmandate_id
. Also try another recurring payment with below structureChecklist
cargo +nightly fmt --all
cargo clippy