PartyBank
What it is
PartyBank is an event's own wallet — every other money-touching guide in this site (Payments/Payouts, Broadcast, Vendors, Promoter) ultimately debits or credits a PartyBank wallet. It's built on PiggyVest Business (PVB) wallet infrastructure underneath, one PVB wallet per event.
Core concepts
- One PartyBank per event, created lazily — it doesn't exist automatically when an event is created;
POST /partybank/event/:eventId/createprovisions it (idempotent — calling it again just returns the existing one). The owner is always the event's creator (event.createdBy), not whoever happens to call the create endpoint. - Creation is a remote kill switch — gated by a Firebase Remote Config flag (
isPartybankCreationEnabled), with a Firebase-configurable error message shown when disabled. This means wallet creation platform-wide can be paused without a deploy (e.g. during a PVB provider incident) — if you see "PartyBank creation is currently disabled" in the wild, check Firebase config before assuming a bug. EventSections.PartyBankis its own planner-permission section, separate fromRsvp/Vendor/TicketHero— a co-planner can have wallet access or not, independent of what else they can do on the event.- Two ways to send money out, deliberately different in strictness:
bank-transfer(simpler, existing validation) vs.bank-transfer-details(requires a transaction PIN andBVN_VERIFIED/KYC_COMPLETEKYC status, and takes richer recipient details). Newer/higher-trust code paths route through the KYC-gated variant.
How it works
Setup
POST /partybank/event/:eventId/create(requires transaction PIN) — provisions the wallet.GET /partybank/event/:eventId/accountStatus— check whether provisioning has completed (creation may be async against the PVB provider).PATCH /partybank/:walletId/pin/set— set the transaction PIN required for money-movement endpoints.POST /partybank/reserve-account— provision a PVB "pocket" sub-account (same underlying concept as the personal-wallet reserve account in the User guide).
Funding and moving money
GET /partybank/:ref/funding-account— dedicated bank details for funding the wallet by transfer.POST /partybank/event/:eventId/bank-transfer— send money out; requiresEventSections.PartyBankplanner access and the validatorjsbankTransferRules(amount, accountNumber, bankCode, transactionPin, narration).POST /partybank/event/:eventId/bank-transfer-details— the stricter path: transaction PIN +isUserKycStatus([BVN_VERIFIED, KYC_COMPLETE])+bankTransferWithDetailsRules(adds recipient, description, bankName, tagId). This is the variant used when the platform needs higher confidence in who's receiving the money — e.g. vendor payouts (see the Vendors guide).GET /partybank/bank-details/verify,GET /partybank/bank-list— resolve an account name from account number + bank code, and list supported banks, both used to validate recipient details client-side before submitting a transfer.
Records and reconciliation
GET /partybank/event/:eventId/transactions(planner) /GET /partybank/admin/event/:eventId/transactions(admin) — transaction history.GET /partybank/event/:eventId/wallet/cards— display-oriented wallet summary cards.POST /partybank/event/:eventId/statement/download/.../statement/pdf— generate a formal statement, the PDF variant via an external PDF-rendering service.- PVB webhook (
piggy.payment.success) is what actually confirms an incoming transfer — the webhook payload'sthird_party_referenceis matched against the platform's own transaction record; if no match is found, the webhook is logged and dropped rather than erroring. This is the same "never trust the client, only the webhook" pattern used throughout Payments. - Both the real webhook path and manual completion funnel through one shared function,
PartybankService.completeTransfer()— it validates the transaction, applies the status, decrements the PartyBank balance, and (if the transaction'sreferenceTagmatches theevt:{eventId}-vdr:{vendorId}pattern) triggers the vendor-amount and weighted-rating recalculation covered in Vendors.manuallyCompleteTransfer()/manuallyFailTransfer()are thin convenience wrappers around it, meant for support/engineering to resolve a transaction stuck inPendingwithout hand-editing the database — there's no CLI or admin-UI wired up to them yet, so using them today means calling the service method directly (e.g. from a one-off script or the Node REPL against the running app), not a documented command.
Testing bank transfers locally
POST /partybank/cron/transfer-webhook-simulation exists specifically to simulate the PVB webhook in development — NODE_ENV !== 'production' only. Rather than faking the webhook call inline, a real bank-transfer request in dev schedules an actual one-shot Google Cloud Scheduler job (10-second delay) that hits this endpoint, so the async timing of a real webhook is reproduced rather than short-circuited. The job self-deletes after running. This means testing a transfer locally still requires gcloud authenticated against the project and a reachable SERVICE_URL (e.g. an ngrok tunnel) — there's no fully-offline mock path.
Key business rules & edge cases
- Don't assume PartyBank exists just because an event does — always check
accountStatusor handle the "not found" case; plenty of events never provision one. - The two bank-transfer endpoints are not interchangeable shortcuts for the same thing —
bank-transfer-detailsexists specifically because some money-movement contexts need the KYC guarantee thatbank-transferdoesn't enforce. Don't "simplify" a caller from the KYC-gated endpoint to the plain one to work around a KYC failure. - Transaction PIN (
encryptedPinMiddleware) and KYC status are two independent gates that can both apply to the same endpoint — failing either blocks the request; check server logs/response for which one actually failed rather than assuming.
Related endpoints
POST /v1/partybank/event/{eventId}/create, GET /v1/partybank/event/{eventId}/accountStatus, GET /v1/partybank/event/{eventId}, GET /v1/partybank/{ref}/funding-account, POST /v1/partybank/event/{eventId}/bank-transfer, POST /v1/partybank/event/{eventId}/bank-transfer-details, GET /v1/partybank/event/{eventId}/transactions. Not yet in the generated OpenAPI spec.
Related code
src/modules/partybank/partybank.routes.ts,partybank.controllers.ts,partybank.service.tssrc/shared/validations/partybank.validation.ts—bankTransferRules,bankTransferWithDetailsRules,reserveAccountPinRule,transactionPinRulesrc/shared/middlewares/isuserkyc.middleware.ts,src/shared/enum/kyc.enum.ts—KycStatusFlagssrc/shared/implementations/piggybusiness/piggyvest-business-implementation.ts— underlying PVB wallet providersrc/shared/utils/feature-flags.util.ts—isPartybankCreationEnabled, disabled-state messaging