WooCommerce to Tally Reconciliation Breaks at the Gateway Fee Line — Here's Why

Month-end reconciliation between WooCommerce and Tally rarely breaks because of bad exports. It breaks because the two systems disagree on what counts as one financial event. We walk through the gateway fee netting problem, the three cases where CSV imports always fail, and what we actually generate instead of an order dump.

16 October 2026·9 min read·Devika Menon

The reconciliation always breaks at the gateway fee line. Razorpay settles T+2 net of fees, Tally wants the gross sale and the fee as two separate ledger entries, and WooCommerce — this is the bit that catches most teams out — doesn't record the fee at all. It only knows the order total that the customer paid. So the accountant pulls the WooCommerce sales report, pulls the Razorpay settlement statement, pulls the bank statement, and sits there with three documents that refuse to tie out. We spent most of a Thursday on exactly this with a client's October books not long ago; the gap was ₹41,320, which turned out to be four export orders paid with international cards where Razorpay's fee is 3% plus GST instead of the usual 2%. Nobody was wrong, exactly. The data model was.

Where the mismatch actually lives

Took us longer than it should have to see this clearly: WooCommerce treats the order as the atomic financial event. One order, one row, one total. Tally treats the voucher as the atomic financial event, and a single customer transaction almost always needs three vouchers — a Sales voucher (gross invoice value, with CGST/SGST or IGST split), a Receipt voucher (money hitting the Razorpay suspense ledger), and a Bank Charges voucher (the fee, with GST input credit). On settlement day you then need a Contra or a Journal to move the net amount from Razorpay suspense to the current account.

No amount of CSV mapping fixes this. You can map "order total" to "sales voucher amount" all day long. The gateway fee simply isn't in the WooCommerce database to be mapped. It lives on Razorpay's side, in the payment.captured webhook payload — specifically in the fee and tax fields of the payment object — and unless you are capturing that webhook and storing those fields somewhere queryable, you have lost the information at the source. Most WooCommerce installs just store the payment ID and the captured amount. The fee breakdown is gone the moment the webhook response is discarded.

