Skip to content

v0.6.0 — The usage-based billing release

Latest

Choose a tag to compare

@swapnull-in swapnull-in released this 18 Jul 18:26

The usage-based billing release: everything between a usage event and the
money it becomes — billable metrics, tiered pricing, unattended renewals,
prepaid wallets, minimum commitments, usage alerts, batch ingestion, an
append-only audit trail — plus the first wave of ecosystem integrations
(GoCardless, Adyen, NetSuite, Avalara, HubSpot, S3 export) shipped as
experimental pending sandbox certification.

Added

  • HubSpot CRM sync (EXPERIMENTAL) — a daily worker upserts every
    customer as a HubSpot contact (keyed by email) carrying
    recurso_customer_id and recurso_subscription_state
    (active/churned). Private-app token via HUBSPOT_ACCESS_TOKEN;
    SaaS egress, so blocked under RESIDENCY_MODE=self_hosted.
    Salesforce follows as its own spec (JWT auth design needed).

  • NetSuite accounting sync (EXPERIMENTAL) — a SuiteTalk REST adapter
    in the existing accounting-sync framework (customer / invoice / item
    upserts with Location-header id capture, ErrExternalGone remapping,
    per-line major-unit amounts). Residency-guarded like QuickBooks/Xero;
    connect with provider netsuite (RealmID = NetSuite account id).

  • Avalara AvaTax provider (EXPERIMENTAL) — US sales-tax quotes via
    uncommitted SalesOrder transactions (AVALARA_ACCOUNT_ID /
    AVALARA_LICENSE_KEY / AVALARA_COMPANY_CODE), sharing TaxJar's error
    taxonomy, 24h rate cache, and the RESIDENCY_MODE=self_hosted guard.
    TaxJar takes precedence when both are configured.

  • S3 finance export (EXPERIMENTAL) — a daily worker ships each
    tenant's general ledger CSV to operator-owned object storage
    (S3_EXPORT_BUCKET/REGION/PREFIX, S3_EXPORT_ENDPOINT for
    MinIO/R2), signed with a dependency-free SigV4 client. Idempotent keys
    per day; disabled unless configured.

  • Automation & Okta docs — Zapier/n8n recipes over the signed webhook
    surface and the Okta SAML SSO walkthrough (Track D6/D7 ride existing
    code).

  • GoCardless + Adyen gateways (EXPERIMENTAL) — bank-debit rails
    (SEPA/BACS via mandate-first billing requests) and global card
    processing (Checkout Sessions, off-session stored-method charges), both
    behind GATEWAY_CURRENCY_OVERRIDES (e.g. EUR=gocardless,SGD=adyen)
    with the existing INR→Razorpay / default→Stripe routing untouched.
    httptest-verified request shapes and idempotency keys; sandbox
    verification pending (founder-gated).

  • Append-only audit trail — every successful config-grade mutation
    (plans, metrics, charges, coupons, webhooks, wallets, alerts, team,
    settings, ...) is recorded via middleware with actor (dashboard user or
    API key), route, entity, and the truncated request payload.
    GET /v1/audit-logs filters by entity/actor/time; the table rejects
    UPDATE and DELETE at the database level.

  • Ingestion hardeningPOST /v1/usage/events/batch ingests up to
    500 events with per-item results, and events accept an optional
    transaction_id idempotency key: a retried event with the same
    (subscription, transaction_id) collapses to the original, so SDK retries
    can never inflate usage. A covering index on
    (subscription_id, dimension, timestamp) backs the aggregation hot path.

  • Usage threshold alerts/v1/usage-alerts configures per-
    subscription thresholds on a billable metric (absolute quantity or
    percent of the matching entitlement limit). The billing-cycle sweep
    evaluates them and fires AT MOST once per billing period per threshold —
    a conditional claim dedups concurrent sweeps — emitting the
    usage.alert.triggered webhook event plus an email to the customer.

  • Minimum commitmentsPUT /v1/subscriptions/{id}/commitment sets a
    per-period floor (minor units); when a period's subtotal (flat + add-ons

    • metered usage) falls short, a "Minimum commitment true-up" line fills
      exactly the difference, taxed at the plan HSN. The usage-amount preview
      reports the projected true-up. Overage needs nothing: usage past the
      floor bills naturally.
  • Prepaid wallets — money-denominated stored value per
    customer+currency (/v1/wallets, top-ups, transactions, auto-recharge).
    Invoices drain the wallet FIRST (wallet → credit notes → gateway), with
    residues consumed oldest-expiring-first so dated promotional credit is
    spent before it lapses; expired residue is written off by the sweep.
    Every movement is append-only with balance-after and posts balanced
    ledger legs (DR Cash / CR Customer Credit on top-up; DR Customer Credit /
    CR AR on drain). Auto-recharge tops up below-threshold wallets from the
    saved payment method on the billing-cycle tick.

  • Unattended renewals — a billing-cycle scheduler claims due,
    locally-billed subscriptions (leased at-most-once claims), generates the
    renewal invoice (flat fee in advance + metered usage in arrears),
    advances the period anchor-preservingly, and best-effort charges the
    saved payment method; declines flow to dunning. BILLING_CYCLE_INTERVAL
    configures the tick (default 5m, 0 disables). cancel_at_period_end
    subscriptions get their final usage rated, then cancel.

  • Metered mandate debits — UPI-mandate debit invoices now carry the
    subscription's rated usage lines, and the subscription billing period
    advances with each cycle (it previously never advanced for mandate
    subscriptions, leaving current-period usage reporting stale). Usage that
    would exceed the mandate's authorized ceiling is billed on a separate
    open invoice instead of over-charging.

  • SDK metering methods — Node recurso@1.3.0, Go v1.1.0, and Python
    1.2.0 expose billable metrics, plan charges, the usage-amount preview,
    and event properties.

  • Usage-based billing v1 (docs/spec_usage_billing.md) — the metering
    engine: billable metrics (POST/GET/PUT/DELETE /v1/billable-metrics)
    aggregate usage events by count, sum, max, or unique (distinct
    values of an event property); charges attach per_unit, graduated,
    volume, or package pricing for a metric to a plan
    (PUT/GET /v1/plans/{id}/charges), with per-currency amounts and
    sub-minor-unit decimal rates (0.0035/call) computed exactly and rounded
    once per line; rating at period close bills the elapsed period's
    usage in arrears on the renewal invoice as tax-resolved line items, with
    a usage_ratings window claim making retried generation idempotent;
    immediate cancellation bills the partial window on a final usage invoice.
    GET /v1/subscriptions/{id}/usage-amount previews the running period,
    and POST /v1/usage/events accepts optional properties.