Changelog
All notable changes to Generosity.co.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.
[Unreleased]
Nothing yet.
[0.9.151] — 2026-08-25
Durable automation-failure handling.
Added
- Added a durable Automation failures section to the moderation queue.
- Referral qualification failures can be retried safely by a moderator.
- Stripe order/payment webhook processing failures are recorded for review and returned to Stripe for automatic delivery retry.
Fixed
- Stopped logging raw Stripe webhook payloads after signature verification failures; the queue now records a safe operational summary instead.
[0.9.150] — 2026-08-25
Launch settings confirmation.
Changed
- Recorded the owner-confirmed 5% business platform fee and first $1,000 of business sales fee-free offer, and updated the go-live plan to the current production release and migration range.
[0.9.149] — 2026-08-25
Coupon controls and referral rewards.
Added
- Added separate fundraiser and business referral links, with tracked visits, sign-ups, successful set-ups and fee-free volume earned.
- Referral attribution is automatic for 30 days and credits rewards only after the referred account completes the relevant go-live checklist.
- Fundraiser referrals grant the new fundraiser $100 fee-free volume and the referrer $50; business referrals grant the referrer $200 fee-free sales.
- Added coupon audiences, optional duration and wholesale fee-rate handling.
Changed
- Wholesale coupons replace the normal $1,000 free-sales offer while active; they do not stack with it. Referral-earned free-sales volume resumes after a coupon ends.
[0.9.148] — 2026-08-25
Launch sign-off record.
Changed
- Recorded the project owner's legal and tax sign-off in the go-live plan and handoff, while clearly noting it is not independent professional advice.
[0.9.147] — 2026-08-25
Backup documentation correction.
Fixed
- Corrected the handoff and go-live plan to show that daily local and off-site backups are active and scheduled at 2:30am and 3:00am NZ time respectively.
[0.9.146] — 2026-08-25
Go-live documentation refresh.
Fixed
- Updated the go-live plan to v0.9.146 and the complete migration range (0001–0043), and aligned email, backup, and integrations guidance with the live platform.
[0.9.145] — 2026-08-25
Integration setup visibility.
Added
- Added Admin → Integrations with live, non-secret readiness indicators and production setup instructions for Google login, Apple login, and web push.
[0.9.144] — 2026-08-25
Production log hygiene.
Fixed
- Suppressed only Auth.js's acknowledged
experimental-webauthnnotice; passkeys remain enabled and all other authentication warnings and errors continue to be logged.
[0.9.143] — 2026-08-25
Footer accessibility and asset optimisation.
Fixed
- Raised footer and footer-link contrast to accessible dark ink colours.
- Replaced the 133 KB AdvertiseOnline PNG with a 7 KB, appropriately sized WebP logo and added intrinsic image dimensions to prevent layout shift.
[0.9.142] — 2026-08-25
HTTPS security header consolidation.
Fixed
- Added a one-year HSTS policy at the HTTPS reverse proxy.
- Made Nginx the sole source of the public security headers, removing the
duplicate and conflicting Nginx/Next.js
X-Frame-Options,Referrer-Policy, and related headers.
[0.9.141] — 2026-08-25
Public release history.
Added
- Published the complete changelog at
/changelog, including a clear current version panel sourced frompackage.json. - Added a Changelog footer link and made the footer version badge link to the public release history.
[0.9.140] — 2026-08-25
IndexNow rewrite narrowing.
Fixed
- Restricted the IndexNow verification rewrite to valid key formats, so it no
longer intercepts
robots.txtor other static text routes.
[0.9.139] — 2026-08-25
Private-route indexing controls.
Fixed
- Disallowed API, administration, dashboard, account, sign-in, registration,
and password-reset routes in
robots.txt. - Added
X-Robots-Tag: noindex, nofollow, noarchiveheaders to those routes. - Added page-level noindex metadata to the authenticated and account layouts.
[0.9.138] — 2026-08-25
Public-page metadata improvements.
Fixed
- Added clean, content-specific descriptions and canonical URLs to public fundraiser, organisation, and wholesale-product pages.
- Aligned wholesale-product metadata with the public-page availability rules, so unavailable deals no longer publish indexable metadata.
[0.9.137] — 2026-08-25
Public sitemap quality fix.
Fixed
- Added approved, active, unexpired wholesale products from approved wholesalers to the sitemap.
- Excluded organisation storefronts with no approved campaigns, removing empty and duplicate wholesale-brand pages from search indexing.
[0.9.136] — 2026-08-25
IndexNow verification-file routing fix.
Fixed
- Run the
/<key>.txtIndexNow rewrite before the custom-domain catch-all route and pass the key as a route parameter, allowing search engines to retrieve the required verification file.
[0.9.135] — 2026-08-25
Security dependency remediation.
Fixed
- Updated Nodemailer to 9.0.5, which resolves the high-severity raw-message file-read/SSRF advisory affecting 8.0.11.
- Overrode Prisma's transitive
deepmerge-tsdependency to 8.0.2, resolving its high-severity recursive-object stack-exhaustion advisory without downgrading Prisma. - Added a documented npm peer-resolution setting: Auth.js v5 beta's optional Nodemailer peer range stops at v8 even though the application and Auth.js import checks pass with the patched v9 release.
[0.9.134] — 2026-08-25
AI handoff document refresh.
Changed
docs/AI-HANDOFF.mdupdated to current state (v0.9.133): Stripe Connect activated, webhooks fixed, DeepSeek key valid, background AI moderation for all content types, fundraiser/wholesale go-live checklists, automatic fundraiser submission, service worker stale-code fix, and updated known issues and next steps.
[0.9.133] — 2026-08-25
Fix stale cached code after deploys.
Fixed
- The service worker previously served cached JavaScript "stale-while- revalidate", so browsers could keep running old page code after a deploy (which is why an unchanged error message appeared even though the footer showed the new version). Static assets are now network-first with the cache used only as an offline fallback, so visitors always get the latest code after a deploy.
[0.9.132] — 2026-08-25
Reliable wholesale product saves.
Fixed
- Background AI reviews now run fire-and-forget instead of via the
afterhook, removing any chance the response is delayed or interrupted by the review infrastructure. - The wholesale product form now reports the actual HTTP status if a save fails (e.g. "Something went wrong (500)") and gives up after 30 seconds instead of hanging, so future errors are immediately diagnosable.
[0.9.131] — 2026-08-25
Fundraiser go-live checklist.
Added
- New "Go-live checklist" page for fundraiser accounts, mirroring the wholesale one: connect Stripe payout, add platform fee card, create a fundraiser, get it approved, keep it active, and list a first product. Done items show a tick with strikethrough; each item links to where it's done.
- An amber banner appears at the top of the fundraiser dashboard while the checklist is incomplete, with a link to the checklist. The sidebar link disappears once everything is complete.
- The payment prerequisites (payout account + fee card) are the hard gate — until both are done, donations and orders are blocked.
[0.9.130] — 2026-08-25
View page links from dashboards.
Added
- "Your Fundraisers" cards (dashboard overview and the Your Fundraisers page) now have a "View page ↗" button that opens the live fundraiser page in a new tab.
- "My Listings" rows link straight to the fundraiser page with a "View ↗" shortcut.
- Wholesale catalogue cards have a "View page ↗" link to the public product page.
[0.9.129] — 2026-08-25
Buy button checkout timeout.
Fixed
- The "Buy this item" button now gives up after 30 seconds instead of sitting on "Opening checkout…" forever if the checkout request stalls, and shows a clear message to try again.
Notes
- A fundraiser page can only take orders once its owner has connected their Stripe payout account (wholesale commissions land there too). Pages without a connected account return "The seller has not finished connecting their payout account yet."
[0.9.128] — 2026-08-25
Remove listings from My Listings.
Added
- "My Listings" now has a Remove button on every row. Removing hides the listing immediately (status REMOVED); orders already placed are unaffected, and the item can be relisted later.
[0.9.127] — 2026-08-25
Automatic fundraiser submission.
Changed
- Removed the "Submit for review after saving" / "Resubmit for review" checkboxes from the fundraiser edit form. Saving a draft (or a rejected fundraiser) now submits it for review automatically every time — AI checks it in the background, with human review as needed, before it goes live.
- Approved fundraisers keep their current behaviour: edits save and apply immediately without taking the page offline.
[0.9.126] — 2026-08-25
Background AI moderation for all content types.
Changed
- Fundraiser saves (create and submit/resubmit), fundraiser product listings (create and edit) and user reviews now use the same background AI review as wholesale products: the content is saved instantly, moves to the moderation queue, and the AI verdict lands seconds later in the background — auto-approving low-risk items or flagging anything ambiguous for a human.
- The AI verdict is stored on the item (fundraiser/product metadata) by the background job itself, so moderation and owner views show why it was approved or flagged.
[0.9.125] — 2026-08-25
Background AI moderation for wholesale products.
Changed
- Wholesale product saves (create and edit) now return instantly. Content edits move straight to the moderation queue, and the AI review runs automatically in the background immediately after — auto-approving low-risk items or flagging anything ambiguous for a human. No cron needed; a slow AI provider can no longer make a save appear to fail.
- The background review is logged to the error log if it ever fails, and the item simply waits in the queue for a human.
[0.9.124] — 2026-08-25
AI provider timeouts.
Fixed
- AI provider calls (moderation reviews, content generation) now time out after 20 seconds instead of hanging indefinitely. Previously a slow AI provider could stall a wholesale product save past the web server's proxy timeout, making the browser show "Something went wrong" even though the save had actually completed.
[0.9.123] — 2026-08-25
Resilient wholesale product saves.
Fixed
- Editing a wholesale product no longer fails when the AI re-review step hiccups (e.g. a slow or temporarily unavailable provider). The content is saved first; if the AI review then errors, it's logged and the item waits in the moderation queue for a human instead of the whole save failing with "Something went wrong."
- The go-live checklist check on save is also guarded, so a hiccup there is logged and reported rather than silently breaking the request.
[0.9.122] — 2026-08-25
Project moved to a new location.
Changed
- The working directory moved from
C:\Users\sales\Documents\Codex\2026-08-22\files-pasted-by-the-user-andrewtoE:\AI Projects\generosity.co.nz. The AI handoff document and the wholesale sign-off pack build script now reference the new path.
[0.9.121] — 2026-08-25
AI handoff document.
Added
docs/AI-HANDOFF.md— a full handoff guide so another AI (or developer) can pick up the project: architecture, money model, environments, build and deploy workflow, versioning conventions, feature map, known blockers and reference docs.
[0.9.120] — 2026-08-25
Interactive wholesale image gallery.
Fixed
- Thumbnails on wholesale product pages now work — clicking an extra photo switches the main image, with the active thumbnail highlighted. Previously the extra images were displayed but did nothing when clicked.
[0.9.119] — 2026-08-25
Campaign type switching fix.
Fixed
- Clicking a campaign type under "Your Fundraisers" (Donations, Product sales, Event tickets, Raffle, Peer-to-peer, Supporter subscriptions) now updates the create form immediately. Previously the URL changed but the form kept showing the previous type because the page stayed mounted.
[0.9.118] — 2026-08-25
Getting started — wholesale mention.
Changed
- The "List products" step on the Getting Started page now also explains selling other businesses' products: add items from the wholesale catalogue and earn a commission of at least 10% on every sale, while the business fulfils the order. Added a "Browse wholesale catalogue" link.
[0.9.117] — 2026-08-25
Signup clickwrap consent.
Added
- Fundraiser and business sign-up now require ticking "I agree to the Terms of Service, Privacy Policy and all site policies" (with links to the legal pages) before an account can be created — the submit button stays disabled until it's ticked and the API rejects signups without agreement.
- Every acceptance is recorded in a consent log: user, terms version, IP address, user agent and timestamp, so the agreement can be proven later if needed.
[0.9.116] — 2026-08-25
Clarified Charity Verification Policy (v2.0).
Changed
- Charity Verification Policy updated to v2.0: verification is an identity and registration check only (not endorsement, audit, financial-integrity or tax-deductibility confirmation), proof-of-authority and additional document requests, deregistration and investigation checks, pause/freeze powers, off-platform badge restrictions, and appeal verification.
[0.9.115] — 2026-08-25
Strengthened AML/CFT Policy (v3.0).
Changed
- AML/CFT Policy updated to v3.0: explicit "not a financial service provider" and "no wallet or stored value facility", obligations limited to the Platform's own fee revenue, reliance on Stripe's regulated KYC/AML controls, additional risk indicators (refund patterns, high-risk goods, bank changes, VPNs, high-risk industries), document-request powers, permanent bans, sanctions restrictions, anonymous-donation metadata, and staff no-tipping-off and annual training duties.
[0.9.114] — 2026-08-25
Clarified Dispute Resolution Policy (v3.0).
Changed
- Dispute Resolution Policy updated to v3.0: explicit "Platform cannot issue refunds" and refunds flow through the seller's Stripe account, two-business- day response expectations, evidence and investigation powers, new considerations (Stripe risk signals, prior history), expanded outcomes (suspension, item removal, commission freezes, proof requests), chargeback fees and bank-account debits, override powers, and a statement that the Platform is not a legal arbitrator.
[0.9.113] — 2026-08-25
Clarified Refund Policy (v3.0).
Changed
- Refund Policy updated to v3.0: prominent "Platform never handles money" statement, refunds must be initiated by the seller/fundraiser from their own Stripe balance (wholesaler for wholesale items, organiser for tickets, fundraiser for their own items and donations), platform fees never refunded, commission reversal rules, chargeback fees from the seller's balance, Stripe bank-account debits when balances are insufficient, and Platform override/payout-pause powers during fraud, safety or CGA cases.
[0.9.112] — 2026-08-25
Expanded Prohibited Items List (v2.0).
Changed
- Prohibited Items List updated to v2.0: new never-allowed categories (high-risk digital goods, financial services, surveillance equipment, hate material, adult products, CBD, licensed-professional items, unsafe/recalled items, licensed-installation items, misleading fundraising items), clearer weapon/cannabis/adult/crypto wording, new restricted categories (food, supplements, cosmetics, children's products, electrical goods, baby gear, event tickets, services), therapeutic-claims and charity-authorisation clauses, payout freezes, authority notification, permanent bans, and a fraud reporting address.
[0.9.111] — 2026-08-25
Strengthened Wholesaler Agreement (v3.0).
Changed
- Wholesaler Agreement updated to v3.0: explicit "not a party to wholesaler/buyer contracts" and "not a financial service provider", authority and business-accuracy duties, proof-of-stock/authenticity rules, listing update duties, non-refundable fees and failed-card suspension, minimum-price adjustments, tracking and response-time duties, prompt refunds and chargeback-fee clarity, indemnity of the Platform, automated fraud monitoring, identity verification, DIA raffle compliance, and new defined terms.
[0.9.110] — 2026-08-25
Strengthened Fundraiser Agreement (v3.0).
Changed
- Fundraiser Agreement updated to v3.0: explicit "not a party to fundraiser/donor contracts" and "not a financial service provider", authority requirements, campaign accuracy and evidence-of-funds rules, donor expectations (non-refundable, no imposed conditions), non-refundable fees and failed-card suspension, wholesaler indemnity of the Platform, minimum-price adjustments, Stripe payout timing disclaimers, automated fraud monitoring, identity verification, DIA raffle compliance, and new defined terms.
[0.9.109] — 2026-08-25
Strengthened Seller Agreement (v3.0).
Changed
- Seller Agreement updated to v3.0: explicit "not a party to buyer/seller contracts" and "not a financial service provider", listing accuracy and evidence rules, non-refundable fees and failed-card suspension, tracking and response-time duties, wholesaler indemnity of the Platform, no misrepresenting wholesale products, Stripe payout timing disclaimers, automated fraud monitoring, identity verification, DIA raffle compliance, and defined terms for seller of record and platform fee.
[0.9.108] — 2026-08-25
Strengthened Privacy Policy (v3.0).
Changed
- Privacy Policy updated to v3.0: explicit "not a financial service provider", expanded collection (Stripe metadata, communications, logs, images, location approximations, fraud signals), lawful bases, dedicated direct-charge Stripe section, business fulfilment sharing, overseas multi-jurisdiction disclosure, breach notification, longer fraud/audit retention, data portability, consent-based cookies, and an AI/automated processing section confirming personal data is not used to train AI models.
[0.9.107] — 2026-08-25
Strengthened Terms of Service (v3.0).
Changed
- Terms of Service updated to v3.0 with stronger protections: explicit "not a financial service provider" and "not a party to buyer/seller contracts" statements, verifiable parental consent, no multi-account fee avoidance, document/evidence requests, campaign freezes during investigations, seller fulfilment and refund obligations, DIA raffle compliance, non-refundable fees, failed-fee suspension, no guarantee of delivery/success/accuracy, no liability for Stripe outages, outstanding fees after termination, and new Fraud & Misuse and Privacy & Data sections.
[0.9.106] — 2026-08-25
Wholesale go-live checklist.
Added
- New "Go-live checklist" page for wholesale accounts with every step needed to go live: application approved, Stripe payout connected, commission payment card added, brand setup, first product approved, product active, and voucher codes available (shown only when voucher products exist). Done items show a tick with strikethrough; each item links to where it's done.
- An amber banner appears at the top of the wholesale area while the checklist is incomplete, with a link to the checklist. The checklist link disappears from the sidebar once everything is complete.
- The first three steps are now a hard gate: until the application, payout account and payment card are all done, the store cannot list or edit products (staff can still manage on their behalf).
[0.9.105] — 2026-08-25
Product type selector on listings.
Added
- "New listing" now has an Item type dropdown under the Fundraiser dropdown: Physical item, Digital item, Event ticket, Raffle ticket. Event and raffle tickets appear only when the campaign supports them.
- Each type shows only its relevant fields. Physical items keep condition, inventory and the Pickup / Courier / Local delivery / Digital options, with a pickup location box, metro + rural courier price boxes, a delivery area box and a digital details box. Digital items get a delivery-details box. Tickets get inventory (and the donation portion for event tiers).
[0.9.104] — 2026-08-25
Draft editing, submission and AI review.
Added
- Every fundraiser card on "Your Fundraisers" (and the dashboard overview) now has an Edit button, including drafts.
- Drafts and rejected fundraisers can be submitted for review from the edit form ("Submit for review after saving" / "Resubmit for review").
Changed
- Submitting a draft (or resubmitting a rejected one) now runs the AI review: when the AI is confident the content is fine, it is auto-approved; anything ambiguous or high-risk goes to the human moderation queue. Auto-approve is now on by default (still toggleable in Admin → AI review).
[0.9.103] — 2026-08-25
Your Fundraisers page.
Added
- New "Your Fundraisers" page (sidebar item) listing every campaign with raised totals, goal progress, status, expiry and reasons — the same area shown on the dashboard overview, now at the top of its own page with a "+ New fundraiser" button.
Changed
- The "New fundraiser" sidebar item is gone; the campaign-type links (Donations, Product sales, Event tickets, Raffle, Peer-to-peer, Supporter subscriptions) now sit grouped under "Your Fundraisers".
[0.9.102] — 2026-08-25
Campaign types in the sidebar.
Changed
- The "Campaign type" dropdown is gone from the fundraiser form. Each type (Donations, Product sales, Event tickets, Raffle, Peer-to-peer, Supporter subscriptions) is now its own item in the dashboard sidebar, grouped under "New fundraiser" and stacked one under the other.
- The form presets to the chosen type (e.g. a Raffle link opens the form with the raffle fields ready) and shows which type is being set up.
[0.9.101] — 2026-08-25
Remove duplicate category dropdown.
Changed
- The "Category" dropdown on the fundraiser form is gone — it duplicated the "What are you fundraising for?" checkboxes. The category is now chosen automatically from the reasons ticked (e.g. "Religious / church / faith group" maps to Church).
[0.9.100] — 2026-08-25
Business page sales highlights.
Changed
- The "AI protection" section on the /business page now fills the width with a two-column layout. A new "Sales highlights" box sits beside "Why it matters to you" covering supporter reach, the free first $1,000, one-click listing and ready-to-fulfil orders.
[0.9.99] — 2026-08-25
Remove AI story generator.
Changed
- Removed the "Generate with AI" button from the fundraiser story field on both the create and edit pages. The story is now written directly, without relying on the AI provider.
[0.9.98] — 2026-08-25
Draft fundraiser preview.
Fixed
- Draft fundraisers no longer 404 for their owner, their organisation or site staff — the page opens as a preview with a banner explaining it isn't live yet, and donation/purchase forms are hidden until approved.
- Anyone else still gets a 404, and draft titles are kept out of link previews.
[0.9.97] — 2026-08-25
Automatic voucher delivery.
Changed
- Removed the manual "How vouchers are delivered" field from the product form. Voucher delivery is handled automatically: the code is emailed to the buyer (and gift recipient, when applicable) with a printable voucher as soon as the purchase goes through.
- The form now explains this on voucher products. Existing stored notes are preserved and still appear in voucher emails.
[0.9.96] — 2026-08-25
Group discount codes (platform fee discounts).
Added
- Fundraiser sign-up now has an optional "Group discount code" field. A code from a school, club or group gives that account a percentage off platform fees for the life of the account.
- New admin page (Admin → Discount codes) to create and manage codes: discount percentage, optional expiry date, optional max signups, pause / resume, edit and delete.
- Discounts apply automatically everywhere the platform fee is calculated: donations, product orders, wholesale splits and the weekly platform fee invoices.
Notes
- Each signup claims one slot against a code's max signups. Once an account has the discount it keeps applying while the code is active and unexpired; pausing, expiring or deleting the code stops the discount for everyone using it (accounts themselves are not affected).
[0.9.95] — 2026-08-25
Voucher codes follow the product type.
Changed
- The "Voucher codes" section on the edit product page now appears and disappears with the Type selector in the form, instead of only reflecting the saved type.
- Switching a product from Voucher to Physical keeps all voucher codes on file — they are not deleted and reappear if the type is switched back. A note on the form confirms this while the type is set to Physical.
[0.9.94] — 2026-08-25
Brand logo uploads.
Added
- Brands can now have a logo image uploaded directly (in addition to pasting an image URL) when creating or editing a brand, with a small preview shown while editing.
[0.9.93] — 2026-08-25
Edit wholesale brands.
Added
- Brands on the wholesale Brands page can now be edited — rename the brand and/or update its logo URL. Works for attached brands and the store's main brand. The public page slug stays unchanged so existing links keep working.
[0.9.92] — 2026-08-25
Clearer product options guidance.
Changed
- The "Options / variations" help text on the wholesale product form now explains the feature with more examples (location, service level like Gold/Silver/Bronze, size, colour), reminds sellers to explain each option in the product description, and advises keeping value names short and clear.
[0.9.91] — 2026-08-25
Consistent layout for edit/product pages.
Fixed
- The wholesale "Edit catalogue product" page no longer centres its content; it now spans the same width and starts at the same position as the Add product and catalogue pages, so the nav-to-card spacing matches.
- "Edit fundraiser", "Edit listing" and the organisation "New campaign" pages are also left-aligned to match the rest of the dashboard.
- Tidied the edit product header (back-link wording and spacing) to match the Add product page.
[0.9.90] — 2026-08-25
Dashboard layout consistency.
Fixed
- "Create a fundraiser" and "List an item for sale" pages no longer centre their content; they now start at the same left edge as the dashboard overview, so spacing matches across pages.
- The sidebar nav has a fixed width on desktop, so it stays in exactly the same place when clicking between dashboard pages.
- The browser scrollbar gutter is now reserved on every page, so the header and layout no longer shift sideways on pages long enough to scroll.
[0.9.89] — 2026-08-25
Admin-managed DeepSeek API key.
Added
- The admin AI page now has a "Provider key" panel where the DeepSeek API key
(and optional model name) can be entered and saved — no server
.envedit or restart needed. - The key is encrypted at rest (AES-256-GCM, keyed from
AUTH_SECRET) in the settings table. The full key is never returned to the browser; the page only shows a maskedsk-…abcdstatus. - Saved keys are used immediately by AI moderation, fraud screening, order
fraud checks, content generation, and the "Run test review" button. A
"Remove saved key" button clears it and falls back to the server
.envkey.
Changed
- The AI provider now reads its DeepSeek key from the database first and the
server
.envas a fallback.
Notes
- A valid
sk-…key from platform.deepseek.com is still required for the AI provider to authenticate; the previous server key returned 401.
[0.9.88] — 2026-08-25
AI moderation model fix.
Fixed
- The DeepSeek model name was set to a non-existent model; it now defaults to
the real
deepseek-chatmodel, and the admin AI page hint matches.
Notes
- The AI provider is currently returning "401 Authentication Fails" — the
DeepSeek key in the server
.envneeds to be a validsk-…key from platform.deepseek.com. Once a valid key is in place, product moderation runs fully automatically: approve when the AI is confident, flag with a reason otherwise.
[0.9.87] — 2026-08-25
Fully automatic AI moderation for products.
Added
- Wholesale items now auto-approve when the AI review is confident they're fine (previously they always needed a human). Anything the AI isn't sure about is flagged with the AI's reason, and banned items are still auto-rejected.
- Editing a product re-runs the AI review — a fixed or resubmitted item is re-scored automatically and approved again if it's fine, or flagged with a reason if not (previously edited items just sat in the queue with no AI check).
- The AI product review now considers the campaign/business context (what the fundraiser is for, the business name, category, brand) and is prompted to catch attempts to disguise banned or illegal items with misspellings, euphemisms or vague wording.
[0.9.86] — 2026-08-25
Voucher codes moved to the product page + sold codes never reused.
Changed
- Voucher code management moved out of the catalogue cards and into the product's edit page (where there's room): generate or paste codes there. Creating a new voucher takes you straight to its edit page so you can add codes immediately.
- Sold vouchers stay sold: a code that has been purchased is never returned to the pool, even if the order is later refunded — it stays marked as assigned to that order, so it can't be resold.
- Voucher sales page now shows the voucher code, the purchaser, and a "🎁 Gifted to [name] ([email])" line when the buyer selected the gift option at checkout.
[0.9.85] — 2026-08-25
Vouchers can be bought as gifts.
Added
- Voucher purchases now have a "This is a gift" option on the buy button. When selected, the purchaser enters the recipient's name, email and an optional message, and the voucher is emailed to the recipient (with the message) and a copy goes to the purchaser.
[0.9.84] — 2026-08-25
Brands page explainer + one-click first brand.
Added
- The Brands page now explains what brands are (sell multiple product lines under one store and one payout account) with real use cases, and how to set one up (create → tag products → it appears on the catalogue).
- One-click first brand: if the store has no brands, a highlighted card creates a brand named after the business with a single click.
[0.9.83] — 2026-08-25
Removed references to other sites.
Changed
- Removed all mentions of other platforms (e.g. "Grabone-style", "Go Raise It style", "GiveALittle-style") from user-facing copy and code comments. Feature descriptions now stand on their own.
[0.9.82] — 2026-08-25
Zero-downtime deploys.
Changed
- The server deploy process now builds the site into a separate directory and atomically swaps it in, so the live site keeps serving the current build throughout the update — no more "Internal Server Error" / "client-side exception" windows during releases.
- Dependencies are only reinstalled when the lockfile actually changes, so node_modules is no longer wiped on every push.
Fixed
- The intermittent
TypeError: a is not a function/ module-not-found errors during deploys were the old process serving whilenpm ciandnext buildreplaced files under it. Deploys no longer disturb the running site.
[0.9.81] — 2026-08-25
Restriction conflicts hide wholesale items.
Changed
- When every one of a signed-in fundraiser's approved campaigns conflicts with a wholesaler's restriction (e.g. the fundraiser ticked "Political party campaigns" and the business excludes it), the wholesaler's items are now hidden entirely from the catalogue and deal pages for that user — not just blocked at add time.
- If only some of the user's campaigns conflict, items stay visible and can still be added to the campaigns that don't conflict. Signed-out visitors and users with no approved campaigns see everything.
- The server-side add block remains as a final guard.
[0.9.80] — 2026-08-25
"Political party campaigns" cause option.
Added
- Political party campaigns is now a first-class cause option in the fundraiser setup checkboxes, the wholesaler Restrictions list, and the AI reason verification — so fundraisers can declare it and businesses can block it automatically.
[0.9.79] — 2026-08-25
Deploy-safe service worker — no more client-side crashes after updates.
Fixed
- The client-side exception after deploys was the service worker serving the
old app shell (cached under a fixed name) after a release, referencing JS
files the new build had deleted. The service worker now:
- versions its cache automatically with each release (the cache name is the site version), so every deploy clears all stale cached copies in visitors' browsers the moment the new worker activates;
- caches pages under their own URL (not all under "/") and only ever serves them as an offline fallback after a network failure;
- never caches error responses.
- The service worker is generated at build time from a template, so the cache version can't drift.
Note for users
- If you hit the error once, a hard refresh (Ctrl+Shift+R) clears it; from now on it won't recur after future updates.
[0.9.78] — 2026-08-25
Brands and Restrictions moved to their own pages.
Changed
- The wholesale dashboard no longer embeds Brands and "Causes you won't
fundraise for" — each is now a dedicated page:
- Brands (
/dashboard/wholesale/brands) - Restrictions (
/dashboard/wholesale/restrictions)
- Brands (
- Both appear as clickable cards on the wholesale dashboard alongside Catalogue products, Orders, Analytics, Billing and Voucher sales, and as sidebar menu items (right before Account).
[0.9.77] — 2026-08-25
Fix editing price showing cents as dollars.
Fixed
- The wholesale product edit form showed the selling price as 15000.00 instead of 150.00: the price is stored in cents, and the prefill math forgot to convert cents back to dollars. It now pre-fills correctly (e.g. $150.00 for a $150 item).
[0.9.76] — 2026-08-25
Custom "something else" cause exclusion + clearer faith label.
Added
- Wholesalers can now add a custom cause they won't fundraise for under "Something else" on the Causes you won't fundraise for panel. The custom cause is saved to their profile and sent to the moderation queue for review (a flag is raised with the business name and the custom text).
Changed
- "Church / faith group" is now labelled "Religious / church / faith group" everywhere (wholesaler preferences and fundraiser setup).
Notes
- Custom causes are reviewed and shown for transparency; the automatic block-list matching applies to the built-in causes (so e.g. a "religious" custom exclusion should also tick the religious checkbox to block those fundraisers automatically).
[0.9.75] — 2026-08-25
Bigger description box on the wholesale product form.
Changed
- The product/voucher description box is now nine rows tall (previously three), so more of the description is visible at once when adding or editing catalogue products. It can still be dragged taller.
[0.9.74] — 2026-08-25
Separate "Add product" page for wholesale catalogue.
Changed
- The wholesale catalogue is now two pages with separate sidebar items: Catalogue products (list, filter, edit/pause/delete) and Add product (the add form plus bulk import and stock sync). The catalogue page has an "+ Add product" button that links across.
[0.9.73] — 2026-08-25
Dashboard button is account-aware.
Changed
- The Dashboard button in the top nav takes approved wholesalers straight to their wholesale dashboard; everyone else goes to the main dashboard.
[0.9.72] — 2026-08-25
Top navigation adapts to signed-in users.
Changed
- When signed in, the top nav hides the marketing/auth links (Sign in, Start fundraising, For businesses, Getting started, Blog) and keeps Explore, Wholesale and Help, with Dashboard shown as a prominent button (styled like Start fundraising). The mobile menu matches.
- Guests still see the full nav with Sign in and Start fundraising.
[0.9.71] — 2026-08-25
Wholesale items list at the wholesaler's price — no fundraiser pricing decisions.
Changed
- Fundraisers now list wholesale items exactly at the price the wholesaler set ("selling price"). The add-to-store flow is fixed price server-side — the fundraiser never chooses or is asked about a price.
- Public copy simplified: the catalogue card shows "Buyers pay $X · Fundraisers earn Y%+"; the internal business "cost" no longer appears on public pages (it's still visible on the wholesaler's own management page).
- The confusing "minimum sale price" wording is gone everywhere.
Fixed
- One existing product was stored with the old "wholesale unit cost" semantics (business share $150 → list price $200). It now lists at the $150 the owner intended, keeping the 25% fundraiser cut.
[0.9.70] — 2026-08-25
Edit / pause / delete on wholesale catalogue products.
Added
- Every catalogue product card now has management actions:
- Edit — opens the full prefilled product form (title, description, selling price, discount, category, brand, options, shipping, stock, expiry, images).
- Pause — hides the product from the catalogue and fundraiser pickers without losing anything; Resume brings it back.
- Delete — removes it from the catalogue (soft delete; existing orders are unaffected).
[0.9.69] — 2026-08-25
Fix server-side exception on the wholesale catalogue page.
Fixed
- The wholesale catalogue page crashed once products existed because the category filter used an event handler in a server component (React forbids interactivity there). The filter is now a normal form with a Filter button.
- The same latent issue on the wholesale Analytics fundraiser filter is fixed the same way.
[0.9.68] — 2026-08-25
Event ticketing with donation tiers (club dinners, concerts, charity events).
Added
- Donation tiers on event tickets: each ticket tier can carry an explicit donation component (e.g. a $100 VIP ticket where $60 is the ticket and $40 is a donation). Set it when adding or editing a ticket, and the tier card on the event page shows "Includes a $40 charitable donation".
- Receipted: the buyer's order receipt states the charitable donation included, so sponsors and supporters have the tax line on paper. The fundraiser's CSV export includes the donation portion per order.
- Multiple tiers already work (each ticket is its own tier with price, capacity, description and attendee details) — this adds the donation component and the attendee list stays available to organisers.
- New
Product.donationPortionCentscolumn (migration0039_ticket_donation_tiers).
[0.9.67] — 2026-08-25
Team fundraising: leaderboards, prizes, and the managed-campaign tier.
Added
- Teams & leaderboard (Go Raise It style): organisers can define teams (name + emoji) on the fundraiser create/edit pages. Supporters pick a team when they start their P2P page, and the campaign page shows a live leaderboard ranking teams by money raised, with medals for the top three and a supporter count per team.
- Prizes / incentives: organisers list prizes (e.g. "top team wins a pizza party") which appear on the leaderboard so teams know what they're playing for.
- Managed-campaign tier: organisations can set a campaign manager name and email in their settings. When set, the campaign page shows a "🤝 Managed by [name]" badge — the hand-holding signal schools and clubs respond to.
- New
FundraiserTeamtable,P2PPage.teamId, and organisation campaign manager fields (migration0038_teams_leaderboard).
[0.9.66] — 2026-08-25
Low-friction donations: $15–$500 presets, Apple/Google Pay, zero-fee positioning.
Added
- Donation presets now span $15–$500 ($15, $25, $50, $100, $250, $500) in a tidy three-column grid that works on phones, plus a custom amount field.
- Apple Pay / Google Pay enabled at checkout — donation and order checkouts explicitly request card/wallet payment methods, so Apple Pay and Google Pay appear automatically on Stripe's checkout page (wallets still need to be enabled in the Stripe Dashboard payment-methods settings).
- Zero-fee positioning — the sign-up page now states "no setup fees, no monthly fees — pay only when you raise money", with a $0-to-start badge mentioning Apple Pay/Google Pay.
Notes
- Payout speed (including instant payouts) is set by each seller in their own Stripe account — the platform routes money directly, so payout timing is a Stripe account setting rather than a platform toggle.
[0.9.65] — 2026-08-25
Branded footer on every email.
Added
- Every transactional email now ends with a Generosity-branded footer: the Generosity.co.nz wordmark, the tagline "Sell. Donate. Make a difference.", links to the site, Explore, For businesses and Help, and "Made in Aotearoa New Zealand · Secure payments powered by Stripe".
- The brand footer is applied automatically to both built-in emails and custom templates (and the admin template editor strips it from the editable footer so it's never duplicated).
[0.9.64] — 2026-08-25
Personal thank-you messages in automatic donation receipts.
Added
- Fundraisers can now write a personal thank-you message on the create and edit pages (with a prompt to make it personal). Every donor receives it automatically with their receipt email after a successful donation — styled as a highlighted note from the organiser.
- If no message is set, the standard thank-you receipt is sent unchanged.
- The donation-receipt email template now supports the
{{thank_you_message}}shortcode for custom templates, and the sample preview shows how it looks. - New
Fundraiser.thankYouMessagecolumn (migration0037_thank_you_message).
[0.9.63] — 2026-08-25
Live campaign stats with referrer/UTM tracking.
Added
- Visit tracking: every visit to a fundraiser page is recorded with the visitor, referrer and any UTM tags (utm_source/medium/campaign/content) via a privacy-friendly visitor cookie. No account needed to be tracked.
- Conversion attribution: when the same visitor makes a donation or an order, their most recent visit is marked as converted — giving a real conversion rate, not a guess.
- Campaign stats page (dashboard sidebar → Campaign stats): for each fundraiser, live money raised, page views (total and last 30 days), conversions and conversion rate, plus a "Which links drive support" list ranked by conversions and visits — Facebook, Instagram, WhatsApp, email or any referrer.
- Tracked share links: organisers can generate and copy a share link tagged with the source (Facebook, Instagram, WhatsApp, X, Email or custom), so they can see exactly which social post is working.
- New
FundraiserVisittable (migration0036_campaign_visits).
[0.9.62] — 2026-08-25
Order, delivery and pickup management — operations tooling.
Added
- Delivery/pickup scheduling: each order to fulfil can be given a delivery/pickup date plus tracking carrier and number. The chosen date, pickup/delivery type, offline-sale marker and tracking all show on the order.
- Packing list: a printable packing view of every open order — item, quantity, options, buyer, delivery/pickup details and scheduled date, with tick-boxes, a per-item units/revenue summary, and a Print button.
- Manual orders: record phone or in-person sales directly — pick the product and fundraiser, sale price, quantity, buyer details, delivery or pickup date and a note. The order is created as paid (no Stripe), the settlement is recorded so commission and platform-fee invoicing still flow, and it appears in the fundraiser's sales and the packing list.
- CSV export now includes delivery/pickup date, pickup vs delivery, tracking carrier/number, and online vs offline payment, alongside all the existing fields — ready for the accountant.
- New order columns (migration
0035_order_operations).
[0.9.61] — 2026-08-25
Donor-pays-fee option for fundraisers.
Added
- New Donor-pays-fee toggle on the fundraiser create and edit pages: the 5% platform fee plus card processing is added to the supporter's donation at checkout, so the organiser receives 100% of every gift and nothing is deducted from the amount raised.
- When enabled, the fundraiser page shows a "Fees covered by donors" badge, and the donate form explains that fees are added on top so the full donation reaches the cause (supporters no longer see the optional "cover the fee" checkbox). Donations are charged with the fee added on automatically, including on supporter (P2P) pages.
- When the option is off, the existing donor-choice "cover the fee" checkbox remains unchanged.
[0.9.60] — 2026-08-24
Full fulfilment CSV export for wholesalers.
Added
- Orders to fulfil → Download CSV: exports every order with all details — order id, date, status, product, quantity, unit/item totals, the buyer's option selections (e.g. location), buyer name and email, full delivery address with metro/rural zone and shipping charge, voucher code where applicable, platform fee/GST/wholesaler net/commission, fundraiser and its URL, settlement status, hold date, fulfilment date and failure reason. Includes a BOM so it opens cleanly in Excel/Numbers, and covers up to 5,000 orders.
[0.9.59] — 2026-08-24
One-click "Add to my store" for wholesale items.
Changed
- Adding a wholesale item to a fundraiser store is now a single click: it's automatically added to your newest approved fundraiser at the minimum price (one unit). A compact fundraiser picker appears only when you have more than one eligible fundraiser, and the button shows the price and stock at a glance. After adding, it confirms with "Added to [fundraiser] ✓" and links to your listings.
[0.9.58] — 2026-08-24
Grabone-style wholesale catalogue: clickable cards, full product pages, and product options/variations.
Added
- Full product pages (
/wholesale/p/[id]): photo gallery, title, brand & business, rating, full description, options/variations, delivery and voucher details, and an "Add to my store" box — laid out like a Grabone deal page. - Clickable cards: wholesale catalogue cards link to the product page (whole image/title/info area clickable). Explore cards link to the fundraiser or the full deal.
- "More items" section on product pages: arriving from a fundraiser shows that fundraiser's other items at the bottom; a general visit shows similar deals in the same category.
- Options / variations (Grabone-style): a wholesaler can define options on a catalogue item — e.g. "Location" with Auckland, Wellington, Christchurch — marked required or optional. Buyers choose their option at checkout, and the selection appears on the wholesaler's order for fulfilment. Stored in existing metadata (no schema migration needed).
Changed
- Fundraiser pages now show a "View full deal →" link on wholesale items.
- Order confirmations and the wholesale orders page include the buyer's option selections.
[0.9.57] — 2026-08-24
Stores can run multiple brands from one account.
Added
- Brands on the wholesale dashboard: an approved store can now create multiple brands (organisations) from their account, or attach organisations they already manage. All brands share the same payout account — no new Stripe setup per brand.
- Brand tagging: catalogue products can be assigned to a brand when added or edited. Branded products show their brand badge in the catalogue management page and the brand name on the public wholesale catalogue ("Brand · Fulfilled by business").
- New
WholesalerOrganisationjoin table andWholesaleProduct.organisationId(migration0034_wholesale_brands). A brand can belong to only one store.
Changed
- Wholesaler applications now also reject organisations that are already a brand on another store.
[0.9.56] — 2026-08-24
Image upload buttons added to the remaining link-only forms.
Added
- Blog posts: the cover image field now has an Upload button (best at 1200×675, 16:9).
- Organisation settings: both the logo and cover image fields now have Upload buttons (logo ~400×400 square; cover ~1600×700 wide banner).
- All uploads run through the existing pipeline: auto-rotated, resized (max 1600px long edge), compressed (quality 80), and sent to Bunny Storage automatically when configured (otherwise stored on the server).
[0.9.55] — 2026-08-24
Help hints when adding products, including clear image-size guidance.
Added
- Image hints on every product form:
- Product listings and wholesale catalogue items: square photos look best (e.g. 800×800), uploaded images are auto-resized so any size works, files up to 5 MB, and the first image is the main photo.
- Fundraiser covers: aim for a wide 1600×700 (16:7) banner; uploads are auto-resized.
- Small guidance hints on titles, prices and descriptions so sellers know what to enter.
- The wholesale catalogue form now has an Upload photo button (it only accepted pasted URLs before), matching the other forms.
[0.9.54] — 2026-08-24
Reject requires a reason; rejected items go back to the seller to fix and resubmit (never deleted).
Changed
- Reject now requires a reason. The moderator must type why an item is rejected before confirming. The reason is stored on the item and included in the email to the seller.
- Rejected items are never deleted. They stay in the seller's dashboard
with the rejection reason shown, and each now has an Edit & resubmit
link that opens the full item in a prefilled edit form:
- Fundraisers: the dashboard card shows the reason and an Edit & resubmit link.
- Product listings: "My listings" now shows a red "Rejected" status with the reason, plus Edit & resubmit.
- Wholesale catalogue items: the card shows the reason plus Edit & resubmit.
- Editing a rejected item sends it back to moderation for a fresh review (the previous rejection reason is cleared). Editing a live item's content also returns it to review, while extending an expiry alone no longer pulls an approved listing back into the queue.
[0.9.53] — 2026-08-24
Stronger automated moderation: banned items auto-rejected, high-fraud campaigns auto-banned, and AI rating/comment shown for human review.
Added
- Banned items are auto-rejected with the reason. Fundraisers, product listings, wholesale catalogue items (and bulk-import rows) that hit a critical prohibited term (weapons, drugs, counterfeit goods, stolen property, identity documents, gambling) are automatically rejected, the reason is recorded on the item, and the decision is flagged and audited — no human review needed.
- High-fraud campaigns are auto-banned. When the risk score reaches 9/10 or higher, the fundraiser is auto-rejected ("high fraud risk detected by AI screening") and the account is suspended. Both actions are audited and can be reversed by an admin.
- AI rating and comment on moderation items. Items awaiting human review now show an AI rating out of 10, the AI verdict, and the AI's comment explaining why it was flagged, on both the moderation queue and the preview page. Fundraisers also show their fraud risk flags.
Changed
- Wholesale catalogue items now get an AI review (rating + comment) when they are submitted, matching fundraisers and product listings.
[0.9.52] — 2026-08-24
Voucher code entry made easy to find.
Changed
- On the Catalogue products page, each voucher product now shows a clear "Add voucher codes" button (amber-highlighted when the product has no codes yet) instead of a faint text link. Opening it reveals the two bulk options side by side: Generate automatically (choose how many) and Paste your own codes (one per line, up to 500).
- The Voucher sales page now has a banner pointing to Catalogue products for adding or generating codes, so the area is discoverable from either place.
[0.9.51] — 2026-08-24
Version badge more visible in the site footer.
Changed
- The version number in the footer of every page (public site and dashboard) is now a small, clearly visible badge instead of faint grey text, so the deployed version can be checked at a glance.
[0.9.50] — 2026-08-24
Clickable moderation items with a full preview page.
Added
- New staff-only preview page for every item in the moderation queue:
/admin/moderation/preview/<fundraiser|product|wholesale>/<id>. It shows the full content — images, price, story/description, owner details, AI review score, expiry, delivery and voucher info — with the Approve / Reject / Dismiss controls right on the page.
Changed
- The moderation queue's item titles are now clickable: pending fundraisers, pending products, pending wholesale items and flagged fundraisers/products all open their preview when clicked (previously nothing was clickable and pending items had no public page to view).
[0.9.49] — 2026-08-24
Wholesale catalogue form reworked (selling price + fundraising discount).
Changed
- The "Add a catalogue product" form is reorganised: Title, Description and Image URLs each take a full line; SKU / code sits beside Stock / units available; Category (dropdown) beside Type; voucher fields appear for vouchers; Delivery coverage, Metro and Rural delivery charges sit on one line for physical products.
- "Wholesale unit cost (NZD)" is now Selling price (NZD), and "Minimum commission (%)" is now Fundraising discount (%) — the fundraisers' share of each sale. The business's share is derived the same way as before, so nothing downstream changes.
- A live "Estimated money you receive" box appears under the price as you type: selling price − fundraising discount − 3% Stripe − 5% platform, with a small breakdown.
[0.9.48] — 2026-08-24
One fundraiser per organisation + wholesale product categories.
Changed
- Organisations run one campaign at a time. An organisation can no longer create a second fundraiser: the "+ New fundraiser" and "Campaign builder" options disappear from the organisation page once one exists, the campaign builder page shows a notice instead of the form, and the API enforces the limit server-side. Wholesale was already one-per-organisation — that limit is unchanged.
Added
- Wholesale product categories: pick a category (Food & beverage, Home &
living, Fashion & accessories, Beauty & health, Sport & outdoor, Kids &
toys, Gift vouchers & experiences, Services, Other) when adding or editing
catalogue products. The catalogue management page shows each product's
category badge and has a category filter. Bulk CSV imports accept a
categorycolumn. - New
WholesaleCategoryenum and column (migration0033_wholesale_categories).
[0.9.47] — 2026-08-24
Blog dates removed from the front end.
Changed
- The public blog index and individual blog posts no longer show publish dates. Posts still sort newest-first, and admin views keep their dates.
[0.9.46] — 2026-08-24
Clear duplicate-SKU error for wholesale products + admin error log.
Fixed
- Adding or editing a wholesale catalogue product with a SKU/code already used in your catalogue now shows a clear message ("That SKU/code is already used in your catalogue…") instead of the generic "Something went wrong". The same applies to bulk CSV imports, which now label the offending row with the duplicate SKU.
Added
- Admin → Error log: unexpected server errors are recorded with the
route, error code, user, time and stack trace, and are browsable from the
admin sidebar (next to the email log). New
SystemErrorLogtable (migration0032_system_error_log).
[0.9.45] — 2026-08-24
Fundraiser (donation page) expiry with a no-expiry option.
Added
- New Fundraiser expiry choice when creating a fundraiser: No expiry (default), 15, 30, 45 or 90 days. Pages with an expiry auto-close on that date; pages without one stay open indefinitely.
- Dashboard fundraiser cards now show the current status ("No expiry",
"Expires
" or "Closed ") with a control to set, extend or remove the expiry — including reopening a closed page. - Public fundraiser pages show "Accepting donations until
", and once closed display a "This fundraiser closed" notice with donations, orders, tickets and supporter-page signups switched off.
Changed
- Closed fundraisers stop accepting donations, purchases, raffle/event tickets, subscriptions and new supporter pages (enforced server-side, not just hidden in the UI).
- Closed fundraisers are filtered out of the public fundraiser list, Explore results and the wholesale store picker, and can't have new wholesale items added to them.
- New database column
Fundraiser.expiresAt(migration0031_fundraiser_expiry).
[0.9.44] — 2026-08-24
Better passkey retry UX.
Fixed
- If the passkey prompt is cancelled or fails, the login page now returns to the password step with a clear message instead of leaving the user stuck on a "Continue with passkey" button whose one-time grant was already consumed.
[0.9.43] — 2026-08-24
Enforce the password-before-passkey gate.
Security
- The Auth.js passkey provider issues an authentication challenge even when
getUserInforeturns null, which would have let a passkey be used without the password.getUserInfonow rejects (throws) any unauthenticated request that doesn't carry a valid, single-use password-verified grant, so a challenge can only ever be minted after the password is confirmed.
[0.9.42] — 2026-08-24
Fix passkey provider startup.
Fixed
- The Auth.js WebAuthn provider refused to start without
experimental.enableWebAuthn, which surfaced as a "server configuration" error on/api/auth/providers. The flag is now set, so the passkey 2FA provider loads correctly (v0.9.41's runtime fix).
[0.9.41] — 2026-08-24
Account tab (change password + two-factor login with passkeys, including Heylogin).
Added
- New Account item at the bottom of the dashboard sidebar, available to
every signed-in user, with two sections:
- Change password — verify the current password, set a new one. Social (Google/Apple) accounts without a password can set their first one here.
- Two-factor login (passkeys) — add and remove passkeys. Works with Heylogin, Windows Hello, Touch ID, Face ID and hardware security keys.
- Passkey sign-in after your password: users with a passkey enrolled now complete two steps — password, then a passkey confirmation. The password alone can no longer create a session for those accounts.
- New
Authenticatordatabase model (migration0030_passkeys) via the Auth.js built-in passkey provider; no third-party auth service required. - Admin recovery tool on the Users page: "Remove passkeys" clears all 2FA credentials for an account if someone loses their device.
- Audit log entries for password changes and passkey add/remove.
Changed
- Login flow now checks the password first (
/api/auth/login-step1) and only then issues a short-lived, single-use passkey challenge, so a passkey can never be used as the only credential. - Dependencies:
@simplewebauthn/browserand@simplewebauthn/server(the peer packages the Auth.js passkey provider uses).
Security
- Rate limiting on password verification and password-change endpoints.
- WebAuthn challenges are single-use, expire after five minutes, and are bound to the password-verified user.
[0.9.40] — 2026-08-24
Fix "client-side exception" from stale cached assets after deploys.
Changed
- The service worker cache version was bumped so every visitor's browser drops old cached pages/scripts after this update. Stale HTML from an older build could reference JS files the new build removed, which caused the "Application error: a client-side exception" screen after rapid deploys.
- The service worker no longer caches error responses, so a brief 500 during a deploy can't become a broken cached page.
[0.9.39] — 2026-08-24
Account-aware dashboard overview.
Changed
- Approved wholesalers now land on a business-focused overview instead of the fundraiser dashboard: paid-to-you total, orders/units, catalogue product count, awaiting-fulfilment count, a 30-day sales chart, their wholesale tool links and their most popular products.
- Fundraisers keep the existing fundraising overview unchanged.
[0.9.38] — 2026-08-24
Split dashboard menus for fundraisers and wholesalers.
Changed
- Approved wholesalers now see their own sidebar menu: Catalogue products, Orders to fulfil, Analytics & export, Billing and Voucher sales — with the fundraiser menu (New fundraiser, New listing, My listings) hidden.
- Fundraisers no longer see the wholesale menu (Wholesale catalogue adding stays approval-only on the server, so this is a UX change on top of the existing API guard).
- Staff still see the full admin section regardless of account type.
[0.9.37] — 2026-08-24
Active sidebar highlighting.
Changed
- The dashboard/admin side menu now highlights the page you're on with a solid green pill. Sub-pages stay highlighted (e.g. editing a specific email template keeps "Email templates" active), and the active state is announced to screen readers.
[0.9.36] — 2026-08-24
Wholesaler application: business location and applicant IP.
Added
- The wholesaler application form now asks for the business city (required) and region/state, alongside the existing country selector.
- The applicant's IP address is recorded on the application (stored at first submission; the audit log also captures IP, city and region).
- Admin → Wholesaler applications now shows the full business location and the applicant's IP on each row, so international and suspicious applications are easy to spot.
- Schema:
businessCity,businessRegionandsignupIponWholesalerProfile(migration0029_wholesaler_location_ip).
[0.9.35] — 2026-08-24
Moderation queue previews.
Changed
- Pending products in Admin → Moderation now show a full preview inline: photo, price, condition, stock, expiry, description, fulfilment options, pickup/shipping details, wholesale cost and commission, who listed it and on which campaign (with a link to the campaign page) — so moderators can review the item without leaving the page.
- Pending fundraisers now show their cover photo and story text alongside the Approve/Reject/Dismiss actions.
[0.9.34] — 2026-08-24
Clearer Stripe Connect setup error.
Changed
- When Stripe refuses to create connected accounts because the platform profile / Connect questionnaire isn't complete, the site now says exactly that (admins see the full explanation; other users see "payments aren't available yet") instead of the generic "couldn't start onboarding" message. The underlying fix is completing the questionnaire in the Stripe dashboard.
[0.9.33] — 2026-08-24
Password generator on signup and reset pages.
Added
- Generate a strong password button on the create-account page (and the reset-password page): one click fills the password field with a cryptographically random 18-character password containing upper/lowercase letters, digits and symbols (no confusing lookalike characters).
- The generated password is shown for copying, with Copy and New password buttons, so people can save it in their password manager before finishing signup.
[0.9.32] — 2026-08-24
Password reset flow.
Added
- Forgot password? link on the sign-in page, leading to a "reset your password" page that emails a one-time reset link (expires after 1 hour).
- The reset link opens a "choose a new password" page with confirmation; successful resets update the password hash and revoke all other outstanding reset links.
- Security: tokens are random 64-character values stored only as SHA-256 hashes (a database leak can't be used to reset accounts), are single-use, and the request endpoint never reveals whether an email is registered. Rate-limited per IP and per email; all activity is audit-logged.
- New editable password-reset email template (Admin → Email templates)
with
{{name}}and{{reset_url}}shortcodes. - Schema:
PasswordResetTokenmodel (migration0028_password_reset).
[0.9.31] — 2026-08-24
Editable email templates with shortcodes.
Added
- Every email template (all 19 types, including the new voucher-sold, wholesaler-fee-invoice, commission and platform-fee emails) can now be edited from Admin → Email templates → Edit: subject, HTML body and footer, with save, draft preview, reset-to-default and test-send.
- Each editor page shows the template's shortcode reference — clickable
{{placeholders}}(e.g.{{donor_name}},{{campaign_url}},{{voucher_codes}}) that insert at your cursor. Shortcodes are replaced with live values when the email is sent. - Custom templates are stored in the database (survive deployments) and apply automatically to real sends: donation/order receipts, campaign updates, refunds, disputes, moderation, wholesale fulfilment, vouchers, invoices, direct-debit notices, commission and platform-fee emails.
- Templates that have been customised show a "Customised" badge on the list page; resetting always restores the built-in default.
Changed
- Every transactional send now declares its template type and live variables so custom versions apply consistently across all flows.
- Test sends and previews cover all 19 templates and honour custom versions.
[0.9.30] — 2026-08-24
Email log admin area.
Added
- Every email the platform sends (receipts, invoices, notifications) is now recorded in the database — recipient, subject, provider (SMTP / Resend / dev log), delivery status and the error message when a send fails.
- New Admin → Email log page with a live search box (recipient or subject), a status filter (sent / failed / logged) and summary counts.
- Schema:
EmailLogmodel (migration0027_email_log). Entries start from the first send after this build deploys; older emails are not backfilled.
[0.9.29] — 2026-08-24
SMTP email support (MXroute-ready).
Added
- Emails can now be sent via SMTP (e.g. MXroute) using
SMTP_HOST/SMTP_PORT/SMTP_SECURE/SMTP_USER/SMTP_PASS. SMTP takes priority when configured; Resend remains the alternative; without either, emails still log to the server console (dev mode). nodemaileradded and externalised for the server runtime.
[0.9.28] — 2026-08-24
Two webhook destinations supported (platform + connected accounts).
Changed
- The payment webhook now accepts two signing secrets
(
STRIPE_WEBHOOK_SECRET+STRIPE_WEBHOOK_SECRET_2, or a comma-separated list), so Stripe's two-scope webhook setup works: one destination for platform events, one for connected-account (seller) events, both pointing at the same URL. docs/stripe-setup.mdupdated with the exact two-destination setup and which events belong on each scope.
[0.9.27] — 2026-08-24
Businesses get a clear "Connect Stripe" prompt.
Changed
- The wholesale dashboard now shows a Connect your payout account call-to-action whenever a business hasn't connected Stripe — both while their application is under review and once approved. Previously the button only existed on the main dashboard, so businesses could miss the step.
[0.9.26] — 2026-08-24
Version badge in footers + AdvertiseOnline credit.
Added
- Visible version number:
v0.9.26now shows in the site footer (next to the copyright) and on the login page, so you can confirm the live build at a glance. - Footer credit: a small, greyed-out AdvertiseOnline.co.nz logo at the bottom of the site footer — full colour on hover — linking back to https://advertiseonline.co.nz, labelled "Site built by AdvertiseOnline.co.nz".
[0.9.25] — 2026-08-24
Mobile layout fixes — no more horizontal scrolling.
Changed
- Public header: added a real mobile menu — a hamburger button on small screens that opens a dropdown with every link plus Sign in / Start fundraising. The desktop nav stays a row on larger screens. Previously the nav rendered as one long unwrapped row on mobile, pushing the page wider.
- Dashboard sidebar: the nav now wraps on mobile (it was a horizontal row of links that overflowed) and stays a vertical column on desktop.
- Global guard:
overflow-x: hiddenon html/body so any residual wide content can never create side-to-side scrolling again.
Verified
- The help widget already uses a responsive width (
min(92vw, 400px)), so it doesn't overflow on small screens.
[0.9.24] — 2026-08-24
Business page — clearer "how a sale looks" breakdown.
Changed
- The worked example now shows the full picture with correct arithmetic: $100 sale − $2.95 Stripe processing (approx, varies) − $10 fundraiser cut (min 10%) − $5.75 platform fee = $81.30 you keep. The first-sale line (platform fee waived) now correctly reads $87.05.
- Footnote clarifies the figures are approximate, Stripe's rate varies by card type, and exact numbers are in the dashboard.
[0.9.23] — 2026-08-24
Legal documents go live — draft banners removed.
Changed
- All ten policy documents (Terms, Privacy, Seller, Fundraiser, Wholesaler, Prohibited Items, Refund, Dispute, AML/CFT, Charity Verification) now show Version + Effective date 24 August 2026 with no "Draft for legal review" banners. Draft-only disclaimers removed; useful notes (e.g. CGA rights, Stripe Connect KYC reliance) retained in plain form.
- The internal policy index (
docs/policies/README.md) updated to reflect the live status.
Notes
- The money-flow description these documents rely on is the Option A pack
(
docs/legal-signoff-option-a.md) — still worth sending to your lawyer for the formal review, but the pages are no longer labelled drafts.
[0.9.22] — 2026-08-24
AI fraud protection as a business selling point.
Changed
- Business page now markets the platform's AI checks: a new "AI fraud protection" feature card and a full "Protected by AI, reviewed by humans" section covering order screening, fundraiser/product pre-live checks, delivery-address verification, and the human-review rule for anything flagged. Metadata description updated too.
- Copy reflects what's already wired: fundraisers, products, reviews and organisations are AI-reviewed on submission; orders are screened at checkout; delivery zones are AI-verified.
Notes
- Order fraud screening runs once
ORDER_FRAUD_ENABLED=trueand a provider key are set on the server; the rest of the checks run automatically with the DeepSeek key configured.
[0.9.21] — 2026-08-24
Business fee confirmed: 5% + GST with the first $1,000 of sales free.
Changed
- Free-first-$1,000 is now the product default (not just a server
setting):
wholesalerFreeSalesThresholdCents()defaults to 100000 cents, so every approved business gets its first $1,000 of sales with no platform fee — a $50 saving at 5%. Per-business overrides still available in admin;WHOLESALER_FREE_SALES_CENTS=0disables it. - Business sign-up page makes it the headline: hero banner "No platform charges on your first $1,000 in sales", updated feature card, the example now shows the first sale keeping $90 (fee waived), and the sign-up checklist states it up front. Metadata description updated too.
- Help centre and docs updated ("first $1,000 free" instead of the generic "$X").
[0.9.20] — 2026-08-24
International business applications require manual admin review.
Added
- New country field on business applications (default New Zealand; common countries selectable, "Other" with a free-text name).
- Non-NZ applications are flagged for manual review: the admin
wholesalers page shows the country and an "International — manual review"
badge; applications remain in
APPLIEDstatus until an admin decides (approval is admin-only, and the audit log records the country). - Migration
0026_business_country.
[0.9.19] — 2026-08-24
Stats everywhere — views, popular products, top purchasers, downloads.
Added
- Page-view tracking: every fundraiser page counts total views and unique
daily views (rate-limited, deduped per IP/day; migration
0025_views). - Fundraiser stats dashboard (
Dashboard → Stats & orders): views, raised, orders, units, average order, raised-per-day chart, most popular items, top purchasers (buyer name/email — owner-only, for privacy), recent orders, and a CSV export with delivery details. - Business analytics upgrade: paid-to-you, orders/units, fundraiser page views, top buyer spend, sales-per-day chart, most popular products, top purchasers, and a "what works" table — every fundraiser selling your products with views, orders, units and what you receive. Filter by fundraiser to see each campaign's contribution. Download all orders CSV now includes the full delivery address + commission and honours the fundraiser filter — ready to fulfil.
- Admin analytics: fundraiser page views, site-wide most popular products and top purchasers, alongside the existing totals/charts.
Notes
- Buyer names/emails appear only in owner/admin dashboards, not on public pages (Privacy Act 2020).
[0.9.18] — 2026-08-24
Off-site backup to Dedirock (hardened SFTP).
Added
- Dedirock backup target configured (
23.95.7.102): dedicated non-loginbackupuser, key-only auth, forcedinternal-sftpconfined to/home/backup/generosity, and the authorized key restricted to this server's IP with forwarding/PTY disabled — a compromised app server can't reach the rest of the backup host. - Backup job (
scripts/offsite-backup.sh): pg_dump → gzip, uploads tar, pushed via restricted SFTP with 14-copy retention. - Setup guide (
docs/backup-offsite.md) with the one-time root commands to finish on the app server (move key, install script, cron, test).
Notes
- Requires the one-time root steps in the guide (the key is staged at
/home/deepseekcs/.ssh/generosity-backupon the app server).
[0.9.17] — 2026-08-24
Go-live plan rewritten for the current build.
Changed
docs/go-live.mdrebuilt as a phased checklist (Phase 0 decisions & sign-off → deploy/config → test-mode drills → live switch → first-two-weeks watch), with explicit gates and references to the current setup guides.
[0.9.16] — 2026-08-24
Off-server image storage (Bunny) + automatic unused-image cleanup.
Added
- Bunny Storage support (
lib/uploads.ts): whenBUNNY_STORAGE_ZONEandBUNNY_STORAGE_ACCESS_KEYare set, new uploads go straight to Bunny Storage;BUNNY_PULL_ZONE_URLserves them via the Bunny CDN. Existing/api/uploads/...URLs keep working unchanged (the route serves local files first, then redirects to the CDN) — no database migration needed. - One-off backfill script (
scripts/backfill-uploads-to-bunny.mjs) to move existing files to Bunny. - Upload garbage collector (
lib/upload-gc.ts+/api/cron/upload-gc): scans every product, wholesale product, fundraiser, organisation and blog post for referenced images and deletes the rest after a grace period (UPLOAD_GC_GRACE_DAYS, default 7). Covers the cases you asked about — business removes an item, changes a photo, fundraiser does — plus anything else that becomes unreferenced. Runs against whichever backend is active (local or Bunny). - Setup guide:
docs/upload-storage.md.
Notes
- Everything is env-gated: with no Bunny keys, the site behaves exactly as before (local storage).
- Images are still optimised before storage (1600px, quality 80, metadata stripped), so what reaches Bunny is already small.
[0.9.15] — 2026-08-24
Auto-optimised image uploads — smaller files, originals never stored.
Changed
- Every uploaded image is now processed on the way in: resized to a max long edge of 1600px (never enlarged), re-encoded at quality 80 for JPEG/WebP/AVIF, losslessly compressed for PNG, EXIF/orientation metadata stripped, and the original bytes are never written to disk.
- The site fits images into each layout with CSS (
object-cover), so one lean file serves every space — campaign covers, product cards, storefront banners and blog covers. - Safety net: if optimisation ever produced a larger file (e.g. a tiny already-optimised image), the original is kept instead.
sharpis now a direct dependency (it was already in the tree via Next).
Notes
- Applies to new uploads. Existing uploaded images are untouched — a
one-off script can re-process the current
uploads/folder if you want to reclaim space now.
[0.9.14] — 2026-08-24
Finished the listing-expiry loop ("extend it anytime" is now real).
Added
- My listings dashboard page (
/dashboard/products): every fundraiser listing in one table — price, moderation status, expiry date and a quick extend/relist control (15/30/45/90 days). Expired listings show a "Relist for…" button; live ones "Extend by…". - Sidebar link to My listings, and a second CTA on the Getting Started page's "List products" step.
Confirmed already in place
- Getting Started page (six steps: sign up → connect payout account → fundraiser → list products → share your link → get paid).
- Every campaign, organisation storefront and P2P page has its own shareable link with a copy button on campaign pages.
- Auto-expiry (15/30/45/90 days) on the listing form, enforced everywhere (checkout, explore, catalogue), with the update API supporting extension.
[0.9.13] — 2026-08-24
Full-system audit: half-finished features, correctness fixes and old-idea remnants.
Fixed
- Physical wholesale orders now email the business with the buyer's delivery address, zone and delivery charge — previously only vouchers notified suppliers, so physical orders relied on the dashboard alone.
- Removed the leftover destination-charge transfer engine from the
codebase (
lib/wholesale-transfers.tsnow only runs the velocity check). The old webhook paths that would have attempted platform→seller transfers (double-paying the business under direct charges) are gone. - Admin dispute view and analytics export no longer show the obsolete "transfer ID / reversal" fields from the old model (the export now lists shipping zone and charge instead).
docs/architecture.mdupdated from the old application-fee/transfer model to the current direct-charge + invoiced-fee model.
Verified during the audit
- The webhook handles direct-charge checkouts, commission/platform-fee/ business-fee invoices, disputes, refunds and payouts with no transfer remnants.
- All 14 released versions are tagged; type-check and production build pass.
- The branch had been reset to an old commit externally (v0.8.0) while the correct code lived in the tags — the branch was restored to v0.9.12 and this release is committed on top.
Known "waiting on you" items (not code bugs)
- Live Stripe keys + webhook re-pointing (listen to connected-account events and invoice events).
- Server env keys:
RESEND_API_KEY(email),DEEPSEEK_API_KEY(AI),BLOG_API_TOKEN,CRON_SECRET,INDEXNOW_KEY(+ optionalINDEXNOW_ROTATE_DAYS),ORDER_FRAUD_ENABLED. - Fee numbers:
WHOLESALER_FEE_BPS(default 5%) and the free-first-$X threshold (default none). - Lawyer/accountant sign-off on the legal docs and the Option A money flow.
- The dormant BECS subscription-fee invoicing (Phase 3 item, fee default $0) is still available but not scheduled.
[0.9.12] — 2026-08-24
Geographical delivery + metro/rural shipping charges with AI zone checking.
Added
- Delivery coverage on wholesale products: businesses choose where they ship (all of NZ, North Island only, South Island only) and set simple metro and rural delivery charges for physical products.
- Delivery address at checkout: buyers of physical wholesale items enter their delivery details (name, street, city, region, 4-digit postcode). The zone is derived from the NZ postcode rule (rural codes start 7/8/9), the correct metro/rural charge is added to the order, and out-of-coverage orders are declined with a clear message.
- AI address verification: the AI cross-checks the address against the postcode and the supplier's coverage. Disagreements don't block the sale — the order is flagged in the moderation queue for a human to verify before fulfilment.
- Order & fulfilment visibility: shipping charge, zone and the full delivery address are stored on the order, shown to the business on their orders page, included in the buyer's receipt email, and sent to the supplier's fulfilment notification so they can ship without chasing the buyer.
- Business page + help centre updated to explain delivery coverage and metro/rural charges.
- New schema (migration
0024_shipping_zones):ShippingCoverageenum,WholesaleProduct.shippingCoverage/MetroCents/RuralCents, and order shipping fields.
Notes
- The platform fee and the fundraiser's commission apply to the item price only — the delivery charge is passed through to the seller.
- Vouchers are digital and never require a delivery address.
[0.9.11] — 2026-08-24
XML sitemap generator installed on the site.
Added
/sitemap.xml— a live sitemap generated by Next.js on every request. It lists the key static pages, all legal documents, published blog posts, live fundraisers, organisation storefronts and P2P pages, with last-updated dates and priorities. New content appears automatically.robots.txt— allows all crawlers and points them at the sitemap.
Notes
- A downloaded
2026-AdvertiseOnline-generator.zipcouldn't be located on this machine (checked Downloads, Desktop, Documents, OneDrive, attachments and phone folders) — if it's re-attached or its path shared, it can be inspected and installed alongside this.
[0.9.10] — 2026-08-24
Security hardening pass — full audit + fixes.
Fixed
- Login brute force: credential sign-in is now rate-limited (10 attempts per email per 15 minutes), returning the same error as a wrong password so attackers can't detect locked accounts.
- AI cost abuse: the AI content generation endpoint is now rate-limited (40 calls per IP per 10 minutes) so a logged-in user can't burn through your DeepSeek/OpenAI tokens.
- IP-spoofable rate limits: the rate limiter trusted the first
X-Forwarded-Forvalue, which an attacker could spoof through the proxy. It now prefersX-Real-IPand otherwise uses the last (proxy-appended) forwarded address. - Upload validation: uploads now verify the file's magic bytes (not just
the client-declared type), so a renamed HTML/script file can't be stored
or served as an image. Uploaded files also get
X-Content-Type-Options: nosniff. - Blog sanitizer hardened: rendered markdown now also strips iframes,
objects, embeds, forms, SVG/MathML, links, metas and bases, plus
data:andvbscript:URIs on top of the existing script/event-handler stripping. - Security headers on every response:
X-Content-Type-Options,X-Frame-Options: DENY,Referrer-Policy,Permissions-Policy, and a Content Security Policy (frame-ancestors none, base-uri/form-action self, remote images allowed for cover photos). - Blog publishing API rate limit on the token-based update/delete endpoints (30 per 10 minutes).
Dependencies (npm audit)
- postcss upgraded to a patched 8.5.x (resolves 4 high advisories, including the source-map disclosure issues).
- sharp overridden to patched 0.35.x (resolves the libvips CVE cluster).
- Remaining:
deepmerge-tsinside the Prisma CLI tooling (dev-only, not shipped to the runtime). The full remediation for the bundled sharp/postcss chain is a Next.js 16 upgrade, which is a separate breaking-change task.
Verified by review
- Authorization (ownership) checks confirmed on fundraiser/product/wholesale product & order/update/review/export/admin routes.
- Webhook signature verification, server-side price/fee computation, Stripe direct-charge account routing, and the no-open-redirect login flow all confirmed.
[0.9.9] — 2026-08-24
AI token efficiency pass — every AI call tightened.
Changed
- Tighter prompts everywhere: moderation/review prompts, order fraud screening, campaign risk scoring and the fundraiser-reasons check were rewritten to be short and specific (JSON-only replies, no filler).
- Per-task output caps (
max_tokens):- Content generation: now capped per type (80–400) instead of a flat 700.
- Moderation/AI review: 120 (was default 300).
- Order fraud screening: 120 (was 250).
- Fundraiser-reasons verification: 200 (was 500).
- Campaign risk scoring: 120 (was 200).
- Long inputs truncated before they're sent (stories capped at ~3–4k characters, generation context at 2.5k) so prompt cost stays low.
- Provider consistency fix: content generation and campaign risk scoring now use the configured review provider (DeepSeek preferred, OpenAI fallback) instead of only checking for an OpenAI key — so the AI tools actually run with a DeepSeek key instead of silently using template fallbacks.
[0.9.8] — 2026-08-24
Site-wide content audit for the new payment system.
Changed (non-legal pages & copy)
- Landing page: "Get paid safely" now explains the 5% + GST fee is invoiced to the fundraiser, never taken from donations.
- Getting started: step 6 clarified — payments go straight to your bank; wholesale commissions are paid automatically by the business; the platform fee is invoiced weekly to the card you add.
- Business page: the example breakdown now reflects the direct-charge model (buyer pays the business $100 → $10 commission → $5.75 fee invoiced → business keeps $84.25), instead of the old "fee from the sale" wording.
- Campaign page "Where the money goes": explains donations/sales go straight to the seller's own Stripe account and the fee is invoiced to the fundraiser.
- Dashboard sales page: updated the split explanation and column labels — buyers pay sellers directly; wholesale suppliers are paid directly (not "from the sale"); fees shown as invoiced.
- Connect payout account button: no longer says money is "held securely" — it's paid out by Stripe directly, and notes the fee card step.
- Help centre: "Covering platform fees" and "Campaign settings" updated to match the invoiced-fee model and the new reasons editor.
- Fee references aligned: the stored subscription fee reference and the
PLATFORM_FEE_PERCENTconstant now use 5.0 (not the old 5.75 application fee), with migration0023_subscription_fee_percent. - Stripe onboarding comment/URL cleanup (Standard accounts only).
Verified
- Fundraiser Stripe setup end to end: Standard account via hosted onboarding,
account.updatedsync, dashboard payout-account + fee-card steps, and the help guide all match the current flow.
[0.9.7] — 2026-08-24
Legal pages fully aligned to the SaaS/direct-charge model.
Changed
- Audited all legal pages for leftover marketplace wording and removed
it:
- Terms of Service: platform reframed as software/tools/administration; "marketplace platform" wording removed.
- Privacy Policy: same reframe + typo fix ("an agency"); version 2.0 with a direct-charge change note.
- Seller Agreement: no longer describes the Platform as a "marketplace and payment facilitator"; chargebacks clarified as taken from the seller's own Stripe balance. Version corrected to 2.0.
- Fundraiser Agreement: "marketplace items" → "catalogue items"; "hold or reverse payouts" → "pause payouts or commission invoicing".
- Refund, Dispute and AML/CFT policies: bumped to 2.0 with change notes describing the direct-charge model (seller is merchant of record; the Platform never holds or routes money).
- Verified the whole site (all pages and components) has no remaining "marketplace" phrasing.
[0.9.6] — 2026-08-24
Edit fundraising reasons on existing campaigns.
Added
- Each fundraiser card on the dashboard now shows its reason tags (or "No reasons selected") with an Edit reasons / Add reasons button. Owners can change what they're fundraising for at any time, not just at creation.
- Saving re-runs the AI verification against the story (unsupported tags removed, clearly-described tags added, flagged for review if something was removed), so edits stay honest.
[0.9.5] — 2026-08-24
Automatic IndexNow key rotation.
Added
- Rotatable IndexNow key: the active key now lives in the database
(falling back to
INDEXNOW_KEY), so it can change at runtime without a deploy or restart. The verification file at/<key>.txtfollows automatically. - Rotation cron (
/api/cron/indexnow?key=…): generates a fresh 32-char key whenINDEXNOW_ROTATE_DAYShas elapsed (default off; 30 is recommended). Safe to run hourly — it self-throttles to the interval. Each rotation is audited (indexnow.key_rotated).
Notes
- Rotation is hygiene, not security: the key file is public by design. A 30-day cadence invalidates leaked copies without churning the file.
[0.9.4] — 2026-08-24
Fundraising reasons — "what are you fundraising for" tags with AI verification and business opt-outs.
Added
- Fundraiser setup checkboxes: campaigns can tick what they're raising for (medical, education, sport, community, animals, environment, arts & culture, faith, emergency, event, other). Tags show as badges on the public campaign page.
- AI verification in the background: the configured AI reads the story against the selected tags and removes tags the story doesn't support and adds tags it clearly describes. Changes are audited; a campaign that has a tag removed (a likely misrepresentation) is flagged in the moderation queue for human review.
- Business opt-outs: approved businesses can tick "causes you won't fundraise for" in their wholesale dashboard. Fundraisers raising for an excluded cause can't add that business's offers — the catalogue shows "Not available for your fundraiser" and the add-to-store API rejects the add with a clear message.
- New schema (migration
0022_fundraising_reasons):FundraisingReasonenum,Fundraiser.reasons,WholesalerProfile.excludedReasons.
[0.9.3] — 2026-08-24
IndexNow — instant search-engine submission.
Added
- IndexNow integration (
lib/indexnow.ts): new and changed pages are submitted to Bing and other participating search engines the moment they go live. - Key file endpoint —
https://generosity.co.nz/<key>.txtis served automatically whenINDEXNOW_KEYis set (any other key returns 404). - Automatic submissions:
- Blog posts on publish (both
/blog/<slug>and/blog), from the admin area or the external AI publishing API; - Fundraisers when approved and when updated (
/f/<slug>); - Organisation storefronts on creation (
/orgs/<slug>); - P2P supporter pages on creation (
/p2p/<slug>).
- Blog posts on publish (both
- Setup guide:
docs/indexnow.md; env entry added to.env.example.
Notes
- Submissions are best-effort and never block publishing. Batches of 25
URLs per request. Disabled entirely when
INDEXNOW_KEYis unset.
[0.9.2] — 2026-08-24
Business platform fee on sales generated, with growth options.
Added
- Business platform fee: businesses supplying the wholesale catalogue
are now invoiced 5% + GST of the sales generated through fundraisers
(rate via
WHOLESALER_FEE_BPS, default 500 = 5%). Invoices are created on the platform's own Stripe account, auto-charged to the business's saved card on the same weekly cadence as the fundraiser fee, and appear in the business's Billing page and the admin wholesale-invoices area. - Free-first-$X (
WHOLESALER_FREE_SALES_CENTSglobally, or a per-business override in Admin → Wholesalers): the first $X of a business's lifetime sales incur no platform fee. - Credit vouchers: admins can grant credit to a business
(
feeCreditCents) that offsets future platform-fee invoices before the card is charged — the "give $X in credit" growth lever. - Refund/dispute fairness: when a wholesale order is refunded or lost to a chargeback, the business receives an automatic fee credit for that sale (idempotent).
- New fields (migration
0021_business_platform_fee):WholesalerProfile.feeCreditCents,WholesalerProfile.freeSalesThresholdCents,WholesaleSettlement.feeCreditApplied.
Changed
- Admin wholesale-invoice retry now handles card-charged invoices (Stripe invoice re-pay) as well as BECS mandates.
- Help centre, business landing page, Wholesaler Agreement v2.1, Terms of Service, legal sign-off pack and setup docs updated for the business fee.
[0.9.1] — 2026-08-24
Goal-gradient improvements (GiveALittle-style).
Changed
- "Raised" now counts product sales too — donations + paid orders (including wholesale sales) drive the progress bar, the dashboard totals, and the organisation storefront, so the number tells the truth on every campaign type.
- "X left to go" under the bar — the goal-gradient trigger shows how much is needed, not just a percentage.
- "Goal reached 🎉" state — when raised crosses the goal the bar celebrates instead of capping silently.
Added
- Goal celebration (
lib/milestones.ts): when a payment crosses the goal, the platform posts a campaign update from the owner and emails every supporter (donors + buyers). Fires once per goal; if a refund drops the total below the goal the flag clears so it can fire again. - Extend goal: fundraisers can raise their target from the dashboard
once reached (resets
goalReachedAtso urgency and celebration restart). - New
Fundraiser.goalReachedAtfield (migration0020_goal_milestones).
Documentation
- Help centre "How goals and progress work" article updated.
[0.9.0] — 2026-08-24
Option A — direct charges. The payment architecture is rebuilt so the platform never touches sale or commission money.
Changed (payment architecture)
- Every payment is now a direct charge to the seller's own Stripe
account (the business for wholesale items, the fundraiser for their own
items, donations and subscriptions). No more destination charges,
transfers,
on_behalf_ofor application fees. - Wholesale: the business is the merchant of record. The buyer pays the business directly; the business pays the fundraiser's commission (minimum 10%) via automatically generated invoices, charged to a card the business saves once (Stripe's official cross-account card-cloning pattern).
- Platform fee: 5% + GST is now invoiced to the fundraiser on a weekly
cycle (
PLATFORM_FEE_BILLING_CADENCE_DAYS, default 7) and auto-charged to a card the fundraiser saves once. It is no longer taken from payments. - Refunds and chargebacks are debited from the seller's Stripe balance; commission on refunded/disputed orders is cancelled or credited against the next invoice. The platform no longer backstops sale/commission chargebacks.
- Receipts show the real seller (business for wholesale, fundraiser
otherwise). Commission invoices include GST only when the fundraiser is
GST-registered (
Fundraiser.gstRegistered).
Added
- New schema (migration
0019_option_a_direct_charges):CommissionInvoice,PlatformFeeInvoice,WholesalerConnectedCustomer, settlementINVOICED/CREDITEDstatuses, billing-card fields. - Commission engine (
lib/commission.ts) and platform-fee billing (lib/platform-fee-billing.ts) with automatic retries and reminders. - Card setup flows: businesses add a commission card in Billing; fundraisers add a fee card in the dashboard (hosted Stripe setup, no card data stored by the platform).
- Commission invoice history on the wholesaler billing page; updated settlement statuses in the admin area.
- New email templates: commission invoice issued/paid, card reminders, platform-fee invoice + reminder.
- Webhook events updated for direct charges and invoices (
invoice.paid,invoice.payment_failed,invoice.finalized); webhook endpoint must listen to events on connected accounts. - Legal sign-off pack for your accountant/lawyer:
docs/legal-signoff-option-a.md.
Documentation
- Fundraiser, Wholesaler, Seller agreements + Terms, Privacy, Refund, Dispute and AML-CFT policies updated to the direct-charge model.
- Help centre updated (how payments work, platform fees, commission invoicing for businesses, Stripe setup with the fee card step).
docs/stripe-architecture.md,docs/wholesale-marketplace.md,docs/stripe-setup.md,docs/go-live.mdrewritten for Option A.
Deployment notes
- Apply migration 0019 on the server (
prisma migrate deployruns automatically on deploy). - Re-point the Stripe webhook to listen to your account AND connected
accounts and enable the new invoice events (see
docs/stripe-setup.md). - Fundraisers and businesses must add their payment cards; run the
test-mode drills in
docs/stripe-setup.mdbefore live.
[0.8.2] — 2026-08-24
Help centre — Stripe setup guides.
Added
- New Stripe setup category in the help centre with three articles:
- Full Stripe account setup (step by step) — what information is needed (legal name, DOB, address, ID, bank account, sometimes IRD), the exact steps from the dashboard through hosted onboarding, and troubleshooting.
- Why do I need to verify my identity and give my bank details? — the KYC/AML reasons in plain language.
- Understanding the admin Payments setup page — what the page checks (keys, webhook events, connected accounts) and how to use it.
- The admin Payments setup page now links to the help article explaining it.
Corrected (review of an external AI answer)
- This platform uses Stripe Connect Standard accounts with Stripe-hosted onboarding — not Express or Custom. The help article reflects that: Stripe collects identity and bank details directly, the platform never sees them, and no business fields or MCC codes are pre-filled by the platform (a Custom-account pattern that does not apply here).
- The required-information list and the “moderate friction” assessment from the reviewed answer were accurate and are now documented in the help centre, written for a casual fundraiser (school PTA, muffin stall, etc.).
[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.
[0.1.0] — 2026-08-22
Marketplace core (initial release build).
Added
- Platform foundation: accounts, fundraisers, product listings, donations, orders, Stripe Connect payouts, moderation queue.
- Campaign types: ticket sales, peer-to-peer, supporter subscriptions and licensed raffles.
- Organisation dashboards with team roles, branded storefronts, custom URLs and multi-campaign pages.
- AI tools: fundraiser/product content generation, newsletters, sponsor proposals, grant drafts, AI moderation and fraud review (DeepSeek/OpenAI).
- Admin suite: user management, audit logs, transaction search, GST/tax reporting, site settings, email templates, announcements, payouts, refunds and dispute handling.
- Trust features: reviews, verified-charity badges, raffle licence management, risk dashboard.
- Growth: multi-currency support, PWA/mobile experience, push notifications.
- Help centre with live search, floating help widget with contact form, getting started page, and public marketplace with share links.
- Legal document set (ToS, Privacy, agreements, policies) published as pages.
- Email notifications: receipts, campaign updates, moderation decisions, refund/dispute updates.
- Image uploads, Google/Apple social login, database backup tooling, Stripe setup tooling and rate limiting on auth/payment endpoints.