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

refactor(payment_methods): handle card duplication #3146

Merged
merged 9 commits into from
Feb 8, 2024

Conversation

Chethan-rao
Copy link
Contributor

Type of Change

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

Description

Currently card duplication and card metadata (card_holder_name, expiry year, expiry month, etc) changes are not being handled in Hyperswitch.

Rust locker has the following changes made -
a new field duplication_check has been added in stored card response. This field can have three values (duplicated, meta_data_changed and null).

  • duplicated : indicates that the entire card details in the card request is already present in the locker.
  • meta_data_changed : indicates that there is already a saved card with same card number but with different card details (like expiry or card holder name).
  • null : indicates that card is not present in the locker.

This PR handles above response from locker in two different flows.

  • During /payments api call with setup_future_usage = on_session
  • During /payment_methods api call to add card directly

Below are the ways in which Hyperswitch will behave based on above 3 values of duplication_check

  • duplicated : Since card already exist on locker, just ensure corresponding payment method entry is present in payment_methods table. If not we create it with the payment_method_id (locker_id) returned by locker.
  • meta_data_changed : Send a delete request to locker. Send a add card request to locker with the updated card details but same payment_method_id (This ensures that we don't re-create payment method entry on Hyperswitch side). Next update the payment_method_data field in payment_methods table to contain the new card details.
  • null : New payment method entry is created as this is a new card.

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 a merchant account, api key and stripe mca.

This PR has to be tested in below 2 flows as these are the two ways in which we can send add card request to locker.
i) Directly adding card using /payment_methods api
ii) Using /payments api with setup_future_usage = on_session

i) Directly adding card using /payment_methods api -

  1. Create a Customer
curl --location 'http://localhost:8080/customers' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_2csdyoNPcAqi4zqAg0oPDbCjQu4zoMzZ1JOrMDQ9zoe0CZ7rfrdI2r9Ju3o1M7ya' \
--data-raw '{
    "email": "[email protected]",
    "name": "David",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "First customer",
    "address": {
        "line1": "1467",
        "line2": "Harrison Street",
        "line3": "Harrison Street",
        "city": "San Fransico",
        "state": "California",
        "zip": "94122",
        "country": "US",
        "first_name": "John",
        "last_name": "Doe"
    },
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
  1. Hit add card request with the customer_id from above request (Try some new test card which shouldn't exist in locker)
curl --location 'http://localhost:8080/payment_methods' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_2csdyoNPcAqi4zqAg0oPDbCjQu4zoMzZ1JOrMDQ9zoe0CZ7rfrdI2r9Ju3o1M7ya' \
--data '{
  "payment_method": "card",
  "payment_method_type": "credit",
  "payment_method_issuer": "Visa",
  "card": {
    "card_number": "4000002500001001",
    "card_exp_month": "01",
    "card_exp_year": "42",
    "card_holder_name": "Devid"
  },
   "customer_id": "{{customer_id}}",
  "metadata": {
    "city": "NY",
    "unit": "245"
  }
}'

Below is the locker response I logged for testing purpose -

StoreCardRespPayload { 
    card_reference: "3e9223a0-6508-4957-b947-9b2b6f7189b0", 
    duplication_check: None 
}

duplication_check says None.

Also make sure payment method entry is created in hyperswitch side in using list_customer_payment_method api

  1. Now try to hit the same add card request without any change in card params.

Locker response -

StoreCardRespPayload { 
    card_reference: "3e9223a0-6508-4957-b947-9b2b6f7189b0", 
    duplication_check: Some(Duplicated) 
}

duplication_check says Duplicated which means whole card is found in locker without any change in card metadata (expiry year, expiry month, card holder name, etc)

Make sure no new entry is created in payment_methods table as this was a duplicate card. Check this by again doing list_customer_payment_method and this should still have 1 card.

  1. Now change the metadata of card (ex - card holder name) and make add card request.

Locker response -

StoreCardRespPayload { 
    card_reference: "3e9223a0-6508-4957-b947-9b2b6f7189b0", 
    duplication_check: Some(MetaDataChanged) 
}

duplication_check says MetaDataChanged which means card was found but some of its metadata was changed. At this point the card details (metadata) must be updated as per request sent.

Do list_customer_payment_method and this response should have the metadata changes you have done.

ii) Using /payments api with setup_future_usage = on_session

  1. Create a Customer
curl --location 'http://localhost:8080/customers' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_2csdyoNPcAqi4zqAg0oPDbCjQu4zoMzZ1JOrMDQ9zoe0CZ7rfrdI2r9Ju3o1M7ya' \
--data-raw '{
    "email": "[email protected]",
    "name": "David",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "First customer",
    "address": {
        "line1": "1467",
        "line2": "Harrison Street",
        "line3": "Harrison Street",
        "city": "San Fransico",
        "state": "California",
        "zip": "94122",
        "country": "US",
        "first_name": "John",
        "last_name": "Doe"
    },
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
  1. Hit /payments api with setup_future_usage = on_session with card thats not present in locker. Make sure to use above customer_id
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_2csdyoNPcAqi4zqAg0oPDbCjQu4zoMzZ1JOrMDQ9zoe0CZ7rfrdI2r9Ju3o1M7ya' \
--data-raw '{
    "amount": 6540,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "customer_id": "{{customer_id}}",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "4000002500001001",
            "card_exp_month": "12",
            "card_exp_year": "42",
            "card_holder_name": "David",
            "card_cvc": "900"
        }
    },
    "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"
    },
    "business_label": "default",
    "business_country": "US"
}'

This time card has to be saved in locker and payment_methods entry has to be created in hyperswitch. Verify by hitting list_customer_payment_method.

  1. Now for the same customer try to make another payment without changing anything (same card). This time as this is a duplicate card, no new entry has to be created in payment_methods table. Verify by hitting list_customer_payment_method and this should still have 1 card.
  2. Change some metadata and make a payment for same customer.
  3. Do list_customer_payment_method and this response should have the metadata changes you have done.

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

@Chethan-rao Chethan-rao added A-core Area: Core flows S-waiting-on-review Status: This PR has been implemented and needs to be reviewed A-payment-methods Area: Payment Methods C-refactor Category: Refactor A-payments Area: payments labels Dec 15, 2023
@Chethan-rao Chethan-rao added this to the December 2023 Release milestone Dec 15, 2023
@Chethan-rao Chethan-rao self-assigned this Dec 15, 2023
@Chethan-rao Chethan-rao requested a review from a team as a code owner December 15, 2023 14:56
@Chethan-rao Chethan-rao linked an issue Dec 15, 2023 that may be closed by this pull request
2 tasks
NishantJoshi00
NishantJoshi00 previously approved these changes Feb 5, 2024
NishantJoshi00
NishantJoshi00 previously approved these changes Feb 7, 2024
vspecky
vspecky previously approved these changes Feb 8, 2024
crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved
@likhinbopanna likhinbopanna added this pull request to the merge queue Feb 8, 2024
Merged via the queue into main with commit dd5630f Feb 8, 2024
10 of 12 checks passed
@likhinbopanna likhinbopanna deleted the handle-card-duplication branch February 8, 2024 12:25
@SanchithHegde SanchithHegde removed the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Feb 11, 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-payment-methods Area: Payment Methods A-payments Area: payments C-refactor Category: Refactor
Projects
No open projects
Status: Code Review
Development

Successfully merging this pull request may close these issues.

[REFACTOR] Handle card duplication
6 participants