Match every order line to the corresponding payout deposit while correctly attributing provider fees, adjustments, and timing differences.
Fetch transaction and payout reports on a fixed cadence
Square, Clover, and Toast each expose separate endpoints for orders and for payouts. You should poll the orders endpoint at least once per hour for the prior 48 hours and the payouts endpoint once per day for the prior seven days. Store the raw JSON responses with the original request timestamp and the provider’s payout_id or batch_id so later joins do not rely on memory.
Clover and Toast return fee lines inside the payout object; Square surfaces them as separate payment adjustments. If your first fetch misses an adjustment because the payout was still processing, the next daily run must re-fetch that same payout_id rather than skip it. Cache the payout state in your own table and only mark a payout complete after its status field shows “COMPLETED” or equivalent.
Auth tokens for all three providers expire. Refresh them on every call and store the new token before making the data request. A failed refresh followed by an immediate retry with the old token produces 401s that look like rate-limit errors; log the exact error code so you can distinguish the two.
Join orders to payouts using provider-native identifiers
Every order carries a transaction_id that appears inside the payout’s line items. Use that field as the primary join key. When a payout line references an order that your local store does not yet have, queue the order_id for an immediate re-fetch instead of waiting for the next hourly batch. This prevents the common case where a same-day payout report arrives before the order record has propagated to your database.
Toast sometimes splits a single order across multiple payout lines when tips and taxes settle on different schedules. Your reconciliation query must therefore group by order_id and sum the fee and net amounts rather than assume a one-to-one mapping. Square’s “payout_entry_type” field further distinguishes “CHARGE”, “REFUND”, and “ADJUSTMENT”; map each type to your internal ledger so later fee reconciliation does not double-count.
Reconcile fees and adjustments line by line
Square, Clover, and Toast each apply different fee schedules and timing. Record the exact fee amount reported in the payout rather than recalculating from your own rate table. When the reported fee differs from your expected calculation, write the delta to an exceptions table with the payout_id and the raw fee line so finance can review it without breaking the automated match.
Refunds and chargebacks appear as negative entries in later payouts. Your matching routine must treat a refund that references an earlier order as a separate reconciliation event rather than attempting to invert the original order row. Store both the original order and the refund as distinct records linked by the same payout_id.
Handle late-arriving data with idempotent retries
Webhook delivery for payout.created events is not guaranteed on any of the three platforms. Implement a daily reconciliation job that walks every open payout and re-checks its status regardless of whether a webhook was received. Make the job idempotent by checking a “last_reconciled_at” timestamp on the payout record before writing new matches.
When an order appears in a payout but the corresponding order record is still missing after three re-fetch attempts, surface the payout_id and order_id to an operator queue. Do not silently drop the row; the most common cause is an order created through a channel that your integration does not yet ingest. Exponential backoff on the fetch calls prevents hammering the provider when their API is returning 429s.
