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 an api to migrate the payment method #5186

Merged
merged 28 commits into from
Jul 10, 2024

Conversation

ShankarSinghC
Copy link
Contributor

@ShankarSinghC ShankarSinghC commented Jul 3, 2024

Type of Change

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

Description

This is the API that will be used to migrate card details and associated data stored in another service to Hyperswitch.

How does this API differ from the existing payment method creation API?

  1. It takes masked card details as input with valid first six and last four card numbers.
  2. It also accepts connector_mandate_details and network_transaction_id if present.

If the masked card number is provided as input, a payment method entry is created in Hyperswitch, and no data is stored in the locker. If a valid card number is present, the card details are stored in the locker, the same as in the save card flow.

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 merchant connector account for cybersource

Test on_session saved payment

-> Crate a payment with setup_future_usage: on_session

{
    "amount": 100,
    "amount_to_capture": 100,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "cu_{{$timestamp}}",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "setup_future_usage": "on_session",
    "return_url": "http://127.0.0.1:4040",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "838"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "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"
    }
}
image

-> Create payment with confirm false for the same customer

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_YsyZ2fWnqbVilsKF7TKSUd0Wuwdgf2vvxQzQVIBaCqeSGU9XRY3Pjmk9mLjNsaRm' \
--data '{
  "amount": 10000,
  "currency": "USD",
  "capture_method": "automatic",
  "authentication_type": "no_three_ds",
  "confirm": false,
  "customer_id": "cu_1720508543"
}'
image

-> Do payment method list with the client_secret

curl --location 'http://localhost:8080/customers/payment_methods?client_secret=pay_kGh4EiDR0G5PliuQVvcn_secret_SGva6IBvDkYjWGzkORVU' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4'
image

-> Confirm the payment with the payment_token

curl --location 'http://localhost:8080/payments/pay_kGh4EiDR0G5PliuQVvcn/confirm' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4' \
--header 'Content-Type: application/json' \
--data '{
    "payment_token": "token_8hR80yevZdbKLaW2rrY4",
    "client_secret": "pay_kGh4EiDR0G5PliuQVvcn_secret_SGva6IBvDkYjWGzkORVU",
    "payment_method": "card",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    }
}'
image

Test off_session saved payment

-> Create payment with setup_future_usage: off_session

{
    "amount": 100,
    "amount_to_capture": 100,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "cu_{{$timestamp}}",
    "email": "[email protected]",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "setup_future_usage": "off_session",
    "return_url": "http://127.0.0.1:4040",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "838"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "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"
    }
}
image

-> Create a payment and payment method list with the client_secret

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_YsyZ2fWnqbVilsKF7TKSUd0Wuwdgf2vvxQzQVIBaCqeSGU9XRY3Pjmk9mLjNsaRm' \
--data '{
  "amount": 10000,
  "currency": "USD",
  "capture_method": "automatic",
  "authentication_type": "no_three_ds",
  "confirm": false,
  "customer_id": "cu_1720509577"
}'
curl --location 'http://localhost:8080/customers/payment_methods?client_secret=pay_4QhSPVojFUcfdK2HxncL_secret_4EwPFiS6npXTWyv9eQd9' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4'
image

-> Confirm the payment with token

curl --location 'http://localhost:8080/payments/pay_JuR2NJ9Xw3aOwVKBF7UL/confirm' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4' \
--header 'Content-Type: application/json' \
--data '{
    "payment_token": "token_aQehaaeF9Z6HSgPjCrPJ",
    "client_secret": "pay_JuR2NJ9Xw3aOwVKBF7UL_secret_k7d6s0Wqn8qcY6iNB2Ti",
    "payment_method": "card",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    }
}'
image

Test payment method migration api with connector mandate details

-> use the appropriate connector_mandate_id save the above payment method for a customer

