feat(core): Add all_keys_required in Capture, Refund and RSync Flows#10178
feat(core): Add all_keys_required in Capture, Refund and RSync Flows#10178likhinbopanna merged 15 commits intomainfrom
Conversation
Changed Files
|
…ys-required-capture-refund
…spay/hyperswitch into feat/all-keys-required-capture-refund
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #10178 +/- ##
=======================================
Coverage ? 6.50%
=======================================
Files ? 1232
Lines ? 307877
Branches ? 0
=======================================
Hits ? 20041
Misses ? 287836
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| match ( | ||
| connector_status, | ||
| attempt_status, | ||
| payment_data.frm_message.to_owned(), |
There was a problem hiding this comment.
no need of nested match cases here. we can directly have payment_data.frm_message.frm_status.to_owned() here, and for Fraud and ManualReview return attempt_status
There was a problem hiding this comment.
These nested match cases were already there. I just introduced an extra check on PSync flow that if a payment is already in terminal status then the status should not be updated after PSync call (find more context here: #9925)
There was a problem hiding this comment.
anyways you can make the changes nits mainly, same code block is repeating twice
There was a problem hiding this comment.
We can’t directly use payment_data.frm_message.frm_status.to_owned() since frm_message is an Option type.
Therefore, this block of code will need to be repeated in both cases:
router_data.get_attempt_status_for_db_update(
&payment_data,
router_data.amount_captured,
router_data
.minor_amount_capturable
.map(MinorUnit::get_amount_as_i64),
)?,
crates/router/src/core/refunds.rs
Outdated
| }; | ||
|
|
||
| Ok(refund.foreign_into()) | ||
| let mut refund_response: api::RefundResponse = refund.foreign_into(); |
There was a problem hiding this comment.
instead of having a mutable variable, can we use the foreign_from for api::RefundResponse?
which is already there at 1636
| let flow_name = core_utils::get_flow_name::<F>()?; | ||
| let updated_attempt_status = | ||
| if flow_name == "PSync" && attempt_status.is_terminal_status() { | ||
| // For PSync flows, if payment is already in terminal state, don't update the status |
There was a problem hiding this comment.
For PSync flow, if the attempt status is terminal, we will not call the connector at all.
There was a problem hiding this comment.
@hrithikesh026, If all_keys_required is set to true, we call the connector to retrieve the raw connector response regardless of the payment status. This is necessary because raw_connector_response is maintained as a stateless field in Hyperswitch. You can find more context here: #9925
There was a problem hiding this comment.
attempt status should directly map with connector status right. If the connector status changes from terminal status, we should also change the attempt status.
crates/router/src/core/refunds.rs
Outdated
| merchant_context, | ||
| refund, | ||
| router_data.clone(), | ||
| None, |
There was a problem hiding this comment.
this should not be none
There was a problem hiding this comment.
We are currently not focusing on UCS shadow flow for whole_connector_response as UCS itself supports this feature.
There was a problem hiding this comment.
here we first do direct HS call and then shadow with UCS. Your feature won't work when shadow mode is enabled if this is hardcoded to none
…ys-required-capture-refund
…spay/hyperswitch into feat/all-keys-required-capture-refund
| // Check if current status is terminal and preserve it in PSync flows | ||
| let flow_name = core_utils::get_flow_name::<F>()?; | ||
| let updated_attempt_status = | ||
| if flow_name == "PSync" && attempt_status.is_terminal_status() { |
There was a problem hiding this comment.
Please create a discussion on why this logic is needed.
crates/api_models/src/refunds.rs
Outdated
| pub issuer_error_message: Option<String>, | ||
| /// Contains whole connector response | ||
| #[schema(value_type = Option<String>)] | ||
| pub whole_connector_response: Option<masking::Secret<String>>, |
There was a problem hiding this comment.
what is reason we have a different keep name here?
| pub whole_connector_response: Option<masking::Secret<String>>, | |
| pub raw_connector_response: Option<masking::Secret<String>>, |
| )?, | ||
| }; | ||
| // Check if current status is terminal and preserve it in PSync flows | ||
| let flow_name = core_utils::get_flow_name::<F>()?; |
There was a problem hiding this comment.
This kind of overloading is wrong, we should not do this in this way, why this logic is required?
There was a problem hiding this comment.
Reverting this change as it'll require a separate discussion. Will take this in a separate PR when the changes will be fully discussed.
…nd revert payment_response changes
…10178) Co-authored-by: Anurag Singh <anurag.singh.001@MacBookPro.lan> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Anurag Singh <anurag.singh.001@Anurag-Singh-WPMHJ5619X.local>
Type of Change
closes this issue
Description
Added all_keys_required in Capture, Refund and RSync Flows.
Additional Changes
Motivation and Context
How did you test it?
Postman Tests
Request:
Response:
{ "payment_id": "pay_zGIAlSjMGujPpQdXF4ES", "merchant_id": "merchant_1763368771", "status": "succeeded", "amount": 1000, "net_amount": 1000, "shipping_cost": null, "amount_capturable": 0, "amount_received": 1000, "connector": "peachpayments", "client_secret": "pay_zGIAlSjMGujPpQdXF4ES_secret_X0njS1ZKwOZDpM4yuUAc", "created": "2025-11-17T10:57:30.574Z", "currency": "USD", "customer_id": "StripeCustomer", "customer": { "id": "StripeCustomer", "name": "John Doe", "email": "abcdef123@gmail.com", "phone": "999999999", "phone_country_code": "+65" }, "description": "Its my first payment request", "refunds": null, "disputes": null, "mandate_id": null, "mandate_data": null, "setup_future_usage": null, "off_session": null, "capture_on": null, "capture_method": "manual", "payment_method": "card", "payment_method_data": { "card": { "last4": "6175", "card_type": null, "card_network": null, "card_issuer": null, "card_issuing_country": null, "card_isin": "492303", "card_extended_bin": null, "card_exp_month": "01", "card_exp_year": "2028", "card_holder_name": "joseph Doe", "payment_checks": null, "authentication_data": null }, "billing": null }, "payment_token": null, "shipping": { "address": { "city": "San Fransico", "country": "US", "line1": "1467", "line2": "Harrison Street", "line3": "Harrison Street", "zip": "94122", "state": "California", "first_name": "John", "last_name": "Doe", "origin_zip": null }, "phone": null, "email": null }, "billing": { "address": { "city": "San Fransico", "country": "US", "line1": "1467", "line2": "Harrison Street", "line3": "Harrison Street", "zip": "94122", "state": "California", "first_name": "John", "last_name": "Doe", "origin_zip": null }, "phone": null, "email": null }, "order_details": null, "email": "abcdef123@gmail.com", "name": "John Doe", "phone": "999999999", "return_url": "https://duck.com/", "authentication_type": "no_three_ds", "statement_descriptor_name": "joseph", "statement_descriptor_suffix": "JS", "next_action": null, "cancellation_reason": null, "error_code": null, "error_message": null, "unified_code": null, "unified_message": null, "payment_experience": null, "payment_method_type": "credit", "connector_label": null, "business_country": null, "business_label": "default", "business_sub_label": null, "allowed_payment_method_types": null, "ephemeral_key": null, "manual_retry_allowed": null, "connector_transaction_id": "2aa0fc35-d182-4ec8-b3e8-96a7f37fcd9b", "frm_message": null, "metadata": { "udf1": "value1", "login_date": "2025-07-25T11:46:12Z", "new_customer": "true" }, "connector_metadata": null, "feature_metadata": { "redirect_response": null, "search_tags": null, "apple_pay_recurring_details": null, "gateway_system": "direct" }, "reference_id": "2aa0fc35-d182-4ec8-b3e8-96a7f37fcd9b", "payment_link": null, "profile_id": "pro_2CXBb8dKJbVIE6FdbhNs", "surcharge_details": null, "attempt_count": 1, "merchant_decision": null, "merchant_connector_id": "mca_6Wos8mYBczVrjlaw6VUC", "incremental_authorization_allowed": false, "authorization_count": null, "incremental_authorizations": null, "external_authentication_details": null, "external_3ds_authentication_attempted": false, "expires_on": "2025-11-17T11:12:30.574Z", "fingerprint": null, "browser_info": { "language": "nl-NL", "time_zone": 0, "ip_address": "127.0.0.1", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", "color_depth": 24, "java_enabled": true, "screen_width": 1536, "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "screen_height": 723, "java_script_enabled": true }, "payment_channel": null, "payment_method_id": null, "network_transaction_id": null, "payment_method_status": null, "updated": "2025-11-17T10:57:37.745Z", "split_payments": null, "frm_metadata": null, "extended_authorization_applied": null, "extended_authorization_last_applied_at": null, "request_extended_authorization": null, "capture_before": null, "merchant_order_reference_id": null, "order_tax_amount": null, "connector_mandate_id": null, "card_discovery": "manual", "force_3ds_challenge": false, "force_3ds_challenge_trigger": false, "issuer_error_code": null, "issuer_error_message": null, "is_iframe_redirection_enabled": null, "whole_connector_response": "{\"transactionId\":\"2aa0fc35-d182-4ec8-b3e8-96a7f37fcd9b\",\"referenceId\":\"pay_zGIAlSjMGujPpQdXF4ES_1\",\"transactionResult\":\"approved_confirmed\",\"cardNotPresentRefundableStatus\":\"not_refundable\",\"responseCode\":{\"value\":\"00\",\"description\":\"Approved or completed successfully\",\"isoCodeDescription\":\"Approved or completed successfully\",\"terminalOutcomeString\":\"Approved\",\"receiptString\":\"Approved\",\"explanation\":\"Transaction approved by acquirer or issuer. Other factors may impact the final outcome of the transaction.\"},\"ecommerceCardPaymentOnlyTransactionData\":{\"rrn\":\"***\",\"stan\":\"***\",\"approvalCode\":\"***\",\"settlementDate\":\"2025-11-17\",\"amount\":{\"amount\":1000,\"currencyCode\":\"ZAR\",\"displayValue\":\"R10.00\"},\"card\":{\"maskedPan\":\"492303**********\",\"binNumber\":\"492303\",\"scheme\":\"Visa\",\"cardholderName\":\"joseph Doe\",\"expiryYear\":\"28\",\"expiryMonth\":\"01\",\"productType\":\"CREDIT\",\"bankName\":\"NEDB\",\"countryCode\":\"ZA\"}}}", "enable_partial_authorization": null, "enable_overcapture": null, "is_overcapture_enabled": null, "network_details": null, "is_stored_credential": null, "mit_category": null, "billing_descriptor": null }Request:
Response:
{ "refund_id": "ref_GUEtZKp01hBpqklxFt4x", "payment_id": "pay_zGIAlSjMGujPpQdXF4ES", "amount": 1000, "currency": "USD", "status": "succeeded", "reason": "Customer returned product", "metadata": { "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" }, "error_message": null, "error_code": null, "unified_code": null, "unified_message": null, "created_at": "2025-11-17T10:58:16.781Z", "updated_at": "2025-11-17T10:58:17.573Z", "connector": "peachpayments", "profile_id": "pro_2CXBb8dKJbVIE6FdbhNs", "merchant_connector_id": "mca_6Wos8mYBczVrjlaw6VUC", "split_refunds": null, "issuer_error_code": null, "issuer_error_message": null, "raw_connector_response": "{\"transactionId\":\"dd637cd1-4987-4653-a3e7-af85b9909f4b\",\"originalTransactionId\":\"2aa0fc35-d182-4ec8-b3e8-96a7f37fcd9b\",\"referenceId\":\"ref_GUEtZKp01hBpqklxFt4x\",\"transactionResult\":\"approved_confirmed\",\"cardNotPresentRefundableStatus\":\"not_refundable\",\"transactionType\":{\"value\":20,\"description\":\"Refund\"},\"responseCode\":{\"value\":\"00\",\"description\":\"Approved or completed successfully\",\"terminalOutcomeString\":\"Approved\",\"receiptString\":\"Approved\"},\"ecommerceCardPaymentOnlyTransactionData\":{\"rrn\":\"*****\",\"stan\":\"****\",\"approvalCode\":\"****\",\"settlementDate\":\"2025-11-17\",\"amount\":{\"amount\":1000,\"currencyCode\":\"ZAR\",\"displayValue\":\"R10.00\"},\"card\":{\"maskedPan\":\"492303**********\",\"binNumber\":\"492303\",\"scheme\":\"Visa\",\"cardholderName\":\"joseph Doe\",\"expiryYear\":\"28\",\"expiryMonth\":\"01\",\"productType\":\"CREDIT\",\"bankName\":\"NEDB\",\"countryCode\":\"ZA\"}},\"refundBalanceData\":{\"amount\":{\"amount\":1000,\"currencyCode\":\"ZAR\",\"displayValue\":\"R10.00\"},\"balance\":{\"amount\":0,\"currencyCode\":\"ZAR\",\"displayValue\":\"R0.00\"},\"refundHistory\":[{\"transactionId\":\"dd637cd1-4987-4653-a3e7-af85b9909f4b\",\"referenceId\":\"ref_GUEtZKp01hBpqklxFt4x\",\"amount\":{\"amount\":1000,\"currencyCode\":\"ZAR\",\"displayValue\":\"R10.00\"},\"transactionTime\":\"2025-11-17T10:58:17.012157224Z\"}]},\"transactionTime\":\"2025-11-17T10:58:17.012157224Z\",\"paymentMethod\":\"ecommerce_card_payment_only\",\"voidableUntilTime\":\"2025-11-17T22:00:00Z\",\"posData\":{\"exiAuthRef\":\"API-KEY: #############\"}}" }Request:
Response:
Checklist
cargo +nightly fmt --allcargo clippy