Plans, with an optional free trial

A product with billing terms. Create it once; its terms become every checkout's defaults.

A plan is a product with subscription terms: a price per period, how often it bills, an optional free trial. No catalog? Write the plan inline in each checkout (next step): QBitFlow keeps a hidden product for it.

Create a plan

Create a product:

Create a product

products-create.sh

# A catalog product with your own reference (unique per space).curl https://api.qbitflow.app/v2/products \  -H "X-API-Key: $QBITFLOW_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{    "name": "T-shirt",    "description": "Blue, size M",    "priceUsd": "4.99",    "reference": "tshirt-blue-m"  }'# 201: a Product
const product = await client.products.create({	name: 'T-shirt',	description: 'Blue, size M',	priceUsd: '4.99', // USD, a decimal string, never a float	reference: 'tshirt-blue-m', // your own id: unique per space});console.log(`product ${product.uuid}, payment link ${product.paymentLink}`);
product = client.products.create(    name="T-shirt",    description="Blue, size M",    price_usd="4.99",  # USD, a decimal string: never a float    reference="tshirt-blue-m",  # your own id: unique per space)print(product.uuid, product.payment_link)
product, err := client.Products.Create(ctx, &qbitflow.CreateProductParams{	Name:        "T-shirt",	Description: "Blue, size M",	PriceUSD:    "4.99",          // a decimal string, never a float	Reference:   "tshirt-blue-m", // your own id: unique per space})if err != nil {	log.Fatal(err)}fmt.Println("created", product.UUID, "payment link:", product.PaymentLink)
$product = $client->products->create(new \QBitFlow\Params\CreateProductParams(	name: 'T-shirt',	priceUsd: '4.99', // a decimal string, never a float	description: 'Blue, size M',	reference: 'tshirt-blue-m', // your own reference, unique per space));echo "Created {$product->uuid}, payment link {$product->paymentLink}\n";

Add a subscription object to the same request and it's a plan:

FieldWhat it isRules
subscription.frequencyHow often it bills: { "value", "unit" }Required. seconds, minutes, hours, days, weeks, months (30 days) or years (365 days). At least 1 hour live, 5 minutes in test; at most 1 year
subscription.trialPeriodA free trial before the first billOptional; { "value": 0 } means none
subscription.minPeriodsThe fewest periods a customer may sign forOptional, 0 to 1,000

priceUsd is each period's price, in USD. The contract's own example, a monthly team plan with a 14-day trial and at least 3 periods (the SDKs take the same fields):

JSON

{  "name": "Team plan",  "priceUsd": "49",  "subscription": {    "frequency": { "value": 1, "unit": "months" },    "trialPeriod": { "value": 14, "unit": "days" },    "minPeriods": 3  }}

The answer writes each duration in its largest exact unit: the 14-day trial comes back as 2 weeks.

A plan for test mode

That price is above test mode's cap of $5 (live plans: above $0.25), and a month is a long wait. Bill every 5 minutes, the shortest test mode allows, and run the bills back to back (step 7):

Shell

curl https://api.qbitflow.app/v2/products \  -H "X-API-Key: $QBITFLOW_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{    "name": "Pro plan (test)",    "priceUsd": "4.99",    "reference": "pro-test",    "subscription": {      "frequency": { "value": 5, "unit": "minutes" }    }  }'

Check it

The answer has your subscription terms, and the plan is in Dashboard/Products (test mode). GET /products with subscription=true lists plans only.

Prices are locked at signup

A subscriber pays the price they signed for, for life, with the lines their checkout added (a tax, the processing fee): a new price applies to new subscribers only. To move someone to a new price, cancel theirs at the end of its paid period (step 6) and send them a new checkout.