Selling from your app
Connect your own Stripe account, define products per app, take payments from your users, and gate your app on what each user bought.
Your app can sell its own products (subscriptions or one-time purchases) to its own users. It runs
on your Stripe account: money moves between your customers and you, and Authoric is not in the
payment path. The catalog and every purchase are rows in your app's own database (products,
user_subscriptions, user_orders), so they are queryable like the rest of your data.
Checkout runs on your page
The backend handles products, payments, subscriptions, cancellation and entitlements, with Stripe's
webhooks reconciled into your app's database. Your app confirms payment on its own page with
Stripe.js, using the clientSecret these routes return.
Set up
Connect Stripe
In Integrations › Providers, choose Connect Stripe and paste your Stripe secret key. It is encrypted before it is stored. Test and live are separate connections, and Authoric creates the webhook endpoint on your Stripe account for you, so purchase status flows back without any setup.
Create products in the app
In your app, open Commerce › Products and create a product: a name, a price in USD, and billing Monthly, Yearly or One-time. A recurring product can add the other interval at a discount. Each app keeps its own product list.
Say what a purchase grants
Give the product entitlements: limits (a number, such as projects: 25) and features (on or
off, such as exports). Your app reads them back per user, so "what did this user buy" becomes one
call.
A price cannot be edited once it exists, because existing subscribers are billed by it. Change Price adds a new one; existing subscribers keep the price they bought.
Who can buy
Purchasing is granted to the user role by the seeded App Product Purchase policy, so everyone
who signs up can buy. A person who joined an organization by invitation holds no role until you give
them one: give them user (or put the purchase actions in a role of your own) if they should be able
to buy. Purchases belong to the signed-in user, never to whoever is named in a request.
Take a payment
Start the purchase on your server or in the browser
POST /{appUid}/autocrud/products/actions/subscribe with a recurring Stripe price id, or
.../products/actions/order with a one-time price id and an optional quantity. Both return a
clientSecret.
Confirm it with Stripe.js
Mount Stripe's Payment Element with the clientSecret and confirm. The card details go to Stripe,
never through your server or Authoric.
The webhook settles it
Stripe reports the outcome to Authoric, which updates the row in your app's database: a
subscription becomes active, an order succeeded. Read the status back rather than trusting the
browser's confirmation.
curl -X POST "$AUTHORIC_URL/$APP_UID/autocrud/products/actions/subscribe" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "priceId": "price_1Q8xYzAbCdEf" }'Test while you build, live when you launch
Each app has a billing mode, set in the console under the app's Products. A new app starts in
test: everyone who buys in it, your testers included, pays with Stripe's test cards. Switch it to
live when you launch, and from then on every purchase in your app is real. The mode is the
app's, not the request's: these routes take no stripeMode, and entitlements are read in the
app's mode, so a test purchase never unlocks anything in a live app. You can still run a test
checkout from the console after launch.
Manage subscriptions
All of these act on the signed-in user's own purchases.
| Route | Does |
|---|---|
GET /{appUid}/autocrud/user_subscriptions/actions/subscriptions | Lists the user's subscriptions: uid, product_uid, status, current_period_end, cancel_at_period_end, and the entitlements each grants |
POST /{appUid}/autocrud/user_subscriptions/actions/{subscriptionUid}/cancel | Cancels at the end of the current period. The user keeps access until then |
POST /{appUid}/autocrud/user_subscriptions/actions/{subscriptionUid}/resume | Undoes a pending cancellation |
Send the subscription's uid in the body as well as the path ({ "subscriptionUid": "..." }).
Gate your app on what was bought
GET /{appUid}/autocrud/user_subscriptions/actions/entitlements merges the limits and features of
every active subscription the user holds:
{
"status": "active",
"limits": { "projects": { "soft": 25, "hard": 25 } },
"features": { "exports": true },
"subscriptions": 1
}A user with no active subscription gets "status": "none" and empty limits and features.
Check entitlements on the server side of anything you charge for: in a handler or a custom action,
not only in your UI.
Errors to branch on
| Code | Status | Meaning |
|---|---|---|
PRICE_NOT_RECURRING | 400 | subscribe was given a one-time price. Use order. |
PRICE_IS_RECURRING | 400 | order was given a recurring price. Use subscribe. |
PRICE_SCOPE_MISMATCH | 403 | The price belongs to a product of another app. |
SUBSCRIPTION_NOT_FOUND | 404 | No such subscription for this user. |
NOT_APP_USER | 403 | Only an app's own signed-in users buy through these routes. |
| (none) | 403 | The caller's roles do not include the purchase actions (products:subscribe, products:order). |