Stripe alternative
The real Stripe comparison is charge-first vs. order-first
Stripe is strong at processing cards. The break happens after the charge, when your team still needs one source of truth for line items, discounts, tax, refunds, invoices, hosted checkout, payment links, subscriptions, and support history.
You build and maintain the commerce layer
Line items, tax, discounts, payments, refunds, activity trail. One record.
Charge-first works early. The pain shows up in the layer above the charge.
The first successful payment is not the hard part. Day one with Stripe is great - you create a PaymentIntent, it works, you ship. Then the real 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. Support needs to look up what was actually purchased - not just the amount that was charged.
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: charge a number.
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
Stripe returns a payment object. Flint returns a commerce record.
That is the actual divide. Payment processors tell you whether money moved. Order-first systems tell you what was sold, how totals were derived, what changed later, and what can happen next.
const paymentIntent = await stripe.paymentIntents.create({amount: 6384,currency: "usd",metadata: { order_id: "internal_1042" },});// Stripe now knows charge state.// Your app still owns:// - line items// - discounts// - tax math// - refund allocation// - customer-facing order history
const order = await flint.orders.create({line_items: [{ name: "Coffee Beans", quantity: 2,unit_price_money: { amount: 1999, currency: "USD" } },{ name: "Pour-Over Mug", quantity: 1,unit_price_money: { amount: 2400, currency: "USD" } },],discounts: [{ coupon: { coupon_code: "WELCOME10" } }],});// Flint computes totals and keeps the record:// order.net_amounts → subtotal, discount, tax, paid, balance// order.activities → every state change// order.payment_intent_ids, refund_ids, checkout_session_ids
Comparison
What actually changes when you move from charge-first to order-first
The practical difference after launch, not the difference in a headline.
| Capability | Stripe | Flint |
|---|---|---|
System of record Which object the business trusts | PaymentIntent + your own order database | Order is the primary record |
Computed totals Subtotal, discount, tax, paid, refunded, balance | You assemble this around the charge | Built into order.net_amounts |
Refund workflow What happens after money is returned | Refund raw amount, reconcile state yourself | Refund stays linked to the order record |
Hosted flows Checkout sessions and payment links | Separate surfaces, less shared context | Order-backed checkout and payment links |
Subscription linkage How billing connects to the sale | Billing sits adjacent to order context | Subscription lineage stays on the order |
Operational history What support and finance read later | Processor events + your own tables | Order activities + linked payment and refund IDs |
Agent readability How easy it is for AI to reason about | Opaque amounts and statuses | Structured data + explicit next actions |
Stripe remains a strong processor. Flint changes what sits above the processor by making the order, not the charge, the durable record.
Refund example
The refund stays inside the order workflow
Flint's public refund API is still amount-based, but the result remains attached to the order instead of becoming a disconnected processor event you reconcile later.
// Query your own order storeconst order = await db.orders.findById(orderId);// Reconstruct what should be refundedconst subtotal = getRefundableSubtotal(order, returnedItems);const tax = recalculateTaxShare(order, returnedItems);// Refund a raw amountawait stripe.refunds.create({payment_intent: order.stripePaymentIntentId,amount: subtotal + tax,});// Then repair your own stateawait db.orders.update(orderId, {refundedAmount: order.refundedAmount + subtotal + tax,status: "partially_refunded",});
const refund = await flint.refunds.create({order_id: "ord_01JY...",amount_money: { amount: 2900, currency: "USD" },reason: "REFUND_REASON_REQUESTED_BY_CUSTOMER",idempotency_key: "refund-support-2900",});// Refund lives on the order record.// order.refund_ids now includes refund.refund_id.// order.net_amounts.refunded_money updates.// order.status can move to PARTIALLY_REFUNDED.
Stripe still processes every card
This is not a pitch to give up Stripe's payment infrastructure. 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.
Migration
Migrate from Stripe without changing the processor
The shape of the integration stays familiar. The important change is where the business truth lives.
Connect your existing Stripe account
Keep Stripe as the processor. Flint uses it for card movement while replacing the commerce state you would otherwise build yourself.
Replace charge creation with order creation
Start from orders.create with real line items and customer context, then derive payment collection from that record.
Move hosted checkout and links onto the order model
Create checkout sessions or payment links that remain tied to the order instead of letting hosted flows fragment your state.
Issue refunds against the order workflow
Refunds target the order or payment intent while staying attached to the order record. No manual reconciliation.
Swap webhook handlers to order-aware events
Consume richer order, refund, and checkout context instead of treating processor events as the full business truth.
Implementation
Go straight to the docs if you want to test the model
This page should narrow the decision. The docs should prove it.
Frequently asked questions
Keep Exploring
Related pages for the charge-first versus order-first decision
If this page captures the initial alternative intent, these are the next pages that deepen the product and workflow story.