Checkout half proven against live Stripe (dry-run session created + expired, zero charge), webhook subscription-id extraction + worker renewal fulfillment covered by unit tests (31 + 13). Remaining gap: full E2E with a Stripe test clock, which needs test-mode keys in the server .env (currently unset).
489 lines
23 KiB
Markdown
489 lines
23 KiB
Markdown
# Billing & Payments Architecture
|
|
|
|
**Last updated:** 2026-06-18
|
|
|
|
> ⚠️ **Reality check (2026-06):** Large parts of this doc describe a *planned*
|
|
> "ERPNext owns all billing via Adyen" architecture that is **NOT live**. What is
|
|
> actually wired today:
|
|
> - **Live payment rail = Stripe Checkout** (card + ACH), plus **PayPal** (direct
|
|
> Orders v2) and **crypto** (SHKeeper) — all in `api/src/routes/checkout.ts`.
|
|
> **Klarna** runs via Stripe.
|
|
> - **Adyen is NOT integrated** (account approval never completed). The
|
|
> `Adyen-*` gateway names below are aspirational labels, not active gateways.
|
|
> - **Recurring billing = Stripe Subscriptions** (see "Stripe-native
|
|
> Subscriptions"), the only recurring billing actually shipping, used by
|
|
> `oig-sam-screening` ($79/mo). ERPNext `createSubscription()` is unused.
|
|
> ERPNext is still the system of record for invoices/accounting, but it is **not**
|
|
> the payment gateway. Treat Adyen/ERPNext-gateway sections as future plan.
|
|
|
|
## 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)
|
|
|
|
> **Status:** the only recurring billing actually wired today is **Stripe-native
|
|
> Subscriptions** (see next section), used by `oig-sam-screening` ($79/mo). The
|
|
> ERPNext-Subscription / Adyen model below is **planned, not yet live** — Adyen
|
|
> is not integrated and ERPNext `createSubscription()` is currently unused. The
|
|
> services listed here are aspirational pricing, not active subscriptions.
|
|
|
|
ERPNext Subscription DocType is intended to handle (NOT YET LIVE):
|
|
- 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)
|
|
|
|
When built, subscriptions would auto-generate invoices (payment via a saved
|
|
payment method or manual payment link).
|
|
|
|
### Stripe-native Subscriptions (healthcare monitoring)
|
|
|
|
Some compliance services are sold as **Stripe Subscriptions** (the billing engine
|
|
is Stripe, not ERPNext). A service opts in via the catalog
|
|
(`api/src/service-catalog.ts`):
|
|
|
|
```ts
|
|
"oig-sam-screening": {
|
|
name: "OIG/SAM Exclusion Screening (Monthly Monitoring)",
|
|
price_cents: 7900, // $79/month
|
|
billing_interval: "month", // -> checkout builds mode:"subscription"
|
|
allowed_methods: ["card", "ach"], // recurring needs off-session-capable rails
|
|
...
|
|
}
|
|
```
|
|
|
|
Flow:
|
|
1. `checkout.ts` sees `billing_interval` -> creates a `mode:"subscription"`
|
|
Checkout Session with recurring `price_data`. The gateway surcharge is
|
|
**absorbed** (a subscription can't carry a one-time surcharge line) so the
|
|
customer is billed a clean `$79/month`.
|
|
2. `allowed_methods` filters the picker in `PaymentStep.astro` (PayPal/Klarna/
|
|
crypto are one-time only and disappear) and is **re-validated server-side**
|
|
in `checkout.ts` (`METHOD_NOT_ALLOWED`).
|
|
3. `webhooks.ts` handles the subscription lifecycle:
|
|
- `checkout.session.completed` (mode=subscription) -> records
|
|
`compliance_orders.stripe_subscription_id`, then first fulfillment.
|
|
- `invoice.paid` with `billing_reason=subscription_cycle` -> re-dispatches the
|
|
service handler (`recurring_cycle:true`) to re-run the screening + deliver a
|
|
fresh dated certificate. (The first invoice is skipped here — already handled
|
|
by `checkout.session.completed`.)
|
|
- `invoice.payment_failed` -> admin alert + first-failure customer nudge.
|
|
- `customer.subscription.deleted` -> order marked `cancelled`, fulfillment stops.
|
|
|
|
**Stripe webhook events (ENABLED on the live prod endpoint** `we_1THBjyB46qMvF2jnYyN8IfkK`
|
|
= `https://api.performancewest.net/api/v1/webhooks/stripe`**):**
|
|
The 6 currently enabled events are `checkout.session.completed`,
|
|
`payment_intent.succeeded`, `payment_intent.payment_failed`, plus the three
|
|
subscription-lifecycle events added 2026-06-18:
|
|
- `invoice.paid`
|
|
- `invoice.payment_failed`
|
|
- `customer.subscription.deleted`
|
|
|
|
Without these three the monthly cycles would charge but never fulfill/alert.
|
|
(Note: the code also *handles* `charge.dispute.created` and `balance.available`
|
|
but those are NOT yet enabled on the endpoint — enable them if/when needed.)
|
|
|
|
To add events without clobbering existing ones, read `enabled_events`, union,
|
|
and PUT the union back via `POST /v1/webhook_endpoints/{id}` with repeated
|
|
`enabled_events[]=` params (re-running is idempotent).
|
|
|
|
> **API-version caveat (important):** this endpoint has **no pinned
|
|
> `api_version`**, so it follows the Stripe *account default* (currently
|
|
> `2024-12-18.acacia`), NOT the `2026-03-25.dahlia` the SDK is pinned to. In
|
|
> acacia the subscription link is the **top-level `invoice.subscription`**; in
|
|
> dahlia it moved to `invoice.parent.subscription_details.subscription`.
|
|
> `invoiceSubscriptionId()` in `webhooks.ts` reads **both** shapes so renewals
|
|
> map back to the order regardless. If you ever pin the endpoint to dahlia, the
|
|
> handler still works; do NOT remove the legacy fallback while the endpoint is
|
|
> unpinned.
|
|
|
|
The `provider-compliance-bundle` ($899/yr) includes **only the first** OIG/SAM
|
|
screening; customers are converted to the $79/mo monitoring subscription after
|
|
that first cycle (the standalone $948/yr of monitoring is no longer given away
|
|
inside the bundle).
|
|
|
|
**Validation status (2026-06-18):**
|
|
- ✅ *Checkout half — proven against LIVE Stripe.* A dry-run created a real
|
|
`mode:subscription` Checkout Session with the exact production params
|
|
(recurring `price_data`, `unit_amount=7900`, `recurring.interval=month`,
|
|
`card`+`us_bank_account`, metadata) — Stripe returned `amount_total=7900`,
|
|
`type=recurring`, then the session was immediately **expired** (creating a
|
|
session never charges anyone; only a completed hosted page does). Net effect
|
|
on prod: zero.
|
|
- ✅ *Webhook subscription-id extraction* — `invoiceSubscriptionId()` unit tests
|
|
(31 in `api/tests/recurring-subscription.test.ts`) cover acacia (top-level)
|
|
AND dahlia (nested) invoice shapes, renewal-cycle gating, surcharge
|
|
suppression, recurring line-item build.
|
|
- ✅ *Worker renewal fulfillment* — `scripts/workers/services/test_npi_recurring.py`
|
|
(13 assertions) runs the real handler and asserts the `[Monthly cycle]` /
|
|
re-screen behaviour; passes locally + in the deployed workers container.
|
|
- ⏳ *Full end-to-end with a Stripe test clock* — NOT yet run. Requires
|
|
`STRIPE_TEST_SECRET_KEY` / `STRIPE_TEST_WEBHOOK_SECRET` in the server `.env`
|
|
(currently unset; prod is `NODE_ENV=production`). Once those exist: place a
|
|
test-card subscription, advance a billing cycle via a test clock, and confirm
|
|
the live `invoice.paid` (subscription_cycle) re-dispatches the screening
|
|
worker and a fresh certificate is issued. This is the last gap before a real
|
|
recurring charge should be marketed.
|
|
|
|
### 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`.
|