Skip to main content

Charging for Access

An app with branded sign-in can charge its users, with your own Stripe account. Payments go straight to you; the platform stores your keys encrypted, takes no fee and never sees card details. Users choose a plan on the app's own sign-in pages, pay on Stripe's checkout page and land in the app; nothing about Stripe appears inside the app.

Separate from Strongly billing

This is your Stripe account charging your app's users. It has nothing to do with your Strongly subscription, credits or FinOps: those are how Strongly bills you, and they are unaffected by anything here.

The two kinds of plan​

Single-user planTeam plan
Who paysEach user pays for themselfOne person, the team owner, pays per seat
Who gets inThat userThe owner and the people they invite, up to the number of seats; members pay nothing
Stripe priceA flat recurring price (for example $9 / month)A recurring price per unit (for example $5 / month per seat)
Good forIndividual customersCompanies, departments, client teams

An app can offer both kinds at once; the Plans page lists every plan you add.

Before you start​

  • Switch on branded sign-in for the app (the top of the app's Auth tab). The plan and billing pages live on the app's own sign-in pages, so paid access needs it.
  • Have a Stripe account. Use Stripe's test mode while you set things up, then repeat the setup with live keys.
  • Make sure the platform has email configured if you will sell team plans: team invitations go out by email.

Step 1: Connect your Stripe account​

  1. Open the app, go to the Auth tab, and switch on Charge for access to this app with your own Stripe account. The payment section opens below the switch.
  2. Paste your Stripe secret key (sk_live_..., or sk_test_... while testing). Click Save. The key is stored encrypted; afterwards the section only shows its mode and last four characters.
  3. Copy the Webhook endpoint URL the section shows. It looks like https://<your-platform>/api/stripe/webhook/app/<app-id>.
  4. In Stripe, open Developers and then Webhooks, add an endpoint with that URL, and select these events:
    • checkout.session.completed
    • customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.paused, customer.subscription.resumed
    • invoice.paid
    • invoice.payment_failed
  5. Open the new endpoint in Stripe and reveal its signing secret (whsec_...). Paste it into Webhook signing secret in the payment section and click Save.

Without the webhook, payments succeed in Stripe but the platform never hears about them, so nobody gets access. Steps 3 to 5 are not optional.

Grace days (default 3) is how long a user keeps access after a payment fails while Stripe retries the card. After that, access pauses until the payment goes through; it resumes on its own when Stripe reports it paid.

Step 2a: Set up a single-user plan​

  1. In Stripe, under Product catalog, add a product (for example "Pro") with a recurring price, flat amount, for example $9.00 / month or $90.00 / year. Copy the price id (price_...).

  2. In the payment section, click Add plan and fill in:

    FieldValue
    NameWhat users see, for example "Pro"
    Stripe price idThe price_... from step 1
    DescriptionWhat the plan includes
    KindIndividual
    Trial daysA free trial at checkout, or 0
  3. Click Save plan.

That is it. With the switch on and this plan saved, every user of the app is asked to choose it.

What a user sees. They sign up (or sign in) on the app's sign-in page and are taken to the Plans page (https://<your-platform>/<url-name>/plans). They click Continue to payment, pay on Stripe's checkout page (card details never touch the platform), return to the app's sign-in page, and are in. On their Billing page (https://<your-platform>/<url-name>/billing) they see their plan and open Stripe's customer portal to change the card, see invoices or cancel.

Step 2b: Set up a team plan​

  1. In Stripe, under Product catalog, add a product (for example "Team seat") with a recurring price that is charged per unit, for example $5.00 / month. In Stripe's price editor this is the default "Flat rate" pricing model with the price per unit; the platform passes the number of seats as the quantity. Do not use a flat amount meant for the whole team. Copy the price id.

  2. In the payment section, click Add plan and fill in:

    FieldValue
    NameFor example "Team"
    Stripe price idThe per-seat price_... from step 1
    DescriptionFor example "Per seat; invite your team"
    KindTeam
    Minimum seatsThe fewest seats a team may buy, for example 2
    Trial daysA free trial at checkout, or 0
  3. Click Save plan.

What the team owner sees. They sign up, open the Plans page, choose the team plan, enter the number of seats including themself, and pay on Stripe for seats × price. Their Billing page then has a Your team panel:

  • Seats: change the number at any time; Stripe prorates the difference on the next invoice. Seats cannot drop below the plan's minimum or below the people already in them.
  • Members: who holds a seat, with a remove button for each (the owner cannot remove themself; they cancel in the Stripe portal instead).
  • Invite someone: enter an email address. The invite takes a seat; when all seats are taken, add seats first. An open invite can be withdrawn.

What a team member sees. They receive an email with a link to https://<your-platform>/<url-name>/join/<token>. Opening it, they create an account (or sign in) with that same email address and are in the team and in the app. They are never asked to pay. If the app approves new accounts, they wait for approval first and then follow the link again. Their Billing page says their access is paid for by the team owner.

Managing subscriptions​

The payment section lists every subscription: who pays, which plan, its status, seats used for a team, the renewal date and the Stripe subscription id. Statuses:

StatusMeaning
ActivePaid up; the user (or team) is served
Payment failed (grace)A payment failed; still served while Stripe retries, for the grace days
SuspendedGrace ran out; paused until Stripe reports a payment
CanceledEnded, by the payer in the Stripe portal, by you removing the user, or by Stripe

Refunds, invoices and disputes are handled in your Stripe dashboard.

Ending subscriptions. Removing a user from the app (the Users table on the Auth tab) cancels the subscription they pay for at once and frees any team seat they held, so nobody keeps paying for an app they cannot use. Removing a team owner therefore ends their whole team's subscription and the members' access with it. Deleting the app cancels every live subscription first. Both stop with Stripe's reason if Stripe refuses a cancellation.

Deactivating a user. Deactivating a user (the Users table on the Auth tab) pauses collection on every subscription they pay for: Stripe charges nothing while the account is deactivated, and nobody on that subscription is served (for a team, its members included). The subscription, its period and its seats are kept. Activating the account again resumes collection and access. The subscriptions table marks such a subscription Payer deactivated.

Stopping charging. Switch the toggle off: users are no longer asked for a plan. Existing subscriptions keep running in Stripe until they are canceled. While the toggle is on, branded sign-in cannot be switched off (the plan and billing pages live on it).

Who never needs a plan​

You, the app's collaborators, its app admins and platform administrators always get in. Only the app's users (people who signed up through the branded pages, were added as app users, or reach a public app) are asked for a plan.

To let a particular user in without a plan (a comped customer, a tester), open the Users table on the Auth tab and click the gift button on their row: their Access column reads Free and they are never sent to the Plans page. Click it again to ask them for a plan like everyone else.

A home app has no platform chrome, so the app provides its own links, each with target="_top" so the whole page navigates:

  • _strongly/billing opens the user's Billing page.
  • _strongly/plans opens the Plans page.
  • _strongly/sign-out signs the user out (see Signing out).

Testing before going live​

Use a Stripe test-mode secret key, test prices and a test webhook endpoint, and complete the checkout with Stripe's test card 4242 4242 4242 4242 (any future expiry, any CVC). Stripe's 4000 0000 0000 0341 card attaches but fails to charge, which exercises the grace period. When everything works, replace the key, the webhook endpoint and the price ids with their live-mode equivalents.

For your app's code​

The signed-in user's token carries user.plan (id, kind of individual or team, and status) while a plan covers them, so the app can show or hide features by plan without calling anything. See User Identity Headers.

Over the API: GET/PUT /apps/:id/paid-access, PUT/DELETE /apps/:id/paid-access/plans/:planId and GET /apps/:id/paid-access/subscriptions in the Apps API.