























I had a user reach out to me on X asking if there was any way to integrate a Stripe credit card entry field with Clerk's sign-up forms.
Kostas Livieratos
@koslib
Can you actually create a sign up + card details input in a single page when using @clerk?
Using the OTP method.
Kostas is building a Chrome extension that uses AI to let users write responses to LinkedIn posts directly from their browser. To reduce the friction of users who want to sign up for the trial, he presented the following requirements:
In this article, I'll walk through the process of building a completely custom sign-up form that matches the above requirements, starting with the end result.
Before walking through how this solution was built, it's worth seeing it in action. The first phase of the signup process has the user entering the details outlined above.

Upon completing this form, the user receives an email from Clerk with their sign-up code. The form in the previous screenshot will automatically update to accept a verification code.

After the code is entered, the user is presented with a loading view, indicating that their account is being created in Clerk and the subscription is being registered in Stripe. Although the user experience appears seamless, the process happening behind the scenes is rather complex with a number of moving parts. Let's explore how this solution was built, starting with the front-end part.

We'll start by exploring the components of both Clerk and Stripe that are used to build the user-facing part of this flow.
Clerk has a great set of predesigned components that developers can drop directly into their application to provide a great sign-up and sign-in experience for their users.
In this scenario, however, the default components are not flexible enough to embed a product selection and credit card form, so we'll need to use Custom flows. Custom flows in Clerk allow you to build custom forms with your own logic to both register and sign in users, as well as customize the logic behind these actions to do whatever you need to for your application. Instead of using any of the components, we can instead build an HTML <form> with an onSubmit function to handle the submit process.
Stripe Elements is a set of prebuilt components that can be used during the payment processing flow of your application.
One of these components is a credit card entry form that can generate a token for a given set of credit card details, allowing us to securely store a reference to the card and not the card details themselves. This token can be used later in the process to tie the card as a form of payment to the customer in Stripe. In order to use Elements in a Next.js application, the component that renders the form must be wrapped in the <Elements> component.
Because we're using a Custom flow, we can create a separate component that renders and handles the form logic and wrap it in <Elements> on the page, allowing us to combine the credit card entry form with our sign-in form.
Users in Clerk have a number of different metadata categories that are used for different purposes:
Since unsafe metadata can be used to store information before the signup process is complete, we can take advantage of this to store information about the selected tier (a “product” in Stripe) and payment details provided in the custom form. When the user completes signup, the data stored locally in unsafe metadata will also be saved with the user on Clerks systems.
After walking through all the moving parts required to solve this on the front end, let's take a look at the code.
To start, we have the page.tsx file which renders one of two forms based on if the signup attempt is verifying or not. If verifying is true, it means that the user has submitted the required details and the application is just waiting for them to add the OTP code they received via email. Take note that SignUpForm is wrapped in the Stripe <Elements> node, which is required to use Elements.
Next let's explore the SignUpForm.tsx which is the form that accepts an email address, product selection, and credit card information. This component accepts a single prop of setVerifying which is only to signal to the page that the form has been submitted and the VerificationForm component can be shown instead.
When the form is submitted, three main things happen:
setVerifying prop is set to true, indicating to the parent that the VerificationForm component can now be rendered.Finally, we have the VerificationForm.tsx component, which simply accepts the code that was sent to the user's email address. The submit handler for this form sends the code to Clerk where it is checked to be valid. If valid, the user account will be created and the user will be redirected to /after-sign-up .
Now that we've covered everything the user sees, let's break down what happens behind the scenes to make sure the user is successfully registered for the trial of their chosen tier.
We'll need a reliable way to signal that a user has been created and something needs to be done about it, and that's where webhooks come in.
Webhooks are HTTP requests that are automatically dispatched to an API endpoint of your choosing when an event happens in Clerk. One of these can be triggered when a user is created, using the user.created event. The dispatched request also contains various details about the user that was created, including the unsafe metadata. By configuring a webhook handler in our application to accept the request, we can read in the selected product and payment info, and create the subscription using the Stripe SDK.

Using the @brianmmdev/clerk-webhooks-handler utility library, we can define functions that automatically validate the webhook signature and allow you to easily handle the payload, including pulling out the unsafe metadata that was set during the signup process.
Creating a subscription in Stripe requires three separate entities:
Since the card info was tokenized and stored in the user's unsafe metadata along with the selected product, we can take advantage of the info sent to our application to create these three entities and tie them to each other. The first thing is to create a payment method based on the tokenized card info:
Next, we can use the captured email address to create a customer in Stripe and tie the payment method to them:
Finally, we can create the subscription entity, attach it to the customer, set the payment method, AND set a trial period:
Although the frontend and backend flows occur separately, we need a way to signal to the front end that processing has been completed on the backend. To do this, we can use metadata again (public metadata in this case) to set data from the Stripe operations to indicate that the process has been completed:
On the front end, our redirect page actually just renders a loading indicator but also polls the user's info from Clerk to redirect them once that data is available. The following is the code that makes up the page for /after-sign-up, which is where the user was redirected after the OTP code was entered.
As you can see there are quite a lot of moving parts that allow for this simple form to do so much. To put everything into context, let's look at the entire flow step by step:

unsafeMetadata.user.created webhook is triggered and the payload is sent to an API route in the application.Custom flows in Clerk open a world of opportunities, allowing you to create your own forms to handle sign-up and sign-in. By taking advantage of webhooks and using the various types of metadata, you can also build in complex and advanced automation, while creating a seamless experience for your users.
If you enjoyed this, share it on X and let us know by tagging @clerk!
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。