Skip to main content

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/create provisions 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.PartyBank is its own planner-permission section, separate from Rsvp/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 and BVN_VERIFIED/KYC_COMPLETE KYC status, and takes richer recipient details). Newer/higher-trust code paths route through the KYC-gated variant.

How it works​

Setup​

  1. POST /partybank/event/:eventId/create (requires transaction PIN) — provisions the wallet.
  2. GET /partybank/event/:eventId/accountStatus — check whether provisioning has completed (creation may be async against the PVB provider).
  3. PATCH /partybank/:walletId/pin/set — set the transaction PIN required for money-movement endpoints.
  4. 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; requires EventSections.PartyBank planner access and the validatorjs bankTransferRules (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's third_party_reference is 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's referenceTag matches the evt:{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 in Pending without 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 accountStatus or 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-details exists specifically because some money-movement contexts need the KYC guarantee that bank-transfer doesn'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.

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.

  • src/modules/partybank/partybank.routes.ts, partybank.controllers.ts, partybank.service.ts
  • src/shared/validations/partybank.validation.ts — bankTransferRules, bankTransferWithDetailsRules, reserveAccountPinRule, transactionPinRule
  • src/shared/middlewares/isuserkyc.middleware.ts, src/shared/enum/kyc.enum.ts — KycStatusFlags
  • src/shared/implementations/piggybusiness/piggyvest-business-implementation.ts — underlying PVB wallet provider
  • src/shared/utils/feature-flags.util.ts — isPartybankCreationEnabled, disabled-state messaging