A wallet (wallet) belongs to one person: a customer, an affiliate or a host. Wallets are opened for them when they first earn (affiliate commission; see Affiliate program), when they open their account's wallet, or by staff. Only the earnings balance can be paid out. Store credit and reward points in the same wallet are only spent; see Store credit, reward points and wallets.
available = balance − held − reservedA payout request reserves its amount. It is a payout record (appengine finance/payout.service.ts):
pending → approved → processing → completed
└──────────┴──────────┴──→ failed (pending, approved, processing)
└──────────┴──→ cancelled (pending, approved)Payouts are sent two ways only: PayPal (the PayPal Payouts API, with the org's PayPal connection) and bank (an ACH file staff take to the bank). Other method types in old records (Venmo, Cash App, check, wire…) can't be added or sent (validatePayoutMethodInput, 1661). Tested: a Venmo method was refused: "Unsupported payout method "venmo". This platform pays by paypal or bank."
A payout pays real money to a real account. PayPal sends the moment it is processed. Never approve, process, complete or batch a payout without the owner's yes on that payout.
Ask first
- Who approves? A person every time, everything automatically, or automatically under an amount.
- How are people paid? PayPal, bank (ACH), or both. For bank: the business's ACH sender details from its bank.
- Minimum payout? Set per wallet. There is no org-wide minimum.
- Which payout? The payout number, who, how much, which method, and whether the method is verified.
Steps
1. Finance › Payout rules
Studio: Finance (/finance), tab Payout rules (websitemint payout-rules.tsx). Saved as a setting with type: "payout_config":
| Field | On screen | What it does |
|---|---|---|
approval.mode | Somebody approves every payout / Approve everything automatically | manual or auto. |
approval.autoUnder | …except anything under | In manual mode, payouts at or under this amount are approved automatically. |
schedule | When payouts are raised | manual (Only when asked), daily, weekly, biweekly, monthly. |
Nothing runs the schedule by itself (runScheduledPayouts, 1090, has no timer). Run a payout cycle now (POST finance/payouts/run-schedule { force: true }) raises one payout for each active wallet with money available, a working method, nothing in flight and at least its own minimum. Wallets set to manual are skipped. It pays nobody by itself.
Stop: auto approval sends payouts to processing without a person. Show the rule and wait for yes.
Check: reopen Payout rules; it shows what was saved.
2. Payout methods
The holder adds their own in My Account (client/finance/payout-methods; see Customer account portal). Staff can add one on the wallet (websitemint payout-method-manager.tsx; POST finance/wallets/<walletId>/payout-methods). Staff need the Storefront › Finance permission for this (payout-method-access.guard.ts).
| Type | Body | Checked |
|---|---|---|
paypal | { type: "paypal", label?, isDefault?, paypal: { email } } | A valid email. |
bank | { type: "bank", label?, isDefault?, bank: { bankName?, accountType: checking | savings, routingNumber, accountNumber, accountHolderName } } | Routing check digit, account of 4 or more, holder name. The numbers are sealed; only the last four can be read again. |
GET finance/payout-method-options says which types are on, whether bank details can be stored, and whether Plaid is set up. With Plaid, the holder links their bank (…/payout-methods/plaid/link-token, then …/plaid/exchange) and the method is verified.
A method is pending, verified, failed or disabled. An unverified method can still be paid. Its verified flag is copied onto each payout so the approver sees it. A disabled one is refused. Adding a method emails the holder payout-method-added.
Tested: a PayPal method added by staff showed pending, not verified, "added by staff".
Stop: this emails the holder. Bank details come from the holder, never from a guess.
Hands on: the method id.
Check: GET finance/wallets/<walletId>/payout-methods lists it.
3. Finance › Wallets: credit, hold and freeze
Tab Wallets (/finance?openWallet=<sk>). With call_appengine (finance/payout.controller.ts):
| Call | Body | Notes |
|---|---|---|
POST finance/wallets/get-or-create | { ownerType, ownerId, email, name } | One wallet per owner. |
POST finance/wallets/<id>/credit | { amount, type, reference, description } | type: earning, tip, bonus, commission, referral, refund, adjustment. Emails the holder wallet-credited. |
POST finance/wallets/<id>/debit | { amount, type, reference, description } | type: payout, fee, chargeback, adjustment. |
POST finance/wallets/<id>/hold | { reason, amount, reference?, expiresAt?, notes? } | reason: dispute, chargeback, security, deposit, pending_review, other. Moves money from available to held. |
POST finance/wallets/<id>/release/<holdIndex> | By the hold's position in holds[], from 0. Read the wallet first. | |
POST finance/wallets/<id>/freeze | { reason } | Stops credits, debits, payouts and store credit changes. "Wallet is frozen: |
POST finance/wallets/<id>/unfreeze | ||
PUT finance/wallets/<id>/payout-settings | { frequency, minimumAmount, dayOfWeek, dayOfMonth } | frequency: manual, daily, weekly, monthly. minimumAmount is also the wallet's minimum payout. dayOfWeek 0–6, dayOfMonth 1–28. |
expiresAt on a hold is only recorded. Release holds by hand.
Stop before a credit (it emails the holder and books the amount as owed), a debit, a hold or a freeze.
Tested: credit $50, hold $5; a request for $48 was refused ("Only 45.00 of earnings…"); release worked.
Check: GET finance/wallets/<id> shows balance, heldBalance, reservedBalance and holds[].
4. The request
The holder presses Request Payout in My Account (POST client/finance/payouts/request { amount, methodId?, notes? }), or staff request for them (POST finance/wallets/<walletId>/request-payout, same body). Checked in order (requestPayout, 854):
- The wallet is active.
- The amount is no more than available.
- The wallet's
limits.minimumPayout,maximumPayoutanddailyLimit. - A method: the one named, else the default, else the first. "No payout method configured. Please add a payout method first." (tested)
The request saves a copy of where the money goes, reserves the amount, emails the holder payout-requested, and is approved at once when the payout rules say so.
Hands on: the payout id and payoutNumber.
Check: it is pending (or approved), and the wallet's reservedBalance went up by the amount (tested).
5. Finance › Payouts: approve, process, complete
Tab Payouts (/finance?openPayout=<sk>; websitemint payout-list.tsx). Show the owner the payout first: who, how much, the method, verified or not.
| Button | Call | From | What happens | |
|---|---|---|---|---|
| Approve | POST finance/payouts/<id>/approve { approvedBy, notes } | pending | approved. Tested; a second approve: "Payout is not pending". | payout-approved |
| Process | POST finance/payouts/<id>/process | pending, approved | PayPal: sent now. Bank: queued for the next ACH file. Either way processing. | payout-submitted |
| Complete | POST finance/payouts/<id>/complete { externalReference } | approved, processing | completed: the balance goes down, the reserve is removed, a ledger line is written and it is booked. | payout-completed |
| Mark Failed | POST finance/payouts/<id>/fail { reason, code } | pending, approved, processing | failed, reserve removed. Tested. | payout-rejected |
| Cancel | POST finance/payouts/<id>/cancel { reason } | pending, approved | cancelled, reserve removed. Tested; a second cancel: "Payout cannot be cancelled". | payout-rejected |
Stop: before every one of these. Process sends money by PayPal at once, and Complete records money as paid.
PayPal payouts complete by themselves: PayPal's webhook (PAYMENT.PAYOUTS-ITEM.SUCCEEDED, FAILED, RETURNED…) settles or fails them (payout-monitor.service.ts). If one is stuck in processing, ask PayPal again with POST finance/payouts/<id>/refresh-status, or POST finance/payouts/sweep for all of them. Don't press Complete on a PayPal payout.
Use Complete by hand only for money paid outside the platform, with the bank or PayPal reference as externalReference.
POST finance/batch/payouts { walletIds, minAmount? } raises a request for each wallet's whole balance (it doesn't approve or send; a wallet with anything held or reserved is refused and skipped). GET finance/stats/payouts gives the totals.
Check: the payout's history shows each step, and the wallet's balance went down only at completion.
6. Finance › ACH runs: bank payouts
Tab ACH runs (websitemint ach-runs.tsx; payout-batch.service.ts).
Sender details (PUT finance/payout-ach-config), from the business's bank. A file can't be built without the first three:
| Field | On screen |
|---|---|
originatorName | Company name (up to 16 characters) |
originatorId | Company ID (usually 1 + the EIN, 10 characters) |
odfiRoutingNumber | Your bank's routing number |
immediateDestinationName | Your bank's name (optional) |
companyEntryDescription | Entry description, e.g. PAYOUT (optional) |
notifyEmail | Tell this email when a file is ready (optional) |
Then, per run:
| Button | Call | What happens |
|---|---|---|
| (list) | GET finance/payout-batches/eligible | Approved or processing bank payouts not yet in a file. |
| Build the file | POST finance/payout-batches { payoutIds?, effectiveDate? } | A NACHA file. The payouts go to processing. Emails payout-batch-ready to notifyEmail. |
| (download) | GET finance/payout-batches/<id>/file | The file to upload at the bank. |
| It is with the bank | POST finance/payout-batches/<id>/sent | Books the payouts. |
| The bank confirmed it | POST finance/payout-batches/<id>/settle | Completes every payout in it. |
| It bounced | POST finance/payout-batches/<id>/items/<payoutId>/return | Fails that payout (ach_return): its reserve is released (the balance was never taken down), its booking is turned around, and the holder gets payout-returned. |
A batch reads Waiting to go to the bank, With the bank, Settled, Partly returned or Cancelled.
Stop: before building, sending and settling. Uploading the file at the bank is a person's job.
Record a bounce before settling the batch. After settling, the payout is completed and can no longer be failed (payout-batch.service.ts 580, payout.service.ts 1432).
(Code only: no ACH file was built in testing.)
Books
Books › Posting choices › "How wallet payouts are booked" (walletPayouts, appengine business-made/ledger/posting-choices.ts):
| Choice | Credits | Payouts |
|---|---|---|
liability (default) | Booked as an expense owed to the holder | Pay the debt off |
expense | Nothing | Booked as an expense when the money leaves |
none | Nothing | Nothing |
A payout is booked once: when its ACH file goes to the bank, or when it completes. A return turns it around. Holds and freezes post nothing. See Finance and Wallets, payouts and payments.
Emails a payout sends
All go to the wallet holder, except payout-batch-ready:
| Template | When |
|---|---|
wallet-credited | Staff credit the wallet |
payout-method-added | A method is added |
payout-requested | A payout is requested |
payout-approved | Approve |
payout-submitted | Process (PayPal sent, or bank queued) |
payout-completed | Complete, a PayPal success, or a settled ACH file |
payout-rejected | Fail or cancel |
payout-returned | An ACH return |
payout-batch-ready | An ACH file is built (to notifyEmail) |
When it's done
- The payout reads
completedwith an external reference, the wallet's balance is down by the amount and its reserve is back to 0. - Or it reads
failed/cancelledwith a reason, and the reserve is removed. - It is in Books under the posting choice.
What it can't do yet
- No timer. The schedule, the wallet's own frequency and day, and the PayPal sweep run only when someone asks.
- Daily and weekly limits.
dailyLimitis checked but the day's total is never counted up, andweeklyLimitisn't used (payout.service.ts902–906). - Frequency choices differ. The wallet record and Payout rules offer biweekly (and the record instant), but a wallet's own payout settings accept only manual, daily, weekly and monthly (
wallet-controls.ts17). - No Venmo, Cash App, check or wire.
- No undo after an ACH settle (step 6).
Don'ts
- Don't approve, process, complete, batch or settle a payout without a yes on that payout.
- Don't press Complete on a PayPal payout: PayPal settles it.
- Don't settle an ACH file before the bank has confirmed every entry.
- Don't release a hold by index without reading
holds[]first. - Don't type bank details for a holder from a message or a guess. The holder adds them, or Plaid verifies them.
- Don't offer to pay out store credit or reward points.