Order form template that takes payment

Build & score

Short answer: this is a complete QuizGen JSON definition for a small-business order form — customer details, product picks, a pickup or delivery date — with a native Stripe payment question at the end so the order and the payment happen in the same step. POST it as-is (with your own products and prices) and you get a hosted order page at a short link. If you're not on the Business plan yet, skip to the fallback section below — the rest of the template still works.

This is what an order form template is for: not a full storefront, but a single link you can text or post that takes someone from "I want one" to "paid" for a fixed, simple catalog — pre-orders, custom orders, small runs, event merch. For the wider case — what a hosted order form does that a printable one can't, and where it stops — see https://quizgen.dev/online-order-form.

The template

A print shop taking custom t-shirt orders, priced by quantity:

{
  "schema_version": 1,
  "kind": "form",
  "title": "Riverside Print Co. — order form",
  "description": "Custom screen-printed shirts. Payment is collected when you submit.",
  "settings": {
    "collect_respondent": "email_required",
    "completion": {
      "message": "Thanks! We'll confirm your order and pickup or delivery time by email."
    }
  },
  "sections": [
    {
      "key": "order",
      "title": "Your order",
      "questions": [
        { "id": "customer_name", "type": "short_text", "prompt": "Your name",
          "required": true },
        { "id": "phone", "type": "phone", "prompt": "Phone number",
          "required": true, "help": "In case we need to reach you about your order." },
        { "id": "style", "type": "choice", "prompt": "Which shirt are you ordering?",
          "required": true,
          "options": [
            { "value": "crew_tee", "label": "Crew neck tee" },
            { "value": "hoodie", "label": "Hoodie" },
            { "value": "tank", "label": "Tank top" },
            { "value": "long_sleeve", "label": "Long sleeve" }
          ] },
        { "id": "fulfillment", "type": "choice", "prompt": "Pickup or delivery?",
          "required": true,
          "options": [
            { "value": "pickup", "label": "Pickup at the shop" },
            { "value": "delivery", "label": "Local delivery" }
          ] },
        { "id": "delivery_address", "type": "short_text", "prompt": "Delivery address",
          "required": true,
          "show_if": { "question": "fulfillment", "op": "eq", "value": "delivery" } },
        { "id": "needed_by", "type": "date", "prompt": "Date you need it by",
          "required": true },
        { "id": "notes", "type": "long_text", "prompt": "Design details, logo placement, colors",
          "help": "Attach nothing here — just describe it. We'll follow up by email if we need a file.",
          "rows": 4 }
      ]
    },
    {
      "key": "payment",
      "title": "Payment",
      "questions": [
        { "id": "package", "type": "payment",
          "prompt": "Choose your quantity and pay now", "required": true,
          "price_options": [
            { "value": "single", "label": "1 shirt", "amount_cents": 2500 },
            { "value": "five_pack", "label": "5-pack", "amount_cents": 11000,
              "detail": "$22 each" },
            { "value": "ten_pack", "label": "10-pack", "amount_cents": 20000,
              "detail": "$20 each" },
            { "value": "twenty_pack", "label": "20-pack (event size)", "amount_cents": 38000,
              "detail": "$19 each" }
          ],
          "button_label": "Pay & confirm order" }
      ]
    }
  ]
}

What each piece is doing

  • kind: "form" — the order shows on one page (not one question per screen), which matches how people expect to fill in an order.
  • collect_respondent: "email_required" — this is the "customer contact" field. You don't need a separate email question; the address is captured automatically and attached to the response.
  • style is a choice question. Note that every style costs the same here — see the rule below about why it has to.
  • fulfillment is a choice, and delivery_address only appears when they pick delivery, via show_if.
  • needed_by is a date question — always prefer it over a text field for dates; the answer comes back as "YYYY-MM-DD".
  • package is the one payment question this quiz is allowed to have. price_options (2–10 of them) is the right choice here instead of a single amount_cents, because the respondent is picking their quantity and paying the exact amount for it — one dropdown, one charge, no separate invoice step.

The one rule to keep: price_options is the only place quantity lives

There is deliberately no separate "How many shirts?" question in this template, and that is the most important thing to copy. A form takes one payment question, and that question charges exactly the amount_cents of the option the customer picked — nothing else on the form can change the total. So the moment you add a free-entry field that also implies a price, the two can disagree and the charge always wins:

A number question asking "How many shirts?" alongside a price_options list is a trap. The customer types 20, picks the 1 shirt — $25 option, and the form accepts it. The order arrives with paid: true against $25 for twenty shirts, and the only way to catch it is for a human to read every row.

