# Billing & Payments Architecture

**Last updated:** 2026-04-05

## Principle: ERPNext Owns All Billing

All payment processing, invoicing, and financial record-keeping flows through
ERPNext. Our Express API and website are the storefront — ERPNext is the
back office. Payment gateways are Frappe apps installed inside ERPNext.

## Payment Methods

| Method | Gateway | Provider App | Integration |
|--------|---------|--------------|-------------|
| Credit/Debit (Visa, MC, Amex) + Apple Pay + Google Pay | Adyen | `frappe_adyen` | ERPNext Payment Request → Adyen Sessions API v71 |
| ACH Direct Debit | Adyen | `frappe_adyen` | ERPNext Payment Request → Adyen ACH |
| Klarna (Pay in 4) | Adyen | `frappe_adyen` | ERPNext Payment Request → Adyen Klarna |
| Cash App Pay | Adyen | `frappe_adyen` | ERPNext Payment Request → Adyen CashApp |
| Amazon Pay | Adyen | `frappe_adyen` | ERPNext Payment Request → Adyen AmazonPay |
| Cryptocurrency (BTC/ETH/USDC/USDT/MATIC/TRX/BNB/LTC/DOGE) | SHKeeper | `frappe_crypto` | ERPNext Payment Request → SHKeeper API (k3s) |
| Stripe Identity | Stripe | Direct (API only) | Identity verification for CRTC orders — NOT used for payments |

**Note:** Adyen account approval is pending. SHKeeper is deployed and running in k3s.

## Payment Surcharges

We pass processor costs through as surcharges on select payment methods:

| Method | Customer Surcharge | Our Gateway Cost | Notes |
|--------|-------------------|-----------------|-------|
| ACH Direct Debit | 0% | $0.40 flat | Recommended — lowest cost |
| Credit/Debit Card | 3% | ~2.2% (IC++) | Visa/MC/Amex + Apple Pay + Google Pay |
| Klarna | 5% | 4.29% + $0.30 | Adyen Klarna rate |
| Cash App Pay | 3% | 2.90% + $0.30 | |
| Amazon Pay | 3% | ~2.9-3.4% | Negotiated rate |
| Cryptocurrency | 0% | $0 | Self-hosted SHKeeper — zero fees |

**Surcharge injection** is handled by `performancewest_erpnext` via a `Payment Request.before_insert` hook that reads the surcharge rate from the payment gateway and adds it to the invoice total.

**Legal notes:**
- Surcharges are prohibited in **CT, MA, and PR** — residents of these
  jurisdictions are not charged surcharges.
- Surcharges apply to **service fees only**, not state filing fees.

### SHKeeper (Crypto Payments)

Self-hosted in k3s (Kubernetes) at `pay.performancewest.net`. Zero processing
fees — fully non-custodial. Supports BTC, ETH, USDC, USDT, MATIC, TRX, BNB,
LTC, DOGE, and any ERC-20/TRC-20/BEP-20 token.

Installed via Helm chart `vsys-host/shkeeper`. k3s runs with `--docker --disable=traefik`
to avoid port conflicts with host nginx.

```
Customer chooses crypto payment
  → ERPNext creates Payment Request (gateway: Crypto)
  → frappe_crypto calls SHKeeper POST /api/v1/<crypto>/payment_request
  → Customer sees wallet address + QR code on crypto_checkout page
  → SHKeeper webhook fires (must return HTTP 202, not 200)
  → frappe_crypto.api.crypto_webhook verifies X-Shkeeper-Api-Key header
  → ERPNext marks Payment Request as Paid
  → ERPNext workflow webhook → Express API → Workers
```

### Adyen (Card/ACH/Klarna/CashApp/AmazonPay)

Pending Adyen account approval. When live, 5 gateway instances will be configured:

| Instance | Payment Methods | Adyen Type |
|----------|----------------|------------|
| Card | Visa, MC, Amex, Apple Pay, Google Pay | scheme, applepay, googlepay |
| ACH | US bank account direct debit | ach |
| Klarna | Pay in 4 installments | klarna |
| CashApp | Cash App Pay | cashapp |
| AmazonPay | Amazon Pay | amazonpay |

`frappe_adyen` uses Adyen Sessions API v71 with HMAC-SHA256 webhook verification
using Adyen's field concatenation algorithm. 74 unit tests passing.

## Payment Flow

