WWebBuddyMarketplace
Selling it

Selling it as SaaS

Plans, credits and top-ups, Stripe Checkout and the customer portal, trial credits, plan feature switches, and which licence you need to charge customers.

9 min readUpdated Oct 17, 2026Applies to Adcraft 1.0.0

On this page

Adcraft is multi-tenant: every customer gets their own workspace with its own brands, team and credit balance, and you can bill them through Stripe. Everything on this page is optional if you only run it for yourself.

Important

Check your licence first. On CodeCanyon, the Regular License covers one end product whose end users are not charged. If your customers pay to use what you build with Adcraft (a subscription, credit packs, a setup fee, any paid plan), you need the Extended License. Read Envato's licence terms for the authoritative wording; this note is a summary, not legal advice.

How credits work

Every workspace has a credit balance. Generations spend credits; plans and top-ups add them. The balance is a ledger: every grant, spend and refund is a row you can see under Settings → Billing → Credit history.

Event Credits
New workspace (trial) +10 by default, set under Admin → Settings
Plan payment (each paid Stripe invoice) + the plan's credits
Top-up +100
Admin adjustment Any amount, positive or negative, from Admin → Organisations
A generation − its cost

Credits do not expire, and cancelling a plan does not remove them.

Most generations (concepts, static ads, product videos and presenter videos started from a brief) are charged after they succeed, so a failure costs nothing. Character Studio work (portraits, looks, avatars, twins and character ads) reserves credits when it starts and refunds them if it fails, in part when only some looks fail.

What each generation costs

Generation Credits Set in
A set of ad concepts for a brief 1 apps/app/src/pipelines/concepts.ts
Static ad, editable layout (every size) 2 STATIC_SCENE_CREDITS in apps/app/src/pipelines/static.ts
Static ad, AI-designed The image model's credits, once per size generated Admin → Models
Product video (about 15 seconds) 20 for a full run, whichever video model is chosen. Regenerating some scenes costs a share of 20. CREDIT_COSTS.productVideo15s
Presenter (UGC) video (about 30 seconds) 40 for a full run. Regenerating only B-roll clips costs a share of a quarter of that. CREDIT_COSTS.ugcVideo30s
A generated character portrait The image model's credits per image Admin → Models
HeyGen look 3 per look CREDIT_COSTS.heygenLook
HeyGen avatar from a photo or description 5 CREDIT_COSTS.heygenAvatar
Digital twin from footage 25 CREDIT_COSTS.digitalTwin
Brief help (Suggest and Improve), resizing and re-rendering an edited layout Free

CREDIT_COSTS lives in apps/app/src/server/billing.ts. Image model prices are per model and can be changed without a redeploy under Admin → Models (fractions are rounded up).

Note

Two entries in CREDIT_COSTS, concepts and staticAd, are only used for the price list shown on the billing page. The amounts actually charged for those two are the constants in concepts.ts and static.ts listed above, so change both places together. Video models also show a per-second credit figure in the catalogue, but product videos are charged the flat 20 above.

Tip

Credits are your price list, not your costs. Compare them with what each provider charges you (see AI models and providers) before you set plan prices.

Plans

Three plans and one top-up are defined in code, in PLANS and TOP_UP in apps/app/src/server/billing.ts:

Plan Display price Credits per paid invoice Brands (display text)
Starter $29 / month 150 1
Studio $79 / month 500 3
Agency $199 / month 1,500 10
Top-up $10 100

These are the defaults that ship with the code. To change a plan's name, display price, credits or bullet points, edit PLANS and rebuild. What a customer actually pays is the price you create in Stripe, so keep the two in step.

Note

Only the plan's credits and its feature switches (below) change what a workspace can do. The brand counts, ad-account counts and items such as "Priority generation queue" are shown on the plan cards and the marketing site, but they are not enforced in version 1.0.0.

Plan feature switches

Under Admin → Settings, each plan has three switches, all on by default:

Switch When off
Video Product videos cannot be created
UGC Presenter videos and Character Studio are unavailable
Publishing The campaign builder is unavailable

A workspace that has never bought a plan follows the Starter switches. A workspace's plan is its most recent subscription record, whatever that subscription's status.

Stripe setup

Stripe is optional. Without STRIPE_SECRET_KEY, see Running without Stripe below before you go live.

  1. Create the products and prices in your Stripe account:

    • a monthly recurring price for Starter, Studio and Agency;
    • a one-time price for the credit top-up.

    The amounts are yours to choose. Adcraft does not read product or price metadata: the plan and the number of credits come from the code.

  2. Set the price IDs in the environment:

    .env
    STRIPE_SECRET_KEY=sk_live_...
    STRIPE_PRICE_STARTER_MONTH=price_...
    STRIPE_PRICE_STUDIO_MONTH=price_...
    STRIPE_PRICE_AGENCY_MONTH=price_...
    STRIPE_PRICE_TOPUP=price_...
  3. Add a webhook endpoint in Stripe for https://<your-domain>/api/stripe/webhook with these events:

    • checkout.session.completed
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.paid
  4. Set STRIPE_WEBHOOK_SECRET to the endpoint's signing secret (whsec_…).

  5. Configure the customer portal in Stripe (Settings → Billing → Customer portal), so Manage payment method and invoices works. Only workspace owners can open it.

  6. Test the whole flow with test keys (sk_test_…) first. Stripe's test card 4242 4242 4242 4242 completes a test checkout.

For local testing, forward webhooks with the Stripe CLI:

Terminal
stripe listen --api-key $STRIPE_SECRET_KEY --forward-to localhost:3000/api/stripe/webhook