curl --location 'http://localhost:8080/payment_methods/merchant_1720508278/migrate' \
--header 'api-key: test_admin' \
--header 'Content-Type: application/json' \
--data '{
    "payment_method": "card",
    "card": {
        "card_number": "424242xxxxxx4242",
        "card_exp_month": "10",
        "card_exp_year": "26",
        "nick_name": "Joh"
    },
    "customer_id": "cu_1720456680",
    "connector_mandate_details": {
        "mca_5opSKiHdcALo37nP7Gl": {
            "connector_mandate_id": "connector_mandate_id",
            "original_payment_authorized_amount": 6560,
            "original_payment_authorized_currency": "USD"
        }
    }
}
'

-> Create a payment for the customer with confirm false and perform payment

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_YsyZ2fWnqbVilsKF7TKSUd0Wuwdgf2vvxQzQVIBaCqeSGU9XRY3Pjmk9mLjNsaRm' \
--data '{
  "amount": 10000,
  "currency": "USD",
  "capture_method": "automatic",
  "authentication_type": "no_three_ds",
  "confirm": false,
  "setup_future_usage": "off_session",
  "customer_id": "cu_1720510734"
}'

-> Payment method list with the client secret

curl --location 'http://localhost:8080/customers/payment_methods?client_secret=pay_nVu4pFzZkJyzIpsZzogt_secret_RU1A52ZevqUfCHsnxSQo' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4'
image

-> Confirm the payment with the token, in this case payment will go through even without the card details as the connector mandate details are present

curl --location 'http://localhost:8080/payments/pay_nVu4pFzZkJyzIpsZzogt/confirm' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4' \
--header 'Content-Type: application/json' \
--data '{
    "payment_token": "token_vm9vZGkoxH2dcWUpsHou",
    "client_secret": "pay_nVu4pFzZkJyzIpsZzogt_secret_RU1A52ZevqUfCHsnxSQo",
    "payment_method": "card",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    }
}'

Test payment method migration api with network_transaction_id

curl --location 'http://localhost:8080/payment_methods/migrate' \
--header 'api-key: test_admin' \
--header 'Content-Type: application/json' \
--data '{
    "payment_method": "card",
    "merchant_id": "merchant_1720508278",
    "card": {
        "card_number": "4242424242424242",
        "card_exp_month": "10",
        "card_exp_year": "26",
        "nick_name": "Joh"
    },
    "customer_id": "cu_1720514044",
    "network_transaction_id": "network_transaction_id",
    "connector_mandate_details": {
        "mca_jqWPPZvDec4Y6KLWzgXe": {
            "connector_mandate_id": "connector_mandate_id",
            "original_payment_authorized_amount": 6560,
            "original_payment_authorized_currency": "USD"
        }
    }
}
'

-> Enable the is_connector_agnostic_mit_enabled for the business profile

curl --location 'http://localhost:8080/account/merchant_1720508278/business_profile/pro_7cMwkt3td8JW7UcwKcAq' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
    "is_connector_agnostic_mit_enabled": true
}'

-> Create payment with confirm false

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_YsyZ2fWnqbVilsKF7TKSUd0Wuwdgf2vvxQzQVIBaCqeSGU9XRY3Pjmk9mLjNsaRm' \
--data '{
  "amount": 10000,
  "currency": "USD",
  "capture_method": "automatic",
  "authentication_type": "no_three_ds",
  "confirm": false,
  "setup_future_usage": "off_session",
  "customer_id": "cu_1720514044"
}'

-> list payment methods for customer

curl --location 'http://localhost:8080/customers/payment_methods?client_secret=pay_CjuYS0q3tS2huyVZdhQy_secret_AY0QHOox9y6WWeMA0Q07' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4'
image

-> Confirm the payment with the token

curl --location 'http://localhost:8080/payments/pay_CjuYS0q3tS2huyVZdhQy/confirm' \
--header 'api-key: pk_dev_cad52206021b4f44932e750bb3afb1d4' \
--header 'Content-Type: application/json' \
--data '{
    "payment_token": "token_a0AggkExh1ReYxueRT6n",
    "client_secret": "pay_CjuYS0q3tS2huyVZdhQy_secret_AY0QHOox9y6WWeMA0Q07",
    "payment_method": "card",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    }
}'
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

