Skip to content

Conversation

srujanchikke
Copy link
Contributor

@srujanchikke srujanchikke commented Sep 23, 2025

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

This PR adds support for partial auth in proxy payments which is triggered in revenue recovery workflow.
enable_partial_authorization is passed in /recovery flow, which update payment intent with enable_partial_authorization to true. Proxy(authorize) will gets triggered by recovery execute workflow, which will then send the same to connector. Based on amount_captured send by connector we either move the payment to success/ PartialCharged.

NOTE : changes are still pending for normal payments partial auth.

This PR also contains few changes regarding revenue recovery workflow where we consider PartialCharged payments as Succeeded. We will the resume later when auto retries system is enabled for partial charged payments.

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

How did you test it?

Reference doc for partial auth testing via worldpay vantiv : http://support.worldpay.com/support/CNP-API/content/testauthincind.htm#TABLE2-1
TEST CASE 1 :
set up revenue recovery payment and billing mca with worldpay vantiv and billing mca

curl --location 'http://localhost:8080/v2/connector-accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-merchant-id: cloth_seller_lPcOqukU9cZQLwpuwleD' \
--header 'x-profile-id: pro_dzQ31gKcfnWgaNQVOkvA' \
--header 'Authorization: admin-api-key=test_admin' \
--data '{
    "connector_type": "payment_processor",
    "connector_name": "worldpayvantiv",
    "connector_account_details": { 
        "auth_type": "SignatureKey",
        "api_key": "", 
        "key1": "", 
        "api_secret": "" 
    },
    "payment_methods_enabled": [
        {
            "payment_method_type": "card",
            "payment_method_subtypes": [
                {
                    "payment_method_subtype": "credit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": -1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_subtype": "debit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": -1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        }
    ],
    "frm_configs": null,
    "connector_webhook_details": {
        "merchant_secret": ""
    },
    "metadata": {
        "report_group": "Hello",
        "merchant_config_currency": "USD"
    },
    "profile_id": "pro_dzQ31gKcfnWgaNQVOkvA"
}'
curl --location 'http://localhost:8080/v2/connector-accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-merchant-id: cloth_seller_lPcOqukU9cZQLwpuwleD' \
--header 'x-profile-id: pro_dzQ31gKcfnWgaNQVOkvA' \
--header 'Authorization: admin-api-key=test_admin' \
--header 'api-key: test_admin' \
--data '{
    "connector_type": "billing_processor",
    "connector_name": "custombilling",
    "connector_account_details": {
        "auth_type": "NoKey"
    },
    "feature_metadata" : {
        "revenue_recovery" : {
            "max_retry_count" : 9,
            "billing_connector_retry_threshold" : 0,
            "billing_account_reference" :{
                "mca_K59DLF4m29U4Mt281n16" : "mca_K59DLF4m29U4Mt281n16"
            },
            "switch_payment_method_config" : {
                "retry_threshold" : 0,
                "time_threshold_after_creation": 0
            }
        }
    },
    "profile_id": "pro_dzQ31gKcfnWgaNQVOkvA"
}'

Trigger failed invoice

curl --location 'http://localhost:8080/v2/payments/recovery' \
--header 'Authorization: api-key=dev_35ephsVynvWKco4X3E6tv0hVNZRhbxm6H141PpqctRlQJ4x59iiuAFqcQhYoC7xd' \
--header 'x-profile-id: pro_dzQ31gKcfnWgaNQVOkvA' \
--header 'x-merchant-id: cloth_seller_lPcOqukU9cZQLwpuwleD' \
--header 'Content-Type: application/json' \
--data '{
    "amount_details": {
        "order_amount": 50000,
        "currency": "USD"
    },
    "merchant_reference_id": "ref_id63",
    "connector_transaction_id": "trans63",
    "connector_customer_id": "cust63",
    "error": {
        "code": "148",
        "message": "Invalid or expired card"
    },
    "enable_partial_authorization" : true,
    "billing": {
        "address": {
            "state": "CA",
            "country": "US"
        }
    },
    "payment_method_type": "card",
    "payment_method_sub_type": "credit",
    "payment_method_data": {
        "primary_processor_payment_method_token": "271349160840009",
        "additional_payment_method_info": {  
            "card_exp_month": "04",
            "card_exp_year": "50",
            "last_four_digits": "0009",
            "card_network": "AMEX",
            "card_issuer": "Wells Fargo",
            "card_type": "debit"
        }
    },
    "billing_started_at": "2024-05-29T08:10:58Z",
    "transaction_created_at": "2024-05-29T08:10:58Z",
    "attempt_status": "failure",
    "action": "schedule_failed_payment",
    "billing_merchant_connector_id": "mca_K59DLF4m29U4Mt281n16",
    "payment_merchant_connector_id": "mca_qM54E3Zn9iIRRGYCe2br",
    "metadata": {
        "BudgetPayId": "TEST_1234"
    }
}'
Screenshot 2025-09-24 at 11 45 03 AM Screenshot 2025-09-24 at 11 45 25 AM Screenshot 2025-09-24 at 11 45 47 AM

