Skip to content
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

feat(router): add routing support for token-based mit payments #4012

Merged
merged 10 commits into from
Mar 11, 2024

Conversation

vspecky
Copy link
Member

@vspecky vspecky commented Mar 7, 2024

Type of Change

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

Description

This PR adds routing support for token-based MIT payments. Basically, the merchant can have their customers set up their payment method for off session payments with Hyperswitch. Behind the scenes, Hyperswitch sets up an MIT/Mandate with the connector.

Now, when the merchant wants to do off-session payments using the customer's saved payment method by using a token to select the payment method, the backend needs to ensure that the payment goes through only those connectors for which that payment method is set up for MITs. This PR adds this check.

Additional Changes

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

Motivation and Context

The merchant may want to do off-session payments by passing a payment method token on behalf of their customers. This would require the smart router to choose an appropriate connector for the payment on the basis of whether the selected payment method is set up with the connector for off-session MIT payments.

How did you test it?

  1. Create a Merchant Account, and a Merchant Connector Account (preferably Cybersource)
  2. Create and Confirm a Setup Mandate request
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_OnB9g3l1v3cqPalObMlJ874jZerIsl9hlNCoTdXzNyiB2K7Edrp3wEQYmdmnXFJM' \
--data-raw '{
    "amount": 0,
    "currency": "USD",
    "confirm": true,
    "profile_id": "pro_9ci3SqA4bSAe7g3C7jCp",
    "capture_method": "automatic",
    "customer_id": "MyCustomer123456",
    "email": "guest@example.com",
    "setup_future_usage": "off_session",
    "customer_acceptance": {
        "acceptance_type": "online"
    },
    "payment_method": "card",
    "payment_method_type": "debit",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "Test Holder",
            "card_cvc": "737"
        }
    },
    "payment_type": "setup_mandate",
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "authentication_type": "no_three_ds"
}'

this will set the given card up for token-based MIT payments, and associate the connector MIT/Mandate details with the saved payment method in DB as shown below in the connector_mandate_details column
image

  1. Next, to make a token-based MIT payment, Create a payment with setup_future_usage = off_session
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_hOic0bCxCZdrp9ShPzyl9b80NQgr2m4vy4gBVyKbKbWNNRCiNBm9yCA5D7McybRs' \
--data-raw '{
    "amount": 499,
    "currency": "USD",
    "confirm": false,
    "profile_id": "pro_tsOrBWXnZfzYRJ9Jm7Dk",
    "capture_method": "automatic",
    "customer_id": "MyCustomer123456",
    "email": "guest@example.com",
    "setup_future_usage": "off_session",
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "authentication_type": "no_three_ds"
}'
  1. Do a list customer payment methods call and confirm the payment with the token (no need to pass card_cvc)
curl --location 'http://127.0.0.1:8080/payments/pay_5C8NWON1GUFGbMLR3tXx/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_hOic0bCxCZdrp9ShPzyl9b80NQgr2m4vy4gBVyKbKbWNNRCiNBm9yCA5D7McybRs' \
--data '{
    "payment_token": "token_ZTiG3Qz4ajaZB1MnBcmQ",
    "payment_method": "card"
}'

This is treated as a token-based MIT payment since we passed setup_future_usage = off_session in create, and are confirming the payment with the token. Routing will detect this, get the connector_mandate_details from the payment method object associated with the token, and do a recurring mandate payment using the connector mandate details. Additionally, routing will ensure that the connector will be picked for a token-based MIT payment if and only if it has some connector mandate details saved in the payment method object. Following is the connector request sent by the backend to cybersource.

{"processingInformation":{"actionList":null,"actionTokenTypes":null,"authorizationOptions":{"initiator":null,"merchantIntitiatedTransaction":{"reason":null,"originalAuthorizedAmount":"0.00"}},"commerceIndicator":"internet","capture":true,"captureOptions":null,"paymentSolution":null},"paymentInformation":{"paymentInstrument":{"id":"1323EB5C00917EC2E063A2598D0A69D7"}},"orderInformation":{"amountDetails":{"totalAmount":"2.00","currency":"USD"},"billTo":{"firstName":"One","lastName":"Two","address1":"here","locality":"bengaluru","administrativeArea":"Karnataka","postalCode":"560095","country":"IN","email":"guest@example.com"}},"clientReferenceInformation":{"code":"pay_iCiIdD8UqO3SdfAxGRrI_1"}}

We can see that paymentInstrument was set to the connector mandate ID by Routing, thus turning it into a recurring MIT payment.

  1. To test and ensure that Routing picks a connector for token-based MIT payments if and only if it has connector_mandate_details for that connector, create another merchant connector account with a different connector Y.
  2. Repeat steps 3 and 4 but in the confirm call, pass a "routing" value to force straight through routing to pick Y.
curl --location 'http://127.0.0.1:8080/payments/pay_5C8NWON1GUFGbMLR3tXx/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_hOic0bCxCZdrp9ShPzyl9b80NQgr2m4vy4gBVyKbKbWNNRCiNBm9yCA5D7McybRs' \
--data '{
    "payment_token": "token_ZTiG3Qz4ajaZB1MnBcmQ",
    "routing": {
        "type": "single",
        "data": {
            "connector": "stripe",
            "merchant_connector_id": "mca_hji9rSpSJPUOYn2A9yL9"
        }
    },
    "payment_method": "card"
}'

Even tho we are forcing connector Y for the token-based MIT payment, Routing will detect that the chosen payment method does not have any MIT/Mandate set up with Y by checking the connector_mandate_details field in the payment method record, and automatically fall back to the first connector.
image

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
  • I added a CHANGELOG entry if applicable

@vspecky vspecky added A-core Area: Core flows C-feature Category: Feature request or enhancement S-waiting-on-review Status: This PR has been implemented and needs to be reviewed A-routing Area: Routing labels Mar 7, 2024
@vspecky vspecky added this to the March 2024 milestone Mar 7, 2024
@vspecky vspecky self-assigned this Mar 7, 2024
@vspecky vspecky linked an issue Mar 7, 2024 that may be closed by this pull request
@vspecky vspecky marked this pull request as ready for review March 8, 2024 11:43
@vspecky vspecky requested review from a team as code owners March 8, 2024 11:43
jagan-jaya
jagan-jaya previously approved these changes Mar 8, 2024
SamraatBansal
SamraatBansal previously approved these changes Mar 11, 2024
Copy link
Contributor

@SamraatBansal SamraatBansal left a comment

Choose a reason for hiding this comment

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

Given approval from connectors crate

Aprabhat19
Aprabhat19 previously approved these changes Mar 11, 2024
Narayanbhat166
Narayanbhat166 previously approved these changes Mar 11, 2024
@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Mar 11, 2024
Merged via the queue into main with commit 43ebfbc Mar 11, 2024
10 of 12 checks passed
@Gnanasundari24 Gnanasundari24 deleted the routing-for-token-based-mit-payments branch March 11, 2024 10:13
@SanchithHegde SanchithHegde removed the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Mar 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-core Area: Core flows A-routing Area: Routing C-feature Category: Feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Routing changes to choose between MIT details and raw card
9 participants