Payout account
Every seller that wants to receive funds needs a PayoutAccount. This account links a seller to an external payment provider (e.g. Stripe Connect).Account lifecycle
| Status | Description |
|---|---|
PENDING | Account created, awaiting provider onboarding |
ACTIVE | Fully onboarded, can receive payouts |
RESTRICTED | Provider has flagged the account (e.g. missing KYC) |
REJECTED | Provider rejected the account |
Onboarding
When a payout account is created, an Onboarding record is attached to it. This stores provider-specific data needed for identity verification or account setup (e.g. Stripe Connect onboarding links).The payout pipeline
The payout flow is fully automated through two scheduled jobs and event-driven subscribers. No manual intervention is needed after configuration.1. Capture check (every 15 minutes)
A scheduled job scans for orders that are ready for payment capture. For each order, it checks:- Payment is in
authorizedstatus - Seller has an
ACTIVEpayout account - Order meets the required fulfillment status (configurable, default:
fulfilled) - No payout has been created yet
order.capture_requested event. If the authorization has already expired, it emits order.authorization_expired instead.
2. Payment capture (event-driven)
A subscriber listens fororder.capture_requested events and runs the Medusa capturePaymentWorkflow to capture the authorized payment. On success, the order is marked as captured. On failure, the order is flagged so it won’t be retried.
3. Daily payouts (once per day)
A daily scheduled job (1 AM UTC) scans for captured orders that haven’t been paid out yet. For each eligible order, it emits apayout.requested event.
An order qualifies for payout when:
- Payment has been captured (
metadata.captured = true) - No existing payout transfer
- Seller has an
ACTIVEpayout account
4. Payout transfer (event-driven)
A subscriber listens forpayout.requested events and runs the createPayoutWorkflow:
- Fetch order — Loads the order with its seller, payout account, and commission lines
- Calculate payout amount — Subtracts commission from the order total:
- Create payout — Calls the payment provider to initiate the transfer and creates a
Payoutrecord - Link payout — Associates the payout with the seller
Payout statuses
| Status | Description |
|---|---|
PENDING | Payout created, transfer initiated |
PROCESSING | Provider is processing the transfer |
PAID | Funds successfully transferred |
FAILED | Transfer failed |
CANCELED | Transfer was canceled |
Webhook handling
A subscriber listens forpayout.webhook_received events and delegates to the processPayoutForWebhookWorkflow. The provider parses the raw webhook payload and returns an action:
Account events:
account.activated→ Status set toACTIVEaccount.restricted→ Status set toRESTRICTEDaccount.rejected→ Status set toREJECTED
payout.processing→ Status set toPROCESSINGpayout.paid→ Status set toPAIDpayout.failed→ Status set toFAILEDpayout.canceled→ Status set toCANCELED
Configuration
The payout module accepts the following options inmedusa-config.ts:
| Option | Default | Description |
|---|---|---|
disabled | false | Disables both scheduled jobs |
authorizationWindowMs | 7 days | How long a payment authorization is valid |
sellerActionWindowMs | 72 hours | Time for seller to fulfill the order |
captureSafetyBufferMs | 24 hours | Safety margin before authorization expiry to trigger capture |
requiredFulfillmentStatus | "fulfilled" | Minimum fulfillment status before capture is allowed |
Data model overview
Configuration example
API examples
Get seller’s payout account (Vendor API)
Create payout onboarding (Vendor API)
Initiates provider onboarding (e.g. Stripe Connect account setup). Thedata and context fields are passed to the payout provider:
List payouts (Admin API)
Provider abstraction
The payout module is provider-agnostic. APayoutProviderService acts as a bridge to the external payment processor. It delegates all external operations — creating accounts, processing payouts, parsing webhooks — to a pluggable provider implementation.
The module expects exactly one payout provider to be registered. Provider-specific data is stored in the data JSON fields on accounts, payouts, and onboarding records.