-> Pass enable_partial_authorization to false , this should make payment succeeded/Fail.
-> Pass enable_partial_authorization to true and pass normal card which will debit complete amount.

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

@srujanchikke srujanchikke requested review from a team as code owners September 23, 2025 06:59
Copy link

semanticdiff-com bot commented Sep 23, 2025

@srujanchikke srujanchikke changed the title feat(Add support for partial auth in proxy payments r feat(core): Add support for partial auth in proxy payments [V2] Sep 23, 2025
@srujanchikke srujanchikke self-assigned this Sep 23, 2025
@srujanchikke srujanchikke added A-core Area: Core flows api-v2 A-payments Area: payments labels Sep 23, 2025
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Sep 23, 2025
pub merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,

/// Allow partial authorization for this payment
pub enable_partial_authorization: Option<bool>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduce a wrapper type for this bool.
Refer to RequestIncrementalAuthorization

AkshayaFoiger
AkshayaFoiger previously approved these changes Sep 24, 2025
Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, diesel::expression::AsExpression,
)]
#[diesel(sql_type = diesel::sql_types::Bool)]
pub struct EnablePartialAuthorizationBool(bool);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to define in two places

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this

fn get_is_partial_approval(&self) -> Option<PartialApprovalFlag> {
self.enable_partial_authorization
.map(PartialApprovalFlag::from)
.map(|partial_auth| PartialApprovalFlag::from(partial_auth.is_true()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can have from implementation on PartialApprovalFlag.
PartialApprovalFlag::from(EnablePartialAuthorizationBool).

This change can be eliminated

Self::ReviewForFailedPayment(attempt_triggered_by)
}
(enums::IntentStatus::Succeeded, _, _) => Self::ReviewForSuccessfulPayment,
(enums::IntentStatus::Succeeded | enums::IntentStatus::PartiallyCaptured, _, _) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only succeded and partially_captiured?

Comment on lines 383 to 385
(enums::IntentStatus::Succeeded | enums::IntentStatus::PartiallyCaptured, _, _) => {
Self::ReviewForSuccessfulPayment
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(enums::IntentStatus::Succeeded | enums::IntentStatus::PartiallyCaptured, _, _) => {
Self::ReviewForSuccessfulPayment
}
(intent_status, _, _) => {
if intent_status.some_impl_function(){
Self::ReviewForSuccessfulPayment
}
else{
Self::InvalidDecision
}
}

@@ -1,3 +1,4 @@
use common_types::primitive_wrappers::EnablePartialAuthorizationBool;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use common_types::primitive_wrappers::EnablePartialAuthorizationBool;
use common_types::primitive_wrappers;

pub payment_channel: Option<common_enums::PaymentChannel>,
pub feature_metadata: Option<Secret<serde_json::Value>>,
pub enable_partial_authorization: Option<bool>,
pub enable_partial_authorization: Option<EnablePartialAuthorizationBool>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub enable_partial_authorization: Option<EnablePartialAuthorizationBool>,
pub enable_partial_authorization: Option< primitive_wrappers::EnablePartialAuthorizationBool>,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-core Area: Core flows A-payments Area: payments api-v2 M-api-contract-changes Metadata: This PR involves API contract changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants