Skip to content

Shop Commerce Integration (WooCommerce + MerchantPro) ​

Status: Built end-to-end (committed on staging, not pushed) as the connectivity slice (F14.1) of F14. Commerce & Access Offers. First real consumer of the Cat B (Connected Account, 1C.5) and Cat D (Inbound Webhook, 1C.6) frameworks.

2026-10-26 — extended (spec only, nothing built): Shop orders, sellers and shop invoices (F14.9–F14.11) — a copy of every shop order in the platform, orders in the patient's account, a second legal entity selling through the clinic, and fiscal invoices issued from shop orders.

Scope of this doc — the connectivity slice only. A clinic connects its external shop(s); the platform reliably receives → verifies → dedups → pulls paid orders and emits a commerce.order_paid internal event (Cat E). What happens to that event — mapping order line items to access (grants / subscriptions) — is the provisioning slice, specified separately under the access-offers design. This doc stops at the clean seam: a verified, authoritative, deduped paid order has arrived.

Why this exists ​

RestartiX clinics sell appointments, programs, and physical-product bundles (which may include free programs/sessions) through external e-commerce shops — today WooCommerce and MerchantPro, two shops per clinic. The shops stay external forever: the platform never builds a cart, checkout, payment, inventory, or shipping. Its responsibility begins at the fulfillment boundary — an order exists and is paid; turn it into platform access.

This is the paid-order trigger of the "one provisioning core, two triggers" design (the other trigger being free marketing-campaign claims). Both triggers converge on the same downstream provisioner; they differ only in the front door. This doc builds the shop front door up to the trigger event.

Locked decisions ​

  1. Cat B api_key, not OAuth. Both shops authenticate API reads with a consumer key + secret (HTTP Basic). The connected-accounts framework's auth_type='api_key' path is fully built; no OAuth-callback machinery is needed. Credentials are stored encrypted in organization_integrations.credentials_encrypted (AES-256-GCM via KMS).
  2. Verify-by-pull is the trust boundary. Neither the post-checkout redirect nor a webhook body is trusted for whether money changed hands. Money-truth always comes from an authenticated pull of the order from the shop API. The redirect order_key is a lookup + lightweight ownership token only.
  3. Per-connection inbound URL token + generated secret. Each connection gets a unique, unguessable inbound URL (/webhooks/{provider}/{token}) and a platform-generated signing secret. The token selects the connection deterministically before the body is read; the connection's secret then verifies the HMAC. The clinic pastes both the URL and the secret into its shop's webhook settings. (Chosen over a shared per-provider URL that sniffs the org from an unsigned payload field.)
  4. One normalized order contract. Woo and MerchantPro collapse to a single internal NormalizedOrder shape behind a connector interface; downstream code never sees the provider difference. (Mirrors the legacy order-provider.js normalization, hardened for multi-tenancy.)
  5. Per-org everything. Unlike the legacy single-clinic system (global env credentials), every credential, token, and order record is organization_id-scoped. The inbound URL token resolves which org + which credentials.
  6. Multiple connections of the same provider are supported. A clinic can connect N WooCommerce shops and N MerchantPro shops. The framework's uniqueness key is (organization_id, integration_service_id, external_account_id) — external_account_id (required, non-empty; the shop's store_url) is the per-shop discriminator. One catalog row + one stateless connector impl per provider serves all of that provider's connections; each shop is its own organization_integrations row with its own credentials, inbound URL token + secret, and display title. The per-connection inbound token (decision 3) routes each shop's webhooks unambiguously — no payload sniffing, no store-URL collision risk. The unique key prevents connecting the same shop twice while allowing many distinct shops.

Architecture ​

  Shop (Woo / MerchantPro)                         API
  ────────────────────────                         ────────
  order paid ──push──▶  POST /webhooks/{provider}/{token}   ┌─ Cat D inbound ─┐
                          1. token → connection             │ verify.go       │
                          2. decrypt connection secret      │ parse.go        │
                          3. verify HMAC (raw body)         │ handler.go      │
                          4. dedup (provider, event_id)     └─────────────────┘
                          5. PULL authoritative order  ◀──── GET /orders/{id}   ┌─ Cat B ─┐
                             (consumer key/secret)                              │connector│
                          6. emit commerce.order_paid (Cat E)                   └─────────┘
                                         │
                                         ▼
                          [provisioning slice — separate doc]

