hyperswitch icon indicating copy to clipboard operation
hyperswitch copied to clipboard

feat(connector): [FISERV] Populated Network Advice Fields in ErrorResponse & Added Integrity Check support for Payment & Refund Flows

Open bsayak03 opened this issue 7 months ago • 1 comments

Type of Change

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

Description

Populated network_advice_code, network_decline_code and network_error_response in ErrorResponse. Also added integrity check support for Authorize, Capture, Refund, PSync and RSync flows.

What is an integrity check? A scenario where there is a discrepancy between the amount sent in the request and the amount received from the connector, which is checked during response handling. https://developer.fiserv.com/product/CommerceHub/api/?type=post&path=/payments/v1/charges&branch=main&version=1.25.0400

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?

For Network Advice Fields in Error Response :

Do a payments create and make the payments fail

cURL :

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_8BoyoSPgaCnEZ7hPrLeVLSyEpsxM1d95WnoRRlgyLJfhUD3Rt9vaYiErFlhICRJy' \
--header 'Cookie: PHPSESSID=0b47db9d7de94c37b6b272087a9f2fa7' \
--data-raw '{
    "amount": 651200,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "First_Customer",
    "name": "John Doe",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "5239290700000051",
            "card_exp_month": "12",
            "card_exp_year": "27",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": "[email protected]"
    }
}'


Response:

{
    "payment_id": "pay_4pBcxTpzeuUjQ1KhAYp1",
    "merchant_id": "merchant_1747656283",
    "status": "failed",
    "amount": 651200,
    "net_amount": 651200,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "fiserv",
    "client_secret": "pay_4pBcxTpzeuUjQ1KhAYp1_secret_LXgKvPls3RGIFfuZ2Z20",
    "created": "2025-05-19T19:37:53.640Z",
    "currency": "USD",
    "customer_id": "First_Customer",
    "customer": {
        "id": "First_Customer",
        "name": "John Doe",
        "email": null,
        "phone": null,
        "phone_country_code": null
    },
    "description": null,
    "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": "0051",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "523929",
            "card_extended_bin": null,
            "card_exp_month": "12",
            "card_exp_year": "27",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": null,
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": "[email protected]"
    },
    "order_details": null,
    "email": null,
    "name": "John Doe",
    "phone": null,
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": "104",
    "error_message": "Unable to assign card to brand: Invalid",
    "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": "First_Customer",
        "created_at": 1747683473,
        "expires": 1747687073,
        "secret": "epk_6982147f9dd1478c931d8d06fb24d276"
    },
    "manual_retry_allowed": true,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_kg6n3seduV6leLIAxPwL",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_EZ1kF48ElbkcXo52Eu0z",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-05-19T19:52:53.639Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-05-19T19:37:56.015Z",
    "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": "104",
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null
}

The issuer_error_code is present now

For Integrity Checks

Case 1: Automatic Capture

cURL:

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_8BoyoSPgaCnEZ7hPrLeVLSyEpsxM1d95WnoRRlgyLJfhUD3Rt9vaYiErFlhICRJy' \
--header 'Cookie: PHPSESSID=0b47db9d7de94c37b6b272087a9f2fa7' \
--data-raw '{
    "amount": 651200,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "First_Customer",
    "name": "John Doe",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            
            "card_number": "4147463011110083",
            "card_exp_month": "12",
            "card_exp_year": "27",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": "[email protected]"
    }
}'

Response:

{
    "error": {
        "type": "api",
        "message": "Integrity Check Failed! as data mismatched for amount expected 651200 but found 651300",
        "code": "IE_00",
        "connector_transaction_id": "CHG0165dcaf19383fef3b997c70f17e33f882"
    }
}

We hardcoded the amount at the connector level to a value greater than the one sent in the request. This is verified at response time, causing a discrepancy between the amount passed in the request and the amount passed to the connector, which triggers the integrity check.

  1. Manual Capture:

cURL :

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_8BoyoSPgaCnEZ7hPrLeVLSyEpsxM1d95WnoRRlgyLJfhUD3Rt9vaYiErFlhICRJy' \
--header 'Cookie: PHPSESSID=0b47db9d7de94c37b6b272087a9f2fa7' \
--data-raw '{
    "amount": 651200,
    "currency": "USD",
    "confirm": true,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "First_Customer",
    "name": "John Doe",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            
            "card_number": "4147463011110083",
            "card_exp_month": "12",
            "card_exp_year": "27",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": "[email protected]"
    }
}'


