Short Answer
A fully automated n8n workflow that triggers on new PayPal payments, creates/updates Xero contacts, generates authorized invoices, and records merchant fees as separate spend-money transactions for perfect reconciliation.
The Problem
Manual entry of PayPal sales into Xero is time-consuming and prone to errors, especially when accounting for PayPal's variable transaction fees. Without automation, the bank balance in Xero rarely matches the actual PayPal balance due to missing fee records.
The Outcome
A fully automated n8n workflow that triggers on new PayPal payments, creates/updates Xero contacts, generates authorized invoices, and records merchant fees as separate spend-money transactions for perfect reconciliation.
Step-by-Step Guide
1. **Setup PayPal Webhook**: In n8n, add a 'Webhook' node. Set the HTTP Method to POST. In your PayPal Developer Dashboard, create a Webhook pointing to this URL and select 'PAYMENT.SALE.COMPLETED'.
2. **Configure Node Credentials**: Navigate to 'Credentials' in n8n. Add 'Xero OAuth2 API' credentials by following the n8n documentation to create a Xero App and authorize access to your organization.
3. **Data Transformation (Set Node)**: Add a 'Set' node after the Webhook. Use expressions like `{{ $json.resource.amount.total }}` for the Gross and `{{ $json.resource.transaction_fee.value }}` for the fee. Calculate the Net amount using `{{ $json.resource.amount.total - $json.resource.transaction_fee.value }}`.
4. **Search/Create Contact**: Add a 'Xero' node. Set Resource to 'Contact' and Operation to 'Get All'. Filter by Email using `{{ $json.payer_email }}`. Use an 'If' node: if no contact exists, use another Xero node to 'Create' the contact.
5. **Generate Invoice**: Add a 'Xero' node set to 'Invoice' -> 'Create'. Map the 'Line Items' to include the product name and the total gross amount. Set status to 'AUTHORIZED'.
6. **Apply Payment**: To close the invoice, use a 'Xero' node with Resource 'Payment' -> 'Create', linking the Invoice ID and the PayPal Bank Account ID in Xero.
7. **Record PayPal Fee**: Add a 'Xero' node set to 'Bank Transaction' -> 'Create'. Set the Type as 'SPEND'. Map the fee amount and assign it to your 'Merchant Fees' chart of accounts code (e.g., 400).
8. **Duplicate Prevention**: Use the PayPal `parent_payment` or `id` as the 'Reference' in the Xero Invoice and Bank Transaction nodes to prevent double-entry.
9. **Error Handling**: Create a 'Error Trigger' workflow. If any node fails (e.g., Xero API down), n8n will trigger this sub-workflow to notify you via Slack or Email.
Data Mapping
| PayPal Field | Xero Field | n8n Expression / Logic |
| :--- | :--- | :--- |
| `resource.id` | `Reference` | `{{ $json.resource.id }}` (Strict matching) |
| `payer.payer_info.email` | `Contact Email` | `{{ $json.resource.payer_email }}` |
| `amount.total` | `Invoice Line Amount` | `{{ parseFloat($json.resource.amount.total) }}` |
| `transaction_fee.value` | `Bank Transaction Amount` | `{{ parseFloat($json.resource.transaction_fee.value) }}` (Mapped to Fee Account) |
| `amount.currency` | `CurrencyCode` | `{{ $json.resource.amount.currency }}` (Requires Multi-currency) |
| `create_time` | `Date` | `{{ $today.format('yyyy-MM-dd') }}` |
Gotchas & Failure Modes
• **Webhook Validation**: PayPal requires a 200 OK response quickly. Ensure your n8n Webhook node is set to 'Respond Immediately' or put heavy processing behind a 'Wait' or 'Execute Workflow' node.
• **Strict Typing**: Xero's API expects numbers for amounts. Use `parseFloat()` in n8n expressions to ensure strings like "10.00" become numbers.
• **Rate Limits**: Xero has a limit of 60 requests per minute. If processing bulk historical data, use the n8n 'Wait' node or 'Split in Batches' set to 50 items.
• **Account IDs**: You must use the UUID of the Xero Bank Account, not the account name. Find this by running a 'Bank Account: Get All' node once to fetch the ID.
Verification Checklist
- [ ] **Webhook Connection**: Trigger a test payment in PayPal Sandbox and verify the 'Webhook' node in n8n receives the JSON payload.
- [ ] **Contact Lookup**: Ensure the 'If' node correctly branches between 'Existing Contact' and 'New Contact'.
- [ ] **Math Check**: Verify the Fee + Net Amount in Xero equals the Gross amount received in PayPal.
- [ ] **Reference Check**: Confirm the PayPal Transaction ID appears in the Xero Reference field for easy bank reconciliation.
- [ ] **Error Path**: Manually disconnect the Xero node to ensure your n8n 'Error Trigger' sends a notification.
Ready to Automate?
Build this automation with n8n in minutes.