@ShankarSinghC ShankarSinghC added C-feature Category: Feature request or enhancement A-payment-methods Area: Payment Methods labels Jul 3, 2024
@ShankarSinghC ShankarSinghC self-assigned this Jul 3, 2024
@ShankarSinghC ShankarSinghC requested review from a team as code owners July 3, 2024 09:22
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Jul 3, 2024
@ShankarSinghC ShankarSinghC force-pushed the payment_methods/migration_api branch from 496e480 to 0d2cfbb Compare July 3, 2024 10:35
@hyperswitch-bot hyperswitch-bot bot removed the M-api-contract-changes Metadata: This PR involves API contract changes label Jul 3, 2024
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Jul 3, 2024
@ShankarSinghC ShankarSinghC force-pushed the payment_methods/migration_api branch from acd993a to e70bbc2 Compare July 4, 2024 16:41
@ShankarSinghC ShankarSinghC linked an issue Jul 8, 2024 that may be closed by this pull request
jagan-jaya
jagan-jaya previously approved these changes Jul 8, 2024
crates/api_models/src/payment_methods.rs Outdated Show resolved Hide resolved
crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved
crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved
crates/router/src/routes/app.rs Outdated Show resolved Hide resolved
last_modified: current_time,
last_used_at: current_time,
payment_method_billing_address,
updated_by: None,
Copy link
Member

Choose a reason for hiding this comment

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

update this field to appropriate value

crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved
Narayanbhat166
Narayanbhat166 previously approved these changes Jul 9, 2024
let card_details = req
.card
.as_ref()
.ok_or(errors::ApiErrorResponse::InvalidRequestData {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.ok_or(errors::ApiErrorResponse::InvalidRequestData {
get_required_value("card")

if crd.saved_to_locker {
crd.scheme.clone_from(&pm.scheme);
Some(crd)
} else {
Copy link
Member

Choose a reason for hiding this comment

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

@Aprabhat19 is it safe to remove this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes its safe to remove

let db = &*state.store;
let customer_id = req.customer_id.clone().get_required_value("customer_id")?;

let connector_mandate_details = req
Copy link
Member

Choose a reason for hiding this comment

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

this field should be mandatory during migration api

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this field should be mandatory during migration api

This is an internal API, and I will be creating another PR on this. I will include this mandatory field change in that upcoming PR.

jagan-jaya
jagan-jaya previously approved these changes Jul 9, 2024
crates/api_models/src/payment_methods.rs Outdated Show resolved Hide resolved
crates/router/src/core/payments/helpers.rs Outdated Show resolved Hide resolved
crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved
crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved

let payment_method_id = generate_id(consts::ID_LENGTH, "pm");

let client_secret = generate_id(
Copy link
Member

Choose a reason for hiding this comment

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

why we are creating client_secret here?

Copy link
Contributor Author

@ShankarSinghC ShankarSinghC Jul 9, 2024

Choose a reason for hiding this comment

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

This is change is form pr (#4134). Where in which the generated client_secret will be stored in db

@ShankarSinghC ShankarSinghC force-pushed the payment_methods/migration_api branch from 7f876bc to a0e1195 Compare July 9, 2024 19:18
/// in order to call /payment_methods
/// Client secret will be generated whenever a new
/// payment method is created
pub client_secret: Option<String>,
Copy link
Member

Choose a reason for hiding this comment

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

This is suppose to be server to server right? so client_secret is not required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes correct, as client secret is only required while adding a payment method through sdk it can be removed in this migration api.

jagan-jaya
jagan-jaya previously approved these changes Jul 10, 2024
@likhinbopanna likhinbopanna added this pull request to the merge queue Jul 10, 2024
Merged via the queue into main with commit 125699f Jul 10, 2024
13 checks passed
@likhinbopanna likhinbopanna deleted the payment_methods/migration_api branch July 10, 2024 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-payment-methods Area: Payment Methods C-feature Category: Feature request or enhancement M-api-contract-changes Metadata: This PR involves API contract changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add an api to migrate the payment method
6 participants