The redirect-driven post-checkout onboarding page (where the buyer signs up + consents, and provisioning completes with the buyer present) is part of the provisioning slice, not this doc. This slice is the machine-to-machine connectivity that makes paid orders reliably available.

Track A — Cat B connectors (connect the shop) ​

Follow connected-account-integration-guide.md.

  • [ ] Seed catalog rows. Migration adding two integration_services rows: slug='woocommerce' and slug='merchantpro', auth_type='api_key', oauth_client_capability=NULL, status='available', config_schema describing store_url (+ provider-specific fields). Catalog mutations are AdminPool-only (written by migration).
  • [ ] Implement integrations.Connector for each, in internal/core/integrations/connectors/{woocommerce,merchantpro}/connector.go:
    • Slug() → "woocommerce" / "merchantpro".
    • ValidateCredentials → require consumer_key, consumer_secret, store_url.
    • ValidateConfig → validate store_url shape.
    • Healthcheck → cheap authenticated GET (Woo: /wp-json/wc/v3/system_status; MerchantPro: a lightweight /api/v2 read), ≤5s timeout. This is the /test connection path.
    • RefreshOAuthToken → nil, nil (non-OAuth).
    • init() calls integrations.Register(&Connector{}); import the package in cmd/api/main.go.
  • [ ] An order-fetch method on each connector (or a sibling ShopClient) — FetchOrder(ctx, creds, orderRef) (NormalizedOrder, error) — used by the inbound handler's pull step. Woo: GET /wp-json/wc/v3/orders/{id}; MerchantPro: GET /api/v2/orders/{id} + field normalization (see contract below).
  • [ ] No new connection endpoints — POST/GET/PATCH/DELETE /v1/organizations/{id}/integrations + /test already exist, gated by organizations.manage_integrations.

NormalizedOrder contract ​

NormalizedOrder {
  order_ref        string            // shop order id
  order_key        string            // redirect token / Woo order_key / MP public_code
  status           string            // normalized: paid | unpaid | refunded | cancelled | pending
  paid             bool              // derived; the only field the trigger gates on
  total, currency  string
  billing          { first_name, last_name, email, phone }
  line_items       [ { product_ref, variation_ref, sku, quantity } ]
}

Only paid == true orders emit the trigger. refunded/cancelled are recorded (for the future refund→revoke path) but do not provision. PII (billing.*) is persisted minimally and classified (see Compliance).

Track B — Cat D inbound (receive orders) ​

Follow inbound-webhook-guide.md. Reference consumer to copy: internal/integration/bunnystream/inbound/.

  • [ ] Per-connection inbound identity (the one net-new bit of framework wiring).
    • Add organization_integrations.inbound_url_token TEXT UNIQUE (indexed; generated at connection create). This is the {token} in the URL.
    • Store a generated inbound signing secret inside the connection's encrypted credentials blob (alongside the consumer key/secret). Shown to the clinic admin once, at create/rotate.
    • A small resolver: token → organization_integrations row → decrypt → (inbound_secret, consumer_creds, store_url).
  • [ ] Per-provider inbound packages internal/integration/{woocommerce,merchantpro}/inbound/:
    • verify.go — Verify(req, secret): base64 HMAC-SHA256 of the raw body, crypto/subtle.ConstantTimeCompare against X-WC-Webhook-Signature (Woo) / X-Webhook-Signature (MerchantPro). MerchantPro cannot sign (its webhook editor has only payload and header templates, confirmed on a live store 2026-10-21), so it also accepts X-Webhook-Secret: <signing secret> set in the headers template, compared in constant time; the body is never trusted either way, since the order is re-fetched with the clinic's API credentials. Payload template: {% return {'id': order.id} %}.
    • parse.go — Parse(body) (Event, error); Event.ID is the dedup event_id (Woo delivery/webhook id or order id; MerchantPro event id).
    • handler.go — the locked flow: resolve token → decrypt secret → Verify → dedup WasProcessed (early 200 on replay) → pull authoritative order via Cat B → gate on paid → emit commerce.order_paid → MarkProcessed.
  • [ ] Mount endpoints under the existing /webhooks group (per-IP rate-limited, no auth, sibling of /v1): POST /webhooks/woocommerce/{token} and POST /webhooks/merchantpro/{token}. Add an inbound_webhook rate-limit policy if not already present.
  • [ ] Register the Cat E event commerce.order_paid in the 1C.3 events registry, emitted at the end of the flow. Payload carries organization_id, integration_id, order_ref, and the normalized line items — the contract the provisioning slice consumes.
  • [ ] Dedup uses the existing inbound_webhook_dedup table (monthly-partitioned, AdminPool-only). Idempotency boundary is (provider, event_id). Retry is the shop's job (5xx → re-POST → dedup short-circuits).

