Payment API for developers

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.

No credit cardFree sandbox forever5 min to first order
Without Flint
Cart DB
Stripe API
Tax Service
Inventory
Discount Logic
Refund Sync
Audit Log

You build all of this. You maintain all of this.

With Flint Pay
flint.orders.create()

Line items, tax, discounts, payments, refunds, audit trail. One call.

What This Page Answers

What should a modern payment API give a developer beyond charging a card?

It should return a real order model with totals, statuses, refunds, checkout flows, billing surfaces, and event context instead of forcing every team to build those pieces around a processor primitive.

This page is strongest when it answers the integration architecture question directly instead of behaving like a generic payment gateway page.

If you are building orders, tax, coupons, and refunds already, your payment API is underspecified for your use case.
The right abstraction is the business record, not the raw amount.
Developer quality comes from removing glue code, not from adding more wrappers around the same charge object.

System Boundary

The difference is where the business record lives

Payment APIs are usually good at moving money and vague about everything else. Flint moves the durable order, refund, and checkout context into the API surface itself.

Typical payment-first stack
Cart / quote state
Order DB
Tax calculation service
Discount rules
Payment processor
Refund reconciliation
Webhook repair logic

The rails work. The integration cost lives in the commerce state you build around them.

Order-first stack with Flint
Keep Stripe
flint.orders.create()
Line items
Computed totals
Checkout context
Refund linkage
Subscription lineage
Activity history

Stripe still processes the card. Flint becomes the commerce record above it.

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 do not build a commerce layer. It is the API.

What Breaks In Payment-First Stacks

The same integration problems keep showing up

These are the recurring operational costs that push teams from a simple charge API into building a shadow commerce platform.

Totals are computed in too many places

Cart logic, tax math, discounts, and post-payment updates spread across the frontend, backend, and webhook handlers.

Teams lose confidence that the final charge, refund, and support record still agree.

Refunds create state drift

The processor sees the refund, but your order record, reporting, and support tools still need repair logic.

Every partial refund is a mini-reconciliation project.

New payment surfaces multiply the glue

Hosted checkout, links, invoices, and subscriptions each add another place where your app has to reconstruct the same business truth.

Every new flow increases maintenance cost instead of compounding on one model.

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.

Typical payment API - you build the rest
// Calculate total yourself
const subtotal = items.reduce(
(sum, i) => sum + i.price * i.qty, 0
);
const tax = await taxService.calculate(subtotal);
const total = subtotal + tax;
// Charge a raw amount
const charge = await stripe.paymentIntents.create({
amount: total,
currency: "usd",
});
// Store in YOUR database
await db.orders.insert({
items, subtotal, tax, total,
stripeId: charge.id,
status: "paid",
});
// Later: manually reconcile refunds
const refund = await stripe.refunds.create({
payment_intent: charge.id,
amount: 2900,
});
await db.orders.update(orderId, {
refundedAmount: 2900,
status: "partially_refunded",
});
Flint - the API does it
// Create an order - totals computed
const order = await flint.orders.create({
lineItems: [
{ name: "Pro Plan", quantity: 1,
unitPriceMoney: { amount: 9900 } },
{ name: "Support Add-on", quantity: 1,
unitPriceMoney: { amount: 2900 } },
],
});
// order.pricingAmounts.totalMoney → 13824
// Apply a coupon
await flint.orders.applyCoupon(order.orderId, {
couponCode: "LAUNCH20",
});
// Pay - amount derived from order
await flint.orders.pay(order.orderId, {
paymentSourceTokens: ["tok_visa"],
});
// Refund - state auto-transitions
await flint.refunds.create({
paymentIntentId: order.paymentIntentIds[0],
amountMoney: { amount: 2900, currency: "USD" },
reason: "requested_by_customer",
});

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 without duplicating data.

Coupons & Discounts

Create percentage or fixed-amount coupons. Apply to orders and let the discount flow 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. Create once and 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 without building the form first.

Payment Links

Create shareable payment links that generate orders on click. Good 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 with signed payloads and automatic retries.

Product Surface

The objects that carry the payment lifecycle

These are the Flint resources that replace the usual hand-built mix of app tables, webhook logic, and disconnected billing tools.

orders

Orders

The primary business record with line items, totals, balances, linked payments, and downstream activity history.

payment_intents

Payments

Processor-backed money movement derived from the order instead of managed as the system of record.

refunds

