Skip to content

fix(connector): [globalpay] handle edge case where currency comes as empty upon payment decline#7812

Merged
likhinbopanna merged 3 commits intomainfrom
connector/fix-globalpay-5xx
Apr 17, 2025
Merged

fix(connector): [globalpay] handle edge case where currency comes as empty upon payment decline#7812
likhinbopanna merged 3 commits intomainfrom
connector/fix-globalpay-5xx

Conversation

@pixincreate
Copy link
Member

@pixincreate pixincreate commented Apr 15, 2025

Type of Change

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

Description

This PR fixes an edge case with GlobalPay where, upon payment decline, we get a deserialization error and that is because currency field is not expected to be empty but rather a valid Currency enum.

Additional Changes

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

Motivation and Context

closes #7813

How did you test it?

cards:

// successful cards
"card_number": "4012001038488884",
"card_number": "4222000006285344",
"card_number": "4012001037167778",
"card_number": "4222000005218627",
"card_number": "4222000009719489",
// failure cards
"card_number": "4242420000000091",
"card_number": "5100000000000131",

create a payment:

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_5WtyfFxKHentO9ERR1HP8qrlIPzPP5s1Khs2G2q7mZBl1zJDOUFC3ZNzPcQtfynW' \
--data-raw '{
    "amount": 1000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 1000,
    "customer_id": "StripeCustomer",
    "email": "abcdef123@gmail.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "Its my first payment request",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
      "payment_method": "card",
        "payment_method_type": "credit",
        "payment_method_data": {
            "card": {
                "card_number": "4242420000000091",
                "card_exp_month": "10",
                "card_exp_year": "25",
                "card_holder_name": "joseph Doe",
                "card_cvc": "101"
            }
        },
    "billing": {
        "address": {
            "country": "US"
        }
    } 
}'
{
    "payment_id": "pay_9iWUNJmnm8Le4l8iZCV9",
    "merchant_id": "postman_merchant_GHAction_95a905d4-93fe-42b4-b125-c1ddafa26313",
    "status": "failed",
    "amount": 1000,
    "net_amount": 1000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "globalpay",
    "client_secret": "pay_9iWUNJmnm8Le4l8iZCV9_secret_RyifkSKcDrOFEEqC0OZ7",
    "created": "2025-04-15T09:21:39.674Z",
    "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": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0091",
            "card_type": "CREDIT",
            "card_network": "Visa",
            "card_issuer": "STRIPE PAYMENTS UK LIMITED",
            "card_issuing_country": "UNITEDKINGDOM",
            "card_isin": "424242",
            "card_extended_bin": null,
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": {
            "city": null,
            "country": "US",
            "line1": null,
            "line2": null,
            "line3": null,
            "zip": null,
            "state": null,
            "first_name": null,
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "abcdef123@gmail.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": "111",
    "error_message": "(111)[ test system ] Strong Customer Authentication Required",
    "unified_code": "UE_9000",
    "unified_message": "Something went wrong",
    "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": {
        "customer_id": "StripeCustomer",
        "created_at": 1744708899,
        "expires": 1744712499,
        "secret": "epk_24e7b6b84ca647aaa39a01fbdf5c0f42"
    },
    "manual_retry_allowed": true,
    "connector_transaction_id": "TRN_uRk00jWtySUhGnJD4e7mZhEQQwZ6vF_Le4l8iZCV9_1",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_I2x5oXH0EQeuMaAZSe6G",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_NN71g0D5WXMeggXKXhBM",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-04-15T09:36:39.674Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-04-15T09:21:41.821Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": 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": "111",
    "issuer_error_message": "(111)[ test system ] Strong Customer Authentication Required"
}

payment now fails with valid error message when failure cards are used.

previously, we would get a 5xx:

