Skip to content

feat(payments): implement routing in payments v2#7709

Merged
likhinbopanna merged 27 commits intomainfrom
payments-routing-v2
Jun 3, 2025
Merged

feat(payments): implement routing in payments v2#7709
likhinbopanna merged 27 commits intomainfrom
payments-routing-v2

Conversation

@hrithikesh026
Copy link
Contributor

@hrithikesh026 hrithikesh026 commented Apr 3, 2025

Type of Change

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

Description

Implemented rule based routing in payments v2.
Straight through routing in v2 will be achieved by first configuring a straight through algorithm using routing endpoint and then passing the routing_algorithm_id field in payment create intent request.

  • Added validation for routing_algorithm_id in payment_inten_create and payment_intent_update.

Three diff types of routing

  • Straight through - routing id sent in request
  • routing id activated in profile
  • Default fallback - in the order the MCA's are created.

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?

Please use this log for checking the final list of connectors decided by routing module - final_selected_connectors_for_routing

  • Straight through routing -
    Create a routing algo entry, with rule and use that routing id in payment intent create
    create routing algorithm -
Screenshot 2025-05-27 at 2 42 36 PM request -
curl --location 'http://localhost:8080/v2/routing-algorithm' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'X-Profile-Id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data '{
    "name": "newalgo",
    "description": "It is my ADVANCED config",
    "algorithm": {
        "type": "advanced",
        "data": {
            "defaultSelection": {
                "type": "priority",
                "data": [
                   {"connector": "bluesnap",
                    "merchant_connector_id":"mca_xMJUJRcvk333cIPrJtdZ"}
                ]
            },

            "rules": [
                {
                    "name": "bluesnap",
                    "connectorSelection": {
                        "type": "priority",
                        "data": [
                       { "connector": "bluesnap",
                    "merchant_connector_id":"mca_xMJUJRcvk333cIPrJtdZ"},
                    {"connector": "adyen",
                    "merchant_connector_id":"mca_IpsaFXCRJa2TNYWuVkNx"},
                    {
                        "connector": "stripe",
                    "merchant_connector_id":"mca_DfyZDqHCZErTASCW170f"
                    }

                        ]
                    },
                    "statements": [
                        {
                            "condition": [
                                {
                                    "lhs": "amount",
                                    "comparison": "greater_than",
                                    "value": {
                                        "type": "number",
                                        "value": 100077
                                    },
                                    "metadata": {}
                                }
                            ],
                            "nested": null
                        }
                    ]
                }
            ],
            "metadata": {}
        }
    },
    "profile_id":"pro_kQqIhnCrULOPt4tmuP5v"
}'

Response -

{
    "id": "routing_bFY4UYfNYLR0pQozeoPX",
    "profile_id": "pro_kQqIhnCrULOPt4tmuP5v",
    "name": "newalgo",
    "kind": "advanced",
    "description": "It is my ADVANCED config",
    "created_at": 1748843132,
    "modified_at": 1748843132,
    "algorithm_for": "payment",
    "decision_engine_routing_id": null
}

Payment intent create -

curl --location 'http://localhost:8080/v2/payments/create-intent' \
--header 'Content-Type: application/json' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data-raw '{
    "amount_details": {
        "order_amount": 100,
        "currency": "USD"
    },
    "capture_method":"manual",
    "authentication_type": "no_three_ds",
    "billing": {
        "address": {
            "first_name": "John",
            "last_name": "Dough"
        },
        "email": "example@example.com"
    },
    "shipping": {
        "address": {
            "first_name": "John",
            "last_name": "Dough",
            "city": "Karwar",
            "zip": "581301",
            "state": "Karnataka"
        },
        "email": "example@example.com"
    },
    "routing_algorithm_id":"routing_bFY4UYfNYLR0pQozeoPX"
}'

Confirm intent -

