3 min read

Making Stripe fees land in the right place

Passing Stripe's fees on to customers sounds like simple maths, until you notice Stripe charges its percentage on the fee you've just added. Here's the formula I use for WeRunTickets.

Pound and percent symbols illustrating Stripe fees

In WeRunTickets, organisers can choose to absorb the fees or pass them on to the buyer. Passing them on makes sense for a lot of people. A promoter selling £10 tickets wants £10 a ticket, not £10 minus a bit for Stripe and a bit for the platform.

It sounds like the easiest sum in the world. Work out the fee, add it on, done. Except it doesn’t quite work, and it’s a very easy one to get wrong.

Why adding the fee on comes up short

Stripe charges a percentage plus a fixed amount per payment. For standard UK cards that’s around 1.5% + 20p (it’s more for European and international cards, so check what you’re actually being charged).

The catch is that Stripe takes its percentage of the total the customer pays, and that total now includes the fee you added. So you’ve worked out the fee on the ticket price, but Stripe works it out on the ticket price plus your fee. You’re always a little bit short.

Here’s a £100 festival ticket with a 50p booking fee on top (the 50p is just an example):

  • The organiser wants £100.00 and the platform wants 50p, so we need £100.50 after Stripe has had its cut.
  • Adding the fee on: 1.5% of £100.50 is about £1.51, plus 20p, so we charge £102.21.
  • Stripe then takes 1.5% of £102.21 plus 20p, which is £1.73.
  • £102.21 minus £1.73 is £100.48. 2p short.

2p doesn’t sound like much, but it’s 2p on every ticket. Sell 500 and someone’s £10 down, and on a ticketing platform that someone is either the organiser or you.

Divide, don’t add

The fix is to work backwards. If you need to end up with an amount after Stripe takes percentage + fixed, the amount to charge is:

charge = (amount you need + fixed fee) / (1 - percentage)

Same ticket:

  • (£100.50 + 20p) / (1 - 0.015) = £102.23…
  • Round up to £102.24.
  • Stripe takes 1.5% of £102.24 plus 20p, which is £1.73.
  • £102.24 minus £1.73 is £100.51. Everyone gets what they’re owed, with a penny of rounding in the organiser’s favour.

Always round up. Rounding to the nearest penny will occasionally leave you a penny short again, which rather defeats the point.

In code

Work in pence, never floats, anywhere money is involved. In Laravel it looks something like this:

function grossUp(int $netPence, float $percentage, int $fixedPence): int
{
    return (int) ceil(($netPence + $fixedPence) / (1 - $percentage));
}

// £100 ticket + 50p booking fee, at 1.5% + 20p
$charge = grossUp(10000 + 50, 0.015, 20); // 10224, so £102.24

A few things to watch for

  • Different cards cost different amounts. You don’t know if it’s a UK, European or international card until the customer’s paying. Either use the UK rate and accept a small loss on the odd foreign card, or use a higher rate and accept that UK customers pay a few pence more. I’d rather be clear and slightly generous than surprise anyone.
  • Refunds don’t give you Stripe’s fee back. If you refund a ticket in full, Stripe keeps its processing fee. Decide up front who covers that, because “it just comes out of the platform” adds up quickly at scale.
  • Be clear who’s paying for what with Connect. With Stripe Connect, the platform takes its cut as an application fee, and depending on how your charges are set up, Stripe’s own fee comes out of either your side or the organiser’s. Get that straight first, then do the gross-up for whoever is meant to be covering it.
  • Show the fee before checkout. Don’t let someone get to the payment page and find the total’s changed. Show the ticket price, the booking fee and the total together from the start.

None of it is complicated once you’ve seen it, but it’s the kind of thing that’s very easy to get slightly wrong, and with money, slightly wrong is still wrong.