How to Reconcile Shopify Orders with Stripe Charges
Automatically match Shopify orders to Stripe charges and flag discrepancies.
What This Integration Does
End-of-day finance work usually means cross-referencing a Shopify orders export with a Stripe charges export, hunting for orders that paid in Stripe but never marked paid in Shopify (or the reverse). It's tedious and error prone. This workflow does it daily and only surfaces the exceptions.
The workflow runs on a schedule, fetches yesterday's orders from Shopify and yesterday's charges from Stripe in parallel, matches them by amount, customer email, and timestamp, and reports any mismatches to Slack or your project tracker. A clean run produces a single all-good summary; a broken run names every problematic order so finance can investigate immediately.
Prerequisites
- A Shopify connection with read access to orders.
- A Stripe connection with read access to charges and payment intents.
- A Slack connection for the daily report.
Step 1: Daily Schedule Trigger
Add a Trigger node set to Schedule. Pick a time that runs after your store's daily cutover, typically 02:00 local time. The trigger exposes the run timestamp; derive yesterday's window from it.
Step 2: Parallel Fetch
Add a Parallel node with two branches.
- Branch A: Connector on shopify using
list-ordersfiltered to the previous day'screated_atrange andfinancial_status=paid. - Branch B: Connector on stripe using
list-chargesfiltered to the same timestamp range withstatus=succeeded.
Each branch is paginated. Use a Loop inside each to walk all pages and collect a flat array.
Step 3: Normalize Both Sides
Add a Transform node that produces two arrays with a common shape:
{
"id": "string",
"email": "string",
"amount": "number", // in cents
"currency": "string",
"createdAt": "string"
}
Shopify amounts are in dollar units; Stripe is in cents. Normalize to cents on both sides with math round after multiplying so floats can't bite you.
Step 4: Match by Amount, Currency, and Email
Add a Transform (or a small execute-javascript step in the code connector) that joins the two arrays. A match is a Shopify order and a Stripe charge with the same amount + currency, the same customer email, and timestamps within a 30 minute window. Output three buckets: matched, shopifyOnly, stripeOnly.
Step 5: AI Tiebreak for Fuzzy Cases (Optional)
Real customers sometimes pay with a slightly different email or have a tip added on the Stripe side that's missing from Shopify. Add an Agent step that takes any remaining shopifyOnly and stripeOnly rows and tries to match them with structured output:
{
"matches": [{ "shopifyOrderId": "string", "stripeChargeId": "string", "confidence": "number", "reason": "string" }],
"stillUnmatched": ["string"]
}
Auto-accept matches above 0.9 confidence; surface the rest for human review.
Step 6: Report
Add a slack send-message node that posts a daily summary: counts of matched / Shopify-only / Stripe-only, the discrepancy total in dollars, and a link to each problem row. Route nothing-to-flag runs to a quieter channel so the loud channel only fires when humans need to act.
Tips
- Skip subscription charges in the Stripe pull unless you also sync them to Shopify. They'll always show as Stripe-only otherwise.
- Track recurring exceptions. If the same vendor or product line keeps producing mismatches, fix the upstream cause instead of accepting daily noise.
- Store yesterday's reconciliation result somewhere queryable so you can chart the discrepancy trend over weeks.
Common Pitfalls
- Timezone boundaries. Shopify and Stripe might report timestamps in different zones depending on your account settings. Normalize both sides to UTC before comparing.
- Refunds and partial captures. A Stripe charge can be partially refunded after the Shopify order is captured. Match on the net amount, not the gross.
- Gift cards and store credit. These show in Shopify as a paid line but never hit Stripe. Exclude orders with non-Stripe payment gateways from the comparison.
Testing
Run the workflow manually for a known clean day and confirm zero exceptions. Then run it for a day you know had a refund or a void; the report should name those orders. Only enable the schedule once the matching logic produces zero false positives over a sample week.