Integrating Payment Elements to build a single page checkout for subscriptions

111 views
Skip to first unread message

HS

unread,
Jul 14, 2023, 10:10:38 AM7/14/23
to Stripe API Discussion
Hi,

I'm trying to understand how to get the Payment Elements working with a single form/page checkout setup with PHP backend and HTML+JS frontend.

I'm looking to build a checkout page (similar to Stripe hosted checkout page), that page needs to contain, email, name, payment details field with an optional address field.

Before reaching to this page (or it can be a modal popup instead of standalone page) a user will basically select a plan from the list of monthly/yearly plans (similar to Stripe pricing tables). So, once the user lands on this page (or the modal is triggered), I'll only have the price_id field available before initializing the Stripe elements.

Stripe docs for this has the example app which is split into multiple pages/forms.

So far, I'm having some issues coming up with the workflow on how to get this subscription setup on that single checkout page with a single form. When initializing the payment element, I run into either the error related to missing client secret or missing payment mode (thus needing to specify amount and usd instead of price Id).

Can anyone help me figure out the best way to have a custom checkout page (that mimics Stripe hosted checkout page) with Payment Elements for recurring subscription setup?

If possible, similar to Stripe checkout page, is it possible to have the email in that form associated with the "Link" element as well so that the user can sign-in using one-time pass code?

Thanks.

Remi J.

unread,
Jul 14, 2023, 10:39:16 AM7/14/23
to api-d...@lists.stripe.com
Hello.

The doc you linked [1] is the canonical integration path today and what I would recommend doing in your case. At a high level you would do the following steps
1/ Create a Customer 
2/ Create a Subscription, passing the right Price id but also `payment_behavior: 'default_incomplete'` (so that the first payment is attempted afterwards) and also using the Expand feature [2] to obtain the first Invoice's PaymentIntent's `client_secret` by passing `expand: ['latest_invoice_payment_intent']`
3/ Send that `client_secret` value client-side to your app so that you can render PaymentElement with all the relevant details (currency, amount, payment methods, etc.)
4/ Collect payment method details and confirm the payment using `confirmPayment()` bringing the Subscription to active

Based on the way you described your app, this seems like the simplest approach. After the end customer selects the price they want to pay you now know exactly how to create the Subscription. This will automatically create an Invoice and a PaymentIntent for you which you can use to render PaymentElement with the right information. What you might have missed here in that doc is the Expand feature I mentioned above that lets you access the underlying PaymentIntent's client_secret as needed during Subscription creation.

An alternative approach would be to collect payment method details first. This is a newer integration path documented here [3]. The main difference is that you render PaymentElement without a client secret first. Instead, you initialize it with specific options such as currency/amount. Doing this though does require you to calculate the right final amount. This can seem easy for basic pricing like $20/month but is harder once you take into account things like discounts, automatic tax or custom pricing scheme like tiered pricing.
In that case, what we usually recommend is to use the Retrieve Upcoming Invoice API [4] server-side as it lets you preview what the first Invoice of a Subscription would look like. Doing this will give you the exact amount/currency that you need to return to your client-side code to properly render PaymentElement.
The advantage of this approach is that you can offer things like an upsell/cross-sell to a different Price, change the quantity or support discounts. You're calculating the amount but the Subscription isn't created yet until they make their decision and enter payment method details.

Both approaches have pros and cons depending on your overall payment flow so I hope one of those will fit your needs!

If you have follow up questions, I'd recommend working with our support team more in a 1:1 setting where they can look at your code, some example objects and advise you on the best path forward.
Best,
Remi


--
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.

S

unread,
Jul 14, 2023, 3:00:19 PM7/14/23
to Stripe API Discussion, re...@stripe.com
Hi Remi,

Thank you for the response. I'll get in touch with the support as well.

Regarding the steps from 1st approach. Here's where I'm confused, it is a single form with email, name and payment elements field. I already have setup the endpoint which creates a customer and a subscription (based on the price id of the plan selected by the user) and then returns the client secret back to confirm the payment. But how do I do it in a single click/submission, i.e. how do I have all the fields available to be filled out by the user and then they just press the button once to complete the subscription instead of this being a multi-step or multi-page workflow?

<div id="payment-element">
<!-- The Payment Element will be inserted here -->
</div>

This is the payment element which is automatically inserted by Stripe. But in order to do that I need to pass the client secret to it, which I won't have until the subscription is setup, which won't happen until the user submits the complete form (including payment details), which won't be available until I have the client secret. And now I'm in the circular loop where I'm always missing one of piece to complete this subscription setup?

Am I might be misunderstanding something here? Is it not possible to have a single click checkout process with Payment Elements?

Or do you think the deferred approach is the only option that will work for this workflow? Basically to initialize the payment element on the frontend using the amount/currency (instead of client secret), and then with a single click: create customer -> create subscription using price id (returns client secret) -> confirmPayment on the frontend. Would there be any conflict if the payment element is initialized using an amount and currency but the subscription is created using a price id instead?

Thanks.
Reply all
Reply to author
Forward
0 new messages