Track C — Clinic UI (configure the connection) ​

No integrations surface exists in the Clinic app today (1D.2 L19, "not-built"). The Cat B API and packages/api-client methods (listIntegrationServices, createIntegration, testIntegration, …) already exist — this track is the missing frontend.

  • [ ] /integrations route in apps/clinic/app/(dashboard)/ — list connected shops (service, status, last-used, error state) + "Connect shop" flow. Mirror the shipped SWR pattern in components/patient-tiers/patient-tiers-editor.tsx; gate on organizations.manage_integrations (sidebar entry already stubbed).
  • [ ] Connect dialog — pick provider from the catalog → credential form (consumer_key, consumer_secret, store_url) → create → run /test.
  • [ ] Write-only secret input (net-new shared pattern; none exists) — masked, never echoed back, with a "replace credentials" affordance. Note: the Cat B API has no credential-rotation endpoint yet (PATCH is title/config only) — rotation is revoke + recreate unless we add it; flag for the guide.
  • [ ] Inbound webhook setup panel (net-new shared pattern; none exists) — display the per-connection webhook URL + signing secret with copy-to-clipboard, a "regenerate" control, and step-by-step "paste this into your WooCommerce/MerchantPro webhook settings" instructions.

Compliance & security ​

  • Credentials (auth_secret class): per-org, KMS-encrypted, never returned on read, redacted in audit/logs. Consumer key/secret + the generated inbound secret all live in credentials_encrypted.
  • Signature verification is mandatory before any action; failed verification → reject + audit. Raw body is captured for HMAC.
  • Idempotency: exactly-once via the dedup table; replays are no-ops.
  • Verify-by-pull: provisioning never trusts a pushed payload's money fields — it re-reads the authoritative order.
  • PII minimization & egress: order billing PII flows shop→clinic; clinic is controller, platform is processor. Persist only what identity-linking/profile needs. Every new column (inbound_url_token, any order/fulfillment record fields) needs a data-classification.md registry entry in the same PR (CI enforces). inbound_url_token is a routing bearer token — classify accordingly (not egressable beyond the clinic-admin surface that displays it).
  • SOUP: prefer stdlib HMAC (no new dep). Any shop SDK added → SOUP row in the same PR.
  • RLS: organization_integrations is already per-org RLS-gated on organizations.manage_integrations. The inbound route is unauthenticated by necessity (the shop has no session) — its security is the unguessable token + HMAC, not RLS.

