PaymentIntent migration

Move the workflow off the PaymentIntent without making payment collection feel alien

The goal is not to hide payments. The goal is to stop forcing one processor object to explain the whole order, hosted checkout, refund, and support story.

Current starting point
stripe.paymentIntents.create(...)
amount
currency
metadata
manual joins later

PaymentIntent is clear for charging money, but thin for everything above it.

Flint starting point
flint.orders.create(...)

Create the business object first, then derive payment collection from it.

What This Page Answers

What actually changes when we stop centering the workflow on PaymentIntents?

The payment intent becomes a linked operation instead of the business record. Orders hold line items, totals, hosted flow linkage, and the post-payment history your application actually needs.

PaymentIntent stays useful, but smaller.
Order becomes the durable truth.
Refunds, checkout, and support stop depending on payment metadata as the glue.

Model change

You are not deleting payments. You are changing what the payment belongs to.

PaymentIntent-led model
payment_intent → app glue
line items in app
totals in app
refund ledger in app
hosted flow joins in app

The payment object carries more responsibility than it was meant to.

Order-led model
Change the owner of the business state
order → payment_intent
line items on order
computed totals
hosted flow linkage
refund history on workflow

The payment becomes one operation on the order instead of the whole system.

PaymentIntent-centered apps usually break once the sale needs more than an amount and a status.

At first, a PaymentIntent feels like enough. Then you add real line items, discounts, taxes, hosted flows, partial refunds, customer support, and recurring billing. Now the payment object needs an entire layer of interpretation wrapped around it.

The order-first move is not about ideology. It is about stopping your application from rebuilding the same commerce model on the side because the payment object cannot safely own all of that meaning.

Code shift

The migration begins at the first write

The earlier your code creates the business record, the less reconciliation work you carry through every later step.

PaymentIntent first
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
Order first
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

What changes

The order absorbs the work your app was doing around the PaymentIntent

This is the practical shift teams feel in checkout, refunds, and day-two operations.

CapabilityPaymentIntent-led appOrder-first app
Starting object
Where workflow begins
PaymentIntent
Order
Totals
Who owns subtotal, discounts, tax, and balance
Application code
Order net amounts
Checkout session meaning
What the hosted page is attached to
A payment attempt and metadata
A specific order workflow
Refund semantics
How support understands what changed
Refund raw money and reconstruct meaning later
Refund against a workflow that already knows the order context
Support history
What operators read
Processor state plus app-specific joins
One order with linked payment and refund state

The migration is worth doing when the business keeps outgrowing a payment object that was only meant to move money.

This does not require exotic new payment behavior

Flint's public docs still expose the payment intent model. What changes is how that payment intent fits into the broader system: it becomes linked to an order and no longer needs to carry the whole application contract by itself.

That makes the migration lower risk because you are changing the state model above the payment, not inventing a brand-new payment primitive from scratch.

Migration steps

A simple order for the migration

Keep the change narrow enough that the team can see the operational improvement quickly.

Step 1

Create the order first

Move line items and totals into the order create flow before changing hosted checkout or refund behavior.

Step 2

Attach hosted checkout to the order

Use checkout sessions or payment links that stay connected to the order instead of inventing new joins around payment metadata.

Step 3

Move refunds next

Once the order exists, let refunds live against that workflow so post-payment support stops being repair work.

Frequently asked questions

Stop making the PaymentIntent carry the whole workflow

Let payments stay focused on payment execution and move the rest of the business truth into an order model your application can actually operate cleanly.