@cogenta/commerce 0.2.0

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.
Files changed (98) hide show
  1. package/LICENSE +373 -0
  2. package/dist/admin/permissions.d.ts +57 -0
  3. package/dist/admin/permissions.d.ts.map +1 -0
  4. package/dist/admin/permissions.js +87 -0
  5. package/dist/admin/permissions.js.map +1 -0
  6. package/dist/admin/router.d.ts +53 -0
  7. package/dist/admin/router.d.ts.map +1 -0
  8. package/dist/admin/router.js +476 -0
  9. package/dist/admin/router.js.map +1 -0
  10. package/dist/cart/store.d.ts +91 -0
  11. package/dist/cart/store.d.ts.map +1 -0
  12. package/dist/cart/store.js +342 -0
  13. package/dist/cart/store.js.map +1 -0
  14. package/dist/cart/totals.d.ts +73 -0
  15. package/dist/cart/totals.d.ts.map +1 -0
  16. package/dist/cart/totals.js +65 -0
  17. package/dist/cart/totals.js.map +1 -0
  18. package/dist/catalog/store.d.ts +53 -0
  19. package/dist/catalog/store.d.ts.map +1 -0
  20. package/dist/catalog/store.js +358 -0
  21. package/dist/catalog/store.js.map +1 -0
  22. package/dist/catalog/types.d.ts +95 -0
  23. package/dist/catalog/types.d.ts.map +1 -0
  24. package/dist/catalog/types.js +5 -0
  25. package/dist/catalog/types.js.map +1 -0
  26. package/dist/coupon/store.d.ts +103 -0
  27. package/dist/coupon/store.d.ts.map +1 -0
  28. package/dist/coupon/store.js +181 -0
  29. package/dist/coupon/store.js.map +1 -0
  30. package/dist/customer/store.d.ts +41 -0
  31. package/dist/customer/store.d.ts.map +1 -0
  32. package/dist/customer/store.js +103 -0
  33. package/dist/customer/store.js.map +1 -0
  34. package/dist/index.d.ts +42 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +23 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/invoice/pdf.d.ts +67 -0
  39. package/dist/invoice/pdf.d.ts.map +1 -0
  40. package/dist/invoice/pdf.js +449 -0
  41. package/dist/invoice/pdf.js.map +1 -0
  42. package/dist/invoice/store.d.ts +96 -0
  43. package/dist/invoice/store.d.ts.map +1 -0
  44. package/dist/invoice/store.js +221 -0
  45. package/dist/invoice/store.js.map +1 -0
  46. package/dist/money.d.ts +55 -0
  47. package/dist/money.d.ts.map +1 -0
  48. package/dist/money.js +146 -0
  49. package/dist/money.js.map +1 -0
  50. package/dist/order/store.d.ts +75 -0
  51. package/dist/order/store.d.ts.map +1 -0
  52. package/dist/order/store.js +301 -0
  53. package/dist/order/store.js.map +1 -0
  54. package/dist/order/types.d.ts +67 -0
  55. package/dist/order/types.d.ts.map +1 -0
  56. package/dist/order/types.js +71 -0
  57. package/dist/order/types.js.map +1 -0
  58. package/dist/payment/manual.d.ts +27 -0
  59. package/dist/payment/manual.d.ts.map +1 -0
  60. package/dist/payment/manual.js +105 -0
  61. package/dist/payment/manual.js.map +1 -0
  62. package/dist/payment/registry.d.ts +23 -0
  63. package/dist/payment/registry.d.ts.map +1 -0
  64. package/dist/payment/registry.js +30 -0
  65. package/dist/payment/registry.js.map +1 -0
  66. package/dist/payment/store.d.ts +38 -0
  67. package/dist/payment/store.d.ts.map +1 -0
  68. package/dist/payment/store.js +253 -0
  69. package/dist/payment/store.js.map +1 -0
  70. package/dist/payment/stripe.d.ts +10 -0
  71. package/dist/payment/stripe.d.ts.map +1 -0
  72. package/dist/payment/stripe.js +419 -0
  73. package/dist/payment/stripe.js.map +1 -0
  74. package/dist/payment/types.d.ts +124 -0
  75. package/dist/payment/types.d.ts.map +1 -0
  76. package/dist/payment/types.js +18 -0
  77. package/dist/payment/types.js.map +1 -0
  78. package/dist/rows.d.ts +26 -0
  79. package/dist/rows.d.ts.map +1 -0
  80. package/dist/rows.js +87 -0
  81. package/dist/rows.js.map +1 -0
  82. package/dist/shipping/store.d.ts +88 -0
  83. package/dist/shipping/store.d.ts.map +1 -0
  84. package/dist/shipping/store.js +165 -0
  85. package/dist/shipping/store.js.map +1 -0
  86. package/dist/subscription/store.d.ts +106 -0
  87. package/dist/subscription/store.d.ts.map +1 -0
  88. package/dist/subscription/store.js +310 -0
  89. package/dist/subscription/store.js.map +1 -0
  90. package/dist/tables.d.ts +41 -0
  91. package/dist/tables.d.ts.map +1 -0
  92. package/dist/tables.js +383 -0
  93. package/dist/tables.js.map +1 -0
  94. package/dist/tax/store.d.ts +68 -0
  95. package/dist/tax/store.d.ts.map +1 -0
  96. package/dist/tax/store.js +126 -0
  97. package/dist/tax/store.js.map +1 -0
  98. package/package.json +42 -0
