Recipe: Payout flow and reconciliation

Goal

Understand (and verify) the money flow for a marketplace order: commission transfer to the vendor’s Stripe account, then payout from Stripe to the vendor’s bank, with both stages recorded on the WooCommerce order.


Before you start

  • Initial Stripe setup and Stripe Connect vendor onboarding are done — vendors are stripe_approved.
  • MAM WooCommerce Product Vendors is active with Stripe selected as the payout processor.

Stage 1 — Transfer (order → vendor’s Stripe balance)

When commissions for an order are paid out, MAM WooCommerce Product Vendors applies the mam_stripe_connect_transfer filter once per vendor with that vendor’s commission amount. The plugin then:

  1. Validates (all deny-by-default): order and vendor exist; the amount does not exceed the order total; the vendor actually has products on the order (filter mam_stripe_transfer_vendor_on_order); the vendor has an acct_… id and is stripe_approved.
  2. Skips duplicates: each vendor/order/amount/currency combination is transferred at most once (the key is stored on the order), so re-running a payout never double-pays.
  3. Creates the Stripe Transfer in the order’s currency, grouped as order_{id}, sourced from the order’s charge when one is recorded, with metadata wp_order_id and vendor_id.
  4. Emails the vendor admins the “Stripe – Seller Transfer Completed” notification (replacements: order_id, amount, paid_amount, paid_date, paid_gateway).

Order meta written by a transfer

Meta key Contents
_mam_stripe_transfer_ids Flat list of all transfer ids (tr_…) for the order
_mam_stripe_transfer_keys The idempotency keys already used (v{vendor}_o{order}_a{cents}_c{currency})
_mam_stripe_transfer_ids_by_vendor Transfer ids grouped by vendor id
_mam_stripe_transfers One record per transfer: transfer_id, payment_id (destination payment), vendor_id, amount (smallest currency unit), currency, created_at, and — empty until stage 2 — payout_id, paid_at

Stage 2 — Payout reconciliation (vendor’s Stripe balance → bank)

Days later, Stripe batches the vendor’s balance into a payout to their bank. The connected account emits payout.created / payout.paid webhook events, which Stripe delivers to this site’s endpoint:

POST https://your-site/wp-json/mam-stripe/v1/webhook

For each verified payout event the plugin lists the payout’s balance transactions on the connected account, finds the transfers it knows about, and fills in payout_id and paid_at on the matching _mam_stripe_transfers record — so the order now shows not just “we sent the vendor their commission” but “Stripe paid it to their bank on this date, in this payout”. Already-linked transfers are never overwritten.

You don’t set this webhook up

The endpoint and its signing secret are self-provisioned (see Recipe: Initial Stripe setup, step 4). Every delivery is signature-verified; events other than payout.created/payout.paid, or events without a connected-account context, are acknowledged and ignored. If you ever created this webhook by hand in the Stripe dashboard, delete the old endpoint once — the auto-provisioned one replaces it.


Verifying / troubleshooting

Check How
Transfer happened Order meta _mam_stripe_transfer_ids has a tr_… id; the transfer is visible in the Stripe dashboard under the order’s transfer group.
Vendor was notified The “Stripe – Seller Transfer Completed” notification was sent to the vendor-admin users.
Payout linked The order’s _mam_stripe_transfers record for that transfer has payout_id/paid_at filled in after Stripe pays the vendor (test-mode payouts can take a simulated day).
Webhook healthy Stripe dashboard → Developers → Webhooks → the auto-provisioned endpoint shows 2xx deliveries. Signature failures usually mean a stale, hand-made endpoint still exists (delete it) — the plugin re-checks and heals its own endpoint automatically.
Transfer refused The debug log (stripe_connect_transfer channel) records the exact reason: amount over order total, vendor not on order, vendor missing account id, or vendor not approved.

  • Recipe: Initial Stripe setup
  • Recipe: Stripe Connect vendor onboarding
  • Hook: mam_stripe_connect_transfer
  • Hooks: Stripe security filters

Metadata

Field Value
Article type Recipe (Admin)
Plugin slug mam-stripe-manager
Applies to plugin version 26.19.1+
Category Building Your App
Audience WordPress admin / store manager
Estimated time 10 minutes (reading; the flow itself is automatic)
Last verified 2026-06-10
Contents

    Need Support?

    Can't find the answer you're looking for? Don't worry we're here to help!