Skip to content

PRD 08 — Sales & Payments

Status: draft · Priority tier: P0 (manual POS, transactions, manual methods, taxes) / P1 (pay later, policies, refund records) / P2 (gateway, recurring billing, wallet) · Size: L · Depends on: PRD 01 (business, currency, locations, staff), PRD 06 (plans, promo), PRD 07 (customers)

1. Summary

Everything money: the sales list, a point-of-sale "add transaction" flow for staff, transaction detail with void/refund/edit, checkout rules (pay later), payment methods (manual offline methods in v1, gateway later), taxes, optional wallet, policy texts, and the rules for retrying failed recurring charges.

2. Problem & goals

  • Staff record a cash sale of a package in ≤ 9 clicks (original: ~9) and the customer gets credits and a receipt immediately.
  • Every sale yields an immutable, numbered transaction with a visible timeline; no silent edits.
  • Totals reconcile: price - discount + tax (or tax-inclusive) = amount, to the minor unit, in 100% of tests.
  • Non-goals (v1): moving real money (no gateway), recurring charges, wallet, retail inventory, payroll.

3. Users & roles

Owner/admin, manager, front desk (create sales, mark paid), accountant ✱ (view + reports, no create). Customer sees receipts and checkout policy text.

4. Scope

In (v1)

  • must: Sales list with date, item type, channel, method, status filters, search
  • must: Add transaction (POS): customer, item (package/membership; retail, course, top-up later), discounts, pay now by manual method (cash, bank transfer, custom) marked as paid
  • must: Free grant (zero-amount) transaction
  • must: Transaction detail, timeline, invoice and receipt (PDF), send receipt
  • must: Manual payment methods (preseeded cash, bank transfer; custom with details and image; deactivate, no hard delete)
  • must: Taxes (one per country, inclusive or exclusive) applied to totals
  • must: Refund as a record (reason, optional notify) and void
  • should: Pay later with due date; reminder email; "allow booking with unpaid plan"
  • should: Edit sale and edit payment with adjustment reason (audited)
  • should: Policy texts (refund, class cancellation, appointment cancellation) shown at payment
  • should: Proof-of-payment upload on manual methods (customer-facing) Later (v2+)

  • must (when gateway lands): Payment provider connection, online card/wallet payments, gateway refunds, retry charge

  • should: Recurring membership billing with retry/dunning rules
  • could: Wallet (top-up, partial pay, refunds to wallet); waive, mark uncollectible, approve/decline for manual payments needing approval
  • could: Bill to account (invoice to a connected processor) Out

  • Handling card data ourselves: use a certified gateway only.

  • "Mark as refund returns no money": we keep refund-as-record semantics for manual methods; gateway refunds are a separate action.

5. User stories

  • US-08-01 As a front desk user, I want to sell a package for cash so that the customer can book right away.
  • US-08-02 As a front desk user, I want to apply a promo code or custom discount so that I can honour offers.
  • US-08-03 As an owner, I want every sale numbered with invoice and receipt so that accounting is clean.
  • US-08-04 As an owner, I want to void a mistaken sale so that the plan is deactivated without touching bookings.
  • US-08-05 As an owner, I want to record a refund with a reason so that books match what I returned outside the system.
  • US-08-06 As an owner, I want to allow pay later with a due date so that corporate clients can pay after.
  • US-08-07 As an owner, I want to define tax and whether prices include it so that receipts are legal.
  • US-08-08 As an owner, I want bank transfer instructions and proof upload so that customers can pay offline.
  • US-08-09 As an owner, I want failed recurring charges retried automatically so that I lose less revenue.
  • US-08-10 As a customer, I want to read the refund and cancellation policy before paying.

6. Functional requirements