Follow-ups (out of scope here) ​

  • Provisioning slice — consume commerce.order_paid: per-org SKU→offer mapping, the shared provisioner minting patient_content_grants / patient_subscriptions, and the post-checkout onboarding page (buyer signs up via Clerk + consents, provisioning completes). This is where "one core, two triggers" converges with the marketing-campaign trigger.
  • Refund → revoke — SHIPPED (F14.5): commerce.order_refunded lapses content grants precisely (via patient_content_grants.fulfillment_id). Auto tier-revoke stays deferred (replace-semantics ambiguous).
  • Reconciliation sweep — periodic pull to catch orders whose webhook was missed. Specified in F14.9 (the sweep and the manual Sync share the worker).
  • Credential-rotation endpoint for Cat B (PATCH currently can't rotate secrets). Still deferred.
  • Registered as F14. Commerce & Access Offers in the implementation plan (2026-06-01).

Shop orders, sellers and shop invoices (F14.9–F14.11) ​

Data shape superseded by the commerce model (2026-10-29)

commerce-model.md replaces the tables named below:

  • shop_orders / shop_order_lines → commerce_orders / commerce_order_lines with channel = shop
  • organization_sellers → commerce_sellers
  • the fiscal_invoices rename → commerce_fiscal_documents

It also puts the restructure first in the build order. What stands from this section and is still the plan: the MerchantPro facts, the queue-then-worker receiver, the sync triggers (webhook, Sync button, sweep, import), the Clinic and Portal surfaces, and the invoicing rules. Superseded on linking: option 4 below (display by verified email) is rejected. External orders are never attached automatically (owner, 2026-10-29). This section is rewritten on the new tables when step 2 starts.

Added 2026-10-26 — owner decision. Spec only; nothing built. Build order: F14.9 → F14.10 → F14.11. F14.9 is additive and touches no existing table, so it can ship without a catch-up script. F14.11 needs F14.10, because the second store belongs to a different company.

Why ​

The clinic sells through two MerchantPro stores, owned by two companies with one owner:

StoreSellsVATCompany
magazin.zerodurere.roservices, digital productswithoutthe clinic's own company
magazin.restartix.rophysical products (some bundle a plan)witha second company, same owner

Each company has its own Stripe account and its own FGO account. The second company sells nothing clinical and the clinic receives nothing from its sales. It appears in the platform only so that its orders sit next to the patient's other purchases and its invoices go out from its own FGO account.

Today the accountant issues every shop invoice by hand in MerchantPro, separately from the Stripe invoices the platform issues (F14.8). Patients cannot see shop orders in their account. The platform only learns about orders that match an access offer, and keeps nothing about them but the reference (access_offer_orders).

MerchantPro stays the shop. Stock, fulfilment, MiniCRM (merchanpro-minicrm-worker) and its other integrations are untouched, and the platform never writes to a store. What moves here: a copy of every order, the orders in the patient's account, and invoicing (owner, 2026-10-26).

What MerchantPro provides ​

From MerchantPro's webhook docs and the MiniCRM worker's notes from live traffic (April–October 2026):

  • Order events: order.created, order.payment_completed, order.invoice_issued, order.fulfillment_created, and order.shipping_{awaiting,confirmed,processed,shipped,delivered,cancelled} (cancelled covers both canceled and returned).
  • No refund event. A payment status that changes to refunded fires nothing. The sweep below catches it.
  • Delivery: POST, a 5-second timeout, at most 3 attempts 5 minutes apart. After 8 failed delivery cycles in a row the webhook is deactivated until someone reactivates it in the dashboard.
  • The order object has everything an invoice needs:
    • line items with unit_price_net/_gross, product_tax_percent and line_tax_amount
    • shipping_amount and shipping_tax_amount
    • payment_method_code (seen live: cash_delivery, wire, euplatesc, tbi), paid_amount, payment_date
    • billing_type (company or an individual), billing_company_name, billing_company_vat (CUI) and billing_company_number (trade register number)
    • invoice_identifier, set when MerchantPro issued an invoice
    • awb_data (parcel tracking)
  • Rate limit is per minute, per store. The June 10 2026 flash sale (~980 orders in one evening) exhausted it for hours.
  • Foreign orders carry the buyer country's VAT (OSS): 20% FR, 19% DE, and so on. Invoices pass the rate through unchanged.
  • Not yet verified: the coupon / discount fields on a real coupon order, partial-refund amounts, and whether the list endpoint can filter by update time.

F14.9 Shop order mirror ​

Every order from every connected store is copied into the platform, whether or not it matches an access offer.

Receiving: the webhook only queues. Today's handler fetches the order and does the database work before answering, all inside MerchantPro's 5-second window. In a burst, three slow attempts lose the order, and eight such cycles switch the webhook off. From now on:

  1. The handler resolves the token, verifies the signature, parses the order id, queues a sync job and answers 200, with no call to MerchantPro.
  2. The sync worker does the rest. It runs in-process in every API instance (like the FGO worker), so it adds no AWS cost. It claims a job with FOR UPDATE SKIP LOCKED, allows one in-flight job per store across all instances, paces calls to about one a second per store, and on 429 waits for Retry-After (else backs off).
  3. For each job it fetches the order (verify-by-pull, unchanged) and upserts the copy. Only then does it call the existing access-offer processor: ProcessPaidOrder when the order is paid and ProcessRefundedOrder when it is refunded or canceled. Both are already idempotent on (org, integration, order_ref), so a repeated call is harmless. The commerce.order_paid / commerce.order_refunded events still fire after the work commits.

Every event the store sends means the same thing, "re-read this order", so the handler does not branch on X-Webhook-Event. The store subscribes order.created, order.payment_completed, order.invoice_issued and every order.shipping_* event. The payload template stays {% return {'id': order.id} %}. The webhook URL and the access-offer behaviour do not change; only where the work runs does.

Three ways an order is queued, all through the same job:

TriggerWhat it queues
Webhookthat order
Sync buttonthat order (on the order), or "recent orders" (on the store)
Sweep (daily)orders created since the last listing (misses), plus re-reads of orders still open from the last 60 days. That second part is how a refund is found, since MerchantPro sends no event for it. At ~1,000 orders a month this is about 2,000 paced reads a day.
Importevery order in a date range the clinic picks, for history; paced, previewed with a count first

Tables (new migration, all state, flat, RLS per org):

  • shop_orders, unique (organization_id, integration_id, order_ref):
    • identity: order_ref, order_key (MP public_code)
    • status: payment_status, shipping_status, payment_method_code, payment_method_name
    • money: total_minor, shipping_minor, currency
    • buyer: billing_type, buyer_name, buyer_email, company_name, company_tax_id, company_reg_number
    • provider_invoice_ref (MP invoice_identifier), tracking (from awb_data)
    • patient_id (nullable; see linking below)
    • placed_at, paid_at, synced_at, timestamps
  • shop_order_lines: shop_order_id, position, kind ∈ {product, shipping, discount}, product_ref, variation_ref, sku, name, quantity, unit_price_minor (gross), vat_rate, line_total_minor. Lines are replaced whole on each sync.
  • shop_order_sync_jobs: integration_id, order_ref (null = "list recent"), reason, attempts, next_attempt_at, locked_until, last_error. One pending job per (integration, order_ref); the row is deleted on success.
  • The sweep's listing cursor, per store.

Personal data is minimised the way F14.8 does it. The copy keeps the buyer's name and email (needed for the list, search and linking) and a company's details (business data, needed on the invoice). The postal address is not stored: the order detail page and invoicing read it live from the store. Every new column gets a data-classification.md row in the same PR (CI enforces it). The CNP never appears; MerchantPro does not ask for it.

access_offer_orders stays as it is. It is the access-provisioning record (matched offers, activation link) and keeps working unchanged. Pointing it at shop_orders is a later clean-up, not part of this step.

Clinic UI: Sales → Comenzi, a server-paginated list across stores:

  • filters by store, payment status, shipping status, payment method and date
  • search by order number, buyer name or email (trigram + unaccent)
  • a detail page with lines, statuses, tracking, the access the order gave (from access_offer_orders), the linked patient, and a Sincronizează button

The patient's record gets a Comenzi card. Reading needs payments.view. Syncing one order needs payments.view too (it only re-reads). Importing a date range needs organizations.manage_integrations.

Portal: the patient sees an order as soon as it is placed (owner, 2026-10-26). Under My purchases, a Comenzi list shows number, date, items, total, payment status, shipping status and tracking. Each linked order shows only to its own patient (RLS on patient_id, caregivers through the existing family rule).

Linking an order to a patient follows the F14.7 rule: typing an email at a checkout proves nothing. An order is linked by:

  1. the order_key on the post-checkout page (/orders/[ref]?key=, F14.3), which already proves the buyer
  2. the activation link, emailed to the order's address (F14.7)
  3. staff attaching it to a patient they know
  4. (decision, recommended) display only: a patient whose verified login email equals the order's email sees that order in their list. This discloses nothing the store has not already sent to that inbox with its own confirmation email. It grants no access and activates nothing; access still needs 1–3.

Emails: the platform sends no order emails in this step; MerchantPro keeps sending its own. Moving order confirmations to the platform (and turning off MerchantPro's) is a later choice, since it depends on MerchantPro allowing those emails to be switched off.

Production safety: new tables only, no catch-up script, existing rows untouched. The receiver change is the risky part, so it is rehearsed on staging against a real store before production.

F14.10 Sellers (merchant of record) ​

A seller is a company that sells through the clinic: the name on the order, the payment and the invoice. Every clinic has exactly one primary seller, its own company. It may add additional sellers. A clinic that never adds one notices nothing.

  • organization_sellers (state, RLS per org): legal_name, tax_id, reg_number, is_primary (exactly one per org, a partial unique index). The migration creates a primary seller for every existing org.
  • Connections belong to a seller: organization_integrations.seller_id (backfilled to the primary seller) for kinds payments, invoicing and commerce. The one-per-org indexes become one per seller: uq_org_integrations_payments_one_per_org (000061) and uq_org_integrations_invoicing_one_per_org (000063). This is a new forward migration; both are on production.
  • Offers belong to a seller: access_offers.seller_id (default the primary). A Stripe checkout charges on that seller's account, and the offer price lives in it. Today checkout takes "the clinic's" account (patientpayments.ConnectionAdmin).
  • Money remembers its seller: patient_payments.seller_id (resolved from the account the payment came in on; Stripe webhooks already resolve by account id) and shop_orders.seller_id (from the store), so filters and invoicing do not depend on a connection's later state.
  • Invoicing follows the money: a payment or order → its seller → that seller's invoicing connection. Today patientinvoices.Repository.Connection(orgID) picks one per org at four call sites (reconcile, overview, issue, backfill). The FGO connection's tax_id must equal its seller's.
  • Clinic UI:
    • Setări → Firme: the primary seller plus "Adaugă firmă"
    • a seller picker on connecting a Stripe account, an FGO account or a store, and in the offer editor (shown only when the clinic has more than one seller)
    • a seller filter on Sales (Plăți, Comenzi, Facturi)
  • The patient sees which company sold what, on orders, payments and invoice downloads.
  • Not organization_billing. That table is the clinic as the platform's customer (F12) and is unrelated.

For the owner and counsel — not a code decision:

  • The clinic stays the data controller for care. An additional seller is a separate controller for its own sales, not a joint one.
  • The patient-facing privacy notice and the terms must name the additional seller and say that its orders appear in the clinic's patient account.
  • The platform processes the additional seller's order data, so that company needs to be covered by a DPA with RestartiX as well.

Settle these before an additional seller's orders appear in a patient's account in production.

F14.11 Fiscal invoices from shop orders ​

The F14.8 engine (reconcile → worker → FGO, invoice / storno / replacement chains, one in-flight call per company) issues invoices for shop orders as well. They appear on the same Facturi page as Stripe payments.

  • Source: an invoice row points at either a patient_payments row or a shop_orders row (exactly one, enforced by a CHECK). Since the table then no longer holds only patient-payment invoices, this migration is the moment to rename patient_payment_invoices → fiscal_invoices (the glossary's term). Decide when building it.
  • Connection: the order's seller's FGO account (F14.10).
  • Lines from the order, not one line per payment: each product line with its own vat_rate, a shipping line, and discounts once their shape is verified. invoicingprovider.Line gains a per-line VAT rate; the Stripe path keeps using the connection's cota_tva.
  • Buyer:
    • a company order (billing_type = company) goes out as a company buyer with company_tax_id and company_reg_number; invoicingprovider.Buyer gains these
    • an individual goes out as today, without a CNP
    • the address is read live from the store at issue time and not stored
  • id_extern: the MerchantPro order number, so the accountant finds the invoice from the order.
  • Already invoiced in MerchantPro: an order whose provider_invoice_ref is set shows "Facturată în MerchantPro" and cannot be issued here. This is the duplicate guard for the switchover, which is just the day the accountant stops issuing invoices in MerchantPro (they are manual there today).
  • Per-store settings (the store connection's config):
    • invoice_series: overrides the FGO connection's series
    • auto_issue: invoice orders paid online as soon as they are paid; off by default, like F14.8
    • cash on delivery is invoiced manually (owner, 2026-10-26). Automatic invoicing at shipped or delivered is a later option for clinics; it is the reason the shipping events are already subscribed in F14.9.
    • recording the payment on the invoice follows F14.8's record_payment (off by default)
  • Refunds: a canceled or refunded order after invoicing gets a storno of the current invoice. Partial refunds wait until a real one shows which fields MerchantPro uses.
  • Email: the F14.8 invoice_ready email to a linked patient, with the download on the order in the Portal. Open: how a buyer who is not linked to a patient receives their invoice (the activation link is the likely answer).
  • Permissions: unchanged from F14.8 (payments.invoice, payments.view).

Open before building ​

  • Discount / coupon fields and partial-refund amounts, from real orders.
  • Whether MerchantPro's order list can filter by update time (it would make the refund sweep cheaper).
  • Display-only linking by verified email (F14.9, option 4): owner's call.
  • Rename to fiscal_invoices (F14.11).
  • The legal items under F14.10.