package/dist/rows.js ADDED
@@ -0,0 +1,87 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ /**
3
+ * Reading numbers back out of three databases that disagree about them.
4
+ *
5
+ * `bigint` columns do not come back as JavaScript numbers everywhere: `pg`
6
+ * hands back `int8` as a **string** (deliberately — it refuses to lose
7
+ * precision silently), `mysql2` may hand back a `bigint`, and `node:sqlite`
8
+ * gives a plain number. Reading `row.total_minor` and trusting it is therefore
9
+ * a bug that only appears on Postgres, which is exactly the kind of thing this
10
+ * project tests three dialects to catch.
11
+ *
12
+ * Every integer read in this package goes through here.
13
+ */
14
+ export function toInt(value, what) {
15
+ if (typeof value === 'number') {
16
+ if (!Number.isFinite(value)) {
17
+ throw corrupt(what, value);
18
+ }
19
+ return Math.trunc(value);
20
+ }
21
+ if (typeof value === 'bigint') {
22
+ if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER)) {
23
+ throw corrupt(what, value);
24
+ }
25
+ return Number(value);
26
+ }
27
+ if (typeof value === 'string' && value.trim() !== '') {
28
+ const parsed = Number(value);
29
+ if (Number.isFinite(parsed))
30
+ return Math.trunc(parsed);
31
+ }
32
+ throw corrupt(what, value);
33
+ }
34
+ function corrupt(what, value) {
35
+ return new CogentaError({
36
+ code: 'COMMERCE_AMOUNT_INVALID',
37
+ message: `The stored value of ${what} is not a usable integer.`,
38
+ hint: 'This row was not written by this package, or the column type was changed by hand.',
39
+ details: { column: what, type: typeof value },
40
+ });
41
+ }
42
+ /**
43
+ * Booleans, likewise.
44
+ *
45
+ * Postgres has a real `boolean`, MySQL stores `tinyint`, and SQLite stores
46
+ * whatever it was handed. `0`, `'0'`, `false` and `'false'` all mean false.
47
+ */
48
+ export function toBool(value) {
49
+ if (typeof value === 'boolean')
50
+ return value;
51
+ if (typeof value === 'number')
52
+ return value !== 0;
53
+ if (typeof value === 'bigint')
54
+ return value !== 0n;
55
+ if (typeof value === 'string')
56
+ return value !== '' && value !== '0' && value !== 'false';
57
+ return false;
58
+ }
59
+ /** The value a boolean is written as. Postgres wants a real boolean. */
60
+ export function fromBool(value, dialect) {
61
+ return dialect === 'postgres' ? value : value ? 1 : 0;
62
+ }
63
+ export function toText(value, what) {
64
+ if (typeof value === 'string')
65
+ return value;
66
+ if (typeof value === 'number' || typeof value === 'bigint')
67
+ return String(value);
68
+ throw new CogentaError({
69
+ code: 'INTERNAL',
70
+ message: `The stored value of ${what} is not text.`,
71
+ hint: 'This row was not written by this package.',
72
+ details: { column: what, type: typeof value },
73
+ });
74
+ }
75
+ export function toNullableText(value) {
76
+ if (value === null || value === undefined || value === '')
77
+ return null;
78
+ if (typeof value === 'string')
79
+ return value;
80
+ return String(value);
81
+ }
82
+ export function toNullableInt(value, what) {
83
+ if (value === null || value === undefined)
84
+ return null;
85
+ return toInt(value, what);
86
+ }
87
+ //# sourceMappingURL=rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../src/rows.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ import type { TaxZone } from '../tax/store.js';
3
+ /**
4
+ * How a stored rate is computed.
5
+ *
6
+ * Three kinds, chosen because they are the three every small shop actually
7
+ * uses. A fourth ("by number of items", "by volume") waits for a real second
8
+ * user — the project forbids abstracting before three real uses, and this is
9
+ * exactly the sort of table that grows a rule engine nobody asked for.
10
+ */
11
+ export declare const SHIPPING_KINDS: readonly ['flat', 'by_weight', 'free'];
12
+ export type ShippingKind = (typeof SHIPPING_KINDS)[number];
13
+ export interface ShippingMethod {
14
+ readonly id: string;
15
+ readonly label: string;
16
+ readonly country: string | null;
17
+ readonly region: string | null;
18
+ readonly kind: ShippingKind;
19
+ readonly currency: string;
20
+ /** Flat part, in minor units. The base of a by-weight rate too. */
21
+ readonly amountMinor: number;
22
+ /** Added per kilogram, rounded up to the next whole kilogram. */
23
+ readonly perKgMinor: number;
24
+ /** Above this order subtotal, shipping is free. Null to never do that. */
25
+ readonly freeOverMinor: number | null;
26
+ /** Names a registered carrier driver, or null for a plain stored rate. */
27
+ readonly carrier: string | null;
28
+ readonly position: number;
29
+ readonly active: boolean;
30
+ readonly createdAt: string;
31
+ }
32
+ export interface CreateShippingMethodInput {
33
+ readonly label: string;
34
+ readonly country?: string | null;
35
+ readonly region?: string | null;
36
+ readonly kind?: ShippingKind;
37
+ readonly currency: string;
38
+ readonly amountMinor?: number;
39
+ readonly perKgMinor?: number;
40
+ readonly freeOverMinor?: number | null;
41
+ readonly carrier?: string | null;
42
+ readonly position?: number;
43
+ readonly active?: boolean;
44
+ }
45
+ /** What a shipment weighs and costs, before shipping is worked out. */
46
+ export interface ShipmentBasis {
47
+ readonly weightGrams: number;
48
+ readonly subtotalMinor: number;
49
+ readonly currency: string;
50
+ }
51
+ export interface ShippingQuote {
52
+ readonly methodId: string;
53
+ readonly label: string;
54
+ readonly amountMinor: number;
55
+ readonly currency: string;
56
+ readonly carrier: string | null;
57
+ }
58
+ /**
59
+ * A real-time rate from a carrier's API.
60
+ *
61
+ * Optional by construction (R1): a shop with no carrier account gets stored
62
+ * rates, which is a complete way to ship. When a carrier driver *is*
63
+ * registered and its call fails, `quote()` falls back to the method's stored
64
+ * rate rather than refusing the order — a checkout that dies because a
65
+ * courier's API is down is a worse outcome than a slightly wrong postage
66
+ * estimate, and the stored rate is the operator's own number.
67
+ */
68
+ export interface CarrierRateProvider {
69
+ readonly name: string;
70
+ rate(method: ShippingMethod, basis: ShipmentBasis, zone: TaxZone | null): Promise<number | null>;
71
+ }
72
+ export interface ShippingStore {
73
+ createMethod(input: CreateShippingMethodInput): Promise<ShippingMethod>;
74
+ deleteMethod(id: string): Promise<void>;
75
+ listMethods(): Promise<readonly ShippingMethod[]>;
76
+ /** The methods that serve this zone, cheapest first. */
77
+ available(zone: TaxZone | null, basis: ShipmentBasis): Promise<readonly ShippingQuote[]>;
78
+ /** One method by id, priced for this shipment. */
79
+ quote(methodId: string, zone: TaxZone | null, basis: ShipmentBasis): Promise<ShippingQuote>;
80
+ }
81
+ /** The stored rate. Never negative, and free over a threshold when set. */
82
+ export declare function storedRate(method: ShippingMethod, basis: ShipmentBasis): number;
83
+ export interface ShippingStoreOptions {
84
+ /** Registered carrier drivers, by name. Empty is the normal case. */
85
+ readonly carriers?: readonly CarrierRateProvider[];
86
+ }
87
+ export declare function createShippingStore(db: DatabaseHandle, options?: ShippingStoreOptions, now?: () => number): ShippingStore;
88
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/shipping/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,165 @@
1
+ import { CogentaError, identifier, newId, sql } from '@cogenta/core';
2
+ import { assertCurrency, assertMinor } from '../money.js';
3
+ import { fromBool, toBool, toInt, toNullableInt, toNullableText, toText } from '../rows.js';
4
+ import { TABLES } from '../tables.js';
5
+ /**
6
+ * How a stored rate is computed.
7
+ *
8
+ * Three kinds, chosen because they are the three every small shop actually
9
+ * uses. A fourth ("by number of items", "by volume") waits for a real second
10
+ * user — the project forbids abstracting before three real uses, and this is
11
+ * exactly the sort of table that grows a rule engine nobody asked for.
12
+ */
13
+ export const SHIPPING_KINDS = ['flat', 'by_weight', 'free'];
14
+ function decode(row) {
15
+ return {
16
+ id: toText(row.id, 'shipping_method.id'),
17
+ label: toText(row.label, 'shipping_method.label'),
18
+ country: toNullableText(row.country),
19
+ region: toNullableText(row.region),
20
+ kind: toText(row.kind, 'shipping_method.kind'),
21
+ currency: toText(row.currency, 'shipping_method.currency'),
22
+ amountMinor: toInt(row.amount_minor, 'shipping_method.amount_minor'),
23
+ perKgMinor: toInt(row.per_kg_minor, 'shipping_method.per_kg_minor'),
24
+ freeOverMinor: toNullableInt(row.free_over_minor, 'shipping_method.free_over_minor'),
25
+ carrier: toNullableText(row.carrier),
26
+ position: toInt(row.position, 'shipping_method.position'),
27
+ active: toBool(row.active),
28
+ createdAt: toText(row.created_at, 'shipping_method.created_at'),
29
+ };
30
+ }
31
+ function serves(method, zone) {
32
+ if (method.country !== null) {
33
+ if (zone === null || method.country !== zone.country)
34
+ return false;
35
+ }
36
+ if (method.region !== null) {
37
+ if (zone?.region == null || method.region !== zone.region)
38
+ return false;
39
+ }
40
+ return true;
41
+ }
42
+ /** The stored rate. Never negative, and free over a threshold when set. */
43
+ export function storedRate(method, basis) {
44
+ if (method.freeOverMinor !== null && basis.subtotalMinor >= method.freeOverMinor)
45
+ return 0;
46
+ if (method.kind === 'free')
47
+ return 0;
48
+ if (method.kind === 'flat')
49
+ return method.amountMinor;
50
+ // Rounded up to the next whole kilogram, the way a carrier's price list
51
+ // does: 1.1 kg is charged as two, never as 1.1.
52
+ const kilograms = Math.ceil(Math.max(0, basis.weightGrams) / 1000);
53
+ return method.amountMinor + method.perKgMinor * kilograms;
54
+ }
55
+ export function createShippingStore(db, options = {}, now = Date.now) {
56
+ const d = db.dialect;
57
+ const table = identifier(TABLES.shippingMethods, d);
58
+ const carriers = new Map((options.carriers ?? []).map((carrier) => [carrier.name, carrier]));
59
+ async function priceOf(method, zone, basis) {
60
+ const fallback = storedRate(method, basis);
61
+ if (method.carrier === null)
62
+ return fallback;
63
+ const carrier = carriers.get(method.carrier);
64
+ // A method naming a carrier that is not registered is not an error: it is
65
+ // a site that turned the integration off. It ships at the stored rate.
66
+ if (carrier === undefined)
67
+ return fallback;
68
+ try {
69
+ const live = await carrier.rate(method, basis, zone);
70
+ if (live === null)
71
+ return fallback;
72
+ return assertMinor(live, 'A carrier rate');
73
+ }
74
+ catch {
75
+ // See CarrierRateProvider: a courier's outage must not close the till.
76
+ return fallback;
77
+ }
78
+ }
79
+ async function allMethods() {
80
+ const result = await db.query(sql `select * from ${table} order by position asc, created_at asc`);
81
+ return result.rows.map(decode);
82
+ }
83
+ return {
84
+ createMethod: async (input) => {
85
+ const id = newId(now);
86
+ await db.query(sql `
87
+ insert into ${table} (id, label, country, region, kind, currency, amount_minor,
88
+ per_kg_minor, free_over_minor, carrier, position, active, created_at)
89
+ values (${id}, ${input.label}, ${input.country?.toUpperCase() ?? null}, ${input.region ?? null},
90
+ ${input.kind ?? 'flat'}, ${assertCurrency(input.currency)},
91
+ ${assertMinor(input.amountMinor ?? 0, 'A shipping amount')},
92
+ ${assertMinor(input.perKgMinor ?? 0, 'A per-kilogram shipping amount')},
93
+ ${input.freeOverMinor ?? null}, ${input.carrier ?? null},
94
+ ${input.position ?? 0}, ${fromBool(input.active ?? true, d)},
95
+ ${new Date(now()).toISOString()})`);
96
+ const result = await db.query(sql `select * from ${table} where id = ${id}`);
97
+ const row = result.rows[0];
98
+ if (row === undefined) {
99
+ throw new CogentaError({
100
+ code: 'COMMERCE_SHIPPING_METHOD_UNKNOWN',
101
+ message: 'The shipping method was not stored.',
102
+ hint: 'Check that the commerce tables exist (ensureCommerceTables).',
103
+ });
104
+ }
105
+ return decode(row);
106
+ },
107
+ deleteMethod: async (id) => {
108
+ await db.query(sql `delete from ${table} where id = ${id}`);
109
+ },
110
+ listMethods: allMethods,
111
+ available: async (zone, basis) => {
112
+ const quotes = [];
113
+ for (const method of await allMethods()) {
114
+ if (!method.active)
115
+ continue;
116
+ if (!serves(method, zone))
117
+ continue;
118
+ if (method.currency !== basis.currency)
119
+ continue;
120
+ quotes.push({
121
+ methodId: method.id,
122
+ label: method.label,
123
+ amountMinor: await priceOf(method, zone, basis),
124
+ currency: method.currency,
125
+ carrier: method.carrier,
126
+ });
127
+ }
128
+ return quotes.sort((left, right) => left.amountMinor - right.amountMinor || left.label.localeCompare(right.label));
129
+ },
130
+ quote: async (methodId, zone, basis) => {
131
+ const result = await db.query(sql `select * from ${table} where id = ${methodId}`);
132
+ const row = result.rows[0];
133
+ if (row === undefined) {
134
+ throw new CogentaError({
135
+ code: 'COMMERCE_SHIPPING_METHOD_UNKNOWN',
136
+ message: 'This shipping method does not exist.',
137
+ hint: 'Choose one of the methods offered for the delivery address.',
138
+ });
139
+ }
140
+ const method = decode(row);
141
+ if (!method.active || !serves(method, zone)) {
142
+ throw new CogentaError({
143
+ code: 'COMMERCE_SHIPPING_UNAVAILABLE',
144
+ message: `"${method.label}" does not ship to this address.`,
145
+ hint: 'Choose another delivery method, or change the delivery country.',
146
+ });
147
+ }
148
+ if (method.currency !== basis.currency) {
149
+ throw new CogentaError({
150
+ code: 'COMMERCE_CURRENCY_MISMATCH',
151
+ message: `"${method.label}" is priced in ${method.currency}, and this order is in ${basis.currency}.`,
152
+ hint: 'Add a shipping method priced in the order currency.',
153
+ });
154
+ }
155
+ return {
156
+ methodId: method.id,
157
+ label: method.label,
158
+ amountMinor: await priceOf(method, zone, basis),
159
+ currency: method.currency,
160
+ carrier: method.carrier,
161
+ };
162
+ },
163
+ };
164
+ }
165
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/shipping/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ import type { CatalogStore } from '../catalog/store.js';
3
+ import type { CustomerStore } from '../customer/store.js';
4
+ import { type OrderStore } from '../order/store.js';
5
+ import type { PaymentStore } from '../payment/store.js';
6
+ export declare const SUBSCRIPTION_STATUSES: readonly ['active', 'paused', 'cancelled'];
7
+ export type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];
8
+ export declare const INTERVAL_UNITS: readonly ['day', 'week', 'month', 'year'];
9
+ export type IntervalUnit = (typeof INTERVAL_UNITS)[number];
10
+ export interface Subscription {
11
+ readonly id: string;
12
+ readonly customerId: string;
13
+ readonly variantId: string;
14
+ readonly quantity: number;
15
+ readonly status: SubscriptionStatus;
16
+ readonly intervalUnit: IntervalUnit;
17
+ readonly intervalCount: number;
18
+ /** The price agreed when the subscription started. It does not follow the
19
+ * catalogue: a subscriber is entitled to the price they signed up at until
20
+ * somebody explicitly changes it. */
21
+ readonly priceMinor: number;
22
+ readonly currency: string;
23
+ readonly paymentDriver: string;
24
+ readonly currentPeriodStart: string;
25
+ readonly currentPeriodEnd: string;
26
+ readonly nextBillingAt: string;
27
+ readonly shippingCountry: string | null;
28
+ readonly shippingRegion: string | null;
29
+ readonly createdAt: string;
30
+ readonly updatedAt: string;
31
+ readonly cancelledAt: string | null;
32
+ }
33
+ export interface SubscriptionCycle {
34
+ readonly id: string;
35
+ readonly subscriptionId: string;
36
+ readonly periodStart: string;
37
+ readonly periodEnd: string;
38
+ readonly orderId: string | null;
39
+ readonly status: 'billed' | 'skipped_out_of_stock' | 'failed';
40
+ readonly createdAt: string;
41
+ }
42
+ export interface CreateSubscriptionInput {
43
+ readonly customerId: string;
44
+ readonly variantId: string;
45
+ readonly quantity?: number;
46
+ readonly intervalUnit: IntervalUnit;
47
+ readonly intervalCount?: number;
48
+ readonly priceMinor?: number;
49
+ readonly currency?: string;
50
+ readonly paymentDriver?: string;
51
+ readonly startAt?: string;
52
+ readonly shippingCountry?: string | null;
53
+ readonly shippingRegion?: string | null;
54
+ }
55
+ /** What one billing run did. Nothing here is a surprise to the operator. */
56
+ export interface BillingRunResult {
57
+ readonly billed: readonly {
58
+ readonly subscriptionId: string;
59
+ readonly orderId: string;
60
+ }[];
61
+ readonly skipped: readonly {
62
+ readonly subscriptionId: string;
63
+ readonly reason: string;
64
+ }[];
65
+ }
66
+ export interface SubscriptionStoreDependencies {
67
+ readonly catalog: CatalogStore;
68
+ readonly customers: CustomerStore;
69
+ readonly orders: OrderStore;
70
+ readonly payments: PaymentStore;
71
+ }
72
+ export interface SubscriptionStore {
73
+ create(input: CreateSubscriptionInput): Promise<Subscription>;
74
+ read(id: string): Promise<Subscription | null>;
75
+ list(options?: {
76
+ readonly customerId?: string;
77
+ readonly status?: SubscriptionStatus;
78
+ }): Promise<readonly Subscription[]>;
79
+ pause(id: string): Promise<Subscription>;
80
+ resume(id: string): Promise<Subscription>;
81
+ cancel(id: string): Promise<Subscription>;
82
+ cycles(id: string): Promise<readonly SubscriptionCycle[]>;
83
+ /**
84
+ * Bills every subscription whose next date has arrived.
85
+ *
86
+ * Called by a scheduler, a cron, or a person clicking a button — this
87
+ * package does not own a clock, which is why `now` is injected and why the
88
+ * run is safe to call twice.
89
+ */
90
+ runBilling(options?: {
91
+ readonly limit?: number;
92
+ }): Promise<BillingRunResult>;
93
+ }
94
+ /**
95
+ * The end of a billing period.
96
+ *
97
+ * Month and year arithmetic is where recurring billing goes wrong, so the rule
98
+ * is stated rather than inherited from `Date`: adding a month to 31 January
99
+ * gives **28 February** (or the 29th in a leap year), not 3 March. JavaScript's
100
+ * `setMonth` overflows into the next month, which would silently move a
101
+ * subscriber's billing day forward every short month until it drifted off the
102
+ * end of the calendar.
103
+ */
104
+ export declare function advancePeriod(from: string, unit: IntervalUnit, count: number): string;
105
+ export declare function createSubscriptionStore(db: DatabaseHandle, dependencies: SubscriptionStoreDependencies, now?: () => number): SubscriptionStore;
106
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/subscription/store.ts"],"names":[],"mappings":"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"}