ID requirement priority evidence
FR-08-01 Sales list columns: date, transaction no, customer, item name and type, payment method, price, discount, tax, amount, status chip, row menu; sortable; paged must S208
FR-08-02 Filters: search, date presets + custom range, item type (booking, package, membership), sales channel (web, back office), payment method, status; apply/cancel must S70-S72
FR-08-03 v1 statuses: paid, unpaid, overdue, voided, refunded; later: retrying, pending approval, declined, waived, uncollectible, refund processing must S72c, 04 Features
FR-08-04 Add transaction step 1: type payable or free, note (printed on invoice), sold-at location, sold-by staff (optional, defaults to current user), customer search by name/phone/email with inline "add customer" must S73, S203, S74
FR-08-05 Item selection: tiles per item type; packages and memberships list with search, category, price shown for the customer's pricing group; ineligible items disabled with reason must S203c, 06 rules
FR-08-06 Cart summary: item, details, edit price (permission), add discounts (promo code or custom cart discount), total, amount to pay must S204, S205d
FR-08-07 Payment step: pay now vs pay later; pay now = mark as paid with method (cash, bank transfer, custom), paid-on date (default now), reference no, optional third-party fee must S205, S205e
FR-08-08 Confirmation dialog before creating a paid transaction ("received outside the system") must S206
FR-08-09 On success: transaction no allocated (sequential per business, prefix by kind), receipt number, customer plan created with credits and dates, receipt emailed, completion screen with link to detail must S207, F73
FR-08-10 Free transaction creates the plan/item with amount 0 and no payment step must S73b
FR-08-11 Pay later: create unpaid invoice with due date; optional generate invoice; plan active or not per checkout setting; reminder email before due date should S205c, S150
FR-08-12 Transaction detail: header (amount, status, paid-on, customer link, method, number), actions, payment submission, timeline, item details, payment details (invoice, reference, receipt no, amount, fee, net), receipt history, payment method/channel/source must S209
FR-08-13 Invoice and receipt PDFs; Send Receipt re-sends email and logs it should S209
FR-08-14 Void: confirm, optional internal reason; items on customer account become inactive; bookings unaffected; promo use restored should S209c
FR-08-15 Refund (record): choose amount ≤ paid, required reason, optional notify customer, warning that no money is returned automatically; status refunded or partially refunded ✱ must S209b
FR-08-16 Edit sale (paid-on date, location, sold-by, note, reference) and edit payment (amount, fee, nontaxable, method) with required adjustment reason; each saved as timeline event with before/after should S209d, S209e
FR-08-17 Transactions are never deleted; corrections are events must derived
FR-08-18 Manual payment methods list: cash and bank transfer preseeded; add custom (name, details ≤500 shown to customer, optional image); edit; deactivate (shows "deactivated", no delete) must S133, S133c, F56
FR-08-19 Manual method option: require proof-of-payment upload and custom input field (customer-facing checkout) — pricing tier: TBD could S133c
FR-08-20 Payment provider connection (search by provider or method) later S133b
FR-08-21 Taxes: one tax per business country with name, percent, optional registration number; edit, remove (confirm); toggle "prices include tax" must S123, F51
FR-08-22 Tax calc: discount applied before tax; exclusive adds tax on top; inclusive extracts tax from the price; "item is nontaxable" per payment; rounding half-up per line must S123, S209e, assumption
FR-08-23 Checkout settings: pay later toggle; due = on fulfillment or within N days; send reminder; allow booking with unpaid plan; require active membership to buy a package should S150
FR-08-24 Wallet: enable setting with regulatory notice; customer balance, top-up, partial payment, refund to wallet could S159
FR-08-25 Policies: refund, class cancellation, appointment cancellation texts (≤2000 each); shown at checkout and in booking confirmation should S155
FR-08-26 Recurring billing rules: time of day to charge; retry up to N times within D days; email customers to update failed method; after all retries: membership status, invoice status, booking status in next cycle; customer self-cancel toggle later S151
FR-08-27 Role permissions for view, create, edit pre/post payment, void, refund, mark paid offline, pay later must S120 matrix
FR-08-28 Time-of-day pricing mode setting (full slot vs pro-rated) out S150

7. Screens

Our screen Modelled on States
Sales list S70, S208 empty, filled, filtered-empty, error
Add transaction (3 steps: customer+item, payment, done) S73, S203-S207 pick customer, cart, pay now, pay later, confirm dialog
Transaction detail + drawers (void, refund, edit sale, edit payment) S209-S209e paid, unpaid, voided, refunded
Settings: Payments (methods) S133 list, add drawer, deactivate confirm
Settings: Taxes S123 empty, one tax
Settings: Checkout S150 default, dirty
Settings: Wallet (later) S159 off
Settings: Policies S155 empty, filled
Settings: Membership billing rules (later) S151 default

8. Flows

  • Cash sale (≤ 9 clicks): Add transaction → customer → Packages tile → tick package → Next → Pay now / Mark as paid / Cash (optional reference) → Confirm → confirm dialog → done → detail.
  • Pay later: same but choose Pay later; invoice due per checkout settings; later "Mark as paid" from detail.
  • Refund record: detail → Mark as refund → amount, reason, notify → confirm.
  • Void: detail → Void → reason → confirm. Edge cases: customer lacks eligibility for item; promo invalid; double submit (idempotency key); tax config missing (no tax); customer suspended (blocked); concurrent edits (optimistic lock).

