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.

Charge-first (Stripe)
stripe.paymentIntents.create()
PaymentIntent
Your Orders DB
Tax Service
Refund Ledger

You build and maintain the commerce layer

Order-first (Flint)
flint.orders.create()

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.

Stripe: create a payment intent
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
Flint: create an order
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.

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

Stripe: refund the amount and fix your own state
// Query your own order store
const order = await db.orders.findById(orderId);
// Reconstruct what should be refunded
const subtotal = getRefundableSubtotal(order, returnedItems);
const tax = recalculateTaxShare(order, returnedItems);
// Refund a raw amount
await stripe.refunds.create({
payment_intent: order.stripePaymentIntentId,
amount: subtotal + tax,
});
// Then repair your own state
await db.orders.update(orderId, {
refundedAmount: order.refundedAmount + subtotal + tax,
status: "partially_refunded",
});
Flint: refund against the order workflow
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.

1

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.

2

Replace charge creation with order creation

Start from orders.create with real line items and customer context, then derive payment collection from that record.

3

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.

4

Issue refunds against the order workflow

Refunds target the order or payment intent while staying attached to the order record. No manual reconciliation.

5

Swap webhook handlers to order-aware events

Consume richer order, refund, and checkout context instead of treating processor events as the full business truth.

Frequently asked questions

Stop treating the charge as the whole system

Keep Stripe for card processing. Move orders, computed totals, refund linkage, checkout context, and subscription-aware workflow state into a model your team can actually operate.