```
┌──────────┐    ┌──────────┐    ┌──────────────┐    ┌──────────────┐
│  Website  │───►│ Express  │───►│   ERPNext    │───►│    Adyen     │
│  (Astro)  │    │   API    │    │              │    │  (Card/ACH/  │
│           │    │          │    │ Sales Invoice│    │  Klarna/     │
│ Customer  │    │ Validate │    │ Payment Req  │    │  CashApp/    │
│ fills     │    │ + create │    │              │    │  AmazonPay)  │
│ order     │    │ in ERPNext│   │              │    ├──────────────┤
└──────────┘    └──────────┘    └──────┬───────┘    │  SHKeeper    │
                                       │            │  (Crypto)    │
                              ┌────────▼────────┐   └──────────────┘
                              │ Customer is     │
                              │ redirected to   │
                              │ payment page    │
                              │                 │
                              │ Adyen checkout  │
                              │ or crypto QR    │
                              └────────┬────────┘
                                       │
                              ┌────────▼────────┐
                              │ Gateway webhook │
                              │ → ERPNext marks │
                              │   Invoice Paid  │
                              │                 │
                              │ ERPNext webhook │
                              │ → Express API   │
                              │ → Workers       │
                              └─────────────────┘
```

## Invoice Types

### Formation Orders
```
Sales Invoice:
  Customer: Sarah Chen
  Items:
    - LLC Formation (Basic): $179.00
    - State Filing Fee (Wyoming): $100.00
    - EIN Obtainment: $49.00
    - Operating Agreement: $99.00
  Discount: -$37.25 (LAUNCH25 — 25% off service fee)
  Total: $360.75
  Payment Gateway: Adyen-Card
  Status: Paid
```

### Compliance Services
```
Sales Invoice:
  Customer: Marcus Johnson
  Items:
    - FLSA Wage & Hour Audit (up to 50 employees): $1,499.00
  Total: $1,499.00
  Payment Gateway: Adyen-ACH
  Status: Unpaid → Payment Request Sent
```

### Recurring Services (Subscriptions)
ERPNext Subscription DocType handles:
- Registered Agent: $99/year per state (Wyoming: $49/year)
- Annual Report Filing: $99/year per state
- Canada CRTC Annual Maintenance: $349/year
- US Formation Maintenance Bundle: $179/year (annual report + RA renewal)
- CA Formation Maintenance Bundle: $179/year (annual return + AMB/RA renewal)

Subscriptions auto-generate invoices. Payment collected via Adyen (saved payment method) or manual payment link.

### Formation Maintenance Bundles

| Bundle | Price | Includes | Savings |
|--------|-------|---------|---------|
| US Formation Maintenance | $179/yr | Annual Report filing ($99) + RA renewal ($99) | $19/yr vs separate |
| CA Formation Maintenance | $179/yr | Annual Return filing ($99) + compliance monitoring ($99). AMB mailbox renewal billed separately at cost. | N/A |
| CRTC Maintenance (existing) | $349/yr | All of CA maintenance + CRTC + CCTS + domain/email + DID | N/A |

Formation maintenance bundles are offered to Complete tier customers. Basic tier customers
can purchase individual services (Annual Report $99/yr, RA $99/yr) separately.

### Canadian Formation Pricing (Standalone — Not CRTC)

| Item | Price | Notes |
|------|-------|-------|
| Canadian Formation | C$449 | Includes: incorporation, org minutes, corporate binder, compliance calendar. AMB mailbox billed separately. |
| Government fees | Passed through | BC ~C$350, ON ~C$360 (BoC rate + 10% buffer) |
| Add-on: CRA BN | $49 | Business Number registration with CRA |
| Add-on: Named company | +gov fee | Name reservation (province-specific) |
| Free DID | Included | With formation + RA renewal (stub — not yet active) |

## Service Bundles

Customers receive **20% off** when purchasing all services in a category
(e.g., all formation add-ons, all compliance services for a given tier).

- Discount applies to **service fees only** — state filing fees and
  registered agent fees included in bundles are not discounted.
- RA fees are NOT discountable in bundles, but YES with discount codes.
- Bundle discount is calculated in ERPNext via Pricing Rules and applied
  automatically when all qualifying items are in the Sales Order.

## Canada CRTC Package Pricing

| Item | Price |
|------|-------|
| CRTC Telecom Registration (one-time) | $3,899 USD |
| Annual Maintenance & Compliance | $349/yr |
| Consulting (regulatory, technical) | $75/hr |
| Accounting Support | 3 hrs free included, then $75/hr |

