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(payments_v2): implement create and confirm setup intent flow #7106

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

SanchithHegde
Copy link
Member

Type of Change

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

Description

This PR introduces the setup intent flow for payments v2. As of now, this PR includes a single endpoint that handles both the create intent and confirm intent steps. This is done so as to allow the payment methods v2 endpoints to create multiple-use tokens for a specified customer's payment method. The implementation mostly builds on top of payment create intent v2 and payment confirm intent v2 implementations. Note that the API contract is not finalized and is possibly subject to change.

Let me know if I should include this endpoint in the OpenAPI specification and Mintlify reference.

Additional Changes

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

This PR introduces a new setup intent endpoint for payments v2.

Motivation and Context

The endpoint introduced in this PR would allow the creation of multiple-use tokens by the payments methods v2 endpoints.

How did you test it?

  1. Using the v2 endpoints, create an organization, merchant account, profile, API key and merchant connector account.

  2. Call the setup intent endpoint:

    • Since the request body is a union of the payment create intent v2 and payment confirm intent v2 request bodies, the required fields for this endpoint are amount_details, payment_method_data, payment_method_type and payment_method_subtype.

    The request and response are included below:

    $ curl --location 'http://localhost:8080/v2/payments/setup-intent' \
        --header 'x-profile-id: pro_9UhMqBMEOooRIvJFFdeW' \
        --header 'Content-Type: application/json' \
        --header 'api-key: <API key>' \
        --data '{
            "amount_details": {
                "order_amount": 0,
                "currency": "USD"
            },
            "payment_method_data": {
                "card": {
                    "card_number": "4242424242424242",
                    "card_exp_month": "01",
                    "card_exp_year": "25",
                    "card_holder_name": "John Doe",
                    "card_cvc": "100"
                }
            },
            "payment_method_type": "card",
            "payment_method_subtype": "credit"
        }'
    {
        "confirm_intent_response": {
            "id": "12345_pay_01949d17c4637540ae2a1d96a449ed63",
            "status": "succeeded",
            "amount": {
                "order_amount": 0,
                "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": 0,
                "amount_to_capture": null,
                "amount_capturable": 0,
                "amount_captured": 0
            },
            "customer_id": null,
            "connector": "stripe",
            "client_secret": "12345_pay_01949d17c4637540ae2a1d96a449ed63_secret_01949d17c4647323a1f55b7c5ef73b6e",
            "created": "2025-01-25T10:52:48.100Z",
            "payment_method_data": {
                "billing": null
            },
            "payment_method_type": "card",
            "payment_method_subtype": "credit",
            "next_action": null,
            "connector_transaction_id": "seti_1Ql6yqD5R7gDAGffgNCIGUVI",
            "connector_reference_id": null,
            "merchant_connector_id": "mca_oC8RE28uRRcVKDjgwfzG",
            "browser_info": null,
            "error": null
        },
        "connector_mandate_reference_id": {
            "connector_mandate_id": "pm_1Ql6yqD5R7gDAGff11qr6pqg",
            "payment_method_id": null,
            "update_history": null,
            "mandate_metadata": null,
            "connector_mandate_request_reference_id": null
        }
    }
    • Additional fields may be passed in the request, such as customer_id, etc. which would be reflected in the response and database entries.

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

@SanchithHegde SanchithHegde 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 M-api-contract-changes Metadata: This PR involves API contract changes A-payments Area: payments api-v2 labels Jan 25, 2025
@SanchithHegde SanchithHegde added this to the January 2025 Release milestone Jan 25, 2025
@SanchithHegde SanchithHegde self-assigned this Jan 25, 2025
@SanchithHegde SanchithHegde requested review from a team as code owners January 25, 2025 11:08
@hyperswitch-bot hyperswitch-bot bot removed the M-api-contract-changes Metadata: This PR involves API contract changes label Jan 25, 2025
@@ -1405,6 +1405,18 @@ impl PaymentAttemptUpdate {
}
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Serialize)]
pub struct ConfirmIntentResponseUpdate {
Copy link
Member Author

Choose a reason for hiding this comment

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

Had to move this as a separate struct because of the clippy::large_enum_variant lint being thrown.

Comment on lines +1078 to +1088
impl
TrackerPostUpdateObjects<
router_flow_types::SetupMandate,
router_request_types::SetupMandateRequestData,
payments::PaymentConfirmData<router_flow_types::SetupMandate>,
>
for RouterData<
router_flow_types::SetupMandate,
router_request_types::SetupMandateRequestData,
router_response_types::PaymentsResponseData,
>
Copy link
Member Author

Choose a reason for hiding this comment

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

This is almost entirely a copy-paste of the implementation of the Autorize flow. Didn't bother to reduce code duplication between these two implementations.

Comment on lines +2463 to +2464
impl<F: Clone> PostUpdateTracker<F, PaymentConfirmData<F>, types::SetupMandateRequestData>
for PaymentResponse
Copy link
Member Author

Choose a reason for hiding this comment

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

Again, a copy-paste of the authorize flow implementation.

.route(web::post().to(payments::payments_create_intent)),
)
.service(
web::resource("/setup-intent")
Copy link
Member Author

Choose a reason for hiding this comment

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

Let me know if the path needs to be changed.

@@ -592,7 +592,6 @@ async fn handle_response(

match status_code {
200..=202 | 302 | 204 => {
logger::debug!(response=?response);
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed this redundant log line, since the response is being logged (with the same key) on line 589, before the match block.

Copy link
Member

@jarnura jarnura left a comment

Choose a reason for hiding this comment

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

This API is not setup-intent, it is just /payments. The term we need to use payments and not setup-intent.


let payload = payments_api::PaymentsCreateIntentRequest::from(&request);

let create_intent_response = Box::pin(payments_intent_core::<
Copy link
Member

Choose a reason for hiding this comment

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

This can be a normal payments_intent_core(PaymentCreateIntent), why we need a separate one?

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-payments Area: payments api-v2 C-feature Category: Feature request or enhancement S-waiting-on-review Status: This PR has been implemented and needs to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants