Changelog
All notable changes to Generous.nz are recorded here, grouped by version. Every deployed build has a version number, a git tag, and an entry below.
Versioning scheme
Versions follow MAJOR.MINOR.PATCH:
- 0.x — pre-launch. Minor bumps mark a completed build phase or major feature set; patches are bug fixes on the current build.
- 1.0.0 — first public launch.
- 1.x+ — post-launch: minor = new features, patch = fixes, major = breaking changes or significant re-architecture.
Each version is tagged in git (v0.2.0) and the current version is kept in
package.json. See docs/version-control.md for how to cut a release.
[0.8.1] — 2026-08-24
Tax-position hardening (seller of record + fee-only income).
Fixed
- Seller of record on every payment:
on_behalf_ofis now set on non-wholesale product orders, one-off donations and recurring donation subscriptions (it already applied to wholesale orders). The fundraiser's Stripe account is the settlement merchant for every transaction, so card statements and charge details show the fundraiser — never the platform. - Receipts and tax invoices name the seller correctly: buyer-facing documents now show the organisation name when a campaign runs under one (otherwise the owner's name) instead of only the personal name — order receipts, donation receipts, voucher emails and the printable voucher all use the same seller identity. The platform is never shown as the seller.
- Agreements now match the actual money flow: the Fundraiser Agreement and Seller Agreement no longer describe a transfer "from the fundraiser's balance" (which Stripe does not support). They now state that the buyer's payment settles directly to the fundraiser's connected account, the platform only facilitates payment, and the wholesaler's share is paid from funds held on settlement.
Verified (no change needed)
- Money never sits in the platform balance: destination charges route the fundraiser's proceeds straight to their account at capture; the platform balance only ever holds the application fee (plus the wholesaler share in transit on wholesale orders).
- Refunds use
reverse_transfer, pulling money back from the fundraiser's account rather than the platform balance. - The GST export reports only platform fees + GST (the platform's own supply), never the gross sale amounts.
[0.8.0] — 2026-08-24
Blog with AI publishing.
Added
- Public blog at
/blogand/blog/{slug}with a list page, SEO metadata, cover images, excerpts and styled Markdown rendering (migration 0018). - Publishing API (
/api/blog) that lets an external AI create, publish, update and delete posts using a bearer token (BLOG_API_TOKEN) — no login or password needed. Draft-first workflow, auto slug generation, and a verification list endpoint. - Admin blog area (Admin → Blog posts): write in Markdown, set author/cover/excerpt/status, publish/unpublish/archive/delete, and a status banner for the publishing API.
- Guide for the writing AI:
docs/blog-publishing.mdwith endpoints, JSON schema, curl examples, markdown rules, a suggested workflow, and a ready-to-paste instruction block. - Blog link in the site header and footer.
Changed
- Go-live checklist includes the
BLOG_API_TOKENconfiguration step.
[0.7.0] — 2026-08-24
Voucher fulfilment build-out.
Added
- Voucher conditions: wholesalers can set voucher terms (availability, times, validity, restrictions) on voucher products, shown on the public catalogue, explore, and fundraiser product pages alongside the voucher count (migration 0017).
- Sold-voucher tracking: the business receives an email on every voucher sale with the customer's name, email and the exact codes issued.
- Printable voucher: buyers get a signed link to a print-ready voucher page (branded, with codes, terms and fulfilment details) — print it or save as PDF. The voucher email now includes the terms and the link.
- Voucher sales area: wholesalers see every sold code and its customer in a new dashboard page, with a CSV export of the full history.
- Bulk voucher import CSV template now supports the
voucher_termscolumn.
Changed
- Voucher delivery email includes conditions and the printable-voucher link.
[0.6.0] — 2026-08-24
AI order fraud screening.
Added
- DeepSeek order screen: when
ORDER_FRAUD_ENABLED=true, every order is screened at checkout with the configured provider (DeepSeek Flash by default) using buyer, velocity, account-age and order signals.- Block (score ≥ 0.8 default): the order is refused before any payment.
- Review (score ≥ 0.5 default): the customer can pay, but the order is held for human review — wholesaler payout and fulfilment pause until a moderator approves it.
- Approve: processed normally.
- If the provider is down, orders fail open for the sale but are queued for review rather than paid out.
- Stripe Radar gate: the payment webhook checks Stripe's own risk level and holds any order flagged "elevated" or "highest" for the same human review, regardless of the AI verdict at checkout.
- Order review in the moderation queue: flagged orders appear with
product, amount, buyer and status. Approve releases the payout hold;
reject cancels the order, refunds the buyer and cancels the settlement
(migration 0016:
ORDERresource type +orderIdon flags).
Changed
- Fraud runbook and go-live docs updated with the order-screen configuration and review workflow.
[0.5.0] — 2026-08-24
Business opportunities landing page.
Added
- New public
/businesspage ("For businesses") with the full supplier offer: free listings that don't expire, the business chooses the fundraiser's share (min 10%), 5% platform fee (+ GST) only on sales, 100% claimable as a business advertising expense (with accountant guidance), and no sales = no charge. - Inline one-step sign-up: name, business name, email and password creates the account and submits the wholesale application automatically, so a business lands on the wholesale dashboard ready to list products.
- Example sale split (wholesale price / fundraiser share / platform fee) and a three-step "how it works" section.
- Header nav link ("For businesses") and a cross-link from the public wholesale catalogue.
[0.4.0] — 2026-08-24
Go-live hardening (build phase 4).
Added
- Daily reconciliation: ledger snapshot with anomaly checks — payments
without a Stripe reference, settlements missing transfers, settlements
stuck for 30+ days, failed transfers/invoices, and invoices that can't be
collected because the wholesaler has no bank mandate. Includes a Stripe
platform-balance snapshot, a daily emailed report, an admin page with
run-now and history (migration 0015), and a
/api/cron/reconcilejob. - Payment failure alerts: declined payments now email the platform so card-testing patterns are visible.
- GST export completeness: the tax report now includes wholesale subscription invoice fees and GST alongside donations and orders.
- Email templates: 7 new previewable/test-sendable templates — wholesale fulfilment request, voucher code delivery, fulfilment review invite, wholesale invoice, and NZ BECS direct-debit notice.
- Fraud runbook:
docs/fraud-runbook.mdcovering fraud models, detection controls, review workflow, escalation path and scenario runbooks. - Go-live checklist:
docs/go-live.mdrewritten with the Phase 4 checklist — live split test, chargeback drill, legal pages, GST export, email templates, failure alerts, reconciliation, and the fraud runbook.
Changed
- Admin Reports page shows wholesale invoice fees in totals and the CSV
export includes a
wholesale_invoicerow type with a totals row.
[0.3.0] — 2026-08-24
Wholesale marketplace — build phase 3 (depth & scale).
Added
- Monthly BECS invoicing (the "auto bill" ask, done properly): wholesalers connect an NZ bank account once via Stripe-hosted mandate setup; the monthly cron creates an invoice per approved wholesaler with a subscription fee, notifies them before each debit (as NZ direct-debit rules require), and charges the saved mandate off-session. Webhooks mark invoices paid/failed, failed debits retry automatically (3 attempts, 72h apart), and admins can retry, waive or mark invoices paid. Wholesaler billing page + admin invoice ledger (migration 0014).
- Organisation-level wholesaler accounts: applications can be attached to an organisation; OWNER/ADMIN members share the same wholesale account and can manage products, vouchers, orders, analytics and billing.
- Bulk catalog import: CSV template, per-row validation, prohibited-item scanning, moderation on import, and a row-by-row error report.
- Inventory sync: bulk stock updates by SKU, auto-pause when a product sells out (including mid-sale in the webhook), low-stock and sold-out emails, and SKU support on products.
- Wholesaler reviews: buyers with a paid/fulfilled order can review a supplier; ratings appear on the public catalogue; review invites are emailed on fulfilment; reviews go through the moderation queue.
payment_intent.succeededwebhook handling for invoice settlement.
Changed
- Wholesaler authorization is now organisation-aware (a member of an org with an approved wholesale account can operate it).
- Wholesale products can carry a SKU used for import and stock sync (unique per wholesaler).
Fixed
- Invoice generation is resilient to email failures — a failed notification no longer skips the direct debit.
[0.2.0] — 2026-08-24
Wholesale marketplace — build phases 1 and 2.
Added — Phase 1: Core wholesale
- Wholesaler application flow with admin approval, rejection and suspension
(
/dashboard/wholesale,/dashboard/admin/wholesalers). - Wholesale catalogue products: stock, auto-expiry (15/30/45/90+ days), physical/voucher types, minimum 10% commission enforcement, moderation.
- Public wholesale marketplace (
/wholesale) plus a wholesale filter and "Fulfilled by [business]" badges on the explore and fundraiser pages. - One-click "Add to my store" for fundraisers with a sale-price floor of cost + minimum commission, and a quantity selector.
- Payment engine: destination charge with a partial transfer (fundraiser's
net routed straight to their account),
on_behalf_ofso the fundraiser is the seller of record, application fee (platform fee + wholesaler share), then an automatic platform→wholesaler transfer with retry handling. WholesaleSettlementrecords for every order: gross, fees, GST, commission, wholesaler net, status, hold, transfer and reversal IDs (migration 0012).- Buyer receipts issued in the fundraiser's name with the fulfilment party shown.
- Refund safety: refunds use
reverse_transferso money is pulled back from the fundraiser, and the wholesaler's share is reversed automatically. - Fraud controls: 30-day payout holds for fresh accounts
(
riskHoldPayoutsUntil), 24-hour velocity monitoring that flags suspicious fundraisers to the moderation queue, admin hold release / retry / cancel in the settlement log. - Wholesaler fulfilment queue: order notifications, buyer details, mark as fulfilled.
Added — Phase 2: Trust & robustness
- Voucher codes: wholesalers generate or paste codes per voucher product; codes are emailed to the buyer automatically on payment; voucher orders fulfil themselves; refunded orders return codes to the pool (migration 0013).
- Proportional refund reversals: partial refunds reverse only the matching share of the wholesaler transfer.
- Automatic dispute outcomes: on
charge.dispute.closed, a lost dispute recovers the wholesaler's share and a won dispute re-pays the fundraiser and wholesaler automatically. - Dispute console: submit evidence to Stripe, accept-and-refund, sync from Stripe, and wholesale settlement context on every dispute.
- Wholesaler analytics (
/dashboard/wholesale/analytics): paid-to-you total, 30-day view, per-product sales, and CSV order export. - Fundraiser "Sales & orders" page (
/dashboard/sales): every order with the full split — total, fees, supplier share, and the fundraiser's keep.
Changed
- Wholesale payment architecture corrected to Stripe's documented split
pattern (destination charge + partial transfer +
on_behalf_of); the platform balance only ever holds the fee plus the wholesaler share in transit. Seedocs/wholesale-marketplace.md. - Settlement engine now retries failed transfers automatically on a schedule and only emails admins on the first failure.
- Required Stripe webhook events updated: added
charge.dispute.closed, replacedtransfer.failedwithtransfer.reversed.
Fixed
- Fundraiser wholesale listings were capped at one unit; fundraisers can now offer as many units as they want (bounded by supplier stock).
- TypeScript/build errors in the webhook event handling.