fix(ucs): Paysafe Apple Pay predecrypt + gift card bridge fixes#13163
fix(ucs): Paysafe Apple Pay predecrypt + gift card bridge fixes#13163shuklatushar226 wants to merge 2 commits into
Conversation
- run wallet predecrypt for one-shot create-with-confirm: broaden the is_operation_confirm gate in get_decrypted_wallet_pm_token_and_set_pm_data so PaymentCreate with confirm=true also mints ApplePayDecrypt before the UCS PaymentMethodToken pre-step (previously the encrypted PKPaymentToken was forwarded to the connector, failing with Paysafe 7523) - map GiftCard(PaySafeCard) to the UCS PaySafeCard payment method in the gRPC request builder (was NotImplemented) - add PaySafeCard fallback in the CompleteAuthorize payment-method-data conversion so the redirect settle leg carries request_details - short-circuit the ApplePay arm on PaymentMethodToken::Token so a connector-minted handle is forwarded instead of rebuilding the SDK wallet - extend UCS Paysafe connector-config account_id mapping with apple_pay (encrypt/decrypt), interac, skrill and pay_safe_card slots Verified E2E against Paysafe sandbox via UCS: one-shot and two-step Apple Pay predecrypt succeeded; paysafecard/Interac redirect settle leg succeeded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Changed Files
|
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
In matches!(
(
format!("{operation:?}").as_str(),
payment_data.get_payment_attempt().confirm,
),
("PaymentCreate", true)
)Using
Fix: Add a method to the let is_confirm_operation = is_operation_confirm(operation)
|| (operation.discriminant() == OperationDiscriminant::PaymentCreate
&& payment_data.get_payment_attempt().confirm); |
|
dummy |
|
Fetching all comments on PR #13163 to retrieve the existing discussion threads. |
|
In
Suggested fix: Use a proper typed method like |
|
[should-fix] The Using |
|
[should-fix] GiftCard arm in The new GiftCard match arm handles |
Automated Review SummaryClassification: Core payments + Unified Connector Service (UCS) bugfixes FindingsNo blocking issues identified. The PR addresses legitimate UCS Paysafe integration gaps:
Pre-existing: The Verdict: ✅ No changes required — implementation is sound. |
|
Issue: In format!("{operation:?}").as_str()Fix: Use a proper enum match or add an |
Review SummaryClassification: Core team — Tier 1 — Blocking Concerns1. Pattern matching on stringified operation names The matches!(
(
format!("{operation:?}").as_str(),
payment_data.get_payment_attempt().confirm,
),
("PaymentCreate", true)
)Fix: Use a proper type-based check. If 2. Missing The changes to
Tier 2 — Suggestions3. Error context for unsupported payment methods The "Unimplemented payment method subtype: {payment_method_type:?}"💡 Consider attaching structured context rather than relying on Overall AssessmentThe Paysafe Apple Pay token short-circuit and gift card bridge implementation is architecturally sound. The |
|
🚨 Critical: Brittle string-based operation check The use of Fix: Use a proper type-based check. If let is_confirm_operation = is_operation_confirm(operation)
|| matches!(operation, payments::Operation::PaymentCreate { .. });Or add a dedicated |
|
In
However, Fix: Consider either:
_ => {
router_env::logger::warn!("Cannot reconstruct payment_method_data for {:?}",
router_data.request.payment_method_type);
None
} |
|
The transformers.rs change reconstructs Some(common_enums::PaymentMethodType::Interac) => Some(
pmd::PaymentMethodData::BankRedirect(pmd::BankRedirectData::Interac {
country: None,
email: None,
}),
),Creating |
|
The Apple Pay short-circuit only handles Some(PaymentMethodToken::Token(token)) => { ... }
_ => { /* full payload path */ }If |
|
The Risk: The reconstructed data uses default/empty values (e.g., Suggestion: Either ensure the original payment method data is persisted during the initial authorize call, or document which fields are safe to default for each payment method type. |
PR Review SummaryVerdict: 🔄 Request Changes 🚨 1 critical · Classification: core-flow-framework · secondary: connector-flow-addition This PR adds Paysafe Apple Pay predecrypt support and gift card bridge functionality. The changes span multiple core files and introduce significant new payment method handling. Critical issue identified with fragile operation detection. Inline CommentsFile: 🚨 The let is_confirm_operation = is_operation_confirm(operation)
|| matches!(
(
format!("{operation:?}").as_str(), // ← Fragile!
payment_data.get_payment_attempt().confirm,
),
("PaymentCreate", true)
);Use a proper type-based check instead: let is_confirm_operation = is_operation_confirm(operation)
|| (std::any::TypeId::of::<F>() == std::any::TypeId::of::<PaymentCreate>()
&& payment_data.get_payment_attempt().confirm);Or add an File:
Verify this doesn't break flows where both the connector token AND the original wallet data are needed for 3DS or risk assessment purposes. File:
Add validation that the reconstructed |
|
test comment |
|
The use of
Suggested fix: Add a semantic method to the // Add to Operation trait
fn is_payment_create(&self) -> bool;
// Then use:
let is_confirm_operation = is_operation_confirm(operation)
|| (operation.is_payment_create() && payment_data.get_payment_attempt().confirm); |
|
In
Suggested fix: Consider returning explicit errors instead of _ => Err(ApiErrorResponse::UnsupportedPaymentMethod {
payment_method_type: pmt.to_string(),
})?,Or ensure downstream connector transformers validate that the reconstructed data is sufficient before using it. |
|
🚨 [blocking] Apple Pay token handling logic has potential edge case issues The PR introduces a short-circuit for
Risk: If a stale or malformed connector token reaches this point, the payment will fail at the connector with a cryptic error instead of falling back to wallet payload processing. Recommendation: Add a validation check on the token before the early return, or ensure the upstream token generation always produces valid tokens for Apple Pay flows. Also verify that the |
Code Review: Paysafe Apple Pay Predecrypt + Gift Card Bridge FixesSummaryPR addresses wallet predecrypt edge cases and adds Paysafe-specific payment method support. Changes span payments core, UCS service layer, and connector configuration. Findings[:rotating_light:] Fragile Operation Detection in payments.rs let is_confirm_operation = is_operation_confirm(operation)
|| matches!(
(
format!("{operation:?}").as_str(),
payment_data.get_payment_attempt().confirm,
),
("PaymentCreate", true)
);Using let is_confirm_operation = is_operation_confirm(operation)
|| matches!(operation, OperationType::PaymentCreate)
&& payment_data.get_payment_attempt().confirm;Or add an [:warning:] Missing Error Chain Context .connector_meta
.as_ref()
.map(serde_json::to_string)
.transpose()
.change_context(UnifiedConnectorServiceError::RequestEncodingFailed)?The .change_context(UnifiedConnectorServiceError::RequestEncodingFailed)
.attach_printable_lazy(|| "failed to serialize connector_meta for Paysafe CompleteAuthorize")?[:bulb:] Pattern Matching Completeness Pre-existing Issues (Not introduced by this PR)
Review conducted per hyperswitch-review skill. Teams: hyperswitch-baseline, hyperswitch-core. |
|
The pattern // Avoid: format!("{operation:?}").as_str()
// Prefer: operation.is_payment_create() && payment_data.get_payment_attempt().confirm |
|
🚨 GiftCard match arm handles only PaySafeCard but wildcards rest The match on match *gift_card_data {
GiftCardData::PaySafeCard => Ok(...),
// Explicitly enumerate other variants or mark unimplemented
} |
|
[blocking] Fragile operation detection via string formatting The pattern matching on Use a typed discriminator instead: let is_confirm_operation = is_operation_confirm(operation)
|| matches!(operation, payments::PaymentCreate { confirm: true, .. });Or add an |
|
[should-fix] Debug string matching for operation type is fragile The
Suggestion: Use explicit variant matching or add a method to check operation type: let is_confirm_operation = is_operation_confirm(operation)
|| (operation.is_payment_create() && payment_data.get_payment_attempt().confirm);This avoids string allocation and makes the code more robust against refactoring. |
|
The Apple Pay token handling logic at line ~1301 introduces a precedence where
Consider adding a more explicit match arm for |
|
💡 New Ensure the corresponding variant exists in:
|
|
💡 The While this works for the settle leg where the connector already has the shopper data from the redirect, consider:
|
|
The PR uses
Suggested fix: Use pattern matching or a typed helper function instead: let is_create_with_confirm = matches!(operation, operations::PaymentCreate)
&& payment_data.get_payment_attempt().confirm;Or extend |
|
[blocking] In Replace with direct pattern matching on the operation type: let is_payment_create_with_confirm = matches!(operation, PaymentCreate)
&& payment_data.get_payment_attempt().confirm;
let is_confirm_operation = is_operation_confirm(operation) || is_payment_create_with_confirm;Or add an |
|
[blocking] In For |
Type of Change
Description
Four fixes to the hyperswitch→UCS bridge, found while E2E-testing the UCS Paysafe connector (Apple Pay, paysafecard, Interac):
Run the wallet predecrypt step for one-shot create-with-confirm (
crates/router/src/core/payments.rs)get_decrypted_wallet_pm_token_and_set_pm_datawas gated onis_operation_confirm, which matches only thePaymentConfirmoperation. A one-shotPOST /paymentswithconfirm: trueruns thePaymentCreateoperation, so the Apple Pay token was never decrypted and the UCSPaymentMethodTokenpre-step forwarded the still-encrypted PKPaymentToken to the connector — failing with Paysafe7523even when the MCA is configured withpayment_processing_details_at: Hyperswitch. The gate now also matchesPaymentCreatewhen the attempt hasconfirm = true, giving one-shot parity with the two-step flow. Plain creates (confirm: false) and non-wallet payments are unaffected.Map
GiftCard(PaySafeCard)in the gRPC request builder (crates/router/src/core/unified_connector_service.rs)The GiftCard arm returned
NotImplemented, so paysafecard payments could not be routed through UCS.PaySafeCard fallback in the CompleteAuthorize conversion (
crates/router/src/core/unified_connector_service/transformers.rs)The redirect settle leg for paysafecard failed with
missing request_details in the payloadbecause the payment-method-data fallback had noPaySafeCardarm.ApplePay token short-circuit + Paysafe account-id config extensions (
crates/router/src/core/unified_connector_service.rs,crates/router/src/core/unified_connector_service/connector_config.rs)When a connector-minted payment handle is already present as
PaymentMethodToken::Token, forward it instead of rebuilding the ApplePay SDK wallet payload. The UCS Paysafe connector-configaccount_idmapping is extended withapple_pay(encrypt/decrypt),interac,skrillandpay_safe_cardper-currency slots, matching the UCSPaysafeConfigproto.Motivation and Context
Testing the UCS Paysafe connector (connector-service
feat/paysafe_grace) end-to-end from hyperswitch surfaced these bridge gaps. Without them, Apple Pay predecrypt, paysafecard and the redirect settle leg cannot complete via UCS.How did you test it?
Local hyperswitch + UCS (connector-service) against the Paysafe sandbox:
POST /paymentswithconfirm: true→succeeded(Paysafe txne0bcf12c-3274-4112-bb2a-6ab2257c94a5); previously7523/confirm→succeeded(regression, both with a real Safari-captured PKPaymentToken and with structured decrypted data)Checklist
cargo +nightly fmt --allcargo clippy🤖 Generated with Claude Code