hyperswitch icon indicating copy to clipboard operation
hyperswitch copied to clipboard

feat(feature): [Zift] Add External 3DS Cards Support

Open awasthi21 opened this issue 1 month ago • 2 comments

Type of Change

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

Description

This PR adds full External 3DS (3-D Secure) card authorization support for the Zift connector. Zift previously supported only non-3DS card payments. With this change, Hyperswitch can now process card payments where the 3DS authentication is completed externally and pass the authentication results to Zift in the required fields.

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?

Request

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_ezb5EyVtVuEWTCN06ITrtavSsmN7OPEeNt1Yv3IGGyIC2CobCE4IuTMz4sFxSSyv' \
--data-raw '{
    "amount": 700,
    "currency": "AUD",
    "confirm": true,
    "capture_method": "automatic",
    "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": "three_ds",
    "return_url": "https://google.com",
    "request_external_three_ds_authentication": true,
    
    "payment_method": "card",
    "payment_method_type": "credit",
    "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": "128.0.0.1"
    },
    "payment_method_data": {
        "card": {
            "card_number": "4111111111111111",
            "card_exp_month": "01",
            "card_exp_year": "50",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    } 
}


'

Response

{
    "payment_id": "pay_aYXEWXab3041kk2eXLrC",
    "merchant_id": "postman_merchant_GHAction_c3226401-d6e7-4d1a-b424-9aad6b12aa35",
    "status": "requires_customer_action",
    "amount": 700,
    "net_amount": 700,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "zift",
    "client_secret": "pay_aYXEWXab3041kk2eXLrC_secret_KQghyBGVSM6zNXaMX6Zi",
    "created": "2025-11-14T12:39:53.505Z",
    "currency": "AUD",
    "customer_id": null,
    "customer": {
        "id": null,
        "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": null,
    "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": "01",
            "card_exp_year": "50",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_jbLuqXqpXLCdDo6zLWor",
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe",
            "origin_zip": null
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe",
            "origin_zip": null
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": null,
    "name": null,
    "phone": null,
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": {
        "type": "three_ds_invoke",
        "three_ds_data": {
            "three_ds_authentication_url": "http://localhost:8080/payments/pay_aYXEWXab3041kk2eXLrC/3ds/authentication",
            "three_ds_authorize_url": "http://localhost:8080/payments/pay_aYXEWXab3041kk2eXLrC/postman_merchant_GHAction_c3226401-d6e7-4d1a-b424-9aad6b12aa35/authorize/zift",
            "three_ds_method_details": {
                "three_ds_method_data_submission": false,
                "three_ds_method_data": null,
                "three_ds_method_url": null,
                "three_ds_method_key": null,
                "consume_post_message_for_three_ds_method_completion": false
            },
            "poll_config": {
                "poll_id": "external_authentication_pay_aYXEWXab3041kk2eXLrC",
                "delay_in_secs": 2,
                "frequency": 5
            },
            "message_version": "2.2.0",
            "directory_server_id": 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": null,
    "manual_retry_allowed": null,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_yG1Oay8kkiDc1IsCmHQH",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_RZczFjrLrapw3U6qX2gQ",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": {
        "authentication_flow": null,
        "electronic_commerce_indicator": null,
        "status": "pending",
        "ds_transaction_id": "22564fe8-147c-40be-bba6-565286c45dcf",
        "version": "2.2.0",
        "error_code": null,
        "error_message": null
    },
    "external_3ds_authentication_attempted": true,
    "expires_on": "2025-11-14T12:54:53.505Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "128.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_channel": null,
    "payment_method_id": null,
    "network_transaction_id": null,
    "payment_method_status": null,
    "updated": "2025-11-14T12:39:54.611Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "extended_authorization_last_applied_at": null,
    "request_extended_authorization": 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,
    "enable_partial_authorization": null,
    "enable_overcapture": null,
    "is_overcapture_enabled": null,
    "network_details": null,
    "is_stored_credential": null,
    "mit_category": null,
    "billing_descriptor": null
}

Request

curl --location 'https://3507d4a847fb.ngrok-free.app/payments/pay_aYXEWXab3041kk2eXLrC/3ds/authentication' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_e72dcf59e31042f291936a9a704618ad' \
--data '{
    "client_secret": "pay_aYXEWXab3041kk2eXLrC_secret_KQghyBGVSM6zNXaMX6Zi",
    "device_channel": "BRW",
    "threeds_method_comp_ind": "N"
}'

Response

{
    "trans_status": "C",
    "acs_url": "https://mock-three-ds-server-small-lake-1937.fly.dev/processor/mock/acs/trigger-otp?redirectUrl=http%3A%2F%2Flocalhost%3A8080%2Fpayments%2Fpay_nOATdFTAfMlqrpUWsrmb%2Fpostman_merchant_GHAction_c3226401-d6e7-4d1a-b424-9aad6b12aa35%2Fauthorize%2Fcheckout",
    "challenge_request": "eyJtZXNzYWdlVHlwZSI6IkNSZXEiLCJ0aHJlZURTU2VydmVyVHJhbnNJRCI6IjZlNmQ3N2IzLWFhZDUtNDQ5YS05ZWNlLTM5OGVjNGNiYWRkNyIsImFjc1RyYW5zSUQiOiIxYWQ1Yzg4Ny1mYTU0LTQ1N2YtYWE0NS02ODEyNDQ4YjhhNTYiLCJjaGFsbGVuZ2VXaW5kb3dTaXplIjoiMDEiLCJtZXNzYWdlVmVyc2lvbiI6IjIuMi4wIn0=",
    "challenge_request_key": "creq",
    "acs_reference_number": "1ad5c887-fa54-457f-aa45-6812448b8a56",
    "acs_trans_id": "1ad5c887-fa54-457f-aa45-6812448b8a56",
    "three_dsserver_trans_id": "6e6d77b3-aad5-449a-9ece-398ec4cbadd7",
    "acs_signed_content": null,
    "three_ds_requestor_url": "https://google.com",
    "three_ds_requestor_app_url": null
}

Request

curl --location 'https://mock-three-ds-server-small-lake-1937.fly.dev/processor/mock/acs/trigger-otp?redirectUrl=http%3A%2F%2Flocalhost%3A8080%2Fpayments%2Fpay_nOATdFTAfMlqrpUWsrmb%2Fpostman_merchant_GHAction_c3226401-d6e7-4d1a-b424-9aad6b12aa35%2Fauthorize%2Fcheckout' \
--header 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
--header 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'cache-control: no-cache' \
--header 'content-type: application/x-www-form-urlencoded' \
--header 'origin: https://juspay-3ds-sdk.netlify.app' \
--header 'pragma: no-cache' \
--header 'priority: u=0, i' \
--header 'referer: https://juspay-3ds-sdk.netlify.app/' \
--header 'sec-ch-ua: "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--header 'sec-fetch-dest: iframe' \
--header 'sec-fetch-mode: navigate' \
--header 'sec-fetch-site: cross-site' \
--header 'sec-fetch-storage-access: active' \
--header 'upgrade-insecure-requests: 1' \
--header 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' \
--header 'api-key: dev_ezb5EyVtVuEWTCN06ITrtavSsmN7OPEeNt1Yv3IGGyIC2CobCE4IuTMz4sFxSSyv' \
--data-urlencode 'creq=eyJtZXNzYWdlVHlwZSI6IkNSZXEiLCJ0aHJlZURTU2VydmVyVHJhbnNJRCI6IjZlNmQ3N2IzLWFhZDUtNDQ5YS05ZWNlLTM5OGVjNGNiYWRkNyIsImFjc1RyYW5zSUQiOiIxYWQ1Yzg4Ny1mYTU0LTQ1N2YtYWE0NS02ODEyNDQ4YjhhNTYiLCJjaGFsbGVuZ2VXaW5kb3dTaXplIjoiMDEiLCJtZXNzYWdlVmVyc2lvbiI6IjIuMi4wIn0='

Response Screenshot 2025-11-14 at 6 14 15 PM

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

awasthi21 avatar Nov 14 '25 12:11 awasthi21

Review changes with  SemanticDiff

Changed Files
File Status
  crates/hyperswitch_domain_models/src/router_request_types.rs  62% smaller
  crates/router/src/core/payments/helpers.rs  59% smaller
  crates/common_enums/src/connector_enums.rs  54% smaller
  crates/hyperswitch_connectors/src/connectors/payload/responses.rs  48% smaller
  crates/router/src/core/payments/transformers.rs  38% smaller
  crates/router/src/core/payments/operations/payment_create.rs  35% smaller
  crates/hyperswitch_connectors/src/connectors/peachpayments.rs  31% smaller
  crates/hyperswitch_connectors/src/connectors/payload.rs  30% smaller
  crates/hyperswitch_connectors/src/connectors/tesouro/transformers.rs  30% smaller
  crates/hyperswitch_connectors/src/connectors/zift/transformers.rs  27% smaller
  crates/euclid_wasm/src/lib.rs  24% smaller
  crates/hyperswitch_connectors/src/connectors/tesouro.rs  23% smaller
  crates/api_models/src/payment_methods.rs  12% smaller
  crates/router/src/core/payments.rs  9% smaller
  crates/router/src/core/payments/routing.rs  7% smaller
  crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs  6% smaller
  crates/openapi/src/openapi.rs  5% smaller
  crates/openapi/src/openapi_v2.rs  3% smaller
  crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs  2% smaller
  crates/api_models/src/payments.rs  2% smaller
  crates/hyperswitch_connectors/src/connectors/payload/transformers.rs  1% smaller
  CHANGELOG.md Unsupported file format
  api-reference/v1/openapi_spec_v1.json  0% smaller
  api-reference/v2/openapi_spec_v2.json  0% smaller
  config/deployments/integration_test.toml Unsupported file format
  config/deployments/production.toml Unsupported file format
  config/deployments/sandbox.toml Unsupported file format
  config/development.toml Unsupported file format
  crates/api_models/src/payments/additional_info.rs  0% smaller
  crates/api_models/src/routing.rs  0% smaller
  crates/common_enums/src/enums.rs  0% smaller
  crates/common_types/src/payments.rs  0% smaller
  crates/euclid/benches/backends.rs  0% smaller
  crates/euclid/src/backend/inputs.rs  0% smaller
  crates/euclid/src/backend/vir_interpreter.rs  0% smaller
  crates/euclid/src/backend/vir_interpreter/types.rs  0% smaller
  crates/euclid/src/dssa/graph.rs  0% smaller
  crates/euclid/src/frontend/ast/lowering.rs  0% smaller
  crates/euclid/src/frontend/dir.rs  0% smaller
  crates/euclid/src/frontend/dir/lowering.rs  0% smaller
  crates/euclid/src/types.rs  0% smaller
  crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs  0% smaller
  crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs  0% smaller
  crates/hyperswitch_connectors/src/connectors/zift.rs  0% smaller
  crates/hyperswitch_connectors/src/utils.rs  0% smaller
  crates/hyperswitch_domain_models/src/payment_method_data.rs  0% smaller
  crates/hyperswitch_domain_models/src/router_request_types/unified_authentication_service.rs  0% smaller
  crates/router/src/core/blocklist/utils.rs  0% smaller
  crates/router/src/core/payments/operations/payment_approve.rs  0% smaller
  crates/router/src/core/payments/operations/payment_cancel.rs  0% smaller
  crates/router/src/core/payments/operations/payment_cancel_post_capture.rs  0% smaller
  crates/router/src/core/payments/operations/payment_capture.rs  0% smaller
  crates/router/src/core/payments/operations/payment_complete_authorize.rs  0% smaller
  crates/router/src/core/payments/operations/payment_confirm.rs  0% smaller
  crates/router/src/core/payments/operations/payment_post_session_tokens.rs  0% smaller
  crates/router/src/core/payments/operations/payment_reject.rs  0% smaller
  crates/router/src/core/payments/operations/payment_session.rs  0% smaller
  crates/router/src/core/payments/operations/payment_start.rs  0% smaller
  crates/router/src/core/payments/operations/payment_status.rs  0% smaller
  crates/router/src/core/payments/operations/payment_update.rs  0% smaller
  crates/router/src/core/payments/operations/payment_update_metadata.rs  0% smaller
  crates/router/src/core/payments/operations/payments_extend_authorization.rs  0% smaller
  crates/router/src/core/payments/operations/payments_incremental_authorization.rs  0% smaller
  crates/router/src/core/payments/operations/tax_calculation.rs  0% smaller
  crates/router/src/core/payments/types.rs  0% smaller
  crates/router/src/core/three_ds_decision_rule/utils.rs  0% smaller
  crates/router/src/core/unified_authentication_service.rs  0% smaller
  crates/router/src/core/unified_authentication_service/types.rs  0% smaller
  crates/router/src/services/api.rs  0% smaller
  crates/router/tests/connectors/aci.rs  0% smaller
  crates/router/tests/payments.rs  0% smaller
  crates/router/tests/payments2.rs  0% smaller
  cypress-tests/cypress/e2e/configs/Payment/Peachpayments.js  0% smaller

semanticdiff-com[bot] avatar Nov 14 '25 12:11 semanticdiff-com[bot]

Codecov Report

:x: Patch coverage is 0% with 67 lines in your changes missing coverage. Please review. :warning: Please upload report for BASE (main@11f08ea). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...tch_connectors/src/connectors/zift/transformers.rs 0.00% 64 Missing :warning:
crates/common_enums/src/connector_enums.rs 0.00% 1 Missing :warning:
...ates/hyperswitch_connectors/src/connectors/zift.rs 0.00% 1 Missing :warning:
crates/router/src/core/payments/types.rs 0.00% 1 Missing :warning:
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #10277   +/-   ##
=======================================
  Coverage        ?    6.50%           
=======================================
  Files           ?     1232           
  Lines           ?   307886           
  Branches        ?        0           
=======================================
  Hits            ?    20041           
  Misses          ?   287845           
  Partials        ?        0           

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

codecov[bot] avatar Nov 14 '25 12:11 codecov[bot]