@cogenta/commerce 0.2.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/permissions.d.ts +8 -0
- package/dist/admin/permissions.d.ts.map +1 -1
- package/dist/admin/permissions.js +1 -0
- package/dist/admin/permissions.js.map +1 -1
- package/dist/admin/router.d.ts +47 -5
- package/dist/admin/router.d.ts.map +1 -1
- package/dist/admin/router.js +942 -24
- package/dist/admin/router.js.map +1 -1
- package/dist/cart/store.d.ts +23 -0
- package/dist/cart/store.d.ts.map +1 -1
- package/dist/cart/store.js +49 -2
- package/dist/cart/store.js.map +1 -1
- package/dist/catalog/csv.d.ts +29 -0
- package/dist/catalog/csv.d.ts.map +1 -0
- package/dist/catalog/csv.js +396 -0
- package/dist/catalog/csv.js.map +1 -0
- package/dist/catalog/store.d.ts +24 -4
- package/dist/catalog/store.d.ts.map +1 -1
- package/dist/catalog/store.js +238 -11
- package/dist/catalog/store.js.map +1 -1
- package/dist/catalog/types.d.ts +92 -0
- package/dist/catalog/types.d.ts.map +1 -1
- package/dist/catalog/types.js +20 -0
- package/dist/catalog/types.js.map +1 -1
- package/dist/coupon/store.d.ts +63 -5
- package/dist/coupon/store.d.ts.map +1 -1
- package/dist/coupon/store.js +140 -7
- package/dist/coupon/store.js.map +1 -1
- package/dist/customer/store.d.ts +17 -0
- package/dist/customer/store.d.ts.map +1 -1
- package/dist/customer/store.js +26 -0
- package/dist/customer/store.js.map +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/invoice/credit-note.d.ts +45 -0
- package/dist/invoice/credit-note.d.ts.map +1 -0
- package/dist/invoice/credit-note.js +109 -0
- package/dist/invoice/credit-note.js.map +1 -0
- package/dist/invoice/sequence.d.ts +21 -0
- package/dist/invoice/sequence.d.ts.map +1 -0
- package/dist/invoice/sequence.js +64 -0
- package/dist/invoice/sequence.js.map +1 -0
- package/dist/invoice/store.d.ts +23 -1
- package/dist/invoice/store.d.ts.map +1 -1
- package/dist/invoice/store.js +23 -56
- package/dist/invoice/store.js.map +1 -1
- package/dist/order/csv.d.ts +24 -0
- package/dist/order/csv.d.ts.map +1 -0
- package/dist/order/csv.js +42 -0
- package/dist/order/csv.js.map +1 -0
- package/dist/order/notify.d.ts +65 -0
- package/dist/order/notify.d.ts.map +1 -0
- package/dist/order/notify.js +180 -0
- package/dist/order/notify.js.map +1 -0
- package/dist/order/store.d.ts +58 -1
- package/dist/order/store.d.ts.map +1 -1
- package/dist/order/store.js +145 -10
- package/dist/order/store.js.map +1 -1
- package/dist/order/types.d.ts +35 -1
- package/dist/order/types.d.ts.map +1 -1
- package/dist/order/types.js +2 -0
- package/dist/order/types.js.map +1 -1
- package/dist/payment/paypal.d.ts +9 -0
- package/dist/payment/paypal.d.ts.map +1 -0
- package/dist/payment/paypal.js +583 -0
- package/dist/payment/paypal.js.map +1 -0
- package/dist/payment/registry.d.ts +15 -6
- package/dist/payment/registry.d.ts.map +1 -1
- package/dist/payment/registry.js +17 -6
- package/dist/payment/registry.js.map +1 -1
- package/dist/payment/types.d.ts +6 -0
- package/dist/payment/types.d.ts.map +1 -1
- package/dist/shipping/store.d.ts +29 -3
- package/dist/shipping/store.d.ts.map +1 -1
- package/dist/shipping/store.js +68 -4
- package/dist/shipping/store.js.map +1 -1
- package/dist/subscription/renewal-notifier.d.ts +17 -0
- package/dist/subscription/renewal-notifier.d.ts.map +1 -0
- package/dist/subscription/renewal-notifier.js +33 -0
- package/dist/subscription/renewal-notifier.js.map +1 -0
- package/dist/subscription/store.d.ts +126 -3
- package/dist/subscription/store.d.ts.map +1 -1
- package/dist/subscription/store.js +348 -8
- package/dist/subscription/store.js.map +1 -1
- package/dist/tables.d.ts +17 -0
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +273 -0
- package/dist/tables.js.map +1 -1
- package/dist/tax/store.d.ts +20 -0
- package/dist/tax/store.d.ts.map +1 -1
- package/dist/tax/store.js +51 -7
- package/dist/tax/store.js.map +1 -1
- package/package.json +3 -2
package/dist/payment/registry.js
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import { createDriverRegistry } from '@cogenta/core';
|
|
2
2
|
import { manualPaymentDriver } from './manual.js';
|
|
3
|
+
import { paypalPaymentDriver } from './paypal.js';
|
|
3
4
|
import { stripePaymentDriver } from './stripe.js';
|
|
4
5
|
/**
|
|
5
6
|
* The payment gateways Cogenta ships.
|
|
6
7
|
*
|
|
7
8
|
* Registered in tier order, exactly like cache, queue, storage and database:
|
|
8
|
-
* Stripe
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Stripe and PayPal are both `optimal` and answer only when real credentials
|
|
10
|
+
* are configured and the gateway's own API accepts them; bank transfer is
|
|
11
|
+
* `degraded` and always answers. So a site with no gateway account does not
|
|
12
|
+
* fail to start and does not fall over at checkout — it takes bank transfers,
|
|
13
|
+
* which is a real way to be paid, not a placeholder.
|
|
14
|
+
*
|
|
15
|
+
* This is also the concrete answer to "what if I want a payment method that
|
|
16
|
+
* isn't Stripe?": `PaymentGateway` (`types.ts`) is a registered interface,
|
|
17
|
+
* exactly like cache/queue/storage, and PayPal is a second, independent
|
|
18
|
+
* implementation of it added without touching Stripe, `manual.ts`, the order
|
|
19
|
+
* store, or the admin payment screen — that screen already renders whatever
|
|
20
|
+
* `registry.list()` returns, so a third driver appears there for free.
|
|
12
21
|
*
|
|
13
22
|
* The registry's two rules apply here with more force than anywhere else in
|
|
14
|
-
* the project. If the configuration **names**
|
|
23
|
+
* the project. If the configuration **names** a driver, its outage is fatal
|
|
15
24
|
* rather than a silent downgrade: quietly switching a shop that expects card
|
|
16
25
|
* payments over to "please make a transfer" would change what customers are
|
|
17
26
|
* asked to do without anybody deciding to. If the configuration names nothing,
|
|
18
|
-
* the fall-through is the point
|
|
27
|
+
* the fall-through is the point — Stripe is tried before PayPal only because
|
|
28
|
+
* it was registered first, not because of any ranking between the two.
|
|
19
29
|
*/
|
|
20
30
|
export function createPaymentRegistry(options = {}) {
|
|
21
31
|
const { logger } = options;
|
|
@@ -24,6 +34,7 @@ export function createPaymentRegistry(options = {}) {
|
|
|
24
34
|
...(logger === undefined ? {} : { logger }),
|
|
25
35
|
});
|
|
26
36
|
registry.register(stripePaymentDriver());
|
|
37
|
+
registry.register(paypalPaymentDriver());
|
|
27
38
|
registry.register(manualPaymentDriver());
|
|
28
39
|
return registry;
|
|
29
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/payment/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAoC,MAAM,eAAe,CAAA;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAOjD
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/payment/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAoC,MAAM,eAAe,CAAA;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAOjD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAO,GAA2B,EAAE;IAEpC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAC1B,MAAM,QAAQ,GAAG,oBAAoB,CAAgC;QACnE,IAAI,EAAE,SAAS;QACf,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;KAC5C,CAAC,CAAA;IAEF,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAA;IACxC,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAA;IACxC,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAA;IAExC,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|
package/dist/payment/types.d.ts
CHANGED
|
@@ -119,6 +119,12 @@ export interface PaymentConfig extends DriverChoice {
|
|
|
119
119
|
readonly apiBaseUrl?: string;
|
|
120
120
|
/** Shown to the shopper by the offline driver. Free text, per site. */
|
|
121
121
|
readonly transferInstructions?: string;
|
|
122
|
+
/** PayPal's REST app client id. Paired with `clientSecret` for the OAuth2 client-credentials call. */
|
|
123
|
+
readonly clientId?: string;
|
|
124
|
+
/** PayPal's REST app client secret. Absent on any site that has not configured one. */
|
|
125
|
+
readonly clientSecret?: string;
|
|
126
|
+
/** The webhook id PayPal shows for a configured webhook endpoint, used to verify inbound signatures. */
|
|
127
|
+
readonly webhookId?: string;
|
|
122
128
|
}
|
|
123
129
|
export type PaymentDriver = Driver<PaymentGateway, PaymentConfig>;
|
|
124
130
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/payment/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/payment/types.ts"],"names":[],"mappings":"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"}
|
package/dist/shipping/store.d.ts
CHANGED
|
@@ -3,12 +3,16 @@ import type { TaxZone } from '../tax/store.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* How a stored rate is computed.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Four kinds. The first three are the ones every small shop actually uses; a
|
|
7
|
+
* fifth ("by number of items", "by volume") still waits for a real second
|
|
8
8
|
* user — the project forbids abstracting before three real uses, and this is
|
|
9
9
|
* exactly the sort of table that grows a rule engine nobody asked for.
|
|
10
|
+
* `pickup` (fiche 54 task 1) is not that kind of abstraction: it needs no new
|
|
11
|
+
* field and no new branch in `storedRate` beyond the one line the existing
|
|
12
|
+
* `free` case already has, because "the customer collects it" and "shipping
|
|
13
|
+
* is free" cost the shop the same nothing.
|
|
10
14
|
*/
|
|
11
|
-
export declare const SHIPPING_KINDS: readonly ['flat', 'by_weight', 'free'];
|
|
15
|
+
export declare const SHIPPING_KINDS: readonly ['flat', 'by_weight', 'free', 'pickup'];
|
|
12
16
|
export type ShippingKind = (typeof SHIPPING_KINDS)[number];
|
|
13
17
|
export interface ShippingMethod {
|
|
14
18
|
readonly id: string;
|
|
@@ -69,8 +73,30 @@ export interface CarrierRateProvider {
|
|
|
69
73
|
readonly name: string;
|
|
70
74
|
rate(method: ShippingMethod, basis: ShipmentBasis, zone: TaxZone | null): Promise<number | null>;
|
|
71
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Fiche feedback: a method had no edit path — only create and delete
|
|
78
|
+
* existed, so fixing a typo'd label or rate meant deleting and recreating
|
|
79
|
+
* it, losing its `createdAt` and its `position` among the other methods.
|
|
80
|
+
* Every field absent from the patch is left exactly as saved; `country`/
|
|
81
|
+
* `region`/`freeOverMinor`/`carrier` are tri-state — `null` clears them
|
|
82
|
+
* back to what an absent field already means at `createMethod()` time.
|
|
83
|
+
*/
|
|
84
|
+
export interface UpdateShippingMethodInput {
|
|
85
|
+
readonly label?: string;
|
|
86
|
+
readonly country?: string | null;
|
|
87
|
+
readonly region?: string | null;
|
|
88
|
+
readonly kind?: ShippingKind;
|
|
89
|
+
readonly currency?: string;
|
|
90
|
+
readonly amountMinor?: number;
|
|
91
|
+
readonly perKgMinor?: number;
|
|
92
|
+
readonly freeOverMinor?: number | null;
|
|
93
|
+
readonly carrier?: string | null;
|
|
94
|
+
readonly position?: number;
|
|
95
|
+
readonly active?: boolean;
|
|
96
|
+
}
|
|
72
97
|
export interface ShippingStore {
|
|
73
98
|
createMethod(input: CreateShippingMethodInput): Promise<ShippingMethod>;
|
|
99
|
+
updateMethod(id: string, patch: UpdateShippingMethodInput): Promise<ShippingMethod>;
|
|
74
100
|
deleteMethod(id: string): Promise<void>;
|
|
75
101
|
listMethods(): Promise<readonly ShippingMethod[]>;
|
|
76
102
|
/** The methods that serve this zone, cheapest first. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/shipping/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAIzF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAE9C
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/shipping/store.ts"],"names":[],"mappings":"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"}
|
package/dist/shipping/store.js
CHANGED
|
@@ -5,12 +5,16 @@ import { TABLES } from '../tables.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* How a stored rate is computed.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Four kinds. The first three are the ones every small shop actually uses; a
|
|
9
|
+
* fifth ("by number of items", "by volume") still waits for a real second
|
|
10
10
|
* user — the project forbids abstracting before three real uses, and this is
|
|
11
11
|
* exactly the sort of table that grows a rule engine nobody asked for.
|
|
12
|
+
* `pickup` (fiche 54 task 1) is not that kind of abstraction: it needs no new
|
|
13
|
+
* field and no new branch in `storedRate` beyond the one line the existing
|
|
14
|
+
* `free` case already has, because "the customer collects it" and "shipping
|
|
15
|
+
* is free" cost the shop the same nothing.
|
|
12
16
|
*/
|
|
13
|
-
export const SHIPPING_KINDS = ['flat', 'by_weight', 'free'];
|
|
17
|
+
export const SHIPPING_KINDS = ['flat', 'by_weight', 'free', 'pickup'];
|
|
14
18
|
function decode(row) {
|
|
15
19
|
return {
|
|
16
20
|
id: toText(row.id, 'shipping_method.id'),
|
|
@@ -28,6 +32,13 @@ function decode(row) {
|
|
|
28
32
|
createdAt: toText(row.created_at, 'shipping_method.created_at'),
|
|
29
33
|
};
|
|
30
34
|
}
|
|
35
|
+
function shippingMethodNotFound(id) {
|
|
36
|
+
return new CogentaError({
|
|
37
|
+
code: 'COMMERCE_SHIPPING_METHOD_UNKNOWN',
|
|
38
|
+
message: `No shipping method with id "${id}".`,
|
|
39
|
+
hint: 'Check the id against the admin\'s "Livraison" screen.',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
31
42
|
function serves(method, zone) {
|
|
32
43
|
if (method.country !== null) {
|
|
33
44
|
if (zone === null || method.country !== zone.country)
|
|
@@ -43,7 +54,10 @@ function serves(method, zone) {
|
|
|
43
54
|
export function storedRate(method, basis) {
|
|
44
55
|
if (method.freeOverMinor !== null && basis.subtotalMinor >= method.freeOverMinor)
|
|
45
56
|
return 0;
|
|
46
|
-
|
|
57
|
+
// Nothing ships: the customer collects the order themselves, so there is no
|
|
58
|
+
// carriage to price — the same zero `free` already returns, for the same
|
|
59
|
+
// reason a courier is never involved.
|
|
60
|
+
if (method.kind === 'free' || method.kind === 'pickup')
|
|
47
61
|
return 0;
|
|
48
62
|
if (method.kind === 'flat')
|
|
49
63
|
return method.amountMinor;
|
|
@@ -104,6 +118,56 @@ export function createShippingStore(db, options = {}, now = Date.now) {
|
|
|
104
118
|
}
|
|
105
119
|
return decode(row);
|
|
106
120
|
},
|
|
121
|
+
updateMethod: async (id, patch) => {
|
|
122
|
+
const result = await db.query(sql `select * from ${table} where id = ${id}`);
|
|
123
|
+
const row = result.rows[0];
|
|
124
|
+
if (row === undefined)
|
|
125
|
+
throw shippingMethodNotFound(id);
|
|
126
|
+
const current = decode(row);
|
|
127
|
+
if (patch.kind !== undefined && !SHIPPING_KINDS.includes(patch.kind)) {
|
|
128
|
+
throw new CogentaError({
|
|
129
|
+
code: 'COMMERCE_SHIPPING_METHOD_UNKNOWN',
|
|
130
|
+
message: `"${patch.kind}" is not a shipping kind.`,
|
|
131
|
+
hint: `Use one of: ${SHIPPING_KINDS.join(', ')}.`,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const label = patch.label ?? current.label;
|
|
135
|
+
const country = patch.country === undefined ? current.country : (patch.country?.toUpperCase() ?? null);
|
|
136
|
+
const region = patch.region === undefined ? current.region : (patch.region ?? null);
|
|
137
|
+
const kind = patch.kind ?? current.kind;
|
|
138
|
+
const currency = patch.currency === undefined ? current.currency : assertCurrency(patch.currency);
|
|
139
|
+
const amountMinor = patch.amountMinor === undefined
|
|
140
|
+
? current.amountMinor
|
|
141
|
+
: assertMinor(patch.amountMinor, 'A shipping amount');
|
|
142
|
+
const perKgMinor = patch.perKgMinor === undefined
|
|
143
|
+
? current.perKgMinor
|
|
144
|
+
: assertMinor(patch.perKgMinor, 'A per-kilogram shipping amount');
|
|
145
|
+
const freeOverMinor = patch.freeOverMinor === undefined ? current.freeOverMinor : (patch.freeOverMinor ?? null);
|
|
146
|
+
const carrier = patch.carrier === undefined ? current.carrier : (patch.carrier ?? null);
|
|
147
|
+
const position = patch.position ?? current.position;
|
|
148
|
+
const active = patch.active ?? current.active;
|
|
149
|
+
await db.query(sql `
|
|
150
|
+
update ${table}
|
|
151
|
+
set label = ${label}, country = ${country}, region = ${region}, kind = ${kind},
|
|
152
|
+
currency = ${currency}, amount_minor = ${amountMinor}, per_kg_minor = ${perKgMinor},
|
|
153
|
+
free_over_minor = ${freeOverMinor}, carrier = ${carrier}, position = ${position},
|
|
154
|
+
active = ${fromBool(active, d)}
|
|
155
|
+
where id = ${id}`);
|
|
156
|
+
return {
|
|
157
|
+
...current,
|
|
158
|
+
label,
|
|
159
|
+
country,
|
|
160
|
+
region,
|
|
161
|
+
kind,
|
|
162
|
+
currency,
|
|
163
|
+
amountMinor,
|
|
164
|
+
perKgMinor,
|
|
165
|
+
freeOverMinor,
|
|
166
|
+
carrier,
|
|
167
|
+
position,
|
|
168
|
+
active,
|
|
169
|
+
};
|
|
170
|
+
},
|
|
107
171
|
deleteMethod: async (id) => {
|
|
108
172
|
await db.query(sql `delete from ${table} where id = ${id}`);
|
|
109
173
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/shipping/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC3F,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAGrC
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/shipping/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EmailTransport } from '@cogenta/channels';
|
|
2
|
+
import type { RenewalNotifier } from './store.js';
|
|
3
|
+
/**
|
|
4
|
+
* A ready-made renewal notifier built on `@cogenta/channels`'s own email
|
|
5
|
+
* primitives (fiche 53 task 5: "réutilise @cogenta/channels") — never a
|
|
6
|
+
* second email renderer inside this package.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately optional and separate from `subscription/store.ts`:
|
|
9
|
+
* `SubscriptionStoreDependencies.notifyRenewal` only ever needs the narrow
|
|
10
|
+
* `RenewalNotifier` function type, so a site that never configures email (or
|
|
11
|
+
* `@cogenta/channels` at all) simply never imports this file, and
|
|
12
|
+
* `sendRenewalNotices` stays a safe no-op (R2). A site that *does* have
|
|
13
|
+
* `@cogenta/channels` wired already owns an `EmailTransport` — the degraded
|
|
14
|
+
* `FileEmailTransport`, or a real one — and passes it here as-is.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createEmailRenewalNotifier(transport: EmailTransport): RenewalNotifier;
|
|
17
|
+
//# sourceMappingURL=renewal-notifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renewal-notifier.d.ts","sourceRoot":"","sources":["../../src/subscription/renewal-notifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGvD,OAAO,KAAK,EAAsB,eAAe,EAAE,MAAM,YAAY,CAAA;AAErE;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,cAAc,GAAG,eAAe,CAqBrF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { buildNotification, renderEmailMessage } from '@cogenta/channels';
|
|
2
|
+
import { formatMoney } from '../money.js';
|
|
3
|
+
/**
|
|
4
|
+
* A ready-made renewal notifier built on `@cogenta/channels`'s own email
|
|
5
|
+
* primitives (fiche 53 task 5: "réutilise @cogenta/channels") — never a
|
|
6
|
+
* second email renderer inside this package.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately optional and separate from `subscription/store.ts`:
|
|
9
|
+
* `SubscriptionStoreDependencies.notifyRenewal` only ever needs the narrow
|
|
10
|
+
* `RenewalNotifier` function type, so a site that never configures email (or
|
|
11
|
+
* `@cogenta/channels` at all) simply never imports this file, and
|
|
12
|
+
* `sendRenewalNotices` stays a safe no-op (R2). A site that *does* have
|
|
13
|
+
* `@cogenta/channels` wired already owns an `EmailTransport` — the degraded
|
|
14
|
+
* `FileEmailTransport`, or a real one — and passes it here as-is.
|
|
15
|
+
*/
|
|
16
|
+
export function createEmailRenewalNotifier(transport) {
|
|
17
|
+
return async ({ subscription, customer, daysUntilRenewal, }) => {
|
|
18
|
+
const amount = formatMoney({
|
|
19
|
+
amountMinor: subscription.priceMinor * subscription.quantity,
|
|
20
|
+
currency: subscription.currency,
|
|
21
|
+
});
|
|
22
|
+
const when = daysUntilRenewal <= 0 ? 'today' : `in ${String(daysUntilRenewal)} day(s)`;
|
|
23
|
+
const message = buildNotification(`Your subscription renews ${when} for ${amount}.`);
|
|
24
|
+
const rendered = renderEmailMessage(message);
|
|
25
|
+
await transport.send({
|
|
26
|
+
to: customer.email,
|
|
27
|
+
subject: rendered.subject,
|
|
28
|
+
text: rendered.text,
|
|
29
|
+
html: rendered.html,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=renewal-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renewal-notifier.js","sourceRoot":"","sources":["../../src/subscription/renewal-notifier.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAGzC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAyB;IAClE,OAAO,KAAK,EAAE,EACZ,YAAY,EACZ,QAAQ,EACR,gBAAgB,GACG,EAAiB,EAAE;QACtC,MAAM,MAAM,GAAG,WAAW,CAAC;YACzB,WAAW,EAAE,YAAY,CAAC,UAAU,GAAG,YAAY,CAAC,QAAQ;YAC5D,QAAQ,EAAE,YAAY,CAAC,QAAQ;SAChC,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,gBAAgB,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAA;QACtF,MAAM,OAAO,GAAG,iBAAiB,CAAC,4BAA4B,IAAI,QAAQ,MAAM,GAAG,CAAC,CAAA;QACpF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;QAE5C,MAAM,SAAS,CAAC,IAAI,CAAC;YACnB,EAAE,EAAE,QAAQ,CAAC,KAAK;YAClB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
2
|
import type { CatalogStore } from '../catalog/store.js';
|
|
3
|
-
import type { CustomerStore } from '../customer/store.js';
|
|
3
|
+
import type { Customer, CustomerStore } from '../customer/store.js';
|
|
4
4
|
import { type OrderStore } from '../order/store.js';
|
|
5
5
|
import type { PaymentStore } from '../payment/store.js';
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* `past_due` (fiche 53 task 3) sits between `active` and `paused`: a
|
|
8
|
+
* subscription lands there the instant a renewal's payment fails, and
|
|
9
|
+
* `runBilling`'s own due-query (`status = 'active'`) then skips it for every
|
|
10
|
+
* subsequent period until the dunning cycle resolves — never suspended on the
|
|
11
|
+
* first failure, and never billed again mid-retry either.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SUBSCRIPTION_STATUSES: readonly ['active', 'past_due', 'paused', 'cancelled'];
|
|
7
14
|
export type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];
|
|
15
|
+
/**
|
|
16
|
+
* The default dunning calendar (fiche 53 § "décision à trancher"): retry a
|
|
17
|
+
* failed renewal payment 1, 3 and 7 days after the first failure, then
|
|
18
|
+
* suspend. Proposed by the fiche itself and used as-is — documented here
|
|
19
|
+
* rather than re-litigated, and configurable per store via
|
|
20
|
+
* `SubscriptionStoreOptions.dunningScheduleDays`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_DUNNING_SCHEDULE_DAYS: readonly [1, 3, 7];
|
|
23
|
+
/** How long a subscriber is warned before a renewal — fiche 53 task 5, configurable per store. */
|
|
24
|
+
export declare const DEFAULT_RENEWAL_NOTICE_DAYS = 3;
|
|
8
25
|
export declare const INTERVAL_UNITS: readonly ['day', 'week', 'month', 'year'];
|
|
9
26
|
export type IntervalUnit = (typeof INTERVAL_UNITS)[number];
|
|
10
27
|
export interface Subscription {
|
|
@@ -63,11 +80,87 @@ export interface BillingRunResult {
|
|
|
63
80
|
readonly reason: string;
|
|
64
81
|
}[];
|
|
65
82
|
}
|
|
83
|
+
/** The open dunning cycle for a subscription whose last renewal payment failed. `null` from `dunning()` means there is none. */
|
|
84
|
+
export interface DunningState {
|
|
85
|
+
readonly subscriptionId: string;
|
|
86
|
+
readonly orderId: string;
|
|
87
|
+
/** The billing period this cycle is trying to collect for — `billOne`'s own `periodKey`, never a second one. */
|
|
88
|
+
readonly periodKey: string;
|
|
89
|
+
readonly failureCount: number;
|
|
90
|
+
readonly firstFailedAt: string;
|
|
91
|
+
/** Null while a retry is in flight, or once the schedule is exhausted (see `suspendedAt`). */
|
|
92
|
+
readonly nextRetryAt: string | null;
|
|
93
|
+
readonly lastReason: string | null;
|
|
94
|
+
/** Set once the schedule is exhausted and the subscription was auto-paused. Null while still retrying. */
|
|
95
|
+
readonly suspendedAt: string | null;
|
|
96
|
+
readonly createdAt: string;
|
|
97
|
+
readonly updatedAt: string;
|
|
98
|
+
}
|
|
99
|
+
/** What one dunning run did — three disjoint outcomes, never inferred from the others. */
|
|
100
|
+
export interface DunningRunResult {
|
|
101
|
+
readonly recovered: readonly {
|
|
102
|
+
readonly subscriptionId: string;
|
|
103
|
+
readonly orderId: string;
|
|
104
|
+
}[];
|
|
105
|
+
readonly retried: readonly {
|
|
106
|
+
readonly subscriptionId: string;
|
|
107
|
+
readonly reason: string;
|
|
108
|
+
}[];
|
|
109
|
+
readonly suspended: readonly {
|
|
110
|
+
readonly subscriptionId: string;
|
|
111
|
+
readonly reason: string;
|
|
112
|
+
}[];
|
|
113
|
+
}
|
|
114
|
+
export interface ChangePlanOptions {
|
|
115
|
+
readonly quantity?: number;
|
|
116
|
+
/** Default `true`. A downgrade never produces a credit — this store has no credit-note mechanism — so a negative proration is reported, not applied. */
|
|
117
|
+
readonly prorate?: boolean;
|
|
118
|
+
}
|
|
119
|
+
export interface ChangePlanResult {
|
|
120
|
+
readonly subscription: Subscription;
|
|
121
|
+
/** Positive: charged immediately via `prorationOrderId`. Zero: nothing was due. Negative: a downgrade's credit that this store cannot issue — surfaced, not silently dropped. */
|
|
122
|
+
readonly prorationMinor: number;
|
|
123
|
+
readonly prorationOrderId: string | null;
|
|
124
|
+
}
|
|
125
|
+
/** What a renewal-notice sender needs to know (fiche 53 task 5). Built on `@cogenta/channels` in `subscription/renewal-notifier.ts` — never required, see `SubscriptionStoreDependencies.notifyRenewal`. */
|
|
126
|
+
export interface RenewalNoticeInput {
|
|
127
|
+
readonly subscription: Subscription;
|
|
128
|
+
readonly customer: Customer;
|
|
129
|
+
readonly daysUntilRenewal: number;
|
|
130
|
+
}
|
|
131
|
+
export type RenewalNotifier = (input: RenewalNoticeInput) => Promise<void>;
|
|
132
|
+
export interface SendRenewalNoticesResult {
|
|
133
|
+
readonly notified: readonly {
|
|
134
|
+
readonly subscriptionId: string;
|
|
135
|
+
}[];
|
|
136
|
+
}
|
|
137
|
+
/** Aggregate figures for the subscriptions screen (fiche 53 task 6) — read-only, no new data. */
|
|
138
|
+
export interface SubscriptionMetrics {
|
|
139
|
+
readonly active: number;
|
|
140
|
+
readonly pastDue: number;
|
|
141
|
+
readonly paused: number;
|
|
142
|
+
readonly cancelled: number;
|
|
143
|
+
/** Monthly recurring revenue: every active/past-due subscription's price normalised to a monthly cadence, grouped by currency. */
|
|
144
|
+
readonly mrrMinor: readonly {
|
|
145
|
+
readonly currency: string;
|
|
146
|
+
readonly amountMinor: number;
|
|
147
|
+
}[];
|
|
148
|
+
/** Cancelled ÷ (active + past_due + paused + cancelled), or 0 with no subscriptions at all. */
|
|
149
|
+
readonly churnRate: number;
|
|
150
|
+
}
|
|
66
151
|
export interface SubscriptionStoreDependencies {
|
|
67
152
|
readonly catalog: CatalogStore;
|
|
68
153
|
readonly customers: CustomerStore;
|
|
69
154
|
readonly orders: OrderStore;
|
|
70
155
|
readonly payments: PaymentStore;
|
|
156
|
+
/** Sends the renewal notice built by `sendRenewalNotices` (fiche 53 task 5). Absent means the method is a safe no-op (R2) — a site with no channel configured just never calls `createEmailRenewalNotifier`. */
|
|
157
|
+
readonly notifyRenewal?: RenewalNotifier;
|
|
158
|
+
}
|
|
159
|
+
export interface SubscriptionStoreOptions {
|
|
160
|
+
/** Days after the first failed renewal payment to retry: fiche 53's own default, `DEFAULT_DUNNING_SCHEDULE_DAYS`. One retry per entry, in order; suspension follows the last one. */
|
|
161
|
+
readonly dunningScheduleDays?: readonly number[];
|
|
162
|
+
/** Days before `nextBillingAt` a renewal notice is due. `DEFAULT_RENEWAL_NOTICE_DAYS` by default. */
|
|
163
|
+
readonly renewalNoticeDays?: number;
|
|
71
164
|
}
|
|
72
165
|
export interface SubscriptionStore {
|
|
73
166
|
create(input: CreateSubscriptionInput): Promise<Subscription>;
|
|
@@ -90,6 +183,36 @@ export interface SubscriptionStore {
|
|
|
90
183
|
runBilling(options?: {
|
|
91
184
|
readonly limit?: number;
|
|
92
185
|
}): Promise<BillingRunResult>;
|
|
186
|
+
/** The open dunning cycle for a subscription, or `null` if its last renewal was collected (or it was never in dunning). */
|
|
187
|
+
dunning(id: string): Promise<DunningState | null>;
|
|
188
|
+
/**
|
|
189
|
+
* Retries every renewal payment whose next scheduled attempt has arrived.
|
|
190
|
+
*
|
|
191
|
+
* Same idempotence discipline as `runBilling`: a rerun before the next
|
|
192
|
+
* retry is due finds nothing (the row's own `next_retry_at` already moved
|
|
193
|
+
* past `now`), and a genuinely concurrent rerun loses a compare-and-set on
|
|
194
|
+
* that same field — either way, a replay never doubles a retry or a charge.
|
|
195
|
+
*/
|
|
196
|
+
runDunning(options?: {
|
|
197
|
+
readonly limit?: number;
|
|
198
|
+
}): Promise<DunningRunResult>;
|
|
199
|
+
/**
|
|
200
|
+
* Switches a subscription to a different variant (and optionally quantity)
|
|
201
|
+
* effective immediately, with an explicit, one-off prorated charge for the
|
|
202
|
+
* remainder of the current period (fiche 53 task 4). Disallowed once
|
|
203
|
+
* cancelled — there is no plan left to change.
|
|
204
|
+
*/
|
|
205
|
+
changePlan(id: string, newVariantId: string, options?: ChangePlanOptions): Promise<ChangePlanResult>;
|
|
206
|
+
/**
|
|
207
|
+
* Sends a renewal reminder for every active subscription whose next charge
|
|
208
|
+
* falls within `renewalNoticeDays` — at most once per billing period
|
|
209
|
+
* (fiche 53 task 5). A safe no-op with no `notifyRenewal` dependency (R2).
|
|
210
|
+
*/
|
|
211
|
+
sendRenewalNotices(options?: {
|
|
212
|
+
readonly limit?: number;
|
|
213
|
+
}): Promise<SendRenewalNoticesResult>;
|
|
214
|
+
/** Aggregate figures for the subscriptions screen (fiche 53 task 6). */
|
|
215
|
+
metrics(): Promise<SubscriptionMetrics>;
|
|
93
216
|
}
|
|
94
217
|
/**
|
|
95
218
|
* The end of a billing period.
|
|
@@ -102,5 +225,5 @@ export interface SubscriptionStore {
|
|
|
102
225
|
* end of the calendar.
|
|
103
226
|
*/
|
|
104
227
|
export declare function advancePeriod(from: string, unit: IntervalUnit, count: number): string;
|
|
105
|
-
export declare function createSubscriptionStore(db: DatabaseHandle, dependencies: SubscriptionStoreDependencies, now?: () => number): SubscriptionStore;
|
|
228
|
+
export declare function createSubscriptionStore(db: DatabaseHandle, dependencies: SubscriptionStoreDependencies, now?: () => number, options?: SubscriptionStoreOptions): SubscriptionStore;
|
|
106
229
|
//# sourceMappingURL=store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/subscription/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/subscription/store.ts"],"names":[],"mappings":"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"}
|