Stripe + Razorpay vs. Airtable Revenue Ledger: Nightly Reconciliation with Anomaly Alerts to Slack via n8n

Catch revenue discrepancies between your payment gateways and your internal ledger before your accountant does — automatically, every night.

The flow
Razorpay logo
Source
Razorpay
Stripe logo
Process
Stripe
n8n logo
Process
n8n
Airtable logo
Process
Airtable
Slack logo
Destination
Slack

The stack in the order it runs — data flows from the source through to where it lands.

Why this stack

If you're running Stripe for international and Razorpay for domestic India, you have a reconciliation gap. Charges captured in one gateway don't always match what your finance team recorded. Monthly manual reconciliation means you find errors weeks after they happened. The real pain isn't fraud — it's mundane: failed webhook retries, partial refunds logged on one side only, currency conversion rounding, settlement timing mismatches that silently inflate or deflate reported revenue.

n8n is the right engine because it handles multi-branch conditional logic natively with no per-task pricing. You need one branch for Stripe, one for Razorpay, a merge node to compare, and a conditional to route mismatches. Zapier charges you a separate Zap per branch and can't do the comparison step inline. Make can do it, but its data-store module is clunky for row-level diffing. Airtable as the ledger gives you a queryable, human-editable source of truth your finance team can open without an API key.

The tradeoff is self-hosting n8n or paying for n8n Cloud. If your team has zero DevOps tolerance, use Make and accept that you'll need a formula column in Airtable to do the diff logic instead of doing it in the workflow. The Slack alert is intentionally simple — a single message with a mismatch count and a link to the filtered Airtable view — so the on-call person knows exactly what to click.

Don't use this if you already have QuickBooks or Xero connected to both gateways via a dedicated sync tool like Synder. This is a stopgap for teams under $500K ARR who haven't justified a full accounting integration yet. Once you cross that threshold, replace this with a real ERP connector.

The stack (5)

  1. Razorpay logo

    Payments + payouts built for India.

    Webhooks and the reports API make revenue an automatable signal, not a dashboard you check.

  2. Stripe logo

    Global payments with first-class APIs.

    Events + Sigma let you wire billing into any ops report or alert.

  3. n8n logo

    Self-hostable workflow automation.

    Own your data and run unlimited steps without per-task pricing.

  4. Airtable logo

    Relational database with a spreadsheet face. Operational memory for every workflow I run.

    The audit trail across views, automations, and webhooks is unmatched in no-code land.

  5. Slack logo

    Team chat where most ops alerts and reports land.

    The default place a small team already lives — pipe reports here instead of email nobody opens.

How it runs

  1. 1

    Set up your Airtable Revenue Ledger base

    Create an Airtable base called 'Revenue Ledger' with a table named 'Transactions'. Required fields: `transaction_id` (single line text, primary), `gateway` (single select: Stripe / Razorpay), `amount` (currency), `currency` (single line text), `status` (single select: captured / refunded / disputed), `captured_at` (date+time), `reconciled` (checkbox), `mismatch_flag` (checkbox), `mismatch_reason` (long text). This is your canonical ledger — both gateway syncs write here.

  2. 2

    Build the Stripe nightly pull node in n8n

    In n8n, create a workflow triggered by Schedule (cron: `0 1 * * *` — 1 AM daily). Add an HTTP Request node pointed at `https://api.stripe.com/v1/charges` with query params `created[gte]` set to yesterday's Unix timestamp using n8n's `$now.minus({days:1}).startOf('day').toUnixInteger()` expression, and `limit=100`. Authenticate via Header Auth with your Stripe secret key as `Bearer`. Add a pagination loop using the `has_more` field and `starting_after` cursor. One thing that bites people: Stripe returns amounts in the smallest currency unit (cents) — divide by 100 before writing to Airtable.

  3. 3

    Build the Razorpay nightly pull node in n8n

    Add a second HTTP Request node pointed at `https://api.razorpay.com/v1/payments` with query params `from` (yesterday 00:00:00 IST as Unix epoch) and `count=100`. Authenticate with Basic Auth using your Razorpay Key ID and Key Secret. Razorpay also returns amounts in paise — divide by 100. Add an IF node after: if `status == 'captured'`, proceed to write; if `status == 'failed'`, skip. Razorpay's pagination uses a `skip` param, not a cursor — loop by incrementing skip by 100 until the returned array length is less than 100.

  4. 4

    Upsert both datasets into Airtable

    After each gateway's fetch loop, add an Airtable node in 'Upsert' mode matched on `transaction_id`. Map fields directly: `transaction_id` → charge ID from Stripe or payment ID from Razorpay, `gateway` → hardcoded 'Stripe' or 'Razorpay', `amount` → converted float, `status` → gateway status field, `captured_at` → ISO timestamp. Set `reconciled` to false on every upsert — you'll flip it during the comparison step. Upsert (not insert) means re-running the workflow on failure won't create duplicates.

  5. 5

    Run the comparison query against your ledger

    Add an Airtable node to list all records from the last 24 hours where `reconciled = false`. Feed this into a Function node that groups records by `transaction_id` and checks: (a) does the same `transaction_id` appear with matching `amount` and `status` in both gateways where it should? (b) does any single-gateway record have an `amount` deviation above your threshold — e.g., ±$0.50 for FX rounding? Output two arrays: `clean_records` and `mismatch_records`. For `clean_records`, batch-update `reconciled = true` via Airtable's batch update endpoint.

  6. 6

    Write mismatch reasons back to Airtable

    For each record in `mismatch_records`, add an Airtable Update node that sets `mismatch_flag = true` and writes a human-readable string to `mismatch_reason` — e.g., 'Amount mismatch: Stripe $102.00 vs ledger $100.00' or 'Razorpay capture missing from ledger'. Create a filtered Airtable view called 'Mismatches – Review' filtered on `mismatch_flag = true` AND `reconciled = false`. That view URL is what you paste into the Slack alert. Whoever investigates should be able to open it and immediately understand what they're looking at.

  7. 7

    Send the Slack alert with mismatch count and direct link

    Add a Slack node targeting `#finance-ops` that fires only if `mismatch_records.length > 0`. Message template: `🚨 *Revenue Reconciliation – {{$today}}* • Stripe transactions processed: {{stripeCount}} • Razorpay transactions processed: {{razorpayCount}} • Mismatches found: {{mismatchCount}} 👉 <AIRTABLE_VIEW_LINK|Review mismatches in Airtable>`. If `mismatch_records.length === 0`, send a quieter confirmation to `#finance-ops-log` so there's an audit trail. Never send zero-mismatch alerts to a busy channel — alert fatigue kills this workflow's usefulness within a week.

  8. 8

    Add a threshold escalation for large discrepancies

    Before the Slack node, add an IF node: if any single mismatch has an `amount` delta greater than $500 or ₹40,000, route to a second Slack message posted to `#exec-alerts` with `@here` and the specific transaction ID and amounts. This separates routine rounding noise from financial errors that need same-day investigation. Set the threshold based on your average transaction size — for low-ACV SaaS ($20–50/month), a $100 delta is meaningful; for high-ACV ($500+/month), use $1,000.

Want me to build this for you instead?

Product Audit and CTO Mode run out of this same thinking. If you’re reading this thinking “I want this, but in my product” — let’s talk.

See services

More like this