Building a subscription service with Stripe Customer Portal and a passwordless auth flow

Building a subscription service with Stripe Customer Portal and a passwordless auth flow

Stripe customer portal provides you an interface for your users to manage their billing history, payment methods, update subscriptions, etc. It saves a lot of development time for any application that requires payments or subscriptions, and also provides a great UX.

In this use case, we will talk about how to build a passwordless authentication flow, using magic links to allow users to access their customer portal, and how to easily integrate subscription plans in your forms using Arengu. It will only take a few minutes to set it up. Let's take a look!

1. Enable Stripe Customer Portal

Go to your Stripe account settings, and click on Customer portal, under the Billing section. There you will find the options to determine the actions you want your users to take when using the portal:

For this use case, we don't need any specific configuration, but you will be required to provide some basic options, like privacy policy and terms of service links. You can also customize the appearance of your customer portal using the Branding settings.

You can find more information about the available options on Stripe documentation site.

2. Create a subscription plan in Stripe

If you have not already done it, go to Products section and create a subscription product. You can also create different pricing models for your subscription: billing period, currency, free trial, etc.

To configure the subscription plan in Arengu, you just need to copy the pricing API ID that starts with price_xxx.

3. Build a subscription form in Arengu

Create a form with at least two fields: email address, which we will also use to create a customer in Stripe, payment field, where we will configure the subscription. Remember to mark both as required.

To configure the payment field you will need:

  1. Public and private keys. You can find them on your Stripe account under Developers > API Keys section.
  2. Payment type. We will just select Subscription.
  3. Price ID. Here we will use the Price API ID of the second step, it should start with price_xxx.
  4. Customer email. This will associate the subscription to provided customer email. You can reference other field values using {{field_id}} syntax, so assuming your email field ID is also email, you just need to use {{email}} as value. You can learn more here about referencing form and flows variables in Arengu.

Now your form is ready to enroll users to a subscription plan.

4. Allow users to access the Customer Portal with a passwordless flow

Stripe provides you an endpoint to create a portal access for our current users but, to secure this access, we will need to authenticate users first. In this use case, we are going to build a simple passwordless authentication flow composed by:

  1. A form that requires the user email address to receive a magic link to access the portal.
  2. A flow that verifies if the user email exists in Stripe in order to retrieve the customer ID from Stripe, if the user doesn't exist, we will display a custom error message. Otherwise, we need to generate the access link and send it via email.

4.1 Let's build the authentication form

This form just need one field, the email addres, but make sure to use a friendly field ID like email because we will need it to reference this field value across different flows.

Arengu allows you to create flows that you can link in different form stages to build custom business logic. In this case, we are going to link a flow before form submission to verify the customer ID from Stripe. If the user doesn't exist, we need to display a custom error message and, if the user exists, to generate the portal access link and send it via email.

Bonus extra. Did you know that...?

  • Instead of using a magic link, you can also build a one-time password flow.
  • A simple redirection flow is possible just displaying a submit button, but you will need to set trusted value (eg. a JWT) to a hidden field, execute a flow to verify it, and dynamically redirect the user to the customer portal.

4.1 Verify if the customer exists in Stripe

As we need the Stripe customer ID to generate the access to the portal, we are going to use the Stripe API to list a customer by an email address:

For this GET request you need:

  • ID: we will use getCustomer as ID to make it easier to reference the output variables of this flow action.
  • URL:
  • URL params: limit and email. As our form email field ID is email, we need to use {{}} to reference that form variable.
  • Basic Authentication: we just need to specify the username with your Stripe secret key sk_xxxx.

This request will return the customer ID we need to use in the next flow action:

  "object": "list",
  "data": [{
    "id": "cus_HZwBJL1koyT2Da",
    "object": "customer",
    "address": null,
    "balance": 0,
    "created": 1593772803,
    "currency": null,
    "default_source": null,
    "delinquent": false,
    "description": null,
    "discount": null,
    "email": "[email protected]"

Now that we have the Stripe customer ID, we are going to use the API to create a session of the customer portal:

For this POST request you need:

  • ID: we are going to use generateSession to make it easier to reference the output variables of this flow action.
  • URL:
  • URL params: we are going to use customer and {{}} to reference the retrieved customer ID. If you haven't specified a default return URL in your Stripe settings, you will need to pass a return_url param too.
  • Basic Authentication: again, we just need to specify the username with your Stripe secret key sk_xxxx.

This request is going to return the URL to access the customer portal:

  "id": "bps_1H2n86HwfdScFlC4nFQfdS7e",
  "object": "billing_portal.session",
  "created": 1594252606,
  "customer": "cus_HZwBJL1koyT2Da",
  "livemode": false,
  "return_url": "",
  "url": ""

We are going to use SendGrid to send the magic link to the user. This is a native integration, so you just need to fill out some variables like the API Key of your SendGrid account, recipient email, subject, etc.

Bonus tip. You can also use your email HTML content in this integration. Code your email using a service like and just remember to reference the generated URL variable in your template using {{generateSession.body.url}}. Here you have an example:

To close this flow, just add a Submit form action including a custom success message.

5. Embed your forms anywhere

Go to  Share tab, and get the snippet code to embed your forms anywhere:

And that's all! Now your customers can easily manage their subscription to your service using Stripe customer portal and access to it in a secure way using a passwordless authentication flow with a magic link.

Start reducing development time, without giving up security and UX. Do you want to try our editor? Sign up free or book a demo with our team. Hope to see you!


Andrea L. Lozano

Social Media & Content Specialist.

View Comments
Next Post

Product Update — July 2020

Previous Post

7 types of forms you need in your website (and that you can build with Arengu)