Skip to content

Conversation

Sakilmostak
Copy link
Contributor

@Sakilmostak Sakilmostak commented Jul 2, 2025

Type of Change

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

Description

  • Save the context of payment_method_id in the session
  • Create temporary token for payment_method_id to pass to payment confirm
  • Save the cvc as a temporary data against the payment_token to be accessed by payment
  • Evict the cvc after first use
  • Encrypt/Decrypt the payload for cvc to store in redis
  • do confirm with payment_token in payment confirm call
  • the relative data are stored temporarily (15 mins) or till the first use
  • payment_method_data and cvc are fetch against the payment_token for the payment

Additional Changes

  • This PR modifies the API contract
    payment token are present in payment method session response
"associated_payment_methods": [
        "token_Dafmj9an9QORtSR2ZUlt"
    ],
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

How did you test it?

Tested through Postman (Hyperswitch v2):

Create a customer:

  • curl for customer create
curl --location '{{baseUrl}}/v2/customers' \
--header 'x-profile-id:{{profile_id}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key={{api_key}}' \
--data-raw '{   
    "name": "John Doe",
    "email": "[email protected]"
}'
  • example response:
{
    "id": "12345_cus_0197c9ed96757970805bab9ee33c297c",
    "merchant_reference_id": null,
    "connector_customer_ids": null,
    "name": "John Doe",
    "email": "[email protected]",
    "phone": null,
    "phone_country_code": null,
    "description": null,
    "default_billing_address": null,
    "default_shipping_address": null,
    "created_at": "2025-07-02T06:58:00.438Z",
    "metadata": null,
    "default_payment_method_id": null
}

Create an Payment Intent:

  • curl for create payment intent
curl --location '{{baseUrl}}/v2/payments/create-intent' \
--header 'Content-Type: application/json' \
--header 'x-profile-id:{{profile_id}}' \
--header 'Authorization: api-key={{api_key}}' \
--data '{
    "amount_details": {
        "order_amount": 100,
        "currency": "EUR"
    },
    "customer_id": "{{customer_id}}",
    "capture_method":"automatic",
    "authentication_type": "no_three_ds"
}'
  • example response
{
    "id": "12345_pay_0197ca1bd1b77843abb91962582f7e4b",
    "status": "requires_payment_method",
    "amount_details": {
        "order_amount": 100,
        "currency": "EUR",
        "shipping_cost": null,
        "order_tax_amount": null,
        "external_tax_calculation": "skip",
        "surcharge_calculation": "skip",
        "surcharge_amount": null,
        "tax_on_surcharge": null
    },
    "client_secret": "cs_0197ca1bd1d07612a7b320f752ac4488",
    "profile_id": "pro_SZTOpU280AYkfajAgo5e",
    "merchant_reference_id": null,
    "routing_algorithm_id": null,
    "capture_method": "automatic",
    "authentication_type": "no_three_ds",
    "billing": null,
    "shipping": null,
    "customer_id": "12345_cus_0197ca1b7f117c20b6e75853d71550af",
    "customer_present": "present",
    "description": null,
    "return_url": null,
    "setup_future_usage": "on_session",
    "apply_mit_exemption": "Skip",
    "statement_descriptor": null,
    "order_details": null,
    "allowed_payment_method_types": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "payment_link_enabled": "Skip",
    "payment_link_config": null,
    "request_incremental_authorization": "default",
    "expires_on": "2025-07-02T08:03:30.279Z",
    "frm_metadata": null,
    "request_external_three_ds_authentication": "Skip"
}

Create a Payment Method Session:

  • curl for payment_method_session create
curl --location 'http://localhost:8080/v2/payment-methods-session' \
--header 'X-Profile-Id: pro_SZTOpU280AYkfajAgo5e' \
--header 'x-feature: hyperswitch-custom-v2' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_asjzrvIGaJOBC19Hx719NnZBxpydXaERsgKO6ltr9X8Z0sg69XouAfZnRoQB14W4' \
--data '{
    "customer_id": "{{customer_id}}"
}'
  • example response
{
    "id": "12345_pms_0197ca1b96677e10bc58ea9b14097714",
    "customer_id": "12345_cus_0197ca1b7f117c20b6e75853d71550af",
    "billing": null,
    "psp_tokenization": null,
    "network_tokenization": null,
    "tokenization_data": null,
    "expires_at": "2025-07-02T08:03:15.079Z",
    "client_secret": "cs_0197ca1b96677e10bc58eaa6c8632a9d",
    "return_url": null,
    "next_action": null,
    "authentication_details": null,
    "associated_payment_methods": null,
    "associated_token_id": null
}

Confirm the session and collect the payment_method_token from associated_payment_methods:

  • curl for confirming payment_method_session:
curl --location '{{baseUrl}}/v2/payment-methods-session/:session_id' \
--header 'x-profile-id:{{profile_id}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: publishable-key={{publishable_key}},client-secret={{client_secret}}' \
--data '{
    "payment_method_type": "card",
    "payment_method_subtype": "debit",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "11",
            "card_exp_year": "29",
            "card_holder_name": "John Doe",
            "card_cvc": "123"
        }
    }
}'
  • example response
{
    "id": "12345_pms_0197ca1b96677e10bc58ea9b14097714",
    "customer_id": "12345_cus_0197ca1b7f117c20b6e75853d71550af",
    "billing": null,
    "psp_tokenization": null,
    "network_tokenization": null,
    "tokenization_data": null,
    "expires_at": "2025-07-02T08:03:15.079Z",
    "client_secret": "CLIENT_SECRET_REDACTED",
    "return_url": null,
    "next_action": null,
    "authentication_details": null,
    "associated_payment_methods": [
        "token_Dafmj9an9QORtSR2ZUlt"
    ],
    "associated_token_id": null
}

