Skip to content

feat(router): introduce feature and feature_data to gsm #7771

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

Merged
merged 35 commits into from
Jul 31, 2025

Conversation

ShankarSinghC
Copy link
Contributor

@ShankarSinghC ShankarSinghC commented Apr 9, 2025

Type of Change

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

Description

This pull request includes a number of changes to the gsm module across multiple files to introduce new features and improve the existing code structure. The most important changes include the addition of new fields and enums, refactoring of existing enums, and introducing domain models for gsm.

Changes to gsm module:

  • crates/api_models/src/gsm.rs:

    • Added new fields alternate_network_possible and feature to GsmCreateRequest, GsmUpdateRequest, and GsmResponse structs.
    • step_up_possible and clear_pan_possible are made as optional in api contract these are retry feature specific data
      and might not be present for other features.
  • crates/common_enums/src/enums.rs:

    • Added new enumGsmFeature to handle GSM decision and feature data.
    • Remove Requeue as feature us not yet implemented

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?

Create gsm rule without passing feature and feature data

curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "adyenplatform",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "retry",
  "step_up_possible": true,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true,
  "alternate_network_possible": false
}'
{
    "connector": "adyenplatform",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "retry",
    "step_up_possible": true,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": true,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": true,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "retry"
        }
    }
}
curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "stripe",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "do_default",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true
}'
curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "stripe",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "do_default",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true
}'

Db entry
image

Update rule "clear_pan_possible": false

curl --location 'http://localhost:8080/gsm/update' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "stripe",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "do_default",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": false
}'
{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": false,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": false,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}

Test case of existing records

(manually create db entries like the existing ones and try to update it)
db entry showing feature and feature_data to be null
image
-> update call will "clear_pan_possible": true

curl --location 'http://localhost:8080/gsm/update' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "novalnet",
  "flow": "Authorize",
  "sub_flow": "sub_flow",
  "code": "525002",
  "message": "Personal Identity Card Number not correct",
  "status": "Failure",
  "router_error": "Internal Server Error",
  "decision": "retry",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true
}'
{
    "connector": "novalnet",
    "flow": "Authorize",
    "sub_flow": "sub_flow",
    "code": "525002",
    "message": "Personal Identity Card Number not correct",
    "status": "Failure",
    "router_error": "Internal Server Error",
    "decision": "retry",
    "step_up_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": true,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "retry"
        }
    }
}

DB entry after update
image

Test case for the new change where in which we pass feature and feature data in the request

curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": true,
    "clear_pan_possible": true,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": false,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}'
{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": false,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": false,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}
image We can see that step_up_possible and clear_pan_possible has been passed as true in the root level whereas it has been passed as false in the feature_data. And in the application response clear_pan_possible and step_up_possible is false in both the places as the values in the feature_data takes precedence.

Note :-
Same goes for db state and update gsm call

-> Update call