Response:

{
    "error": {
        "type": "api",
        "message": "Integrity Check Failed! as data mismatched for amount expected 651200 but found 651300",
        "code": "IE_00",
        "connector_transaction_id": "CHG01e6b342a643cf24269162a523ae884b49"
    }
}

Reason: We hardcoded the amount in the code to an amount which is more than the one being sent in the connector request.

  1. Refund

First, do a payments create( a successful one) and donot hardcode anything.

cURL :

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_8BoyoSPgaCnEZ7hPrLeVLSyEpsxM1d95WnoRRlgyLJfhUD3Rt9vaYiErFlhICRJy' \
--header 'Cookie: PHPSESSID=0b47db9d7de94c37b6b272087a9f2fa7' \
--data-raw '{
    "amount": 651200,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "First_Customer",
    "name": "John Doe",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            
            "card_number": "4147463011110083",
            "card_exp_month": "12",
            "card_exp_year": "27",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": "[email protected]"
    }
}'


Response of Payments - Create :

{
    "payment_id": "pay_9YN7bLpjbmbFkA1laXqC",
    "merchant_id": "merchant_1747656283",
    "status": "succeeded",
    "amount": 651200,
    "net_amount": 651200,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 651200,
    "connector": "fiserv",
    "client_secret": "pay_9YN7bLpjbmbFkA1laXqC_secret_onbFfEy4CWh2pd3jAdTt",
    "created": "2025-05-19T20:59:12.836Z",
    "currency": "USD",
    "customer_id": "First_Customer",
    "customer": {
        "id": "First_Customer",
        "name": "John Doe",
        "email": null,
        "phone": null,
        "phone_country_code": null
    },
    "description": null,
    "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": "0083",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "414746",
            "card_extended_bin": null,
            "card_exp_month": "12",
            "card_exp_year": "27",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": null,
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": "[email protected]"
    },
    "order_details": null,
    "email": null,
    "name": "John Doe",
    "phone": null,
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "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": {
        "customer_id": "First_Customer",
        "created_at": 1747688352,
        "expires": 1747691952,
        "secret": "epk_f6e6cc342a9147eb91b4d8144bc28958"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "964bbc9b9e494be8bf575977cf2582b5",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "CHG01012441523bde085d4ec1a37b6c72c768",
    "payment_link": null,
    "profile_id": "pro_kg6n3seduV6leLIAxPwL",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_EZ1kF48ElbkcXo52Eu0z",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-05-19T21:14:12.836Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-05-19T20:59:14.534Z",
    "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": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null
}

Now attempt a Refund with this payment_id. In code I have hardcoded the refund amount same as the captured amount but in the request will be passing an amount which will be less than that.

Refunds - Create cURL:

curl --location 'http://localhost:8080/refunds' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_8BoyoSPgaCnEZ7hPrLeVLSyEpsxM1d95WnoRRlgyLJfhUD3Rt9vaYiErFlhICRJy' \
--header 'Cookie: PHPSESSID=0b47db9d7de94c37b6b272087a9f2fa7' \
--data '{
    "payment_id": "pay_kshnzcowY4iP7ZhiBArY",
    "amount": 651100,
    "reason": "Customer returned product",
    "refund_type": "instant",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'

Response :

{
    "refund_id": "ref_qvFT2TJmnx0irSvV5MTX",
    "payment_id": "pay_kshnzcowY4iP7ZhiBArY",
    "amount": 651100,
    "currency": "USD",
    "status": "review",
    "reason": "Customer returned product",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "error_message": "Integrity Check Failed! as data mismatched for fields refund_amount expected 651100 but found 651200",
    "error_code": "IE",
    "unified_code": null,
    "unified_message": null,
    "created_at": "2025-05-19T21:12:21.095Z",
    "updated_at": "2025-05-19T21:12:22.577Z",
    "connector": "fiserv",
    "profile_id": "pro_kg6n3seduV6leLIAxPwL",
    "merchant_connector_id": "mca_EZ1kF48ElbkcXo52Eu0z",
    "split_refunds": null,
    "issuer_error_code": null,
    "issuer_error_message": null
}

Checklist

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

bsayak03 avatar May 20 '25 04:05 bsayak03

Review changes with  SemanticDiff

Changed Files
File Status
  crates/hyperswitch_connectors/src/connectors/fiserv.rs  21% smaller
  crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs  8% smaller

semanticdiff-com[bot] avatar May 20 '25 04:05 semanticdiff-com[bot]