Automated Stripe Revenue & Fee Sync to QuickBooks Online (n8n)
Eliminate manual bookkeeping by automatically syncing Stripe transactions to QuickBooks with fee reconciliation using n8n.
Tools: Stripe → QuickBooks Online
Platform: n8n
Short Answer
A fully automated workflow that detects new Stripe charges, converts cents to decimals, creates or updates customers in QBO, and generates Sales Receipts that include a negative line item for Stripe fees, ensuring 1:1 bank reconciliation.
The Problem
Manual entry of Stripe sales into QuickBooks leads to human error and reconciliation headaches. Businesses often fail to account for Stripe's 'net' payouts, resulting in bank balances that don't match accounting records due to ignored processing fees.
The Outcome
A fully automated workflow that detects new Stripe charges, converts cents to decimals, creates or updates customers in QBO, and generates Sales Receipts that include a negative line item for Stripe fees, ensuring 1:1 bank reconciliation.
Step-by-Step Guide
1. **Create Credentials**: In n8n, go to 'Credentials' and add 'Stripe API' (Secret Key) and 'QuickBooks Online OAuth2'. Follow the OAuth flow to authorize n8n access to your QBO company.
2. **Stripe Webhook Node**: Add a 'Stripe Trigger' node. Set the event to `charge.succeeded`. Copy the 'Test URL' and paste it into the Stripe Dashboard (Webhooks section) to start receiving real-time data.
3. **QuickBooks Customer Search**: Add a 'QuickBooks Online' node. Set the Resource to 'Customer' and Operation to 'Get All'. Use a Filter with the expression `{{ $json["billing_details"]["email"] }}` to find an existing customer.
4. **Handle New Customers**: Add an 'IF' node to check if the Customer Search returned a result. If false, add another QBO node to 'Create' a Customer using Stripe's `billing_details.name` and `email`.
5. **Data Transformation (The Math)**: Stripe sends amounts in cents (e.g., 5000 = $50.00). Use a 'Set' node or 'Edit Fields' node. Create an expression for `GrossAmount`: `{{ $json["amount"] / 100 }}` and `FeeAmount`: `{{ $json["balance_transaction"]["fee"] / 100 }}`.
6. **Format Date**: Use the expression `{{ $now.format('yyyy-MM-dd') }}` or convert Stripe's Unix timestamp using `{{ DateTime.fromSeconds($json["created"]).toISODate() }}` to match QBO's date format.
7. **Create Sales Receipt**: Add a QBO node set to 'Sales Receipt' > 'Create'. Map the Customer ID from Step 3/4. Add two Line Items: Line 1 for Revenue (Gross) and Line 2 for Fees (as a negative value reaching your 'Bank Fees' account).
8. **Error Handling**: Connect an 'Error Trigger' node to a separate flow (like Slack or Discord) to notify you if the QBO API fails (e.g., due to a disconnected token or expired trial).
Data Mapping
| Stripe Field | QBO Destination Field | Transformation / Expression |
| :--- | :--- | :--- |
| `customer.email` | `Customer: Search/Match` | `{{ $json["billing_details"]["email"] }}` |
| `amount` | `Line Item 1: Amount` | `{{ $json["amount"] / 100 }}` (Required) |
| `balance_transaction.fee` | `Line Item 2: Amount` | `{{ ($json["fee"] / 100) * -1 }}` (For fee reconciliation) |
| `currency` | `Currency` | `{{ $json["currency"].toUpperCase() }}` |
| `id` | `Private Note / Memo` | `Stripe Charge ID: {{ $json["id"] }}` |
| `created` | `Transaction Date` | `{{ DateTime.fromSeconds($json["created"]).toISODate() }}` |
Gotchas & Failure Modes
* **Cents vs. Decimals**: Stale entries often occur because Stripe uses integers (100) while QBO expects floats (1.00). Always divide by 100 in your expressions.
* **The Webhook Loop**: If you have other automations that update Stripe from QBO, ensure you don't create an infinite loop. Stick to a one-way 'Stripe as Source' model.
* **OIDC/OAuth Timeouts**: QBO credentials in n8n can occasionally expire if the workflow isn't run frequently. For self-hosted users, ensure your `WEBHOOK_URL` env variable is correctly set so QBO can send the callback.
* **Multi-Currency**: If Stripe collects a payment in EUR but your QBO base is USD, QBO requires the exchange rate. Use n8n's 'Amount/Currency' nodes if you handle high-volume cross-border sales.
Verification Checklist
- [ ] **Webhook Handshake**: Send a 'Test Hook' from Stripe and verify the 'Stripe Trigger' node in n8n receives the JSON payload.
- [ ] **Math Check**: Verify the 'Set' node output shows `50.00` and not `5000` for a $50 charge.
- [ ] **Duplicate Logic**: Run the node twice for the same email to ensure the 'IF' node correctly identifies existing customers.
- [ ] **QBO Line Items**: Log into QBO and ensure the Sales Receipt shows the gross amount on line 1 and the negative fee on line 2.
- [ ] **Production Switch**: Move the Stripe Webhook from the 'Test URL' to the 'Production URL' in n8n and toggle the workflow to 'Active'.
Ready to Automate?
Build this automation with n8n in minutes.