openvibers/openvibe.billing Commits
Turn the 3-day renewal grace on by default
Changes
@@@ -78,8 +78,8 @@ BILLING_SESSION_SECRET=#BILLING_ESCROW_DAYS=14#BILLING_STRIPE_GRACE_DAYS=3# Days a failed credit renewal stays past_due (retried by the hourly sweep on the same key) before it ends.--# 0 (default) ends it at once; turn on only after VIP reads past_due (docs/renewal-grace-cutover.md).--#BILLING_RENEWAL_GRACE_DAYS=0++# 3 is the default (VIP reads past_due, plan s4); set 0 to restore immediate expiry (docs/renewal-grace-cutover.md).++#BILLING_RENEWAL_GRACE_DAYS=3# ── Providers: each adapter is ENABLED only when its secrets are set ──# PowerChat (the only rail carrying money in production)@@@ -350,11 +350,11 @@ there — a share already recycled or cashed out goes to `chargeback_loss`, flag`POST /api/v1/admin/sweep`): a credit subscription whose period ended is charged once from the subscriber'scredit (never the promo allowance) under `renew:<sub>:<period end>` — `…:<k>` after `k` charges under thatperiod's earlier keys were refunded or charged back — so a retried, replayed or concurrent sweep charges and-- credits the creator share once. A renewal the credit cannot pay ends the subscription at once-- (`renewal_insufficient_credit`) unless `BILLING_RENEWAL_GRACE_DAYS` > 0 (default 0): then it is `past_due`-- until the period end + the grace (`grace_until`; no access meanwhile, event reason `renewal_failed`), every-- later sweep retries the same key (a top-up is picked up; a cancel ends it), and past `grace_until` it expires-- (`grace_ended`). Turning the grace on: [docs/renewal-grace-cutover.md](docs/renewal-grace-cutover.md).++ credits the creator share once. A renewal the credit cannot pay leaves the subscription `past_due`++ for `BILLING_RENEWAL_GRACE_DAYS` days past the period end (default **3**; set `0` to end it at once++ with `renewal_insufficient_credit`): no access meanwhile (event reason `renewal_failed`), every++ later sweep retries the same key (a top-up is picked up; a cancel ends it), and past `grace_until` it++ expires (`grace_ended`). Turning the grace on (or off): [docs/renewal-grace-cutover.md](docs/renewal-grace-cutover.md).- **Freeze** before any risky change: the console's Freeze page, `POST /api/v1/admin/freeze {"on": true, "reason": "…"}`, or on thehost `node scripts/freeze.js on "<reason>"` (same switch; after `off` the running service processes theheld webhooks on its next retry tick).@@@ -16,7 +16,7 @@"provider receipts stored before processing, settled once (PowerChat, Stripe, PayPal, CCBill, NOWPayments adapters)","intents, transfers, refunds, recycle, cashouts with escrow","subscriptions and entitlements",-- "renewal grace: a failed credit renewal is past_due and retried on its key until grace_until (BILLING_RENEWAL_GRACE_DAYS, off by default)",++ "renewal grace: a failed credit renewal is past_due for BILLING_RENEWAL_GRACE_DAYS (default 3) and retried on its key until grace_until, then expires with grace_ended; set 0 for immediate expiry","staff refund of a credit-paid subscription period (POST /api/v1/subscriptions/:id/refund) and reconciliation of every paid period against its charge (subscriptions.period_charged)","EXTERNAL PowerChat receipts (billing.receipt.external under BILLING_AUTHORITY=billing)","economy freeze",@@@ -1,11 +1,13 @@--# Cutover runbook — renewal grace (OpenVibe.Billing, plan T5 step 11, s2)++# Cutover runbook — renewal grace (OpenVibe.Billing, plan T5 step 11, s2 + s5)A credit renewal the subscriber cannot pay used to end the subscription at once (`expired`, reason`renewal_insufficient_credit`). With this change it can instead move to **`past_due`** for`BILLING_RENEWAL_GRACE_DAYS` past the period end: the hourly sweep retries the same charge under the **sameidempotency key** until it is paid (`active` again, reason `renewed`, one charge, one creator share) or--`grace_until` passes (`expired`, reason `grace_ended`). **The grace ships off** (`BILLING_RENEWAL_GRACE_DAYS`--defaults to `0`), so the deploy changes no behavior: no row becomes `past_due` until the grace is turned on.++`grace_until` passes (`expired`, reason `grace_ended`). **The grace shipped off in s2** (`BILLING_RENEWAL_GRACE_DAYS`++defaulted to `0`, so that deploy changed no behavior); **s5 turns it on** — the default is now `3`, so a failed++renewal is `past_due` for three days, retried on the same key, and only then expires. Setting the env back to `0`++restores immediate expiry (see Rollback).Also in this change, whatever the grace: the renewal key is `renew:<sub>:<period end>` as before, and becomes`renew:<sub>:<period end>:<k>` only after `k` charges under that period's earlier keys were refunded or charged back@@@ -69,7 +71,7 @@ runbook:SELECT pg_get_constraintdef(oid) FROM pg_constraintWHERE conrelid = 'subscriptions'::regclass AND conname = 'subscriptions_status_check';SELECT id, name, phase FROM ov_migrations WHERE id = '0005'; -- 0005 | renewal_grace | expand--SELECT COUNT(*) FROM subscriptions WHERE status = 'past_due'; -- 0 (the grace is off)++SELECT COUNT(*) FROM subscriptions WHERE status = 'past_due'; -- 0 before the flip; failed renewals list here after s5```and compare `node scripts/reconcile.js` before and after: the same account, transaction and entry counts, every@@@ -83,28 +85,34 @@ After the deploy, on production (read-only):`migrations: applied 0005_renewal_grace (expand, NNms)`; `ov_migrations` has the `0005` row.2. `\d subscriptions` lists `grace_until`, `renewal_failed_at` (nullable text) and `renewal_attempts`(`bigint NOT NULL DEFAULT 0`), and `subscriptions_due_idx`.--3. `SELECT count(*) FROM subscriptions WHERE status = 'past_due'` is **0**: the grace is still off-- (`BILLING_RENEWAL_GRACE_DAYS` unset or `0` in `/etc/openvibe/billing.env`), so the deploy changed no-- behavior. An unpaid renewal still ends at once (`expired`, reason `renewal_insufficient_credit`).++3. `SELECT count(*) FROM subscriptions WHERE status = 'past_due'` — before s5 it is **0** (the grace is off,++ `BILLING_RENEWAL_GRACE_DAYS` unset or `0`, so an unpaid renewal still ends at once with++ `renewal_insufficient_credit`); after s5's flip it holds exactly the renewals inside their 3-day grace.++ The flip itself changes no row: a subscription only enters `past_due` when the sweep next tries and++ fails to renew its ended period. `SELECT id, status, grace_until, renewal_attempts FROM subscriptions++ WHERE status = 'past_due'` lists them.4. `node scripts/reconcile.js` shows the same totals as before the deploy, every check ok.## Order1. **Contracts** — v0.94.2 (`past_due`, `renewal_failed`, `grace_ended`, `grace_until`) is released (s1).2. **Backup** — the snapshot above, and confirm it.--3. **Billing, grace 0** — merge and deploy this change through its pipeline. Leave `BILLING_RENEWAL_GRACE_DAYS`-- unset (= 0). Verify with the checks above. Renewals behave as before.++3. **Billing, grace 0 (s2)** — merge and deploy this change through its pipeline. Before the s5 flip the env was++ left unset (then = 0). Verify with the checks above. Renewals behave as before.4. **VIP consumes `past_due`** — VIP (plan s4) treats `subscription.status: past_due` as not entitled, reads`grace_until` for its "renew your membership" notice and records each period's charge. Do not go further untilVIP with that change is deployed.--5. **Turn the grace on** — plan s5 sets the default to 3; on a host before that, set-- `BILLING_RENEWAL_GRACE_DAYS=3` in `/etc/openvibe/billing.env` and restart through the deploy pipeline. Check-- after the next sweep: the log line `subscription sweep: … past due …` and, for a failed renewal,-- `SELECT id, status, grace_until, renewal_attempts FROM subscriptions WHERE status = 'past_due'`.++5. **Turn the grace on — done (plan s5)** — the default is now `3`. No env change is needed: with++ `BILLING_RENEWAL_GRACE_DAYS` unset in `/etc/openvibe/billing.env`, deploy this change through the pipeline++ (VIP s4, which reads `past_due` and `grace_until`, must already be deployed). Check after the next sweep: the++ log line `subscription sweep: … past due …` and, for a failed renewal,++ `SELECT id, status, grace_until, renewal_attempts FROM subscriptions WHERE status = 'past_due'`. A host that++ sets the env explicitly (e.g. `BILLING_RENEWAL_GRACE_DAYS=0`) keeps that value — remove it to take the default.## Rollback--- **Turn the grace off:** set `BILLING_RENEWAL_GRACE_DAYS=0` (or unset it) and restart. The next sweep retries each++- **Turn the grace off:** set `BILLING_RENEWAL_GRACE_DAYS=0` in `/etc/openvibe/billing.env` and restart (do++ **not** merely unset it — unset now means the default 3). The next sweep retries each`past_due` subscription once more on its key — a subscriber who topped up renews, with one charge — and endsthe rest (`expired`, reason `grace_ended`). Nothing else changes. This is the way back for behavior; noschema restore is needed.@@@ -113,7 +113,7 @@ function loadConfig(env = process.env) {stripeGraceDays: num(env.BILLING_STRIPE_GRACE_DAYS, 3),// A failed credit renewal stays past_due (retried by the sweep) this many days past the period end;// 0 = end it at once (renewal_insufficient_credit).-- renewalGraceDays: Math.max(0, num(env.BILLING_RENEWAL_GRACE_DAYS, 0)),++ renewalGraceDays: Math.max(0, num(env.BILLING_RENEWAL_GRACE_DAYS, 3)),},// Provider adapters: each is enabled only when its secrets are set.@@@ -17,9 +17,9 @@* entitled to a streamer while a non-revoked row covers now. It needs nothing from Live.** A credit renewal the subscriber cannot pay leaves the subscription past_due for-- * BILLING_RENEWAL_GRACE_DAYS (0 = end it at once): no period covers the grace, so the entitlement-- * is not active, and the sweep retries the same charge under the same key until it is paid or-- * grace_until passes.++ * BILLING_RENEWAL_GRACE_DAYS (3 by default; 0 = end it at once): no period covers the grace, so the++ * entitlement is not active, and the sweep retries the same charge under the same key until it is++ * paid or grace_until passes.*/const { post, getTxn, requireFunds, iso, prefixedId, money } = require('../ledger');const { enqueue } = require('../outbox');@@@ -10,7 +10,8 @@ const { boot, fund, check, done } = require('./helpers/app');const DAY = 86_400_000;(async () => {-- const t = await boot();++ // The grace is on by default now; this file asserts the pre-grace behavior, so pin it to 0.++ const t = await boot({ env: { BILLING_RENEWAL_GRACE_DAYS: '0' } });const fan = t.user(31);const streamer = t.user(32);console.log('subscriptions');
@@ -78,8 +78,8 @@ BILLING_SESSION_SECRET=
@@ -78,8 +78,8 @@ BILLING_SESSION_SECRET=
#BILLING_ESCROW_DAYS=14
#BILLING_ESCROW_DAYS=14
#BILLING_STRIPE_GRACE_DAYS=3
#BILLING_STRIPE_GRACE_DAYS=3
# Days a failed credit renewal stays past_due (retried by the hourly sweep on the same key) before it ends.
# Days a failed credit renewal stays past_due (retried by the hourly sweep on the same key) before it ends.
-# 0 (default) ends it at once; turn on only after VIP reads past_due (docs/renewal-grace-cutover.md).
-#BILLING_RENEWAL_GRACE_DAYS=0
+# 3 is the default (VIP reads past_due, plan s4); set 0 to restore immediate expiry (docs/renewal-grace-cutover.md).
+#BILLING_RENEWAL_GRACE_DAYS=3
# ── Providers: each adapter is ENABLED only when its secrets are set ──
# ── Providers: each adapter is ENABLED only when its secrets are set ──
# PowerChat (the only rail carrying money in production)
# PowerChat (the only rail carrying money in production)
@@ -350,11 +350,11 @@ there — a share already recycled or cashed out goes to `chargeback_loss`, flag
@@ -350,11 +350,11 @@ there — a share already recycled or cashed out goes to `chargeback_loss`, flag
`POST /api/v1/admin/sweep`): a credit subscription whose period ended is charged once from the subscriber's
`POST /api/v1/admin/sweep`): a credit subscription whose period ended is charged once from the subscriber's
credit (never the promo allowance) under `renew:<sub>:<period end>` — `…:<k>` after `k` charges under that
credit (never the promo allowance) under `renew:<sub>:<period end>` — `…:<k>` after `k` charges under that
period's earlier keys were refunded or charged back — so a retried, replayed or concurrent sweep charges and
period's earlier keys were refunded or charged back — so a retried, replayed or concurrent sweep charges and
- credits the creator share once. A renewal the credit cannot pay ends the subscription at once
- (`renewal_insufficient_credit`) unless `BILLING_RENEWAL_GRACE_DAYS` > 0 (default 0): then it is `past_due`
- until the period end + the grace (`grace_until`; no access meanwhile, event reason `renewal_failed`), every
- later sweep retries the same key (a top-up is picked up; a cancel ends it), and past `grace_until` it expires
- (`grace_ended`). Turning the grace on: [docs/renewal-grace-cutover.md](docs/renewal-grace-cutover.md).
+ credits the creator share once. A renewal the credit cannot pay leaves the subscription `past_due`
+ for `BILLING_RENEWAL_GRACE_DAYS` days past the period end (default **3**; set `0` to end it at once
+ with `renewal_insufficient_credit`): no access meanwhile (event reason `renewal_failed`), every
+ later sweep retries the same key (a top-up is picked up; a cancel ends it), and past `grace_until` it
+ expires (`grace_ended`). Turning the grace on (or off): [docs/renewal-grace-cutover.md](docs/renewal-grace-cutover.md).
- **Freeze** before any risky change: the console's Freeze page, `POST /api/v1/admin/freeze {"on": true, "reason": "…"}`, or on the
- **Freeze** before any risky change: the console's Freeze page, `POST /api/v1/admin/freeze {"on": true, "reason": "…"}`, or on the
host `node scripts/freeze.js on "<reason>"` (same switch; after `off` the running service processes the
host `node scripts/freeze.js on "<reason>"` (same switch; after `off` the running service processes the
held webhooks on its next retry tick).
held webhooks on its next retry tick).
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
"provider receipts stored before processing, settled once (PowerChat, Stripe, PayPal, CCBill, NOWPayments adapters)",
"provider receipts stored before processing, settled once (PowerChat, Stripe, PayPal, CCBill, NOWPayments adapters)",
"intents, transfers, refunds, recycle, cashouts with escrow",
"intents, transfers, refunds, recycle, cashouts with escrow",
"subscriptions and entitlements",
"subscriptions and entitlements",
- "renewal grace: a failed credit renewal is past_due and retried on its key until grace_until (BILLING_RENEWAL_GRACE_DAYS, off by default)",
+ "renewal grace: a failed credit renewal is past_due for BILLING_RENEWAL_GRACE_DAYS (default 3) and retried on its key until grace_until, then expires with grace_ended; set 0 for immediate expiry",
"staff refund of a credit-paid subscription period (POST /api/v1/subscriptions/:id/refund) and reconciliation of every paid period against its charge (subscriptions.period_charged)",
"staff refund of a credit-paid subscription period (POST /api/v1/subscriptions/:id/refund) and reconciliation of every paid period against its charge (subscriptions.period_charged)",
"EXTERNAL PowerChat receipts (billing.receipt.external under BILLING_AUTHORITY=billing)",
"EXTERNAL PowerChat receipts (billing.receipt.external under BILLING_AUTHORITY=billing)",
"economy freeze",
"economy freeze",
@@ -1,11 +1,13 @@
@@ -1,11 +1,13 @@
-# Cutover runbook — renewal grace (OpenVibe.Billing, plan T5 step 11, s2)
+# Cutover runbook — renewal grace (OpenVibe.Billing, plan T5 step 11, s2 + s5)
A credit renewal the subscriber cannot pay used to end the subscription at once (`expired`, reason
A credit renewal the subscriber cannot pay used to end the subscription at once (`expired`, reason
`renewal_insufficient_credit`). With this change it can instead move to **`past_due`** for
`renewal_insufficient_credit`). With this change it can instead move to **`past_due`** for
`BILLING_RENEWAL_GRACE_DAYS` past the period end: the hourly sweep retries the same charge under the **same
`BILLING_RENEWAL_GRACE_DAYS` past the period end: the hourly sweep retries the same charge under the **same
idempotency key** until it is paid (`active` again, reason `renewed`, one charge, one creator share) or
idempotency key** until it is paid (`active` again, reason `renewed`, one charge, one creator share) or
-`grace_until` passes (`expired`, reason `grace_ended`). **The grace ships off** (`BILLING_RENEWAL_GRACE_DAYS`
-defaults to `0`), so the deploy changes no behavior: no row becomes `past_due` until the grace is turned on.
+`grace_until` passes (`expired`, reason `grace_ended`). **The grace shipped off in s2** (`BILLING_RENEWAL_GRACE_DAYS`
+defaulted to `0`, so that deploy changed no behavior); **s5 turns it on** — the default is now `3`, so a failed
+renewal is `past_due` for three days, retried on the same key, and only then expires. Setting the env back to `0`
+restores immediate expiry (see Rollback).
Also in this change, whatever the grace: the renewal key is `renew:<sub>:<period end>` as before, and becomes
Also in this change, whatever the grace: the renewal key is `renew:<sub>:<period end>` as before, and becomes
`renew:<sub>:<period end>:<k>` only after `k` charges under that period's earlier keys were refunded or charged back
`renew:<sub>:<period end>:<k>` only after `k` charges under that period's earlier keys were refunded or charged back
@@ -69,7 +71,7 @@ runbook:
@@ -69,7 +71,7 @@ runbook:
SELECT pg_get_constraintdef(oid) FROM pg_constraint
SELECT pg_get_constraintdef(oid) FROM pg_constraint
WHERE conrelid = 'subscriptions'::regclass AND conname = 'subscriptions_status_check';
WHERE conrelid = 'subscriptions'::regclass AND conname = 'subscriptions_status_check';
SELECT id, name, phase FROM ov_migrations WHERE id = '0005'; -- 0005 | renewal_grace | expand
SELECT id, name, phase FROM ov_migrations WHERE id = '0005'; -- 0005 | renewal_grace | expand
-SELECT COUNT(*) FROM subscriptions WHERE status = 'past_due'; -- 0 (the grace is off)
+SELECT COUNT(*) FROM subscriptions WHERE status = 'past_due'; -- 0 before the flip; failed renewals list here after s5
```
```
and compare `node scripts/reconcile.js` before and after: the same account, transaction and entry counts, every
and compare `node scripts/reconcile.js` before and after: the same account, transaction and entry counts, every
@@ -83,28 +85,34 @@ After the deploy, on production (read-only):
@@ -83,28 +85,34 @@ After the deploy, on production (read-only):
`migrations: applied 0005_renewal_grace (expand, NNms)`; `ov_migrations` has the `0005` row.
`migrations: applied 0005_renewal_grace (expand, NNms)`; `ov_migrations` has the `0005` row.
2. `\d subscriptions` lists `grace_until`, `renewal_failed_at` (nullable text) and `renewal_attempts`
2. `\d subscriptions` lists `grace_until`, `renewal_failed_at` (nullable text) and `renewal_attempts`
(`bigint NOT NULL DEFAULT 0`), and `subscriptions_due_idx`.
(`bigint NOT NULL DEFAULT 0`), and `subscriptions_due_idx`.
-3. `SELECT count(*) FROM subscriptions WHERE status = 'past_due'` is **0**: the grace is still off
- (`BILLING_RENEWAL_GRACE_DAYS` unset or `0` in `/etc/openvibe/billing.env`), so the deploy changed no
- behavior. An unpaid renewal still ends at once (`expired`, reason `renewal_insufficient_credit`).
+3. `SELECT count(*) FROM subscriptions WHERE status = 'past_due'` — before s5 it is **0** (the grace is off,
+ `BILLING_RENEWAL_GRACE_DAYS` unset or `0`, so an unpaid renewal still ends at once with
+ `renewal_insufficient_credit`); after s5's flip it holds exactly the renewals inside their 3-day grace.
+ The flip itself changes no row: a subscription only enters `past_due` when the sweep next tries and
+ fails to renew its ended period. `SELECT id, status, grace_until, renewal_attempts FROM subscriptions
+ WHERE status = 'past_due'` lists them.
4. `node scripts/reconcile.js` shows the same totals as before the deploy, every check ok.
4. `node scripts/reconcile.js` shows the same totals as before the deploy, every check ok.
## Order
## Order
1. **Contracts** — v0.94.2 (`past_due`, `renewal_failed`, `grace_ended`, `grace_until`) is released (s1).
1. **Contracts** — v0.94.2 (`past_due`, `renewal_failed`, `grace_ended`, `grace_until`) is released (s1).
2. **Backup** — the snapshot above, and confirm it.
2. **Backup** — the snapshot above, and confirm it.
-3. **Billing, grace 0** — merge and deploy this change through its pipeline. Leave `BILLING_RENEWAL_GRACE_DAYS`
- unset (= 0). Verify with the checks above. Renewals behave as before.
+3. **Billing, grace 0 (s2)** — merge and deploy this change through its pipeline. Before the s5 flip the env was
+ left unset (then = 0). Verify with the checks above. Renewals behave as before.
4. **VIP consumes `past_due`** — VIP (plan s4) treats `subscription.status: past_due` as not entitled, reads
4. **VIP consumes `past_due`** — VIP (plan s4) treats `subscription.status: past_due` as not entitled, reads
`grace_until` for its "renew your membership" notice and records each period's charge. Do not go further until
`grace_until` for its "renew your membership" notice and records each period's charge. Do not go further until
VIP with that change is deployed.
VIP with that change is deployed.
-5. **Turn the grace on** — plan s5 sets the default to 3; on a host before that, set
- `BILLING_RENEWAL_GRACE_DAYS=3` in `/etc/openvibe/billing.env` and restart through the deploy pipeline. Check
- after the next sweep: the log line `subscription sweep: … past due …` and, for a failed renewal,
- `SELECT id, status, grace_until, renewal_attempts FROM subscriptions WHERE status = 'past_due'`.
+5. **Turn the grace on — done (plan s5)** — the default is now `3`. No env change is needed: with
+ `BILLING_RENEWAL_GRACE_DAYS` unset in `/etc/openvibe/billing.env`, deploy this change through the pipeline
+ (VIP s4, which reads `past_due` and `grace_until`, must already be deployed). Check after the next sweep: the
+ log line `subscription sweep: … past due …` and, for a failed renewal,
+ `SELECT id, status, grace_until, renewal_attempts FROM subscriptions WHERE status = 'past_due'`. A host that
+ sets the env explicitly (e.g. `BILLING_RENEWAL_GRACE_DAYS=0`) keeps that value — remove it to take the default.
## Rollback
## Rollback
-- **Turn the grace off:** set `BILLING_RENEWAL_GRACE_DAYS=0` (or unset it) and restart. The next sweep retries each
+- **Turn the grace off:** set `BILLING_RENEWAL_GRACE_DAYS=0` in `/etc/openvibe/billing.env` and restart (do
+ **not** merely unset it — unset now means the default 3). The next sweep retries each
`past_due` subscription once more on its key — a subscriber who topped up renews, with one charge — and ends
`past_due` subscription once more on its key — a subscriber who topped up renews, with one charge — and ends
the rest (`expired`, reason `grace_ended`). Nothing else changes. This is the way back for behavior; no
the rest (`expired`, reason `grace_ended`). Nothing else changes. This is the way back for behavior; no
schema restore is needed.
schema restore is needed.
@@ -113,7 +113,7 @@ function loadConfig(env = process.env) {
@@ -113,7 +113,7 @@ function loadConfig(env = process.env) {
stripeGraceDays: num(env.BILLING_STRIPE_GRACE_DAYS, 3),
stripeGraceDays: num(env.BILLING_STRIPE_GRACE_DAYS, 3),
// A failed credit renewal stays past_due (retried by the sweep) this many days past the period end;
// A failed credit renewal stays past_due (retried by the sweep) this many days past the period end;
// 0 = end it at once (renewal_insufficient_credit).
// 0 = end it at once (renewal_insufficient_credit).
- renewalGraceDays: Math.max(0, num(env.BILLING_RENEWAL_GRACE_DAYS, 0)),
+ renewalGraceDays: Math.max(0, num(env.BILLING_RENEWAL_GRACE_DAYS, 3)),
},
},
// Provider adapters: each is enabled only when its secrets are set.
// Provider adapters: each is enabled only when its secrets are set.
@@ -17,9 +17,9 @@
@@ -17,9 +17,9 @@
* entitled to a streamer while a non-revoked row covers now. It needs nothing from Live.
* entitled to a streamer while a non-revoked row covers now. It needs nothing from Live.
*
*
* A credit renewal the subscriber cannot pay leaves the subscription past_due for
* A credit renewal the subscriber cannot pay leaves the subscription past_due for
- * BILLING_RENEWAL_GRACE_DAYS (0 = end it at once): no period covers the grace, so the entitlement
- * is not active, and the sweep retries the same charge under the same key until it is paid or
- * grace_until passes.
+ * BILLING_RENEWAL_GRACE_DAYS (3 by default; 0 = end it at once): no period covers the grace, so the
+ * entitlement is not active, and the sweep retries the same charge under the same key until it is
+ * paid or grace_until passes.
*/
*/
const { post, getTxn, requireFunds, iso, prefixedId, money } = require('../ledger');
const { post, getTxn, requireFunds, iso, prefixedId, money } = require('../ledger');
const { enqueue } = require('../outbox');
const { enqueue } = require('../outbox');
@@ -10,7 +10,8 @@ const { boot, fund, check, done } = require('./helpers/app');
@@ -10,7 +10,8 @@ const { boot, fund, check, done } = require('./helpers/app');
const DAY = 86_400_000;
const DAY = 86_400_000;
(async () => {
(async () => {
- const t = await boot();
+ // The grace is on by default now; this file asserts the pre-grace behavior, so pin it to 0.
+ const t = await boot({ env: { BILLING_RENEWAL_GRACE_DAYS: '0' } });
const fan = t.user(31);
const fan = t.user(31);
const streamer = t.user(32);
const streamer = t.user(32);
console.log('subscriptions');
console.log('subscriptions');