The same trap catches per-option surcharges. If style had a "Hoodie +$15" label, picking the hoodie would still charge the plain bundle price, because price_options is the entire pricing model — labels are text, not arithmetic. Either fold the surcharge into the price list (a "5-pack hoodies" option at its real price) or charge one price across styles, as this template does.

The rule that follows: anything that changes what someone owes belongs in price_options, and nowhere else. Questions outside the payment question are for things you need in order to fulfil the order — style, colour, address, deadline, notes — not for things that determine the total. If your combinations won't fit in ten price options, that is the signal to reach for a real store rather than to add a quantity box.

Payment needs the Business plan — here's the fallback

The payment question type is live, but it's gated: it only works on the Business plan ($49/mo), and only after the creator finishes Stripe onboarding at quizgen.dev/dashboard/payments. Until that's done, creating a quiz with a payment question returns a 402 payments_not_ready error. Money always goes to the creator's own Stripe account — QuizGen takes a 2% platform fee on top, nothing more.

If you're not on Business yet, the rest of this template still works — just drop the payment section and pick one of two fallbacks:

  1. Collect now, invoice after. Take the order with everything above, then send an invoice with the total and a payment link once you've confirmed details (custom sizing, design proofs, whatever needs a human look first).
  2. Redirect to an external payment link. Add "completion": { "redirect_url": "https://buy.stripe.com/YOUR_LINK" } to settings and the respondent lands on your existing Stripe Payment Link or PayPal.me page right after submitting. You lose the "pick your size, pay the right amount" convenience of price_options, but the order and the payment still happen in one flow.

Either fallback is a completely normal way to run this template — plenty of small shops will use redirect links even after they're eligible for native payment, especially if they already have Stripe Payment Links set up.

Reading orders back

GET /api/v1/quizzes/:id/responses returns each order's answers (style, package, fulfillment, notes) plus, for a paid order, paid: true and the Stripe payment_intent id — that's what you reconcile against your Stripe dashboard if a number ever looks off. Filter with ?complete=true to skip abandoned carts.

For same-day routing, don't poll — set a webhook on the quiz. Every completed order POSTs itself to your endpoint the moment it's paid, so you can push it straight into your fulfillment queue, a Slack channel, or a spreadsheet instead of checking the dashboard.

When a real store is the better tool

Be honest with yourself about the size of the catalog before reaching for this. QuizGen's order form is for a fixed, simple catalog — a handful of products or package sizes, no per-item options exploding into hundreds of SKUs. It has no inventory tracking, no variant matrix (size × color × style all at once), and no shipping-rate calculator. If you need any of those — real inventory counts, a cart with multiple line items, calculated shipping — use Shopify or Square; they're built for exactly that and it would be a mistake to fight this template into doing their job. Reach for this template instead for pre-orders, custom one-off orders, small production runs, and event merch — the cases where "one page, one payment, done" is the whole requirement.

Let an AI assistant fill it in

Connect QuizGen's MCP server and say:

Read https://quizgen.dev/blog/order-form-template.md and make me an order form for my candle shop: 3 scents, one size, $18 each, pickup only, needed-by date, and a notes field for gift messages. I'm on the Business plan and already connected to Stripe.

The assistant adapts the products and prices, POSTs the definition, and hands back the live link.

FAQ

Can I sell more than one product in the same order? Change style to multi_choice so people can select several items, but keep the payment question's price_options as fixed bundles or package sizes — there's no per-line-item cart math, so price each option as a complete order. If selecting a second item is supposed to cost more, that difference has to appear as its own price option; a multi_choice selection can't add to the total on its own.

How do I let someone order a quantity I didn't list? You don't — not in the same submission. Add a "More than 20 / custom quantity" choice option with no price attached and show_if a notes field off it, then quote them separately with an invoice. A form charges one listed amount; anything negotiated needs a second step.

What happens if someone abandons before paying? The response is saved with complete: false and no paid field. Nothing is charged, and nothing ships — you can safely ignore incomplete responses when you tally orders.

Can I cap how many orders I take? Yes — "max_responses": 50 in settings closes the form automatically once you hit it, which is exactly what a limited production run needs.

Does the order form need to be a quiz? No — kind: "form" renders everything on one page, which is what people expect from an order form. kind is advisory only; anything you set in settings overrides it.

$ curl quizgen.dev/blog/order-form-template.md

This guide as raw markdown — no HTML for your agent to parse.

View .md

Keep reading