Confirm the payment with the payment_method_token:

  • curl for confirming payment
curl --location '{{baseUrl}}/v2/payments/{{payment_id}}/confirm-intent' \
--header 'x-client-secret:{{client_secret}}' \
--header 'x-profile-id:{{profile_id}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: publishable-key={{publishable_key}},client-secret={{client_secret}}' \
--data '{
    "payment_method_data": {
        "card_token": {
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "debit",
    "payment_token": "token_Dafmj9an9QORtSR2ZUlt"
}'
  • example response
{
    "id": "12345_pay_0197ca1bd1b77843abb91962582f7e4b",
    "status": "succeeded",
    "amount": {
        "order_amount": 100,
        "currency": "EUR",
        "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": 100
    },
    "customer_id": "12345_cus_0197ca1b7f117c20b6e75853d71550af",
    "connector": "stripe",
    "created": "2025-07-02T07:48:30.279Z",
    "payment_method_data": {
        "billing": null
    },
    "payment_method_type": "card",
    "payment_method_subtype": "debit",
    "connector_transaction_id": "pi_3RgLPJD5R7gDAGff1vB5q7OL",
    "connector_reference_id": null,
    "merchant_connector_id": "mca_pWR7OWBqRtRvEAr9zgkE",
    "browser_info": null,
    "error": null,
    "shipping": null,
    "billing": null,
    "attempts": null,
    "connector_token_details": {
        "token": "pm_1RgLPJD5R7gDAGff8n5Q2798",
        "connector_token_request_reference_id": "Og4oxt3597iaNjG9wS"
    },
    "payment_method_id": "12345_pm_0197ca1ba6ec7d63a37f47fa75794839",
    "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,
    "merchant_reference_id": 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

@Sakilmostak Sakilmostak added this to the June 2025 Release milestone Jul 2, 2025
@Sakilmostak Sakilmostak self-assigned this Jul 2, 2025
@Sakilmostak Sakilmostak added A-core Area: Core flows C-feature Category: Feature request or enhancement A-payment-methods Area: Payment Methods labels Jul 2, 2025
Copy link

semanticdiff-com bot commented Jul 2, 2025

@Sakilmostak Sakilmostak marked this pull request as ready for review July 2, 2025 07:57
@Sakilmostak Sakilmostak requested review from a team as code owners July 2, 2025 07:57
jarnura
jarnura previously approved these changes Jul 3, 2025
@@ -12,7 +12,7 @@ pub struct PaymentMethodSession {
pub return_url: Option<common_utils::types::Url>,
#[serde(with = "common_utils::custom_serde::iso8601")]
pub expires_at: time::PrimitiveDateTime,
pub associated_payment_methods: Option<Vec<common_utils::id_type::GlobalPaymentMethodId>>,
pub associated_payment_methods: Option<Vec<String>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

we can have a type for this instead of string

associated_payment_methods: Some(vec![parent_payment_method_token.clone()])
};

vault::insert_cvc_using_payment_token(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
vault::insert_cvc_using_payment_token(
vault::insert_cvc_using_payment_method_token(

.attach_printable("Failed to get redis connection")?;

let key = format!(
"pm_token_{}_{}_hyperswitch_cvc",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"pm_token_{}_{}_hyperswitch_cvc",
"pm_token_{}_{}_card_cvc",


let payload = payload_to_be_encrypted
.encode_to_string_of_json()
.change_context(errors::ApiErrorResponse::InternalServerError)?;
Copy link
Contributor

Choose a reason for hiding this comment

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

please add attach printable

Some(fullfillment_time),
)
.await
.change_context(errors::StorageError::KVError)
Copy link
Contributor

Choose a reason for hiding this comment

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

is this change_context needed?

.attach_printable("Failed to get redis connection")?;

let key = format!(
"pm_token_{}_{}_hyperswitch_cvc",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"pm_token_{}_{}_hyperswitch_cvc",
"pm_token_{}_{}_card_cvc",

@Sakilmostak
Copy link
Contributor Author

would be addressing these nits in a separate pr 👍

@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Jul 7, 2025
Merged via the queue into main with commit 4aca455 Jul 7, 2025
20 of 24 checks passed
@Gnanasundari24 Gnanasundari24 deleted the hs_saas_vault_confirm branch July 7, 2025 11:56
pixincreate added a commit that referenced this pull request Jul 9, 2025
…ayload-webhooks

* 'main' of github.com:juspay/hyperswitch:
  refactor(connector): Move connector mappings and endpoints to dedicated modules (#8562)
  ci(cypress): fix `hipay` test cases (#8563)
  chore(version): 2025.07.09.0
  fix(payment_method): update entity id used for Vault to global customer id (#8380)
  refactor(routing): add conditional check for invoking DE routing flows (#8559)
  feat(connector): [AUTHIPAY] Integrate cards non 3ds payments (#8266)
  ci(cypress): add payu connector (#8567)
  feat(connector): [silverflow] template code (#8553)
  chore(version): 2025.07.08.0
  feat(cypress): [worldpayvantiv] add cypress test (#8234)
  feat(connectors): [worldpayvantiv] add connector mandate support  (#8546)
  feat(connector): [Celero] add Connector Template Code (#8489)
  feat(payment-methods): create payment_token in vault confirm / do payment-confirm with temp token from session (#8525)
  ci(cypress): Add Tsys,Square cypress test (#8543)
  chore(version): 2025.07.07.0
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 api-v2 C-feature Category: Feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants