Stop building order systems
on top of payment APIs.
You've been there. The homegrown orders table. The manual total calculations. The webhook state machine that almost works. Flint is the order system - with payments, tax, coupons, invoices, refunds, subscriptions, and webhooks built in. Stripe processes the cards.
You build all of this. You maintain all of this.
Line items, tax, discounts, payments, refunds, audit trail. One call.
The problem
The payment-first integration trap
Day one with a payment API is great. You create a charge, it works, you ship. Then the requirements arrive. Line items with quantities and prices. Tax calculations that update when discounts apply. Partial refunds that need to reconcile against the original order. Coupons. Tips. Subscriptions.
So you build an orders table. Then a line items table. Then a refunds ledger. Then a webhook handler that tracks payment state transitions. Six months later you have 2,000+ lines of commerce glue code that sits between your application and a payment API that only understands one thing: amount: 3828
Every team that integrates a payment-first API ends up building the same thing - a homegrown order management system that the payment provider should have given them. You maintain it. You debug it at 2 a.m. You pray the ledger stays in sync.
Flint takes the opposite approach. The order is the primitive. Payments, refunds, tax, coupons, and subscriptions are operations on that order. You don't build a commerce layer - it's the API.
See the difference
Payment-first vs. order-first
Same feature. One approach requires you to build and maintain the commerce logic. The other gives it to you.
// Calculate total yourselfconst subtotal = items.reduce((sum, i) => sum + i.price * i.qty, 0);const tax = await taxService.calculate(subtotal);const total = subtotal + tax;// Charge a raw amountconst charge = await stripe.paymentIntents.create({amount: total,currency: "usd",});// Store in YOUR databaseawait db.orders.insert({items, subtotal, tax, total,stripeId: charge.id,status: "paid",});// Later: manually reconcile refundsconst refund = await stripe.refunds.create({payment_intent: charge.id,amount: 2900,});await db.orders.update(orderId, {refundedAmount: 2900,status: "partially_refunded",});
// Create an order - totals computedconst order = await flint.orders.create({lineItems: [{ name: "Pro Plan", quantity: 1,unitPriceMoney: { amount: 9900 } },{ name: "Support Add-on", quantity: 1,unitPriceMoney: { amount: 2900 } },],});// order.netAmounts.totalMoney → 13824// (subtotal + tax, auto-computed)// Apply a couponawait flint.orders.applyCoupon(order.orderId, {couponCode: "LAUNCH20",});// Totals recomputed with discount + tax// Pay - amount derived from orderawait flint.orders.pay(order.orderId, {paymentSourceTokens: ["tok_visa"],});// status → PAID. No manual calculation.// Refund - state auto-transitionsawait flint.refunds.create({paymentIntentId: order.payments[0].id,amountMoney: { amount: 2900, currency: "USD" },reason: "requested_by_customer",});// status → PARTIALLY_REFUNDED. Done.
Left: you build the orders table, tax integration, and refund reconciliation. Right: the API handles it.
What you get
One integration, not six products
Orders, payments, catalog, billing, invoices, checkout, and real-time events. No separate services to stitch together.
Core
Orders
Create orders with structured line items, quantities, and prices. Totals, tax, and balances are computed automatically on every response.
Payments
Create payment intents from orders. Amount and currency are derived automatically. Stripe handles card processing under the hood.
Refunds
Issue full or partial refunds with one API call. The order balance, tax, and status recalculate automatically. No manual reconciliation.
Commerce
Items & Inventory
Define your product catalog with prices and track inventory. Attach items to orders by reference - no duplicate data.
Coupons & Discounts
Create percentage or fixed-amount coupons. Apply to orders and the discount flows through to computed totals automatically.
Tax
Tax is computed server-side on every order response. Amounts update automatically when line items, coupons, or tips change.
Customers
Store customer profiles with saved payment methods. Attach customers to orders for purchase history and repeat billing.
Billing
Subscription Plans
Define plans with recurring line items and billing intervals. Plans are templates - create once, subscribe many customers.
Subscriptions
Subscribe customers to plans with automatic invoicing and payment collection. Pause, resume, or cancel with one API call.
Distribution
Checkout Sessions
Generate a hosted checkout page from any order. Customers complete payment on a Flint-hosted page - no frontend form required.
Payment Links
Create shareable payment links that generate orders on click. Perfect for invoices, one-off charges, and event tickets.
Invoices
Send formal invoices when the buyer pays later or needs internal approval, without pushing billing into a separate system.
Webhooks
Receive real-time notifications for payment, order, and subscription events. Signed payloads with automatic retries.
How it works
From order to refund in five API calls
Create an order, apply a coupon, pay, refund a line item, and check the final state. Totals update and statuses transition automatically at every step.
const order = await flint.orders.create({customerId: "cus_01HZ4X8",lineItems: [{name: "Pro Plan - Annual",quantity: 1,unitPriceMoney: { amount: 9900, currency: "USD" },},{name: "Priority Support Add-on",quantity: 1,unitPriceMoney: { amount: 2900, currency: "USD" },},],});// order.status → "OPEN"// order.netAmounts.subtotalMoney → { amount: 12800 }// order.netAmounts.taxMoney → { amount: 1024 }// order.netAmounts.totalMoney → { amount: 13824 }// order.netAmounts.balanceMoney → { amount: 13824 }
const updated = await flint.orders.applyCoupon("ord_01HZ4X9", {couponCode: "LAUNCH20",});// 20% off applied. Server recomputed everything:// updated.netAmounts.subtotalMoney → { amount: 12800 }// updated.netAmounts.discountMoney → { amount: 2560 }// updated.netAmounts.taxMoney → { amount: 819 }// updated.netAmounts.totalMoney → { amount: 11059 }// updated.netAmounts.balanceMoney → { amount: 11059 }
const result = await flint.orders.pay("ord_01HZ4X9", {paymentSourceTokens: ["tok_visa"],buyerEmail: "alex@example.com",});// No amount specified - derived from order balance.// Card charged via Stripe. Order status → PAID.// result.order.netAmounts.paidMoney → { amount: 11059 }// result.order.netAmounts.balanceMoney → { amount: 0 }
const refund = await flint.refunds.create({paymentIntentId: "pi_01HZ4XA",amountMoney: { amount: 2900, currency: "USD" },reason: "requested_by_customer",idempotencyKey: "refund-support-addon-01",});// Stripe processes the refund.// Order status auto-transitions → partially_refunded.// refund.status → "pending"
const order = await flint.orders.get("ord_01HZ4X9");// order.status → "partially_refunded"// order.netAmounts.totalMoney → { amount: 11059 }// order.netAmounts.paidMoney → { amount: 11059 }// order.netAmounts.refundedMoney → { amount: 2900 }// order.netAmounts.balanceMoney → { amount: 0 }// No manual reconciliation. No webhook state machine.
Comparison
Flint vs. traditional payment APIs
What you get out of the box with Flint versus what you build yourself on top of a payment-first API.
| Capability | Payment-first | Flint Pay |
|---|---|---|
Order management Line items, quantities, prices | Build from scratch | Built-in order object |
Totals & balances Auto-computed net amounts | Calculate yourself | Computed on every response |
Tax calculation Compute per-item tax | Build or buy separately | Built-in, per line item |
Discounts & coupons Apply, stack, enforce limits | Build from scratch | Built-in with stacking rules |
Refund handling Refund and recalculate | Multi-step across systems | One call, auto-reconciled |
State machine Deterministic lifecycle | Build from scratch | OPEN → PAID → CLOSED |
Subscription billing Plans, trials, retries | Cron jobs + state machine | Auto-billing with lifecycle |
AI agent support Structured data for LLMs | Not possible | Semantic responses + /SKILL.md |
Why Flint
Built different, on purpose
A payment API should do more than move money. It should understand what you sell and handle the complexity for you.
Order-first, not payment-first
The order is the primitive. Payments, refunds, coupons, and tax attach to it. You never build an orders table or reconcile across systems.
Server-computed totals
Subtotals, tax, discounts, balances, and refunded amounts are computed on every response. You never calculate a total or maintain a ledger.
Automatic state transitions
Pay an order - status moves to PAID. Refund part of it - PARTIALLY_REFUNDED. Refund the rest - REFUNDED. No webhooks to poll, no state to reconcile.
Idempotent by design
Every mutating endpoint accepts an idempotency key. Safe retries, safe webhooks, safe AI agents. No duplicate charges, no double refunds.
Stripe processes every card
Card data goes directly to Stripe. Same PCI scope, same fraud detection, same dispute handling. Flint adds the commerce layer on top.
AI-ready responses
Responses include line item names, available actions, and deterministic statuses. AI agents can reason about orders without parsing opaque amounts.
Lifecycle
Deterministic order status machine
Every order follows a predictable lifecycle. Transitions are enforced server-side - no ambiguous states, no manual bookkeeping.
Trust & security
Stripe handles every card
Card data goes to Stripe directly. Flint never sees or stores card numbers. You get the same PCI scope, the same fraud detection, and the same dispute handling you'd get with a direct Stripe integration.
Flint adds the commerce layer on top - orders, line items, computed totals, automatic state transitions, coupons, tax, and refund reconciliation. The hard parts that every team ends up building themselves.
{"flow": ["Your app → Flint API (order + line items)","Flint → Stripe (card charge, PCI-scoped)","Stripe → Flint (payment confirmed)","Flint → Your app (order status: PAID)"],"cardDataTouches": ["Stripe"],"pciScope": "SAQ-A (same as direct Stripe)","fraudDetection": "Stripe Radar (included)"}// Flint never touches card data.// Same security. Better commerce.
Keep Exploring
Related pages for teams shaping the payment model
If this page is the entry point, these are the next pages that explain Flint's broader product surface and the adjacent buying decisions.
const order = await flint.orders.create({lineItems: [{name: "Pro Plan - Annual",quantity: 1,unitPriceMoney: { amount: 9900 },}],});const result = await flint.orders.pay({orderId: order.id,paymentSourceTokens: ["tok_visa"],});// order.status → "PAID"// Total, tax, balance: all computed.