Refunds

Refund state stays attached to the order workflow instead of becoming an isolated amount you reconcile later.

checkout_sessions

Hosted Checkout

Create an order-backed payment surface for configured purchases, quotes, or balances.

payment_links

Payment Links

Launch reusable hosted flows without abandoning order structure or post-payment context.

invoices

Invoices

Support formal billing and receivables-style follow-up without moving into a disconnected billing system.

subscriptions

Subscriptions

Recurring billing stays close to the same commerce model rather than split into a separate product brain.

webhooks

Events

Receive order-aware event payloads with enough business context to drive support, ops, and automation safely.

Implementation Path

What the order-first integration looks like in practice

This is the cleanest way to think about the system boundary before you get into endpoint-level examples.

1

Create the order first

Start from line items, customer context, discounts, and totals instead of starting from an amount.

This makes the business record durable before any payment is attempted.

2

Derive payment collection from that order

Collect money through direct payment, hosted checkout, payment links, or invoices without leaving the same object model.

The collection surface changes. The business record does not.

3

Mutate the order as the workflow evolves

Apply coupons, refund part of an order, or move into a subscription lifecycle while the net amounts stay computed server-side.

That removes most of the repair logic that normally accumulates around a processor primitive.

4

Drive support, ops, and automation from one record

Operators and internal tools can read one semantic object instead of reconciling multiple systems.

That is the part most teams only realize they need after launch.

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.

1Create an order with line items
1-create-order.ts
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.pricingAmounts.totalMoney → { amount: 13824 }
// order.settlementAmounts.outstandingMoney → { amount: 13824 }
2Apply a coupon - totals recompute automatically
2-apply-coupon.ts
const updated = await flint.orders.applyCoupon("ord_01HZ4X9", {
couponCode: "LAUNCH20",
});
// updated.pricingAmounts.discountMoney → { amount: 2560 }
// updated.pricingAmounts.taxMoney → { amount: 819 }
// updated.pricingAmounts.totalMoney → { amount: 11059 }
3Pay the order - amount derived automatically
3-pay-order.ts
const result = await flint.orders.pay("ord_01HZ4X9", {
paymentSourceTokens: ["tok_visa"],
buyerEmail: "alex@example.com",
});
// No amount specified.
// Card charged via Stripe.
// result.order.status → "PAID"
4Refund a line item - state transitions automatically
4-refund.ts
const refund = await flint.refunds.create({
paymentIntentId: "pi_01HZ4XA",
amountMoney: { amount: 2900, currency: "USD" },
reason: "requested_by_customer",
idempotencyKey: "refund-support-addon-01",
});
// order.status → "PARTIALLY_REFUNDED"
5Check the final state - everything reconciled
5-final-state.ts
const order = await flint.orders.get("ord_01HZ4X9");
// order.status → "PARTIALLY_REFUNDED"
// order.settlementAmounts.refundedMoney → { amount: 2900 }
// order.settlementAmounts.outstandingMoney → { amount: 0 }

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.

CapabilityPayment-firstFlint 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 + predictable actions

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 and status moves to PAID. Refund part of it and it moves to PARTIALLY_REFUNDED. No webhook state machine required.

Idempotent by design

Every mutating endpoint accepts an idempotency key. Safe retries, safe webhooks, safe AI agents.

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.

What Changes In Your Stack

Flint removes glue code by making the business record first-class

This is the implementation-level difference your team should expect if you adopt an order-first payment model.

You stop building

The shadow commerce layer

Homegrown order and refund reconciliation tables around the processor
Manual tax and discount recalculation after every order mutation
Webhook-only state machines whose job is to patch your own business record

You keep

The product workflows you already own

Your app still owns cart, quote, registration, booking, or operator flows
Stripe still processes the cards and handles PCI-scoped payment collection
Your existing product state still determines when payment should be requested

You gain

A cleaner system boundary

A single order object for checkout, refunds, invoices, and support history
Deterministic lifecycle transitions your team can reason about
A better base for internal tooling, automation, and agent-safe workflows

Lifecycle

Deterministic order status machine

Every order follows a predictable lifecycle. Transitions are enforced server-side, with no ambiguous states and no manual bookkeeping.

OPEN
PAID
PARTIALLY_REFUNDED
REFUNDED
or
PAID
CLOSED
OPENOrder is mutable. Add line items, apply coupons, add tips. All amounts recompute on every change.
PAIDPayment captured. Only refunds and metadata updates are allowed.
PARTIALLY_REFUNDEDSome amount refunded. Balance, tax, and totals recalculated. Further refunds still allowed.
REFUNDEDFully refunded. Balance is zero. No further mutations allowed.
CLOSEDSettled and archived. Terminal state for completed transactions.

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 would 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.

payment-flow.json
{
"orderId": "ord_01HZ4X9",
"status": "PAID",
"lineItems": [
{ "name": "Pro Plan", "quantity": 1, "unitPriceMoney": { "amount": 9900 } }
],
"pricingAmounts": {
"subtotalMoney": { "amount": 9900 },
"taxMoney": { "amount": 693 },
"totalMoney": { "amount": 10593 }
},
"settlementAmounts": {
"paidMoney": { "amount": 10593 },
"outstandingMoney": { "amount": 0 }
},
"paymentIntentIds": ["pi_01HZ4XA"],
"refundIds": [],
"activities": [
{ "type": "ORDER_CREATED" },
{ "type": "PAYMENT_CAPTURED" }
]
}

Operational Surface

The model is easier to operate after launch

These are the practical views internal teams usually need once real customers, refunds, and support tickets show up.

Support

Support tools can start from the order record

The order carries amounts, linked payment intents, refunds, checkout context, and status history, which means support no longer has to correlate three systems to answer a customer question.

See how totals were derived without re-running discount or tax logic
Trace refunds and payment attempts from the same order surface
Answer customer questions with one durable record instead of metadata fragments

Automation

Webhooks and internal jobs operate against semantic state

Event handlers can read business meaning directly from the order payload rather than guessing intent from a processor event plus internal joins.

webhook-handler.ts
if (event.type === "order.updated") {
const order = event.data;
if (order.status === "PARTIALLY_REFUNDED") {
notifySupport(order.orderId);
updateCustomerLedger(order.customerId, order.settlementAmounts);
}
}

Internal Tooling

Why better payment structure improves internal software too

The order-first model is not just about checkout. It changes what support, finance, and AI tooling can do safely.

Support

Support agents see one business record

They can answer what was bought, what was refunded, and what still needs action without reconciling several systems.

Look up order state without cross-checking processor events
Issue a refund with clearer downstream context
Explain payment history using buyer-facing business terms

Ops

Operations workflows get richer event context

Webhook consumers receive payloads that match what the business needs to know, not just whether money moved.

Trigger workflows from order state instead of raw charges
Route follow-up actions based on payment and balance context
Reduce post-payment repair logic in internal tools

AI

Agent tooling becomes safer

A structured order model gives AI systems deterministic objects and actions instead of ambiguous payment blobs.

Reason over line items, balances, and refund state directly
Use idempotent payment actions with clearer business boundaries
Produce fewer operator-facing mistakes in support flows

Pick The Next Layer

Which page should the reader open next?

This should route buyers into the next highest-signal page based on what they are actually deciding.

Fit

Use this API when you need commerce state, not only card collection

This is the dividing line between a payment gateway page and a developer-first payment API page.

Use This When

You need one model for checkout, refunds, invoices, subscriptions, and support history.
Your product already owns booking, cart, quote, or registration logic and needs a better commerce backend behind it.
You want internal tools or AI agents to operate on semantic order data instead of opaque payment amounts.

Choose Something Else When

You only need to charge a single amount and never reason about order structure afterward.
Your team is comfortable building and maintaining refunds, totals, and event reconciliation beside the processor.
You are evaluating a simple PSP integration rather than a broader commerce API boundary.

Vertical SaaS collecting structured purchases

A software platform wants line items, discounts, and post-purchase state in the API so product teams do not keep rebuilding them.

Internal support or agent tooling needs clean semantics

The order object becomes usable by humans and automation because it returns business context directly instead of forcing joins.

Get started

Ship commerce, not glue code.

Orders, payments, tax, coupons, subscriptions, refunds, and webhooks. One API. Stripe processes the cards. Free to start.

No credit card requiredFree sandbox foreverProduction ready in minutes
quickstart.ts
const order = await flint.orders.create({
customerId: "cus_01HZ4X8",
lineItems: [
{
name: "Pro Plan",
quantity: 1,
unitPriceMoney: { amount: 9900, currency: "USD" },
},
],
checkoutFlow: "HOSTED",
});
return order.checkoutSessionUrl;