curl --location 'http://localhost:8080/v2/payments/12345_pay_01972f2d4b007c119e0373142eac05d3/confirm-intent' \
--header 'x-client-secret: cs_01972f2d4b1a7fa091ec990921d3bea7' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'Content-Type: application/json' \
--header 'api-key: pk_dev_47a62e65c5124ddab58d82c3bb4d0072' \
--data-raw '{
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "01",
            "card_exp_year": "25",
            "card_holder_name": "John Doe",
            "card_cvc": "100"
        },
        "billing": {
            "address": {
                "first_name": "John",
                "last_name": "Dough"
            },
            "email": "example@example.com"
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "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": "125.0.0.1"
    }
}'

final list of connectors decided by routing module
Screenshot 2025-06-02 at 11 17 12 AM
payment happened with Bluesnap

Rule based routing activation at profile level -
Creating new routing algo , with adyen as priority.
Screenshot 2025-06-02 at 11 20 29 AM

Activate the routing algorithm id
req -

curl --location --request PATCH 'http://localhost:8080/v2/profiles/pro_kQqIhnCrULOPt4tmuP5v/activate-routing-algorithm' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'X-Profile-Id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data '{
    "routing_algorithm_id": "routing_VdGQBkci7Wt5eoFRMJ6L"
}'

response -

{
    "id": "routing_VdGQBkci7Wt5eoFRMJ6L",
    "profile_id": "pro_kQqIhnCrULOPt4tmuP5v",
    "name": "newalgo",
    "kind": "advanced",
    "description": "It is my ADVANCED config",
    "created_at": 1748843407,
    "modified_at": 1748843407,
    "algorithm_for": "payment",
    "decision_engine_routing_id": null
}

verify from profile entry, the routing algo id will be populated
Screenshot 2025-06-02 at 11 22 11 AM

Create intent without passing routing id at all in the request

Screenshot 2025-06-02 at 11 23 47 AM

Final list of connectors -
Screenshot 2025-06-02 at 11 24 27 AM

Confirm intent
Screenshot 2025-06-02 at 11 24 10 AM

Default Fallback -
Verify in profile
Screenshot 2025-06-02 at 11 10 08 AM
Screenshot 2025-06-02 at 11 11 19 AM

Payment Intent create -

curl --location 'http://localhost:8080/v2/payments/create-intent' \
--header 'Content-Type: application/json' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data-raw '{
    "amount_details": {
        "order_amount": 100,
        "currency": "USD"
    },
    "capture_method":"manual",
    "authentication_type": "no_three_ds",
    "billing": {
        "address": {
            "first_name": "John",
            "last_name": "Dough"
        },
        "email": "example@example.com"
    },
    "shipping": {
        "address": {
            "first_name": "John",
            "last_name": "Dough",
            "city": "Karwar",
            "zip": "581301",
            "state": "Karnataka"
        },
        "email": "example@example.com"
    }
    
}'

use the client secret to confirm the intent
Confirm request -

curl --location 'http://localhost:8080/v2/payments/12345_pay_01972f2414617513a2b2387237f61eb9/confirm-intent' \
--header 'x-client-secret: cs_01972f2414cb7720be7f596e088f9bfd' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'Content-Type: application/json' \
--header 'api-key: pk_dev_47a62e65c5124ddab58d82c3bb4d0072' \
--data-raw '{
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "01",
            "card_exp_year": "25",
            "card_holder_name": "John Doe",
            "card_cvc": "100"
        },
        "billing": {
            "address": {
                "first_name": "John",
                "last_name": "Dough"
            },
            "email": "example@example.com"
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "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": "125.0.0.1"
    }
}'

Confirm Intent Response -
here the payment went through stripe -