(We used to think the Razorpay WooCommerce plugin stored the fee in post meta. It doesn't, or at least it didn't the last three times we checked — somewhere around plugin version 4.4 or 4.5. If that has changed recently, good, but we still write our own webhook handler because we need the acquirer_data fields too, and those definitely aren't stored.)

So the mismatch isn't a bug in anyone's software. WooCommerce is answering a sales question — what did the customer buy and for how much — while Tally is after ledger movements, which is a different question even when the rupee amounts happen to line up most of the time. Treating them as the same question is where every month-end goes sideways.

Why CSV exports keep failing on the same three cases

If order-level CSV reconciliation worked, nobody would be reading this. It works fine for the simple case — domestic card, no refund, no partial, no COD. The problem is that the simple case is maybe 70% of orders for a D2C brand, and the other 30% is where all the reconciliation effort goes.

The partial-refund case is the one that costs the most time, usually, because it looks like it ought to work. Customer orders three SKUs for ₹4,800, returns one worth ₹1,600. WooCommerce lets you record a partial refund against the order, which updates the order's refunded amount but doesn't create a separate transaction row in the sales export — it modifies the existing row. Tally needs a Credit Note voucher dated to the refund date, not the original order date, with its own GST reversal entry. If you re-export the sales CSV in November and the refund happened in November against an October order, the row either shows up with October's date and November's net amount (wrong for both months), or it shows up twice if you're using a plugin that tries to be clever. We have seen both. Neither reconciles.

Then there's COD remittance from the courier, which is a different flavour of the same disease. Shiprocket or Delhivery collects the cash on delivery, holds it for a week, and remits it to the bank net of RTO charges, forward shipping charges, and sometimes a COD handling fee. The remittance CSV from Shiprocket has — if we're remembering the current format correctly — separate columns for the collected amount, the RTO deduction, the freight deduction, and the net remitted. Most CSV imports pick up the collected amount or the net remitted and ignore the deduction columns entirely. So Tally sees a receipt that doesn't match either the WooCommerce order total (because of RTO on returned orders in the same batch) or the bank credit (because of the netting). This one is painful because the deduction columns use header names that have changed at least twice in the last two years.

The third case is the gateway fee netting that opened this post, and the specific thing worth adding is the T+2 lump-sum reconstruction problem — the bank credit on settlement day is a single number covering dozens of orders, net of fees, and the only way back to per-order fees is to walk the Razorpay settlement report line by line. The settlement report has the fee and tax per payment. The WooCommerce order table does not. If you want them in the same place, you have to put them there yourself.

Collapsing all this into one Journal entry per order — which is what most agencies end up doing to make the numbers tie — breaks GSTR-1 reconciliation. GSTR-1 wants invoice-wise outward supply with GST split. A Journal entry that nets sales, fees, and refunds into a single line doesn't give you that. The CA will flag it in the next quarter and you will spend a weekend unpicking it. We have done that weekend more than once.

Oh, and the "WooCommerce Tally Integration" plugins on CodeCanyon — the ones in the ₹3,000 to ₹8,000 range that claim to sync orders to Tally — none of the ones we evaluated (which, to be fair, was maybe four or five of them, over 2022 and 2023) handle the Razorpay settlement report at all. They map order totals to sales vouchers. They do not touch the settlement side. Which means they solve the easy 70% and leave you with the exact same 30% problem you started with. Possibly a worse version of it, because now the easy 70% is in Tally and the hard 30% is in a spreadsheet and the two don't talk.

What we generate instead of an order export

The thing we settled on, after enough of these month-ends, is to stop exporting orders and start generating vouchers. For every order, we generate three Tally-shaped events at the WooCommerce side and reconcile those against Razorpay and the bank.

The way it hangs together: a webhook listener catches payment.captured from Razorpay and writes the full payload — fee, tax, acquirer data, international flag — to a custom table. We started by storing it in post meta against the order, which is where everyone starts, but querying post meta for reconciliation is slow and the serialisation is annoying enough that we moved to a proper table after the second month-end. Twenty minutes of setup, saves hours later.

At month-end we query wp_wc_orders joined to wp_wc_order_stats and our custom payment table, and emit three CSV files in Tally's XML-import-friendly shape: one Sales voucher per order (gross, with GST ledger split pulled from the tax lines in wp_wc_order_tax_lookup), one Receipt voucher per captured payment (gross, posted to a Razorpay suspense ledger), and one Bank Charges voucher per payment (fee + GST on fee, posted to bank charges and input tax credit respectively). On settlement day we pull the Razorpay settlement report and emit a Contra voucher moving the net settled amount from Razorpay suspense to the current account. The suspense ledger should zero out, settlement by settlement. When it doesn't, you have found your reconciliation break and you have found it at the specific payment ID. For Shiprocket COD, we parse the remittance CSV directly — collected, RTO, freight, net columns — and emit a Receipt voucher for the collected amount plus a Shipping Charges voucher for the deductions, with the net posting to the bank.

This isn't elegant. It's a lot of code to maintain, the Razorpay settlement CSV format has changed on us twice, and the Shiprocket API has a rate limit that bites if you try to do too much in one go. But the reconciliation ties out, and when it doesn't tie out, we can point at exactly which voucher is off by exactly how much. That is the thing you cannot get from an order-level export, no matter how many columns you add to it.

What we still don't have a clean answer for is the reversed-settlement case. A payment gets captured on day T, settles T+2, and then on day T+40 the cardholder disputes it and Razorpay claws back the settlement. Tally wants this as a Contra against the original Receipt voucher — reversing the money movement — but by day T+40 the original voucher is sitting in a closed period, and Tally doesn't let you post against closed periods without unlocking them, which the accounts team won't do because it opens up every other voucher in that period too. We currently handle it as a manual Journal entry in the current period, with a narration pointing back to the original payment ID. We know that's wrong. The GSTR-1 for the original month is now subtly inconsistent with the ledger, and the only reason it hasn't blown up is that chargebacks are rare and small for most of our clients. If somebody has figured out a cleaner pattern for this — one that doesn't require unlocking closed periods and doesn't leave the GST return out of sync — we would genuinely like to hear it.

Devika Menon

Written by

Devika Menon

Studio Director

Fifteen years running design and engineering studios, now anchored in Gurgaon. Owns the calendar most weeks — kickoffs, scope reviews, the awkward conversation when a client wants three extra pages in week five. Writes about process here, mostly about how the work actually feels from inside a studio you trust.

                    

Ready to build something that works?

Get a free website mockup for your business. No commitment, no fluff — just what it could look like.

Call us — picked up by a human+91 88820 82228WhatsApp us — replies in minutes+91 88820 82228