{"error":{"type":"api","message":"Something went wrong","code":"HE_00"}}
├╴at crates/router/src/services/api.rs:806:14
│
├─▶ {"error":{"type":"server_not_available","code":"HE_00","message":"Something went wrong"}}
│   ╰╴at crates/router/src/core/errors/utils.rs:281:17
│
├─▶ Failed to deserialize connector response
│   ╰╴at crates/hyperswitch_connectors/src/connectors/globalpay.rs:757:14
│
├─▶ Failed to parse struct: Globalpay PaymentsResponse
│   ├╴at /router/crates/common_utils/src/ext_traits.rs:175:14
│   ╰╴Unable to parse hyperswitch_connectors::connectors::globalpay::response::GlobalpayPaymentsResponse from bytes b"{\"id\":\"TRN_dsWF5Tk8k8m1ylUC4Fz3319DPwyUt0_mCSfvURrUj_1\",\"time_created\":\"2025-04-14T14:43:41.794Z\",\"type\":\"SALE\",\"status\":\"DECLINED\",\"channel\":\"CNP\",\"capture_mode\":\"AUTO\",\"amount\":\"\",\"currency\":\"\",\"country\":\"US\",\"merchant_id\":\"MER_7e3e2c7df34f42819b3edee31022ee3f\",\"merchant_name\":\"Sandbox_merchant_3\",\"account_id\":\"TRA_c9967ad7d8ec4b46b6dd44a61cde9a91\",\"account_name\":\"transaction_processing\",\"reference\":\"pay_gO7eiUCttdmCSfvURrUj_1\",\"payment_method\":{\"result\":\"111\",\"message\":\"(111)[ test system ] Strong Customer Authentication Required\",\"entry_mode\":\"ECOM\",\"authentication\":{\"cavv_result\":\"\"},\"card\":{\"funding\":\"CREDIT\",\"brand\":\"MASTERCARD\",\"masked_number_last4\":\"XXXXXXXXXXXX0131\",\"authcode\":\"\",\"brand_reference\":\"YWLv8HtAwSh6aG8F\",\"brand_time_created\":\"\",\"tag_response\":\"\",\"cvv_result\":\"MATCHED\",\"avs_address_result\":\"MATCHED\",\"avs_postal_code_result\":\"MATCHED\",\"avs_action\":\"\",\"provider\":{\"result\":\"111\",\"cvv_result\":\"M\",\"avs_address_result\":\"M\",\"avs_postal_code_result\":\"M\"}}},\"risk_assessment\":[{\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\",\"rules\":[{\"reference\":\"0c93a6c9-7649-4822-b5ea-1efa356337fd\",\"description\":\"Cardholder Name Rule\",\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\"},{\"reference\":\"a539d51a-abc1-4fff-a38e-b34e00ad0cc3\",\"description\":\"CardNumber block\",\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\"},{\"reference\":\"d023a19e-6985-4fda-bb9b-5d4e0dedbb1e\",\"description\":\"Amount test\",\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\"}]}],\"currency_conversion\":{\"payer_amount\":\"\",\"payer_currency\":\"\",\"exchange_rate\":\"\",\"margin_rate_percentage\":\"\",\"commission_percentage\":\"\",\"exchange_rate_source\":\"\",\"exchange_rate_time_created\":\"\"},\"batch_id\":\"\",\"action\":{\"id\":\"ACT_dsWF5Tk8k8m1ylUC4Fz3319DPwyUt0\",\"type\":\"AUTHORIZE\",\"time_created\":\"2025-04-14T14:43:41.794Z\",\"result_code\":\"DECLINED\",\"app_id\":\"OXKlGGm6ecZLIqMyRgPHRfMxdUAiEcp8\",\"app_name\":\"rotate_again\"}}"
│
╰─▶ unknown variant ``, expected one of `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BTN`, `BWP`, `BYN`, `BZD`, `CAD`, `CDF`, `CHF`, `CLF`, `CLP`, `CNY`, `COP`, `CRC`, `CUC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ERN`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `IRR`, `ISK`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLE`, `SLL`, `SOS`, `SRD`, `SSP`, `STD`, `STN`, `SVC`, `SYP`, `SZL`, `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`, `ZWL` at line 1 column 195
    ╰╴at /router/crates/common_utils/src/ext_traits.rs:175:14

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

@pixincreate pixincreate added A-connector-integration Area: Connector integration S-waiting-on-review Status: This PR has been implemented and needs to be reviewed labels Apr 15, 2025
@pixincreate pixincreate added this to the April 2025 Release milestone Apr 15, 2025
@pixincreate pixincreate self-assigned this Apr 15, 2025
@semanticdiff-com
Copy link

semanticdiff-com bot commented Apr 15, 2025

Review changes with  SemanticDiff

Changed Files
File Status
  crates/hyperswitch_connectors/src/connectors/globalpay/response.rs  0% smaller
  crates/hyperswitch_connectors/src/utils.rs  0% smaller

@pixincreate pixincreate changed the title fix(connector): handle edge case where currency comes as empty upon payment decline fix(connector): [globalpay] handle edge case where currency comes as empty upon payment decline Apr 15, 2025
@pixincreate pixincreate added the C-bug Category: Bug label Apr 15, 2025
@pixincreate pixincreate marked this pull request as ready for review April 15, 2025 09:28
@pixincreate pixincreate requested a review from a team as a code owner April 15, 2025 09:28
/// The country in ISO-3166-1(alpha-2 code) format.
pub country: Option<String>,
/// The currency of the amount in ISO-4217(alpha-3)
#[serde(deserialize_with = "deserialize_optional_currency")]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we do this change for amount as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

i do not think changes to amount is needed.

StringMinorUnit is inherently a String. so, an empty string does not cause any deserialization issues.

and yes, the after change, i'm unable to reproduce the bug even though the connector sends amount as "".

@likhinbopanna likhinbopanna added this pull request to the merge queue Apr 17, 2025
Merged via the queue into main with commit 689e738 Apr 17, 2025
16 of 20 checks passed
@likhinbopanna likhinbopanna deleted the connector/fix-globalpay-5xx branch April 17, 2025 13:23
@pixincreate pixincreate removed the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Apr 17, 2025
pixincreate added a commit that referenced this pull request Apr 21, 2025
…acilitapay-pix-pmt

* 'main' of github.com:juspay/hyperswitch: (21 commits)
  refactor(required_fields): move pm required fields to pm crate (#7539)
  fix(connector): [noon] address `next_action_url` being `null` for cards in 3ds payment (#7832)
  refactor(middleware): add middleware to record metrics for request count and duration (#7803)
  chore(version): 2025.04.18.0
  chore(postman): update Postman collection files
  fix(connector): [globalpay] handle edge case where currency comes as empty upon payment decline (#7812)
  refactor(cypress-v2): change `Authorization` and `payment_methods_enabled` for v2 cypress tests (#7805)
  fix(connector): [Cybersource] send type selection indicator for co-batch cards (#7828)
  feat(payment_method): add logic for setup_future_usage downgrade and add filter based on zero mandate config (#7775)
  refactor(accounts): move dashboard_metadata table to accounts_schema and point v2 to v1 dashboard_metadata (#7793)
  chore(analytics): opensearch client creation based on config (#7810)
  ci(postman): update assertion error message for nmi collection (#7765)
  feat: add primary key not null query to generic filter function (#7785)
  chore(version): 2025.04.17.0
  chore: change payment method files ownership to `hyperswitch-payment-methods` (#7808)
  feat(vsaas): modify api key auth to support vsaas cases (#7593)
  ci(cypress): verify mandate id to be `null` if payment id not `succeeded` (#7749)
  feat(connector): [chargebee] consumes required fields to support transaction monitoring (#7774)
  ci(configs): remove vault private key from configs (#7825)
  chore(version): 2025.04.16.0
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-connector-integration Area: Connector integration C-bug Category: Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG(GlobalPay)] 5xx when making a failure payment

4 participants