{
    "id": "12345_pay_01972f2414617513a2b2387237f61eb9",
    "status": "failed",
    "amount": {
        "order_amount": 100,
        "currency": "USD",
        "shipping_cost": null,
        "order_tax_amount": null,
        "external_tax_calculation": "skip",
        "surcharge_calculation": "skip",
        "surcharge_amount": null,
        "tax_on_surcharge": null,
        "net_amount": 100,
        "amount_to_capture": null,
        "amount_capturable": 0,
        "amount_captured": null
    },
    "customer_id": null,
    "connector": "stripe",
    "created": "2025-06-02T05:36:23.178Z",
    "payment_method_data": {
        "billing": {
            "address": {
                "city": null,
                "country": null,
                "line1": null,
                "line2": null,
                "line3": null,
                "zip": null,
                "state": null,
                "first_name": "John",
                "last_name": "Dough"
            },
            "phone": null,
            "email": "example@example.com"
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "credit",
    "connector_transaction_id": null,
    "connector_reference_id": null,
    "merchant_connector_id": "mca_DfyZDqHCZErTASCW170f",
    "browser_info": null,
    "error": {
        "code": "No error code",
        "message": "No error message",
        "unified_code": null,
        "unified_message": null,
        "network_advice_code": null,
        "network_decline_code": null,
        "network_error_message": null
    },
    "shipping": null,
    "billing": null,
    "attempts": null,
    "connector_token_details": null,
    "payment_method_id": null,
    "next_action": null,
    "return_url": "https://google.com/success",
    "authentication_type": "no_three_ds",
    "authentication_type_applied": "no_three_ds",
    "is_iframe_redirection_enabled": 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

@hrithikesh026 hrithikesh026 self-assigned this Apr 3, 2025
@hrithikesh026 hrithikesh026 requested review from a team as code owners April 3, 2025 06:58
@semanticdiff-com
Copy link

semanticdiff-com bot commented Apr 3, 2025

@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Apr 3, 2025
@hrithikesh026 hrithikesh026 requested review from a team as code owners April 11, 2025 08:24
@hyperswitch-bot hyperswitch-bot bot added the M-database-changes Metadata: This PR involves database schema changes label Apr 11, 2025
@hrithikesh026 hrithikesh026 requested a review from a team as a code owner April 23, 2025 09:43
@hyperswitch-bot hyperswitch-bot bot removed the M-database-changes Metadata: This PR involves database schema changes label Apr 23, 2025
@hyperswitch-bot hyperswitch-bot bot removed the M-api-contract-changes Metadata: This PR involves API contract changes label Apr 23, 2025
@hrithikesh026 hrithikesh026 removed request for a team April 23, 2025 11:23
jarnura
jarnura previously approved these changes May 21, 2025
@hyperswitch-bot hyperswitch-bot bot added the M-database-changes Metadata: This PR involves database schema changes label May 22, 2025
Comment on lines +6688 to +6690
payment_data.set_connector_in_payment_attempt(routing_data.routed_through);

payment_data.set_merchant_connector_id_in_attempt(routing_data.merchant_connector_id);
Copy link
Contributor

Choose a reason for hiding this comment

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

        let fallback_config = admin::ProfileWrapper::new(business_profile.clone())
            .get_default_fallback_list_of_connector_under_profile()
            .change_context(errors::RoutingError::FallbackConfigFetchFailed)
            .change_context(errors::ApiErrorResponse::InternalServerError)?;

        let first_chosen_connector = fallback_config
            .first()
            .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?;

        let connector_name = first_chosen_connector.connector.to_string();
        let merchant_connector_id = first_chosen_connector
            .merchant_connector_id
            .clone()
            .get_required_value("merchant_connector_id")?;

in older implementation for set_connector_in_payment_attempt we were using the first_chosen_connector.connector and for set_merchant_connector_id_in_attempt we were using first_chosen_connector.merchant_connector_id. now the implementation is different it intended?

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, initially there was no routing in v2, so we have fetched default fallback from profile, picked the first connector.
this code is no more needed, as we are using routing core to decide connector

jagan-jaya
jagan-jaya previously approved these changes May 29, 2025
jagan-jaya
jagan-jaya previously approved these changes May 30, 2025
@prasunna09 prasunna09 linked an issue Jun 2, 2025 that may be closed by this pull request
@likhinbopanna likhinbopanna added this pull request to the merge queue Jun 3, 2025
Merged via the queue into main with commit 8871f31 Jun 3, 2025
23 of 31 checks passed
@likhinbopanna likhinbopanna deleted the payments-routing-v2 branch June 3, 2025 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-v2 M-database-changes Metadata: This PR involves database schema changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Introduce routing in V2

5 participants