-
Notifications
You must be signed in to change notification settings - Fork 3.9k
feat(router): add support for partial authorization #8833
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
Conversation
@@ -1677,6 +1680,8 @@ pub enum IntentStatus { | |||
PartiallyCaptured, | |||
/// The payment has been captured partially and the remaining amount is capturable | |||
PartiallyCapturedAndCapturable, | |||
/// The payment has been authorized for a partial amount and requires capture | |||
PartiallyAuthorizedAndRequiresCapture, |
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.
Is this status needed? Wouldn't amount_capturable
indicate if the payment was Fully authorized or Partially authorized?
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.
There should be some explicit way of communicating the partial authorization to merchant, it's very similar to how we have PartialCharged (even though we give amount_captured value in the response)
@@ -65,6 +65,7 @@ pub struct PaymentIntent { | |||
pub is_iframe_redirection_enabled: Option<bool>, | |||
pub is_payment_id_from_merchant: Option<bool>, | |||
pub payment_channel: Option<common_enums::PaymentChannel>, | |||
pub enable_partial_authorization: Option<bool>, |
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 can make this a wrapper domain type. Like RequestIncrementalAuthorization
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.
In V1, we are following this convention. Can take this up in a separate PR if 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.
lets takes this in a separate PR, and moving forward don't repeat this irrespective of any versions
@@ -44,6 +44,7 @@ pub struct RouterData<Flow, Request, Response> { | |||
pub connector_meta_data: Option<common_utils::pii::SecretSerdeValue>, | |||
pub connector_wallets_details: Option<common_utils::pii::SecretSerdeValue>, | |||
pub amount_captured: Option<i64>, | |||
pub amount_capturable: Option<i64>, |
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 needed if minor_amount_capturable
is already there
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've added it in similar way to minor_amount_captured, currently minor_amount_captured is also not being utilized in the core. So, we are using amount_captured and amount_capturable only in payments_response_update_tracker. Introducing minor_amount_capturable allows the extensibility to possible refactor
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.
Amount should always be represented with MinorAmount
type instead of i64
…tch into add-partial-auth-support
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.
Core changes LGTM
@@ -65,6 +65,7 @@ pub struct PaymentIntent { | |||
pub is_iframe_redirection_enabled: Option<bool>, | |||
pub is_payment_id_from_merchant: Option<bool>, | |||
pub payment_channel: Option<common_enums::PaymentChannel>, | |||
pub enable_partial_authorization: Option<bool>, |
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.
lets takes this in a separate PR, and moving forward don't repeat this irrespective of any versions
@@ -44,6 +44,7 @@ pub struct RouterData<Flow, Request, Response> { | |||
pub connector_meta_data: Option<common_utils::pii::SecretSerdeValue>, | |||
pub connector_wallets_details: Option<common_utils::pii::SecretSerdeValue>, | |||
pub amount_captured: Option<i64>, | |||
pub amount_capturable: Option<i64>, |
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.
Amount should be strictly MinorAmount
only and not i64
== capturable_amount.map(MinorUnit::new) | ||
{ | ||
enums::AttemptStatus::Authorized | ||
} else if capturable_amount.is_some() { |
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.
Here we are implicitly assuming if the object present that means PartiallyAuthorized
, instead if this also the explicit key which states this is partial_auth is enabled and the amount is less then PartiallyAuthorized
, in other cases if amount is less and the object present is bug, so lets avoid implicit decision making instead do deterministic explicit conditions.
@@ -1936,7 +1939,16 @@ impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for | |||
straight_through_algorithm: m_straight_through_algorithm, | |||
error_code: m_error_code, | |||
error_message: m_error_message, | |||
amount_capturable: Some(authorized_amount), | |||
amount_capturable: if payment_data |
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.
Instead of having None here, have a three branch enum which has strictly no value, then non changeable authorized_amount and changeable authorized_amount
@@ -248,6 +248,8 @@ pub struct Authorization { | |||
#[serde(skip_serializing_if = "Option::is_none")] | |||
pub original_network_transaction_id: Option<Secret<String>>, | |||
#[serde(skip_serializing_if = "Option::is_none")] | |||
pub allow_partial_auth: Option<bool>, |
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.
there's a feature called partial auth that already exists which is completely different than this. Make sure no one confuses this.
…ordea-sepa * 'main' of github.com:juspay/hyperswitch: fix(router): [worldpayvantiv] dispute validations and statuses (#8862) chore(version): 2025.08.07.0 feat(connector): [WORLDPAYVANTIV] Populate Network Decline Error Code & Message (#8856) feat(router): add support for partial authorization (#8833) feat(gRPC): build gRPC client interface to initiate communication with recovery-decider service (#8178) fix(connector): [CYBERSOURCE] fix response field for netcetera authentication response (#8850) chore(events): making events nanosecond level precision (#8759)
Type of Change
Description
add support for partial authorization in payments flow
Implementation
enable_partial_authorization
field to payments request and include it in the payment_intent tableamount_capturable
andamount_captured
populated in the connectors flow based on the amount approved in case of partial authorizationsamount_capturable
andamount_captured
values populated from connector flows and use them to update our payment_intent and payment_attempt tables in post update trackerspartially_authorized_and_requires_capture
and attempt status aspartially_authorized
in case of partial authorizationpartially_authorized_and_requires_capture
intent status to perform capture operationAPI Changes
enable_partial_authorization
to payments request and responsepartially_authorized_and_requires_capture
intent statuspartially_authorized
attempt statusDatabase Changes
enable_partial_authorization
field to the payment_intent tableAdditional Changes
Motivation and Context
Partial authorization helps minimize the number of declined transactions due to insufficient funds, increasing sales for merchants
How did you test it?
2. Create a partial auth payment (with capture_method = manual) with `worldpayvantiv` connector, perform subsequent capture and refunds with partially approved amount (verify amount_capturable if populated correctly)
a. Create a Payment Intent with `"enable_partial_authorization": "true"` CURLResponse
b. Confirm the payment with following card_details and observe amount_capturable (should be less than total amount)
CURL
Response
c. force_sync the payment, and observe the intent status to be
partially_authorized_and_requires_capture
with partial amount set in amount_capturableCURL
Response
d. Perform capture for amount > amount_capturable which results in error
e. Perform capture for amount <= amount_capturable which results in successful capture and verify if amount_captured is populated as expected
CURL
Response
f. Perform refund for amount <= amount_captured
Create a partial auth payment (with capture_method = automatic) with
worldpayvantiv
connector, perform subsequent refund with partially approved amount (verify amount_captured if populated correctly)Perform sanity CIT and MIT payment by sending
enable_partial_authorization
as truePerform sanity Partial Captures
Checklist
cargo +nightly fmt --all
cargo clippy