A Stripe deposit in your bank feed is a single number. Behind it sit dozens or hundreds of charges, refunds, fees and adjustments, netted together over several days. The usual month-end routine is to export a CSV, rebuild the deposit in a spreadsheet and hope the totals agree.
There is a more reliable way to do it, and it is the same whether you do it by hand or write a script: work from Stripe's balance transactions. Each automatic payout is a batch of them, and Stripe gives you everything you need to prove the batch adds up to the deposit before you post anything to Xero.
Every movement is a balance transaction
Stripe's payout reconciliation guide puts it plainly: "Every funds movement in or out of your Stripe account creates a BalanceTransaction." A sale, a refund, a Stripe fee and the payout itself are each one record.
Four fields on each record do most of the work. According to Stripe's API reference for the object:
- amount is the gross amount, in the smallest currency unit. For an Australian account that means cents, so 10000 is A$100.00.
- fee is the fee paid on the transaction, as a positive integer.
- net is the net effect on your Stripe balance, which Stripe defines as amount minus fee.
- reporting_category groups the record for accounting purposes.
Because the figures are integers in cents, there is no floating-point rounding to argue about. If the numbers do not tie, something is missing. Rounding is never the explanation.
Pull the batch for one payout
For automatic payouts, Stripe knows which transactions each payout settled. You can see the batch three ways: open the payout in the Dashboard, download the payout reconciliation report, or ask the API. The API call is the list balance transactions endpoint with the payout ID (it starts with po_) passed as the payout filter.
Two details trip up first attempts:
- Pagination. The list endpoint returns 10 results by default. The guide says to set the limit parameter or use auto-pagination if you need more. A script that forgets this will "reconcile" the first 10 transactions of a 400-transaction payout and report a large unexplained difference.
- The payout is in the list too. The guide notes that the payout itself is also a balance transaction, with type payout. Leave that row out when you add up the components, or it cancels the batch and your total comes to zero instead of the deposit.
Expanding the source field returns the underlying charge or refund object in the same call. That is where you find the metadata, such as an order number stored when the payment was created, that lets you match a Stripe line to a sale in your own records.
Classify by reporting category, not type
The type field tells you what kind of object made the transaction. Card payments show up as charge and other payment methods as payment, and refunds are split the same way. Stripe's reporting categories page explains that reporting_category "improves the type field by providing a more useful grouping for most finance and reporting purposes". Some examples:
| Balance transaction type | Reporting category |
|---|---|
| charge, payment | charge |
| refund, payment_refund | refund |
| payout_cancel, payout_failure | payout_reversal |
| stripe_fee | fee |
| reserved_funds | risk_reserved_funds |
- charge, payment
- Reporting category
- charge
- refund, payment_refund
- Reporting category
- refund
- payout_cancel, payout_failure
- Reporting category
- payout_reversal
- stripe_fee
- Reporting category
- fee
- reserved_funds
- Reporting category
- risk_reserved_funds
The same page explains that the catch-all adjustment type is split into separate categories for disputes, dispute reversals and failed refunds. It also notes that capturing less than you authorised produces a refund-type transaction for the uncaptured part, labelled partial_capture_reversal so it is not mistaken for a real refund. Each of these maps naturally to a line in your journal or a reason code on an exception.
A worked payout
Here is an illustrative A$4,812.37 payout, grouped by reporting category. The figures are made up for the example and are not Stripe's rates.
| Category | Gross | Fee | Net |
|---|---|---|---|
| Charges | 5,200.00 | 91.20 | 5,108.80 |
| Refunds | -180.00 | 0.00 | -180.00 |
| Disputes | -116.43 | 0.00 | -116.43 |
| Total | 4,903.57 | 91.20 | 4,812.37 |
- Charges
- Gross
- 5,200.00
- Fee
- 91.20
- Net
- 5,108.80
- Refunds
- Gross
- -180.00
- Fee
- 0.00
- Net
- -180.00
- Disputes
- Gross
- -116.43
- Fee
- 0.00
- Net
- -116.43
- Total
- Gross
- 4,903.57
- Fee
- 91.20
- Net
- 4,812.37
The control is a single equality: the total net, excluding the payout row, equals the payout amount. Here 5,108.80 − 180.00 − 116.43 = 4,812.37, which is the deposit in the bank feed. If it does not hold to the cent, stop and find out why before you journal anything.
A second check runs on every row: gross minus fee equals net. It catches CSV exports that have been edited, sorted badly or partly pasted.
Fees and GST
The fee field is a total. The fee_details array on each balance transaction breaks it down, and its type is one of application_fee, payment_method_passthrough_fee, stripe_fee, tax or withheld_tax. The tax and withheld_tax lines are where tax on Stripe's fees appears; the payout reconciliation report describes its withheld_tax column as "Sales tax, VAT or GST withheld" and notes that it is already included in the fee.
Stripe's support note for Australian businesses says GST "applies to all Stripe fees at the prevailing standard rate" and that "You will be provided with a monthly tax invoice for the GST that is charged on your Stripe fees." Stripe also says those invoices are available by the 10th of the following month from Settings, Plans and fees, Invoice history.
That invoice matters for your BAS. As business.gov.au puts it, "To claim GST credits, the buyer must have a valid tax invoice." So the fee GST you post from payout data during the month should tie back to Stripe's monthly invoice once it arrives. Adding up the tax lines in fee_details for the month gives you the figure to check against.
Month-end cut-off
Payouts do not line up with month boundaries. The payout reconciliation report groups payouts by their estimated arrival date, "not the date your bank posts the deposit". Its Ending balance reconciliation section breaks down the transactions that had not been settled by the report's end date.
That section supports your Stripe clearing account at month-end. Sales captured on 30 and 31 October that are paid out in November are still sitting in Stripe on 31 October. If you record sales at gross into a clearing account and clear it with each payout, as covered in our Afterpay guide, the clearing balance on 31 October should equal the gross total of that section, with the fees on those transactions booked when their payout arrives. A difference means a sale, refund or payout was missed or posted twice.
Manual and instant payouts
All of this depends on Stripe knowing what each payout contained. For manual payouts, the guide says Stripe "can't identify which transactions are included in each payout", and the report page says the same about instant payouts. You reconcile those against your transaction history yourself, and Stripe points to its Balance report for teams that track the Stripe balance like a bank account. If you are choosing a payout setting with the close in mind, this is worth knowing before you change it.
What this means for your close
The method fits in one sentence: pull the batch, classify it by reporting category, prove the nets equal the deposit, then journal. Each step can be checked, and the checks are exact. That is the property finance work needs, and it is why we argue that arithmetic belongs to code, not to a model or a tired person at 6pm on day three. Disputes need their own treatment, which we cover in our chargebacks guide.
Vert is built around this same proof: it splits each payout into its parts and shows they sum to the deposit before it drafts an entry for your review. If you would like to see it on your own Stripe payouts, join the pilot.