What happens after checkout

  • Subscribing. An owner chooses a plan under Settings → Billing and pays on Stripe Checkout. The webhook records the subscription. Only owners can buy a plan or a top-up or open the portal; editors and viewers are told so.
  • Credits arrive with invoice.paid. Each paid invoice adds the plan's credits once (the invoice ID makes it idempotent). They do not arrive the moment checkout finishes; they arrive when Stripe confirms payment.
  • Top-ups add 100 credits when checkout.session.completed arrives for the payment.
  • Cancelling in the portal marks the subscription cancelled. No more invoices, so no more credits; credits already granted stay.
  • Failed payments mark the subscription past due (through customer.subscription.updated). Nothing is restricted automatically, so follow up from Stripe.
  • Plan status does not gate features. A workspace keeps its last plan's feature switches after cancelling or falling past due; it simply stops receiving credits.
  • Signatures. The webhook verifies Stripe's signature. Without STRIPE_WEBHOOK_SECRET it answers 501.
  • Only subscriptions started from the app count. Checkout tags the subscription with the workspace ID; a subscription created by hand in the Stripe dashboard has no such tag and is ignored.
  • Return address. Checkout and the portal send customers back to the address they came from, worked out from the Host and X-Forwarded-Proto headers, so your reverse proxy must pass them through.

Checkout does not enable promotion codes or Stripe Tax. If you need either, add the options to the checkout.sessions.create calls in apps/app/src/server/billing-actions.ts.

Tip

If a plan payment succeeds but no credits appear, check the webhook deliveries in Stripe. Credits are granted when invoice.paid finds the subscription already recorded; if that event was processed before customer.subscription.created, grant the credits from Admin → Organisations.

Warning

Only monthly plans can be bought from the app in 1.0.0. The code can look up STRIPE_PRICE_<PLAN>_YEAR prices, but the billing page always starts a monthly checkout, and the "Yearly, save 20%" toggle on the marketing pricing page only changes the displayed numbers. If you do sell a yearly price some other way, note that credits are granted once per paid invoice, so a yearly invoice adds one plan allowance, not twelve.

Note

Choosing a different plan starts a new Stripe Checkout and creates a new subscription; it does not change the existing one. Ask customers to cancel the old plan in the portal, or cancel it for them in Stripe.

Running without Stripe

With STRIPE_SECRET_KEY unset, the plan cards and the top-up button stay visible. What happens when an owner clicks them depends on the environment:

  • In development, the purchase is simulated: the workspace gets the plan and its credits immediately, with a note that the purchase was simulated.
  • In production (NODE_ENV=production, which the Docker image always uses), the purchase is refused with "Payments are not set up on this site yet, so plans and credits can't be bought right now." No plan or credits are granted.
  • In production with ALLOW_SIMULATED_BILLING=true, purchases are simulated as in development.

Warning

ALLOW_SIMULATED_BILLING=true lets every workspace owner give themselves a plan and credits for free. Use it only for a demo site that should hand out free credits. If you are not charging, leave it unset and hand out credits from Admin → Organisations; the billing page remains visible either way.

Granting credits and plans by hand

From Admin → Organisations → (a workspace) a platform admin can:

  • Adjust credits by any positive or negative amount, with a reason. It is written to the credit history and the audit log.
  • Change plan to Starter, Studio or Agency, optionally granting that plan's credits at the same time.

Neither touches Stripe. Use them for free accounts, refunds and custom deals, not for customers who pay through Stripe.

Guardrails against runaway spend

Credits limit what a customer can generate, but your provider bills are in dollars. Adcraft also has guardrails. Platform defaults are set under Admin → Settings; some can be overridden per workspace by its owner under Settings → Guardrails:

Guardrail Default Per-workspace override Effect
Monthly provider cost cap $200 per workspace per calendar month (UTC) No New generations are refused once the workspace's recorded provider cost reaches it. Leave blank to turn off.
Monthly credit limit Off Yes Caps how many credits the workspace spends a month
Spend approval threshold 100 (daily budget, in the ad account's currency) Yes, higher, lower, or 0 for off Non-owners can only publish campaigns above it as paused
HeyGen custom voices per workspace 2 No Limits cloned and designed voices, which use slots on your HeyGen account
Actions per minute 120 No Sets the generation rate limit: a workspace may start a quarter of this number (at least 5) of each kind of generation per minute

The cost cap uses the estimated cost recorded for each generation, so treat it as a safety net rather than an exact bill. The rate limit is kept in memory per process.

Costs and margins

  • Admin → Generations & costs lists every AI call with its model, status, credits and an estimated provider cost, with cost per model.
  • Admin → Models & pricing shows each model's credits next to its measured cost and margin.
  • Admin → Billing shows subscriptions with links to Stripe, top-ups, plan distribution and an MRR figure. MRR is worked out from the plan list prices in PLANS, not from Stripe.
  • Admin → Overview shows a gross margin estimate.

Estimated costs come from each model's approximate price in the catalogue; check them against your provider invoices.

Before you launch a SaaS

  • Buy the right licence (see the top of this page).
  • Configure Stripe. Without it, the billing page refuses purchases in production (or gives credits away if you set ALLOW_SIMULATED_BILLING=true).
  • Decide on trial credits and the plan feature switches under Admin → Settings.
  • Set spending limits with your AI providers, and review the guardrail defaults.
  • Replace the prices and claims on the marketing pricing page with your own; see Rebranding.
  • Publish your own terms, privacy policy and DPA; the included pages are templates.
  • Read Known limitations so your sales pages promise only what the product does.