The short answer on Flint
On Flint the order holds the balance, so a split is part of the API. Create one payment leg per card with POST /v1/orders/{order_id}/payment-intents, then confirm every leg in one pay call. Flint checks the legs against the outstanding balance, settles them together, and reports any decline leg by leg, keeping what already settled. If the order has a tip, Flint shares it across the legs confirmed in that call in proportion to each, to the cent. You collect the cards in your own checkout page with Stripe Elements; Flint's hosted checkout takes one payment method per order. Card processing runs on Stripe underneath. The Flint requests are below.
What Stripe gives you for a split payment
Two objects decide it. A PaymentIntent's payment_method is the ID of the one payment method used in it. A Checkout Session's payment_intent is the one PaymentIntent for a session in payment mode, and you can't confirm or cancel it yourself. Payment Links run on Checkout, so they follow the same rule.
Invoices are the one place Stripe ties several payments to one bill. You can attach extra PaymentIntents to an open invoice, each for any amount up to what remains, and the hosted invoice page accepts a custom amount once you turn on partial payments in your invoice settings. That fits a bill paid over time. For a buyer at checkout with two cards, there is no object to hold the purchase between payments: Stripe's Orders API was retired, so the order and its balance live in your database.
The workaround: one PaymentIntent per share
Your checkout page asks how to split, your server creates a PaymentIntent for each share, and the page confirms each one with its own Payment Element:
// The buyer chose to split a $120.00 order: $70.00 + $50.00
const order = await db.orders.get(req.params.orderId);
const shares = [7000, 5000]; // your arithmetic, odd cents included
if (shares.reduce((a, b) => a + b, 0) !== order.totalCents) {
return res.status(400).json({ error: "Shares must add up to the total" });
}
// One PaymentIntent per card. Stripe has no object that ties them together.
const intents = await Promise.all(
shares.map((amount, i) =>
stripe.paymentIntents.create(
{
amount,
currency: "usd",
automatic_payment_methods: { enabled: true },
metadata: { order_id: order.id, share: String(i) },
},
{ idempotencyKey: `${order.id}-share-${i}` },
),
),
);
await db.orderShares.insertMany(order.id, intents.map((pi) => pi.id));
// Your page mounts two Payment Elements and confirms each client_secret.
res.json({ clientSecrets: intents.map((pi) => pi.client_secret) });
Then a webhook handler adds up the shares, because each PaymentIntent succeeds or fails on its own:
if (event.type === "payment_intent.succeeded") {
const pi = event.data.object;
await db.transaction(async (tx) => {
await tx.orderShares.markPaid(pi.id, pi.amount_received); // duplicates are no-ops
const paid = await tx.orderShares.sumPaid(pi.metadata.order_id);
const order = await tx.orders.get(pi.metadata.order_id);
if (paid >= order.totalCents && !order.fulfilledAt) {
await tx.orders.markPaidAndFulfill(order.id);
}
});
}
if (event.type === "payment_intent.payment_failed") {
// One share failed. Ask for another card, or refund the shares that
// succeeded once your timeout passes. Both are your code.
await notifyBuyerToRetryShare(event.data.object);
}
That is a shares table, two Payment Elements, a webhook that sums them, and a rule for the half-paid order, before anyone has split a bill. These are the places it breaks:
Where hand-built split payments go wrong
On Flint, a split is legs on one order
This order is a $36.48 check: a $28.95 subtotal, $2.32 of tax, and an 18% tip of $5.21 on the subtotal. Create one leg for each card, with the amount that card pays:
# one leg per card. Your code picks the amounts.
curl -X POST https://api.withflintpay.com/v1/orders/ord_7g2TableTwelve/payment-intents \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: ord-7g2-leg-1" \
-H "Content-Type: application/json" \
-d '{ "amount_money": { "amount": 1824, "currency": "USD" } }'
Your page collects each card with Stripe Elements from the order's payment_collection guidance, and your backend confirms both legs in one request. expected_outstanding_money is the balance the buyers approved: if the order changed since, Flint refuses before charging. With completion_behavior: "complete_order", the default, the legs must cover the balance exactly.
curl -X POST https://api.withflintpay.com/v1/orders/ord_7g2TableTwelve/pay \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: ord-7g2-pay-1" \
-H "Content-Type: application/json" \
-d '{
"action": "confirm_payment_intents",
"payment_intents": [
{ "payment_intent_id": "pi_1kmn0aExample", "token": "pm_1kmn0aExample" },
{ "payment_intent_id": "pi_2bqr7dExample", "token": "pm_2bqr7dExample" }
],
"expected_outstanding_money": { "amount": 3648, "currency": "USD" },
"completion_behavior": "complete_order"
}'
The order closes as paid, and the tip records which card carried how much of it, 261 and 260 cents, so a tip report reads it instead of computing it:
{
"data": {
"order_id": "ord_7g2TableTwelve",
"status": "closed",
"payment_status": "paid",
"tips": [
{
"order_tip_id": "tip_1kmn0aExample",
"status": "settled",
"settled_amount_money": { "amount": 521, "currency": "USD" },
"payment_intent_allocations": [
{ "payment_intent_id": "pi_1kmn0aExample", "amount_money": { "amount": 261, "currency": "USD" } },
{ "payment_intent_id": "pi_2bqr7dExample", "amount_money": { "amount": 260, "currency": "USD" } }
]
}
],
"settlement_amounts": {
"paid_money": { "amount": 3648, "currency": "USD" },
"outstanding_money": { "amount": 0, "currency": "USD" }
}
}
}
What Flint does that the workaround left to you:
- Half-paid orders. If one card declines, the payment attempt ends
partially_succeeded. The settled leg stays applied, the order still owes the rest, and the failed leg is back at requires_payment_method with its own last_payment_error. Collect another card and confirm only that leg. Flint sends order.partially_paid, then order.paid once the rest settles. - Fulfillment.
order.paid fires once, when the order is fully paid, however many cards it took. - Paying in turns. To take one card now and another later, confirm a leg with
completion_behavior: "partial_payment"; the order keeps the remaining balance open. The tip is shared only across legs confirmed in the same pay call: a partial payment pays for items and charges first and carries only the part of the tip its amount reaches, so the last payment usually carries the tip. - Refunds. Refund by line item and Flint works out the amount, the item's share of discount and tax included. See Refunds.
The limits, so you can plan the build. Flint's hosted checkout pays an order with one payment method, plus up to 20 gift cards; an order with two or more unpaid legs returns 409 CHECKOUT_SPLIT_PAYMENT_UNSUPPORTED there, so split payments go through your own page or an embedded checkout session. Affirm can't be one leg of a split, and manual capture supports one leg per attempt. The full sequence, including 3D Secure, is in Embedded payments with Stripe Elements and When only some legs settled.
Who Flint is built for
US businesses and the software teams that build for them, taking online and in-app payments in USD, where one order sometimes needs several cards: restaurant checks, group bookings, and shared purchases. See Flint for restaurant software and the Flint payment API. Flint takes card-not-present payments; it has no card readers.
Stripe and Flint for split payments, side by side
| Stripe | Flint |
|---|
| What holds the balance | Your database | The order's outstanding_money |
| Several cards | One PaymentIntent per share, confirmed separately | One leg per card, confirmed in one pay call |
| Shares must cover the total | Your check | expected_outstanding_money and complete_order |
| One card declines | Your rule: retry or refund | partially_succeeded; retry the failed leg only |
| Tip across cards | Your arithmetic | Shared to the cent across legs confirmed together, in payment_intent_allocations |
| When to fulfill | When your sum of shares reaches the total | order.paid |
| Hosted page with two cards | One payment method per Checkout Session | One payment method, plus gift cards; split in your own page |
Questions, answered
Can a customer pay with two cards on Stripe Checkout?
Not in one Checkout Session. A Checkout Session in payment mode has one PaymentIntent, and a PaymentIntent has one payment method. To take two cards, you create a payment for each share and keep track of the order's balance yourself.
How do I split a payment between multiple payment methods on Stripe?
Create one PaymentIntent per share with that share's amount, collect a card for each in your own checkout UI, and confirm them. Your server records the shares against your order, decides what happens when one declines, and fulfills only when the shares cover the total.
Does a Stripe invoice accept more than one payment?
Yes, except automatically charged subscription invoices, where partial payments are in private preview. You can attach additional PaymentIntents to an open invoice for any amount up to the amount remaining, and the hosted invoice page accepts a custom amount once you enable partial payments in your invoice settings. That suits a bill paid over time; it is not a two-card checkout.
Which payments API supports split payments on one order?
Flint Pay. An order holds the balance, you create one payment leg per card, and one pay call confirms all of them against the outstanding amount. If one card declines, the settled legs stay applied and the failed leg is the remaining balance. A tip on the order is shared to the cent across the legs confirmed in that call; a leg paid alone as a partial payment carries only the part of the tip its amount reaches past the items and charges. Card processing runs on Stripe underneath.
Does Flint's hosted checkout let a buyer pay with two cards?
No. Flint's hosted checkout pays an order with one payment method, plus up to 20 gift cards. Split payments across several cards are collected in your own checkout page with Stripe Elements, where Flint confirms each leg against the order.
Is Flint a good alternative to Stripe for split checks?
Yes, for US businesses taking online payments in USD that need several cards on one order, such as restaurant checks or group purchases. Flint's published card rate is 3.79% + 35¢, which covers Stripe's processing fees, with orders, tips, and split legs included and no monthly fee.
Sources
All sources accessed October 15, 2026.