Checkout & payments
B2B Payment Terms & Deposits
Indicative price, not an offer: €39/mo; automatic invoice generation, aging reports, dunning
Generated from docs/plan/paid/b2b-payment-terms-deposits/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
B2B merchants need credit management to extend Net-30/60 invoicing and accept partial deposits. This paid module adds credit-term enforcement, invoice aging tracking, and automated dunning sequences, enabling wholesale workflows without full upfront payment.
The problem
My wholesale customers expect Net-30 invoicing with deposit options, but every order requires full payment at checkout. I lose B2B revenue because I can’t extend credit terms, and I have no way to age receivables or automate collection follow-ups.
What it does
- Store per-customer credit limit and current balance on Customer entity
- Enforce credit terms: allow orders on ‘net 30’, ‘net 60’, ‘50% deposit’ payment methods when customer has available credit
- Generate formal tax invoices (not receipts) with terms, due date, and customer PO number
- Track payment against invoices: partial payments, late payments, overdue amounts
- Dunning automation: first reminder at +5 days overdue, escalate at +15 days, optional collection cascade
- Aging report: 0–30 days, 30–60, 60+ overdue buckets by customer
- Disable checkout when customer exceeds credit limit, with an admin-only override flow
- Reject orders where deposit + prior balance exceeds total credit; enforce reservation
- Record credit transactions in audit log; flag manual overrides
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Credit scoring or automated limit-setting — this module trusts the merchant’s manual judgment of each customer. Scoring is a risk assessment responsibility that needs support commitment.
- Payment routing to accounts receivable systems (SAP, NetSuite) — the merchant reconciles manually via CSV export. Full integration requires per-installation support.
- Multi-currency credit — all credit balances are in the shop’s frozen currency. Cross-currency trade is a separate feature.
- Lien or security interest tracking — this module tracks credit; legal encumbrance is a per-country compliance layer that belongs to a separate GPSR/finance pack.
Data model
Migration: add credit_limit_cents (nullable), credit_used_cents (int, default 0) to Customer. Add invoice_number (string, unique per shop), due_date (date), payment_terms (enum: ‘immediate’, ‘net_30’, ‘net_60’, ‘net_90’, ‘deposit_50’, ‘deposit_25’), po_number (string, optional) to Order. Add payment_records array: [ { amount_cents, payment_date, method, reference } ] to track partial payments against invoices.
API
- GET /api/customers/{id}/credit — show current credit limit and used balance
- PATCH /api/customers/{id}/credit — admin sets credit limit (requires admin role)
- GET /api/orders/{id}/invoice — fetch formal invoice PDF/HTML
- POST /api/orders/{id}/invoice/email — send invoice to customer
- GET /api/orders?payment_terms=net_30 — filter orders by payment terms
- POST /api/credit/dunning/{id} — trigger manual dunning sequence (admin only)
- GET /api/reports/aging — fetch aging report by customer
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
New ‘B2B Credit’ panel under Settings shows per-customer limits, current usage, and overdue amounts. Order creation screen shows live credit check: ‘Customer has €5,000 limit, €3,200 used, €1,800 available.’ Merchants can override the check with a confirmation dialog. Dunning log shows automated and manual reminders sent.
The seam — why this is paid
Core owns the Order data model, customer lookup, and audit log. Paid module owns credit enforcement rules, dunning workflow, and the aging report. Checkout gates via canWriteCommerce + custom credit-check hook. Invoicing PDF is merchant’s responsibility (module returns structured data; PDF generation stays in storefront theme or merchant’s accounting integration).
Support commitment: credit-term enforcement, invoice reconciliation, AR aging audits
Dependencies
- structured-address-model — invoices need proper billing address
- payment-status-tracking — must distinguish paid vs. unpaid state before dunning
- order-confirmation-page — customer sees invoice summary on confirmation
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Customer with €5,000 limit and €3,000 used balance can place a €2,000 order; order total is €3,200 and is rejected
- Merchant sets customer to Net-30 terms; order created with due_date = 30 days from today
- Partial payment of €500 on a €1,200 invoice reduces balance to €700; second payment of €700 marks invoice paid
- Dunning sequence fires at +5 overdue, +15 overdue, and +30 overdue; each sends audit-logged email
- Aging report shows ‘OG-1042: €850 (45 days overdue)’ in the 30–60 bucket; next customer shows immediately under 0–30
- Admin override sets credit limit to 0; checkout rejects next order unless merchant clicks ‘Place anyway’; action is audit-logged with admin name
- Order.payment_terms is frozen at checkout; changing customer’s future terms does not retroactively change past orders
- Invoice PDF includes customer PO number, due date, and itemized breakdown (line totals, tax, shipping, discount)
Risks
Partial-payment tracking can be forged by a client if validation is skipped — amount must never be derived from user input, only from payment webhook + manual entries. Dunning emails sent to addresses without verification can spam non-customers — validate email belongs to customer before sending. Credit limit enforcement only works if checkout gates every order; bypassing the gate (e.g., direct API call without auth middleware) ships unpaid goods.
Commercial context
| Suggested price | €39/mo; automatic invoice generation, aging reports, dunning |
| Rival anchor | Stripe: no app; Magento: ~€90 extension + custom accounting |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.