curl --location 'http://localhost:8080/gsm/update' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": false,
    "clear_pan_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": true,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}'
{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": true,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": true,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": true,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}
image

-> Test a retry payment

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-feature: router-custom' \
--header 'x-tenant-id: hyperswitch' \
--header 'api-key: dev_NZClQhIklvWYnqEzfkfdzGOynCohMCTnkbYrPN0HPkQbm6rTnngnthX51kLWDIn2' \
--data-raw '{
    "amount": 1000000,
    "amount_to_capture": 1000000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "setup_future_usage": "on_session",
    "capture_on": "2022-09-10T10:11:12Z",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "customer_id": "cu_1753886032",
    "return_url": "http://127.0.0.1:4040",
    "payment_method": "card",
    "payment_method_type": "debit",
    "payment_method_data": {
        "card": {
            "card_number": "4111111111111111",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "email": "[email protected]"
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "browser_info": {
        "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",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
{
    "payment_id": "pay_cLWcRqZDJcOaiN3oyxJh",
    "merchant_id": "merchant_1753885328",
    "status": "succeeded",
    "amount": 1000000,
    "net_amount": 1000000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 1000000,
    "connector": "cybersource",
    "client_secret": "pay_cLWcRqZDJcOaiN3oyxJh_secret_CCZupjoT0zDE6m4s1OCu",
    "created": "2025-07-30T14:32:41.449Z",
    "currency": "USD",
    "customer_id": "cu_1753885961",
    "customer": {
        "id": "cu_1753885961",
        "name": "John Doe",
        "email": "[email protected]",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "1111",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "411111",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": {
                "avs_response": {
                    "code": "X",
                    "codeRaw": "I1"
                },
                "card_verification": null
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_eWHrgeRsB1CJo79hTJVt",
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "phone": null,
        "email": "[email protected]"
    },
    "order_details": null,
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "http://127.0.0.1:4040/",
    "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": "debit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1753885961",
        "created_at": 1753885961,
        "expires": 1753889561,
        "secret": "epk_fa14f011aa934fb29ae59ecdcf6cdd86"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7538859630876495904805",
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_cLWcRqZDJcOaiN3oyxJh_2",
    "payment_link": null,
    "profile_id": "pro_OMlZtENVSWshV7Mh9NPv",
    "surcharge_details": null,
    "attempt_count": 2,
    "merchant_decision": null,
    "merchant_connector_id": "mca_jAKVTh39vFjuD4LN7ZuX",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-07-30T14:47:41.449Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.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_method_id": null,
    "payment_method_status": null,
    "updated": "2025-07-30T14:32:43.450Z",
    "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,
    "whole_connector_response": null
}

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

@ShankarSinghC ShankarSinghC added C-feature Category: Feature request or enhancement M-database-changes Metadata: This PR involves database schema changes A-payments Area: payments labels Apr 9, 2025
@ShankarSinghC ShankarSinghC self-assigned this Apr 9, 2025
@ShankarSinghC ShankarSinghC requested review from a team as code owners April 9, 2025 05:17
Copy link

semanticdiff-com bot commented Apr 9, 2025

@ShankarSinghC ShankarSinghC changed the title Gsm/introduce domain models feat(router): introduce feature and feature_data to gsm Apr 9, 2025
@ShankarSinghC ShankarSinghC changed the title feat(router): introduce feature and feature_data to gsm feat(router): introduce feature and feature_data to gsm Apr 9, 2025
@ShankarSinghC ShankarSinghC linked an issue Apr 10, 2025 that may be closed by this pull request
jarnura
jarnura previously approved these changes Jul 28, 2025
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Jul 30, 2025
@Gnanasundari24 Gnanasundari24 enabled auto-merge July 31, 2025 09:20
@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Jul 31, 2025
Merged via the queue into main with commit 1fa20a9 Jul 31, 2025
16 of 20 checks passed
@Gnanasundari24 Gnanasundari24 deleted the gsm/introduce-domain-models branch July 31, 2025 10:59
pixincreate added a commit that referenced this pull request Aug 1, 2025
…rver

* 'main' of github.com:juspay/hyperswitch: (25 commits)
  chore: `xof` currency to cybersource cards (#8799)
  chore(version): 2025.08.01.0
  feat(core): Implement UCS based  upi for  paytm and phonepe (#8732)
  feat(connector): [katapult]add template code for katapult (#8783)
  feat(router): introduce `feature`  and `feature_data` to gsm (#7771)
  feat(connector): [cybersource] add changes for field CybersourceConsumerAuthInformation (#8768)
  feat(authentication): added authentication sync api (#8596)
  feat(connector): [facilitapay] fix refunds, add webhook and void support (#8778)
  feat(connector): [payload] add recurring payments (#8597)
  chore(version): 2025.07.31.0
  feat(connector): [Flexiti]Add support for flexiti connector  (#8743)
  chore(router): events enhancement for kafka (#8780)
  ci(cypress): Making a mandate payment with large customer user agents (#8790)
  fix(openapi): update create_platform endpoint in api-reference docs (#8782)
  chore(version): 2025.07.30.0
  fix(connector): [GLOBALPAY] Added Tokenization Flow for CITs (#8568)
  feat(routing): Add api-refs for new decision engine endpoints (#8709)
  fix: replace xtrim with xdel to support  older redis version (#8515)
  fix(connector): [Worldpay] handle multiple ddc submission for CompleteAuthorize (#8741)
  feat(connector): [Adyen] receive incoming webhooks for pix expiry (#8720)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-payments Area: payments C-feature Category: Feature request or enhancement M-api-contract-changes Metadata: This PR involves API contract changes M-database-changes Metadata: This PR involves database schema changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

refactor(gsm): introduce feature and feature_data to gsm
4 participants