-
Notifications
You must be signed in to change notification settings - Fork 4.6k
fix(revenue_recovery): get recovery list and get recovery intent response missing values fix #10508
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
base: main
Are you sure you want to change the base?
Conversation
…onse missing value fixes
Changed Files
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #10508 +/- ##
=======================================
Coverage ? 6.47%
=======================================
Files ? 1251
Lines ? 311581
Branches ? 0
=======================================
Hits ? 20165
Misses ? 291416
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| pub expires_on: PrimitiveDateTime, | ||
|
|
||
| /// Time when the payment was created | ||
| #[serde(with = "common_utils::custom_serde::iso8601")] |
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.
generate openapi spec for the new introduced fields?
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.
Please generate openapi spec
| .and_then(|active_attempt_id| { | ||
| attempts | ||
| .iter() | ||
| .find(|attempt| attempt.id == *active_attempt_id) |
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 would active_attempt_id not be populated in intent?
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.
when the payment enters revenue recovery from an external source and the attempts are marked as TriggeredBy::External so in this case active_attempt_id is set to None cause they're the external attempts and not managed by hyperswitch so active attempt is not tracked. When hyperswitch revenuer recovery creates a internal retry attempt ,in this case active attempt id is set to attempt id.
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.
don't list payment attempt from DB and filter it. Find a way to point query the appropriate attempt from DB.
Like if active_attempt_id is present, fetch attempt using that id.
If not present, fetch only the latest attempt for the intent_id
| let attempt_futures: Vec<_> = list | ||
| .iter() | ||
| .map(|(payment_intent, payment_attempt)| { | ||
| let platform_clone = platform.clone(); |
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.
| let platform_clone = platform.clone(); | |
| let platform_ref = &platform; |
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.
If an intent has an active attempt, its id should be populated in active_attempt_id of intent.
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 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.
Please make this change https://github.com/juspay/hyperswitch/pull/10508/files#r2584064017
we should not clone any object. instead use reference wherever possible
| db.find_payment_attempts_by_payment_intent_id( | ||
| &payment_intent.id.clone(), | ||
| platform_clone.get_processor().get_key_store(), | ||
| platform_clone.get_processor().get_account().storage_scheme, | ||
| ) | ||
| .await | ||
| .ok() | ||
| .and_then(|attempts| { | ||
| payment_intent | ||
| .active_attempt_id | ||
| .as_ref() | ||
| .and_then(|active_attempt_id| { | ||
| attempts | ||
| .iter() | ||
| .find(|attempt| attempt.id == *active_attempt_id) | ||
| .cloned() | ||
| }) | ||
| .or_else(|| { | ||
| attempts | ||
| .iter() | ||
| .max_by_key(|attempt| attempt.created_at) | ||
| .cloned() | ||
| }) |
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.
don't list payment attempt from DB and filter it. Find a way to point query the appropriate attempt from DB.
Like if active_attempt_id is present, fetch attempt using that id.
If not present, fetch only the latest attempt for the intent_id
…onse missing value fixes
Type of Change
Description
In revenue recovery get intent and get recovery payment list had some missing values due to missing active attempt id in payment intent which is not able to get the latest attempt even though payment having a payment attempt in the db. so this pr will fix that issues and related data is been populated in the response even though there's a missing active attempt id store in the payment intent
API Response Improvements
RevenueRecoveryGetIntentResponsestruct now usesPaymentAmountDetailsResponsefor theamount_detailsfield, allowing it to represent both intent-level and attempt-level amount details more accurately.created_atfield toRevenueRecoveryGetIntentResponseto expose the payment creation timestamp in API responses.Payment Attempt Selection Logic
revenue_recovery_get_intent_coreandrevenue_recovery_list_paymentsto fetch the most relevant payment attempt for each payment intent. The selection prioritizes the active attempt if available, otherwise the most recent attempt is chosen. This ensures downstream consumers receive the correct attempt data.Transformer Signature Update
generate_revenue_recovery_get_intent_responsefunction signature has been updated to accept an optionalpayment_attempt, supporting the improved amount details logic and new fields.Additional Changes
Motivation and Context
How did you test it?
Get recovery list payments api
response
{ "count": 1, "total_count": 1, "data": [ { "id": "12345_pay_019adf05556570019fa6d4ce294716b6", "merchant_id": "cloth_seller_FNV6DwhshGLsuQLgGQfM", "profile_id": "pro_0uwqqjOjujEd9pIlwPaJ", "customer_id": null, "status": "scheduled", "amount": { "order_amount": 2250, "currency": "USD", "shipping_cost": null, "order_tax_amount": null, "external_tax_calculation": "skip", "surcharge_calculation": "skip", "surcharge_amount": null, "tax_on_surcharge": null, "net_amount": 2250, "amount_to_capture": null, "amount_capturable": 0, "amount_captured": 0 }, "created": "2025-12-02T12:24:20.076Z", "payment_method_type": "card", "payment_method_subtype": "credit", "connector": "worldpayvantiv", "merchant_connector_id": "mca_VAT2dYqYmtgURuqDz42a", "customer": null, "merchant_reference_id": "12453444456762", "description": null, "attempt_count": 0, "error": { "code": "No error code", "message": "PaymentStatusNotFound", "reason": "PaymentStatusNotFound", "unified_code": null, "unified_message": null, "network_advice_code": null, "network_decline_code": null, "network_error_message": null }, "cancellation_reason": null, "modified_at": "2025-12-02T12:24:22.201Z", "last_attempt_at": "2024-05-29T08:10:58.000Z" } ] }Get revenue recovery intent
response
{ "id": "12345_pay_019adec8a8177b70ba9d7916792da9f5", "status": "processing", "amount_details": { "order_amount": 2250, "currency": "USD", "shipping_cost": null, "order_tax_amount": null, "external_tax_calculation": "skip", "surcharge_calculation": "skip", "surcharge_amount": null, "tax_on_surcharge": null, "net_amount": 2250, "amount_to_capture": null, "amount_capturable": 2250, "amount_captured": 2250 }, "client_secret": null, "profile_id": "pro_0uwqqjOjujEd9pIlwPaJ", "merchant_reference_id": "12453444456762", "routing_algorithm_id": null, "capture_method": "automatic", "authentication_type": "no_three_ds", "billing": { "address": { "city": null, "country": "US", "line1": null, "line2": null, "line3": null, "zip": null, "state": "CA", "first_name": null, "last_name": null, "origin_zip": null }, "phone": null, "email": null }, "shipping": null, "customer_id": null, "customer_present": "present", "description": null, "return_url": null, "setup_future_usage": "off_session", "apply_mit_exemption": "Skip", "statement_descriptor": null, "order_details": null, "allowed_payment_method_types": null, "metadata": null, "connector_metadata": null, "feature_metadata": { "redirect_response": null, "search_tags": null, "apple_pay_recurring_details": null, "revenue_recovery": { "total_retry_count": 2, "payment_connector_transmission": "ConnectorCallSucceeded", "billing_connector_id": "mca_6GovbAMzXHjCpkmnGUsE", "active_attempt_payment_connector_id": "mca_VAT2dYqYmtgURuqDz42a", "billing_connector_payment_details": { "payment_processor_token": "271349160840009", "connector_customer_id": "853818" }, "payment_method_type": "card", "payment_method_subtype": "credit", "connector": "worldpayvantiv", "billing_connector_payment_method_details": { "type": "card", "value": { "card_network": "AmericanExpress", "card_issuer": "Wells Fargo" } }, "invoice_next_billing_time": null, "invoice_billing_started_at_time": null, "first_payment_attempt_pg_error_code": "No error code", "first_payment_attempt_network_decline_code": null, "first_payment_attempt_network_advice_code": null } }, "payment_link_enabled": "Skip", "payment_link_config": null, "request_incremental_authorization": "false", "split_txns_enabled": "skip", "expires_on": "2025-12-02T11:33:03.544Z", "created_at": "2025-12-02T11:18:03.544Z", "frm_metadata": null, "request_external_three_ds_authentication": "Skip", "enable_partial_authorization": null, "card_attached": 3 }Checklist
cargo +nightly fmt --allcargo clippy