The CRTC package is a single Sales Order in ERPNext with all line items.
Annual maintenance is handled via ERPNext Subscription (auto-renew via
Adyen saved payment method).

Payment flexibility: ~$975/mo x 4 via Klarna Pay in 4 (5% surcharge applies).

## Sales Agent Commissions

Sales agents earn commissions on referred sales:

| Detail | Value |
|--------|-------|
| Canada CRTC sale | $300 per sale |
| Formation sale | $50 per sale |
| Bundle sale | $100 per sale |
| Payment timing | 14 days after order delivery |
| Payment method | Relay ACH transfer |
| Tracking | ERPNext Commission Ledger DocType + PostgreSQL backup |

Commission workflow:
1. Customer purchases using agent's REF-XXXXX referral code
2. Client gets 5% off service fee
3. Order is fulfilled and delivered
4. 14-day holdback period begins
5. After holdback, commission becomes eligible
6. Admin approves and pays via Relay ACH
7. Commission Ledger record updated with payment details

## Refunds

Refunds flow through ERPNext's Credit Note system:

1. Admin creates a Credit Note against the original Sales Invoice
2. ERPNext processes the refund via the original gateway (Adyen automatic refund, or manual ACH via Relay)
3. Credit Note tracks: amount, reason, linked invoice, approval
4. Our `refunds` table in PostgreSQL is a backup/audit — ERPNext is the source of truth

For state filing fees specifically:
- State fee refunds require contacting the state directly (most states don't refund filing fees)
- Our service fee is always refundable if the failure was our fault
- ERPNext tracks whether state fee is recoverable separately

## Express API Changes

Our API routes no longer handle payment directly:

| Before | After |
|--------|-------|
| API collects payment info | API creates Sales Invoice + Payment Request in ERPNext |
| API charges gateway | ERPNext gateway (Adyen/SHKeeper) handles payment |
| API stores payment records | ERPNext manages invoices |
| API handles refunds | ERPNext issues Credit Notes |

The checkout flow:
1. Customer fills order on Astro site → submits to Express API
2. Express API creates ERPNext Sales Invoice + Payment Request
3. Customer is redirected to ERPNext payment page (Adyen checkout or crypto QR)
4. Payment gateway webhook → ERPNext marks as Paid
5. ERPNext workflow webhook → Express API → Workers start fulfillment

Old `providers/` directory (stripe.ts, btcpay.ts, adyen.ts) and `webhooks-stripe.ts` have been deleted.

## ERPNext Setup Status

Completed:
- [x] Install Payments app (`bench get-app payments`)
- [x] Install `frappe_crypto` (SHKeeper gateway) — v1.0.0
- [x] Install `frappe_adyen` (Adyen gateway) — v1.0.0
- [x] Install `performancewest_erpnext` (surcharge hooks, identity gate) — v1.0.0
- [x] Create 16 Item records for all services (formation, compliance, add-ons, CRTC)
- [ ] Update Subscription Plans to new pricing (RA $99/yr ($49 WY), Annual Report $99/yr, CRTC Maintenance $349/yr)
- [x] Import 7 custom DocTypes
- [x] Import 3 workflows (Formation, CRTC, Renewal)
- [x] Configure custom fields on Sales Order, Sales Invoice, Payment Request

Remaining:
- [ ] Configure Crypto Payment Settings in ERPNext UI (point to `https://pay.performancewest.net` with SHKeeper API key)
- [ ] Create Payment Gateway Account for `Crypto-Crypto`
- [ ] Configure 5 Adyen Settings instances when account is approved
- [ ] Create Payment Gateway Accounts for each Adyen instance
- [ ] Configure email templates for invoice/payment notifications
- [ ] Test end-to-end payment flows

## Subscription Management

ERPNext Subscription handles recurring billing:

| Service | Frequency | Auto-Renew |
|---------|-----------|------------|
| Registered Agent | Annual | Yes (Adyen saved payment method) |
| Annual Report Filing | Annual | Yes (Adyen saved payment method) |
| CRTC Annual Maintenance | Annual | Yes (Adyen saved payment method) |

Subscription lifecycle:
1. Customer purchases RA service during formation
2. ERPNext creates Subscription (start date = formation date)
3. 11 months later: ERPNext sends renewal reminder email
4. On anniversary: ERPNext auto-generates invoice + Payment Request
5. Adyen charges saved payment method (or customer pays via link)
6. If payment fails: ERPNext sends dunning emails
7. After grace period: service paused, customer notified

## Compliance Calendar Renewal Billing

The `renewal_worker.py` (daily cron at 7 AM) manages billing for compliance calendar entries.
This is separate from ERPNext Subscriptions — it handles the 17 per-carrier compliance
obligations that have varying due dates and amounts.

### Lifecycle

```
Upcoming → Due Soon (30 days out) → Invoice Sent → Paid → Completed → re-calendar next year
```

1. **Upcoming:** Entry exists with future due date
2. **Due Soon:** 30 days before deadline — renewal worker sends reminder email
3. **Invoice Sent:** Worker creates ERPNext Sales Invoice for billable items
4. **Paid:** ERPNext webhook fires on invoice payment → `handle_renewal_payment` job
5. **Completed:** Entry marked done, admin ToDo created for the actual filing/action
6. **Re-calendar:** New entry auto-created for next period (annual/quarterly/monthly)

### Billable vs Non-Billable Entries

| Type | Billable | Amount | ERPNext Item |
|------|----------|--------|--------------|
| CRTC Annual Maintenance | Yes | $349 USD | `CRTC-MAINT-ANNUAL` |
| Mailbox Renewal | Yes | ~$199 USD | `MAILBOX-RENEWAL` |
| BC Annual Report | Yes | ~$50 CAD | `BC-ANNUAL-REPORT` |
| Domain + Hosting Renewal | Yes | ~$25 USD | `DOMAIN-RENEWAL-CA` |
| DID Renewal | Yes | ~$10 USD | (included in CRTC maintenance) |
| CCTS Renewal | No | $0 | — |
| T2 Tax Return | No | $0 (client's accountant) | — |
| GST/HST Return | No | $0 (client's accountant) | — |
| CRTC Registration Update | No | $0 | — |
| ATS Surveys | No | $0 (we prepare, client files) | — |

### ERPNext Items Needed for Renewal Invoicing

These ERPNext Items must be created for the renewal worker to generate invoices:

| Item Code | Item Name | Rate (USD) |
|-----------|-----------|-----------|
| `CRTC-MAINT-ANNUAL` | CRTC Annual Maintenance & Compliance | $349 |
| `MAILBOX-RENEWAL` | Vancouver Mailbox Renewal (Annual) | $199 |
| `BC-ANNUAL-REPORT` | BC Annual Report Filing | $50 |
| `DOMAIN-RENEWAL-CA` | .ca Domain + Hosting + Email Renewal | $25 |
| `COMPLIANCE-OTHER` | Compliance Service (Miscellaneous) | Variable |

### Compliance Calendar DocType Fields

The Compliance Calendar ERPNext DocType includes these billing-related fields:

| Field | Type | Purpose |
|-------|------|---------|
| `amount_usd` | Currency | Billable amount in USD |
| `amount_cad` | Currency | Billable amount in CAD (for Canadian gov fees) |
| `invoice` | Link (Sales Invoice) | ERPNext invoice reference |
| `recurring` | Check | Whether entry recurs annually |
| `recurrence_period` | Select | Annual / Quarterly / Monthly |
| `renewal_of` | Link (Compliance Calendar) | Previous entry this renews |
| `compliance_type` | Select | Regulatory / Tax / Survey |
| `entity_name` | Data | Carrier/company name |
| `order_reference` | Link (Sales Order) | Original CRTC order |
| `reminder_date` | Date | When to send reminder (30 days before due) |

## Environment Variables

```
# ERPNext API (set in Express API .env)
ERPNEXT_URL=https://crm.performancewest.net
ERPNEXT_API_KEY=<key>
ERPNEXT_API_SECRET=<secret>

# SHKeeper (set in server .env)
SHKEEPER_API_KEY=<key>              # NEEDS TO BE SET
SHKEEPER_URL=https://pay.performancewest.net

# Stripe Identity only (NOT for payments)
STRIPE_IDENTITY_WEBHOOK_SECRET=<secret>   # NEEDS TO BE SET

# SMTP
SMTP_PASS=<password>                # NEEDS TO BE SET

# Customer Portal
CUSTOMER_JWT_SECRET=<secret>        # NEEDS TO BE GENERATED: openssl rand -base64 32
```

All Adyen keys are configured inside ERPNext's Adyen Settings DocType, not in the Express API `.env`.