9. Data model (✱ = guess)

  • Transaction(id, number, kind payable|free, customer_id, location_id, sold_by, note, channel web|back_office, status, price, discount, tax, amount, fee, net, tax_inclusive_snapshot, paid_at, due_at, method_id, reference_no, invoice_no, receipt_no, currency, created_at)
  • TransactionItem(transaction_id, kind, item_id, name_snapshot, unit_price, qty)
  • TransactionEvent(transaction_id, type, actor, before, after, reason, at) — timeline
  • Refund(id, transaction_id, amount, reason, notified, created_by)
  • PaymentMethod(id, kind provider|manual, provider ✱, name, details, image, proof_required, custom_field, active)
  • Tax(id, country, name, percent, registration_no) + Business.tax_inclusive
  • CheckoutSettings, MembershipBillingRules, Policies (as in recon entities)
  • WalletAccount(customer_id, balance) ✱ + WalletEntry (ledger) ✱
  • Amounts as integer minor units; currency from business.

10. Business rules & edge cases

  1. Totals: discount before tax; amount = net + tax (exclusive) or net (inclusive); refund ≤ amount paid minus prior refunds.
  2. Voiding a paid transaction deactivates plans it created; refund does not (owner decides separately) ✱.
  3. Edits after payment require the post-payment edit permission and a reason.
  4. Receipts record the tax registration number and policy snapshot at the time of sale.
  5. A free class booking creates a zero-amount transaction (FREE_ prefix) for traceability (shared with PRD 02).
  6. Transaction number sequences never reuse numbers.
  7. Pay-later items stay "unpaid"; overdue is derived by due date.
  8. Deactivating a payment method does not alter past transactions.
  9. Customer notifications on receipt and refund follow notification settings (PRD 01).
  10. Idempotent create: same key returns the same transaction.

11. Permissions

Action Owner Manager Front desk Accountant ✱
View transactions yes yes yes yes
Create (incl. pay later) yes yes yes no
Mark paid offline yes yes yes no
Edit pre-/post-payment yes yes pre only no
Void / refund yes yes no no
Payments, taxes, checkout, wallet, policies settings yes no no no
Mirrors the original granular Sales and Settings permission nodes.

12. Acceptance criteria

  • Given a 5.00 package with 10% exclusive tax, When sold for cash, Then amount 5.50, tax 0.50, status paid, customer plan active.
  • Given tax-inclusive prices, Then amount stays 5.00 and tax shows the extracted portion.
  • Given a 20% promo and tax, Then tax is computed on the discounted price.
  • Given a paid transaction, When voided, Then the plan becomes inactive and existing bookings remain.
  • Given refund without reason, Then the action is disabled.
  • Given refund amount above remaining paid amount, Then rejected.
  • Given pay later with reminders on, Then a reminder is sent before the due date (lead time ✱); after due date status is overdue.
  • Given a deactivated method, Then it does not appear in the add-transaction method list but remains on old transactions.
  • Given the same submit twice, Then one transaction exists.
  • Given a customer who is suspended, Then they cannot be selected for a payable sale.

13. Dependencies & open questions

Depends on PRD 01, 06, 07, 02/04 (booking payments), email (existing mail package). Open questions:

  1. Payment gateway choice (OPEN, not decided). Candidates for an Indonesia/SEA launch: Xendit, Midtrans, Stripe (and others). Needs: local methods (bank transfer/virtual account, QRIS, e-wallets, cards), recurring/tokenised charges, fees, settlement time, marketplace/split needs. Original evidence only shows a provider-agnostic provider search; no gateway flow was observed. Blocks recurring memberships, online checkout, and gateway refunds.
  2. Currency, tax regime (e.g. PPN 11% in Indonesia) and rounding rules per target country.
  3. Legal invoice requirements (e-faktur etc.) vs simple receipts.
  4. Manual payment approval flow (pending approval, approve, decline) with proof upload: statuses seen, flow not observed.
  5. Refund semantics: record only (as original) vs gateway refund; partial refunds.
  6. Wallet: regulatory constraints (e-money rules) before enabling; scope v2.
  7. Bill to account and statuses (waived, uncollectible) not observed.
  8. Sales list had no export in original (reports hold exports): add CSV export on list?
  9. Pricing tier: TBD for proof-of-payment option and gateway fees.

14. Evidence

See evidence/evidence.md.