@owlmeans/server-payment 0.1.18-rc.20 → 0.1.18-rc.21

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 (144) hide show
  1. package/README.md +1 -1
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/server-payment/SKILL.md +402 -260
  4. package/build/config.d.ts +15 -1
  5. package/build/config.d.ts.map +1 -1
  6. package/build/config.js +99 -3
  7. package/build/config.js.map +1 -1
  8. package/build/consts.d.ts +27 -0
  9. package/build/consts.d.ts.map +1 -1
  10. package/build/consts.js +27 -0
  11. package/build/consts.js.map +1 -1
  12. package/build/consumer/capture.d.ts +74 -0
  13. package/build/consumer/capture.d.ts.map +1 -0
  14. package/build/consumer/capture.js +291 -0
  15. package/build/consumer/capture.js.map +1 -0
  16. package/build/consumer/format.d.ts +27 -0
  17. package/build/consumer/format.d.ts.map +1 -0
  18. package/build/consumer/format.js +81 -0
  19. package/build/consumer/format.js.map +1 -0
  20. package/build/consumer/handlers.d.ts +28 -0
  21. package/build/consumer/handlers.d.ts.map +1 -0
  22. package/build/consumer/handlers.js +173 -0
  23. package/build/consumer/handlers.js.map +1 -0
  24. package/build/consumer/index.d.ts +7 -0
  25. package/build/consumer/index.d.ts.map +1 -0
  26. package/build/consumer/index.js +6 -0
  27. package/build/consumer/index.js.map +1 -0
  28. package/build/consumer/mail.d.ts +27 -0
  29. package/build/consumer/mail.d.ts.map +1 -0
  30. package/build/consumer/mail.js +314 -0
  31. package/build/consumer/mail.js.map +1 -0
  32. package/build/consumer/origin.d.ts +14 -0
  33. package/build/consumer/origin.d.ts.map +1 -0
  34. package/build/consumer/origin.js +47 -0
  35. package/build/consumer/origin.js.map +1 -0
  36. package/build/consumer/reconcile.d.ts +12 -0
  37. package/build/consumer/reconcile.d.ts.map +1 -0
  38. package/build/consumer/reconcile.js +317 -0
  39. package/build/consumer/reconcile.js.map +1 -0
  40. package/build/consumer/records.d.ts +78 -0
  41. package/build/consumer/records.d.ts.map +1 -0
  42. package/build/consumer/records.js +296 -0
  43. package/build/consumer/records.js.map +1 -0
  44. package/build/consumer/service.d.ts +51 -0
  45. package/build/consumer/service.d.ts.map +1 -0
  46. package/build/consumer/service.js +760 -0
  47. package/build/consumer/service.js.map +1 -0
  48. package/build/consumer/withdrawal.d.ts +57 -0
  49. package/build/consumer/withdrawal.d.ts.map +1 -0
  50. package/build/consumer/withdrawal.js +247 -0
  51. package/build/consumer/withdrawal.js.map +1 -0
  52. package/build/index.d.ts +4 -2
  53. package/build/index.d.ts.map +1 -1
  54. package/build/index.js +4 -2
  55. package/build/index.js.map +1 -1
  56. package/build/model.d.ts +6 -1
  57. package/build/model.d.ts.map +1 -1
  58. package/build/model.js +134 -4
  59. package/build/model.js.map +1 -1
  60. package/build/observer.d.ts +4 -0
  61. package/build/observer.d.ts.map +1 -1
  62. package/build/observer.js +13 -0
  63. package/build/observer.js.map +1 -1
  64. package/build/plugins/checkout-plugins.d.ts +49 -0
  65. package/build/plugins/checkout-plugins.d.ts.map +1 -0
  66. package/build/plugins/checkout-plugins.js +124 -0
  67. package/build/plugins/checkout-plugins.js.map +1 -0
  68. package/build/plugins/estimate.d.ts.map +1 -1
  69. package/build/plugins/estimate.js +41 -8
  70. package/build/plugins/estimate.js.map +1 -1
  71. package/build/plugins/events.d.ts +2 -0
  72. package/build/plugins/events.d.ts.map +1 -1
  73. package/build/plugins/events.js +126 -9
  74. package/build/plugins/events.js.map +1 -1
  75. package/build/plugins/fx.d.ts +5 -0
  76. package/build/plugins/fx.d.ts.map +1 -1
  77. package/build/plugins/fx.js +25 -0
  78. package/build/plugins/fx.js.map +1 -1
  79. package/build/plugins/portal.d.ts.map +1 -1
  80. package/build/plugins/portal.js +11 -1
  81. package/build/plugins/portal.js.map +1 -1
  82. package/build/plugins/stripe.d.ts +21 -2
  83. package/build/plugins/stripe.d.ts.map +1 -1
  84. package/build/plugins/stripe.js +397 -61
  85. package/build/plugins/stripe.js.map +1 -1
  86. package/build/resource.d.ts +8 -1
  87. package/build/resource.d.ts.map +1 -1
  88. package/build/resource.js +59 -2
  89. package/build/resource.js.map +1 -1
  90. package/build/service.d.ts +2 -1
  91. package/build/service.d.ts.map +1 -1
  92. package/build/service.js +28 -5
  93. package/build/service.js.map +1 -1
  94. package/build/subscription.d.ts +6 -0
  95. package/build/subscription.d.ts.map +1 -1
  96. package/build/subscription.js +1 -0
  97. package/build/subscription.js.map +1 -1
  98. package/build/sync.d.ts +7 -0
  99. package/build/sync.d.ts.map +1 -1
  100. package/build/sync.js +108 -15
  101. package/build/sync.js.map +1 -1
  102. package/build/types.d.ts +707 -4
  103. package/build/types.d.ts.map +1 -1
  104. package/build/utils.d.ts +22 -1
  105. package/build/utils.d.ts.map +1 -1
  106. package/build/utils.js +27 -1
  107. package/build/utils.js.map +1 -1
  108. package/package.json +14 -13
  109. package/src/config.ts +111 -7
  110. package/src/consts.ts +34 -0
  111. package/src/consumer/capture.ts +362 -0
  112. package/src/consumer/format.ts +90 -0
  113. package/src/consumer/handlers.ts +211 -0
  114. package/src/consumer/index.ts +6 -0
  115. package/src/consumer/mail.ts +368 -0
  116. package/src/consumer/origin.ts +63 -0
  117. package/src/consumer/reconcile.ts +329 -0
  118. package/src/consumer/records.ts +374 -0
  119. package/src/consumer/service.ts +868 -0
  120. package/src/consumer/withdrawal.ts +302 -0
  121. package/src/index.ts +6 -4
  122. package/src/model.ts +148 -6
  123. package/src/observer.ts +15 -2
  124. package/src/plugins/checkout-plugins.ts +155 -0
  125. package/src/plugins/estimate.ts +49 -9
  126. package/src/plugins/events.ts +135 -11
  127. package/src/plugins/fx.ts +29 -0
  128. package/src/plugins/portal.ts +11 -1
  129. package/src/plugins/stripe.ts +476 -60
  130. package/src/resource.ts +87 -4
  131. package/src/service.ts +28 -6
  132. package/src/subscription.ts +7 -0
  133. package/src/sync.ts +124 -17
  134. package/src/types.ts +756 -6
  135. package/src/utils.ts +56 -7
  136. package/tests/checkout-consumer.spec.ts +348 -0
  137. package/tests/checkout-plugins.spec.ts +164 -0
  138. package/tests/consumer-events.spec.ts +218 -0
  139. package/tests/consumer-fixtures.ts +132 -0
  140. package/tests/consumer-ops.spec.ts +351 -0
  141. package/tests/consumer-rights.integration.spec.ts +150 -0
  142. package/tests/consumer-rights.spec.ts +501 -0
  143. package/tests/context.ts +20 -2
  144. package/tests/fake-stripe.ts +188 -18
@@ -0,0 +1,291 @@
1
+ import { billingLanguageOf, ConsentKind, CONSUMER_RIGHTS_COPY_VERSION, inScope, PurchaseKind, regionOf, withdrawalDeadlineOf, } from '@owlmeans/payment';
2
+ import { STRIPE_PAYGATE_ALIAS } from '../consts.js';
3
+ import { billingProfiles, compact, conditionalSet, consumerConsents, dateOf, idOf, isMissingObject, payment, purchases, } from '../utils.js';
4
+ import { sendConsumerMail } from './mail.js';
5
+ import { createPurchase, hasEvent, lockProfile, patchPurchase, purchaseIdOf, } from './records.js';
6
+ /**
7
+ * The buyer and totals of a Checkout Session. `presentment_details` (Adaptive Pricing) is not typed
8
+ * by the pinned SDK, hence the narrow accessor.
9
+ */
10
+ export const sessionEvidenceOf = (session) => {
11
+ const details = session.customer_details;
12
+ const presentment = session.presentment_details;
13
+ const subtotal = session.amount_subtotal ?? 0;
14
+ const tax = session.total_details?.amount_tax ?? 0;
15
+ return compact({
16
+ country: details?.address?.country?.toUpperCase() ?? undefined,
17
+ email: details?.email ?? undefined,
18
+ name: details?.name ?? undefined,
19
+ business: (details?.tax_ids?.length ?? 0) > 0 || details?.tax_exempt === 'reverse' ? true : undefined,
20
+ currency: (session.currency ?? '').toLowerCase(),
21
+ subtotalMinor: subtotal,
22
+ taxMinor: tax,
23
+ totalMinor: session.amount_total ?? subtotal + tax,
24
+ presentmentCurrency: presentment?.presentment_currency?.toLowerCase(),
25
+ presentmentAmountMinor: presentment?.presentment_amount,
26
+ termsAccepted: session.consent?.terms_of_service === 'accepted' ? true
27
+ : session.consent?.terms_of_service != null ? false : undefined,
28
+ });
29
+ };
30
+ /**
31
+ * An invoice's evidence, best effort: an unreachable invoice is logged and yields nothing — the
32
+ * withdrawal reads it again when it needs it. `invoice.payment_intent` is top-level on the pinned
33
+ * API version.
34
+ */
35
+ export const invoiceEvidenceOf = async (stripe, invoiceId) => {
36
+ if (stripe == null || invoiceId == null) {
37
+ return {};
38
+ }
39
+ try {
40
+ const invoice = await stripe.invoices.retrieve(invoiceId);
41
+ const typed = invoice;
42
+ return compact({
43
+ invoiceNumber: invoice.number ?? undefined,
44
+ invoiceLineId: invoice.lines?.data?.[0]?.id,
45
+ paymentIntentId: idOf(typed.payment_intent),
46
+ country: invoice.customer_address?.country?.toUpperCase() ?? undefined,
47
+ subtotalMinor: invoice.subtotal ?? undefined,
48
+ taxMinor: typed.tax ?? undefined,
49
+ totalMinor: invoice.total ?? undefined,
50
+ currency: invoice.currency ?? undefined,
51
+ });
52
+ }
53
+ catch (error) {
54
+ if (!isMissingObject(error)) {
55
+ console.warn(`[payment] invoice "${invoiceId}" unreadable for its purchase`, error);
56
+ }
57
+ return {};
58
+ }
59
+ };
60
+ const deadlineOf = (policy, scoped, purchasedAt) => scoped ? withdrawalDeadlineOf(purchasedAt, policy) : undefined;
61
+ /** Lock the entity's billing country from a completed checkout, when the policy locks countries. */
62
+ const lockFromSession = async (ctx, policy, entityId, session, evidence) => {
63
+ const metadata = session.metadata ?? {};
64
+ const country = evidence.country ?? metadata.country;
65
+ if (!policy.mechanisms.countryLock || country == null || country === '') {
66
+ return null;
67
+ }
68
+ const { record } = await lockProfile(ctx, policy, {
69
+ entityId, country, source: 'checkout', customerId: idOf(session.customer), sessionId: session.id,
70
+ ipCountry: metadata.ipCountry, email: evidence.email, name: evidence.name, business: evidence.business,
71
+ currency: evidence.currency, language: metadata.language,
72
+ });
73
+ return record;
74
+ };
75
+ /**
76
+ * Mail the purchase confirmation once per purchase: only the delivery that claims the purchase's
77
+ * `confirmationMailAt` (a conditional write — concurrent deliveries in several processes, a
78
+ * redelivery, the checkout refining a subscription) sends it; one that failed is retried by
79
+ * `reconcile`, never here. A purchase mailed before the claim existed is recognised by its event.
80
+ */
81
+ const confirmPurchase = async (ctx, policy, purchase) => {
82
+ if (!policy.mechanisms.purchaseConfirmation || !purchase.inScope) {
83
+ return;
84
+ }
85
+ if (await hasEvent(ctx, purchase.purchaseId, 'mail', 'purchase')) {
86
+ return;
87
+ }
88
+ const claimed = await conditionalSet(purchases(ctx), { purchaseId: purchase.purchaseId, confirmationMailAt: null }, {
89
+ confirmationMailAt: new Date(),
90
+ });
91
+ if (!claimed) {
92
+ return;
93
+ }
94
+ await sendConsumerMail(ctx, policy, 'purchase', purchase.purchaseId);
95
+ };
96
+ /**
97
+ * A completed, paid ONE-TIME checkout as a purchase — the window and the contract registry. Runs
98
+ * BEFORE the credits are granted: the billing country is locked first (first write wins; another
99
+ * country is a `lock-mismatch` event), then the purchase row is written, then its confirmation is
100
+ * mailed. `null` when no consumer-rights policy is declared.
101
+ */
102
+ export const capturePaymentPurchase = async (ctx, stripe, session, extra = {}, opts = {}) => {
103
+ const policy = await payment(ctx).consumerRightsPolicy();
104
+ if (policy == null) {
105
+ return null;
106
+ }
107
+ const metadata = session.metadata ?? {};
108
+ const entityId = metadata.entityId;
109
+ if (entityId == null || metadata.productSku == null) {
110
+ return null;
111
+ }
112
+ const evidence = sessionEvidenceOf(session);
113
+ const profile = await lockFromSession(ctx, policy, entityId, session, evidence);
114
+ const existing = await purchases(ctx).byPurchaseId(purchaseIdOf(session.id));
115
+ if (existing != null) {
116
+ if (opts.mail !== false)
117
+ await confirmPurchase(ctx, policy, existing);
118
+ return { purchase: existing, created: false };
119
+ }
120
+ const country = evidence.country ?? metadata.country ?? profile?.country;
121
+ const region = regionOf(country, policy);
122
+ // Protected when either the buyer's own address or the organization's locked country is in scope.
123
+ const scoped = inScope(region, country, policy) || (profile != null && inScope(profile.region, profile.country, policy));
124
+ const invoice = await invoiceEvidenceOf(stripe, idOf(session.invoice));
125
+ const purchasedAt = extra.at ?? new Date();
126
+ const draft = compact({
127
+ purchaseId: purchaseIdOf(session.id),
128
+ entityId,
129
+ kind: PurchaseKind.TopUp,
130
+ paygate: STRIPE_PAYGATE_ALIAS,
131
+ sessionId: session.id,
132
+ paymentIntentId: idOf(session.payment_intent) ?? invoice.paymentIntentId,
133
+ invoiceId: idOf(session.invoice),
134
+ invoiceNumber: invoice.invoiceNumber,
135
+ invoiceLineId: invoice.invoiceLineId,
136
+ productSku: metadata.productSku,
137
+ planSku: metadata.planSku,
138
+ profileId: metadata.profileId,
139
+ country,
140
+ region: region ?? undefined,
141
+ ipCountry: metadata.ipCountry,
142
+ inScope: scoped,
143
+ language: profile?.language ?? metadata.language ?? billingLanguageOf(country, policy),
144
+ email: evidence.email,
145
+ name: evidence.name,
146
+ business: evidence.business,
147
+ currency: evidence.currency,
148
+ amountSubtotalMinor: evidence.subtotalMinor,
149
+ amountTaxMinor: evidence.taxMinor,
150
+ amountTotalMinor: evidence.totalMinor,
151
+ presentmentCurrency: evidence.presentmentCurrency,
152
+ presentmentAmountMinor: evidence.presentmentAmountMinor,
153
+ netAmountMinor: extra.netAmountMinor,
154
+ amountCurrency: extra.amountCurrency,
155
+ units: extra.units,
156
+ taxBehavior: extra.taxBehavior,
157
+ termsAccepted: evidence.termsAccepted,
158
+ textVersion: metadata.termsVersion ?? policy.textVersion,
159
+ copyVersion: metadata.copyVersion ?? CONSUMER_RIGHTS_COPY_VERSION,
160
+ purchasedAt,
161
+ deadline: deadlineOf(policy, scoped, purchasedAt),
162
+ });
163
+ const { record, created } = await createPurchase(ctx, draft);
164
+ if (opts.mail !== false)
165
+ await confirmPurchase(ctx, policy, record);
166
+ return { purchase: record, created };
167
+ };
168
+ /** The start request a subscription's metadata names, when it is a real start request of that entity. */
169
+ const startRequestOf = async (ctx, entityId, id) => {
170
+ if (id == null || id === '') {
171
+ return null;
172
+ }
173
+ const consent = await consumerConsents(ctx).load(id).catch(() => null);
174
+ return consent != null && consent.kind === ConsentKind.SubscriptionStart && consent.entityId === entityId ? consent : null;
175
+ };
176
+ /**
177
+ * A subscription's FIRST invoice as a purchase — called while the subscription is committed, BEFORE
178
+ * the `created` observers grant anything, so the window exists before the bundle can be spent. The
179
+ * buyer's country and totals are refined when the checkout completes. The start request (when the
180
+ * metadata names one) is the purchase's consent: `servicesStartedAt` and `consentedAt`.
181
+ */
182
+ export const captureSubscriptionPurchase = async (ctx, stripe, subscription, row) => {
183
+ const policy = await payment(ctx).consumerRightsPolicy();
184
+ if (policy == null) {
185
+ return null;
186
+ }
187
+ const purchaseId = purchaseIdOf(subscription.id);
188
+ const existing = await purchases(ctx).byPurchaseId(purchaseId);
189
+ if (existing != null) {
190
+ return { purchase: existing, created: false };
191
+ }
192
+ const metadata = subscription.metadata ?? {};
193
+ const item = subscription.items?.data?.[0];
194
+ const invoiceId = idOf(subscription.latest_invoice);
195
+ const invoice = await invoiceEvidenceOf(stripe, invoiceId);
196
+ const profile = await billingProfiles(ctx).byEntity(row.entityId);
197
+ const country = invoice.country ?? profile?.country ?? metadata.country;
198
+ const region = regionOf(country, policy);
199
+ const scoped = inScope(region, country, policy) || (profile != null && inScope(profile.region, profile.country, policy));
200
+ const start = await startRequestOf(ctx, row.entityId, metadata.startRequestId);
201
+ const subtotal = invoice.subtotalMinor ?? (item?.price?.unit_amount ?? 0) * (item?.quantity ?? 1);
202
+ const tax = invoice.taxMinor ?? 0;
203
+ const purchasedAt = dateOf(subscription.created) ?? new Date();
204
+ const draft = compact({
205
+ purchaseId,
206
+ entityId: row.entityId,
207
+ kind: PurchaseKind.Subscription,
208
+ paygate: STRIPE_PAYGATE_ALIAS,
209
+ subscriptionId: subscription.id,
210
+ paymentIntentId: invoice.paymentIntentId,
211
+ invoiceId,
212
+ invoiceNumber: invoice.invoiceNumber,
213
+ invoiceLineId: invoice.invoiceLineId,
214
+ productSku: row.productSku,
215
+ planSku: row.planSku,
216
+ profileId: metadata.profileId,
217
+ country,
218
+ region: region ?? undefined,
219
+ ipCountry: metadata.ipCountry,
220
+ inScope: scoped,
221
+ language: profile?.language ?? metadata.language ?? billingLanguageOf(country, policy),
222
+ currency: (invoice.currency ?? subscription.currency ?? item?.price?.currency ?? 'usd').toLowerCase(),
223
+ amountSubtotalMinor: subtotal,
224
+ amountTaxMinor: tax,
225
+ amountTotalMinor: invoice.totalMinor ?? subtotal + tax,
226
+ taxBehavior: item?.price?.tax_behavior ?? undefined,
227
+ textVersion: metadata.termsVersion ?? policy.textVersion,
228
+ copyVersion: metadata.copyVersion ?? CONSUMER_RIGHTS_COPY_VERSION,
229
+ startRequestId: start?.id,
230
+ servicesStartedAt: start?.decidedAt,
231
+ consentId: start?.id,
232
+ consentedAt: start?.decidedAt,
233
+ purchasedAt,
234
+ deadline: deadlineOf(policy, scoped, purchasedAt),
235
+ });
236
+ const { record, created } = await createPurchase(ctx, draft);
237
+ return { purchase: record, created };
238
+ };
239
+ /**
240
+ * Refine a subscription purchase with its completed checkout: the buyer's own country (and so the
241
+ * scope and deadline), e-mail, the charged totals, the terms acceptance and the session id; lock
242
+ * the billing country; mail the confirmation once.
243
+ */
244
+ export const completeSubscriptionPurchase = async (ctx, stripe, session, purchase, opts = {}) => {
245
+ const policy = await payment(ctx).consumerRightsPolicy();
246
+ if (policy == null) {
247
+ return purchase;
248
+ }
249
+ const evidence = sessionEvidenceOf(session);
250
+ const profile = await lockFromSession(ctx, policy, purchase.entityId, session, evidence);
251
+ const country = evidence.country ?? purchase.country ?? profile?.country;
252
+ const region = regionOf(country, policy);
253
+ const scoped = inScope(region, country, policy) || (profile != null && inScope(profile.region, profile.country, policy));
254
+ const invoice = purchase.invoiceLineId == null || purchase.paymentIntentId == null
255
+ ? await invoiceEvidenceOf(stripe, purchase.invoiceId ?? idOf(session.invoice)) : {};
256
+ const purchasedAt = new Date(purchase.purchasedAt);
257
+ const fields = compact({
258
+ sessionId: session.id,
259
+ invoiceId: purchase.invoiceId ?? idOf(session.invoice),
260
+ invoiceNumber: purchase.invoiceNumber ?? invoice.invoiceNumber,
261
+ invoiceLineId: purchase.invoiceLineId ?? invoice.invoiceLineId,
262
+ paymentIntentId: purchase.paymentIntentId ?? invoice.paymentIntentId,
263
+ country,
264
+ region: region ?? undefined,
265
+ inScope: scoped,
266
+ deadline: scoped ? purchase.deadline ?? withdrawalDeadlineOf(purchasedAt, policy) : undefined,
267
+ email: evidence.email ?? purchase.email,
268
+ name: evidence.name ?? purchase.name,
269
+ business: evidence.business ?? purchase.business,
270
+ currency: evidence.currency !== '' ? evidence.currency : purchase.currency,
271
+ amountSubtotalMinor: evidence.subtotalMinor,
272
+ amountTaxMinor: evidence.taxMinor,
273
+ amountTotalMinor: evidence.totalMinor,
274
+ presentmentCurrency: evidence.presentmentCurrency,
275
+ presentmentAmountMinor: evidence.presentmentAmountMinor,
276
+ termsAccepted: evidence.termsAccepted,
277
+ ipCountry: purchase.ipCountry ?? session.metadata?.ipCountry,
278
+ language: profile?.language ?? purchase.language,
279
+ });
280
+ await patchPurchase(ctx, purchase.purchaseId, fields);
281
+ const updated = { ...purchase, ...fields };
282
+ if (!scoped && purchase.deadline != null) {
283
+ // Out of scope after all (the buyer's own country): no window.
284
+ await patchPurchase(ctx, purchase.purchaseId, { deadline: null });
285
+ delete updated.deadline;
286
+ }
287
+ if (opts.mail !== false)
288
+ await confirmPurchase(ctx, policy, updated);
289
+ return updated;
290
+ };
291
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/consumer/capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /** `CR-YYMMDD-XXXXXX` — the purchase's UTC date and six characters of an unambiguous alphabet. */
2
+ export declare const makeContractRef: (at?: Date) => string;
3
+ /** A contract reference as a person may type it: trimmed, upper-cased, look-alike dashes unified. */
4
+ export declare const normalizeContractRef: (value: string) => string;
5
+ export declare const normalizeEmail: (value: string | null | undefined) => string;
6
+ /**
7
+ * Whether an address is on a reserved top-level domain (`.test`, `.example`, `.invalid`,
8
+ * `.localhost` — RFC 2606 / 6761), any subdomain included: `a@shop.test`, `a@mail.shop.test`,
9
+ * `a@localhost`. Case, surrounding spaces, a display-name form (`Name <a@shop.test>`) and a
10
+ * trailing root dot are all read through.
11
+ */
12
+ export declare const isReservedAddress: (address: string) => boolean;
13
+ export declare const escapeHtml: (value: string) => string;
14
+ /** Minor units as a localized amount with its currency (`12,56 €`, `$12.56`). */
15
+ export declare const formatMoney: (amountMinor: number, currency: string, lng: string) => string;
16
+ /** A date and time in UTC, localized (`23 September 2026, 14:05`). */
17
+ export declare const formatDateTime: (at: Date, lng: string) => string;
18
+ /** A calendar date in UTC, localized. */
19
+ export declare const formatDate: (at: Date, lng: string) => string;
20
+ /**
21
+ * The last included day of a window whose deadline is EXCLUSIVE (the first instant it is over),
22
+ * as a UTC date — the copy says "until the end of <date>": 13 October 00:00 UTC is "12 October".
23
+ */
24
+ export declare const formatDeadline: (deadline: Date, lng: string) => string;
25
+ /** A country's name in a language, else its code. */
26
+ export declare const countryName: (country: string, lng: string) => string;
27
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/consumer/format.ts"],"names":[],"mappings":"AAIA,kGAAkG;AAClG,eAAO,MAAM,eAAe,QAAQ,IAAI,KAAgB,MAMvD,CAAA;AAED,qGAAqG;AACrG,eAAO,MAAM,oBAAoB,UAAW,MAAM,KAAG,MACE,CAAA;AAEvD,eAAO,MAAM,cAAc,UAAW,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAA4C,CAAA;AAE9G;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,YAAa,MAAM,KAAG,OAMnD,CAAA;AAED,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAEM,CAAA;AAWjD,iFAAiF;AACjF,eAAO,MAAM,WAAW,gBAAiB,MAAM,YAAY,MAAM,OAAO,MAAM,KAAG,MAQhF,CAAA;AAED,sEAAsE;AACtE,eAAO,MAAM,cAAc,OAAQ,IAAI,OAAO,MAAM,KAAG,MAMtD,CAAA;AAED,yCAAyC;AACzC,eAAO,MAAM,UAAU,OAAQ,IAAI,OAAO,MAAM,KAAG,MAMlD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,aAAc,IAAI,OAAO,MAAM,KAAG,MACb,CAAA;AAEhD,qDAAqD;AACrD,eAAO,MAAM,WAAW,YAAa,MAAM,OAAO,MAAM,KAAG,MAM1D,CAAA"}
@@ -0,0 +1,81 @@
1
+ import { randomInt } from 'node:crypto';
2
+ import { lastWithdrawalDayOf } from '@owlmeans/payment';
3
+ import { CONTRACT_REF_ALPHABET, RESERVED_MAIL_TLDS } from '../consts.js';
4
+ /** `CR-YYMMDD-XXXXXX` — the purchase's UTC date and six characters of an unambiguous alphabet. */
5
+ export const makeContractRef = (at = new Date()) => {
6
+ const date = [at.getUTCFullYear() % 100, at.getUTCMonth() + 1, at.getUTCDate()]
7
+ .map(part => String(part).padStart(2, '0')).join('');
8
+ const suffix = Array.from({ length: 6 }, () => CONTRACT_REF_ALPHABET[randomInt(CONTRACT_REF_ALPHABET.length)]).join('');
9
+ return `CR-${date}-${suffix}`;
10
+ };
11
+ /** A contract reference as a person may type it: trimmed, upper-cased, look-alike dashes unified. */
12
+ export const normalizeContractRef = (value) => value.trim().toUpperCase().replace(/[‐-―−\s]+/g, '-');
13
+ export const normalizeEmail = (value) => (value ?? '').trim().toLowerCase();
14
+ /**
15
+ * Whether an address is on a reserved top-level domain (`.test`, `.example`, `.invalid`,
16
+ * `.localhost` — RFC 2606 / 6761), any subdomain included: `a@shop.test`, `a@mail.shop.test`,
17
+ * `a@localhost`. Case, surrounding spaces, a display-name form (`Name <a@shop.test>`) and a
18
+ * trailing root dot are all read through.
19
+ */
20
+ export const isReservedAddress = (address) => {
21
+ const bare = normalizeEmail(address).replace(/^.*<([^>]*)>.*$/, '$1');
22
+ const domain = (bare.split('@').pop() ?? '').trim().replace(/\.+$/, '');
23
+ const tld = domain.split('.').pop() ?? '';
24
+ return RESERVED_MAIL_TLDS.includes(tld);
25
+ };
26
+ export const escapeHtml = (value) => value
27
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
28
+ .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
29
+ const fractionDigitsOf = (currency) => {
30
+ try {
31
+ return new Intl.NumberFormat('en', { style: 'currency', currency: currency.toUpperCase() })
32
+ .resolvedOptions().maximumFractionDigits ?? 2;
33
+ }
34
+ catch {
35
+ return 2;
36
+ }
37
+ };
38
+ /** Minor units as a localized amount with its currency (`12,56 €`, `$12.56`). */
39
+ export const formatMoney = (amountMinor, currency, lng) => {
40
+ const digits = fractionDigitsOf(currency);
41
+ const major = amountMinor / 10 ** digits;
42
+ try {
43
+ return new Intl.NumberFormat(lng, { style: 'currency', currency: currency.toUpperCase() }).format(major);
44
+ }
45
+ catch {
46
+ return `${major.toFixed(digits)} ${currency.toUpperCase()}`;
47
+ }
48
+ };
49
+ /** A date and time in UTC, localized (`23 September 2026, 14:05`). */
50
+ export const formatDateTime = (at, lng) => {
51
+ try {
52
+ return new Intl.DateTimeFormat(lng, { dateStyle: 'long', timeStyle: 'short', timeZone: 'UTC' }).format(at);
53
+ }
54
+ catch {
55
+ return at.toISOString().replace('T', ' ').slice(0, 16);
56
+ }
57
+ };
58
+ /** A calendar date in UTC, localized. */
59
+ export const formatDate = (at, lng) => {
60
+ try {
61
+ return new Intl.DateTimeFormat(lng, { dateStyle: 'long', timeZone: 'UTC' }).format(at);
62
+ }
63
+ catch {
64
+ return at.toISOString().slice(0, 10);
65
+ }
66
+ };
67
+ /**
68
+ * The last included day of a window whose deadline is EXCLUSIVE (the first instant it is over),
69
+ * as a UTC date — the copy says "until the end of <date>": 13 October 00:00 UTC is "12 October".
70
+ */
71
+ export const formatDeadline = (deadline, lng) => formatDate(lastWithdrawalDayOf(deadline), lng);
72
+ /** A country's name in a language, else its code. */
73
+ export const countryName = (country, lng) => {
74
+ try {
75
+ return new Intl.DisplayNames([lng], { type: 'region' }).of(country.toUpperCase()) ?? country;
76
+ }
77
+ catch {
78
+ return country;
79
+ }
80
+ };
81
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/consumer/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { EntrypointProtocolDeclaration } from '@owlmeans/entrypoint';
2
+ import type { CheckoutReadProtocols, ConsumerRightsAccountProtocols, ConsumerRightsPublicProtocols } from '@owlmeans/payment';
3
+ import type { ServerProtocolEntrypoint } from '@owlmeans/server-entrypoint';
4
+ import type { CheckoutReadHandlerOptions, ConsumerRightsHandlerOptions } from '../types.js';
5
+ type Bound = ServerProtocolEntrypoint<EntrypointProtocolDeclaration>;
6
+ /** The protocol tree `makeConsumerRightsProtocols` builds, with or without its public subtree. */
7
+ export type ConsumerRightsTree = ConsumerRightsAccountProtocols & {
8
+ public?: ConsumerRightsPublicProtocols;
9
+ };
10
+ /**
11
+ * Server bindings of `makeConsumerRightsProtocols`' tree over the consumer-rights service. The
12
+ * account routes act for the request's organization (`resolveEntity`, default `req.entity.id`);
13
+ * the money-moving acts (consent, start request, withdrawal, cancellation) pass `guardMoney` first
14
+ * (refuse an API key there). The public routes — the statutory functions without a login — need
15
+ * `throttle` (a wiring error otherwise), drop a filled honeypot silently, and answer every
16
+ * declaration with the same receipt shape after at least `publicMinMs`, matched or not. Every hook
17
+ * gets the request's context as its last argument.
18
+ *
19
+ * @throws SyntaxError when the tree has a public subtree and no `throttle` is given
20
+ */
21
+ export declare const consumerRightsEntrypoints: (protocols: ConsumerRightsTree, opts?: ConsumerRightsHandlerOptions) => Bound[];
22
+ /**
23
+ * Server bindings of `makeCheckoutReadProtocols`' tree: the entity's amount policy as the checkout
24
+ * plugins narrow it now (the same computation the checkout enforces) and the synced plan prices.
25
+ */
26
+ export declare const checkoutReadEntrypoints: (protocols: CheckoutReadProtocols, opts?: CheckoutReadHandlerOptions) => Bound[];
27
+ export {};
28
+ //# sourceMappingURL=handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/consumer/handlers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,6BAA6B,EAAE,MAAM,sBAAsB,CAAA;AAE1F,OAAO,KAAK,EACV,qBAAqB,EAAE,8BAA8B,EAAE,6BAA6B,EAErF,MAAM,mBAAmB,CAAA;AAI1B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AAK3E,OAAO,KAAK,EACV,0BAA0B,EAAE,4BAA4B,EAEzD,MAAM,aAAa,CAAA;AAEpB,KAAK,KAAK,GAAG,wBAAwB,CAAC,6BAA6B,CAAC,CAAA;AAEpE,kGAAkG;AAClG,MAAM,MAAM,kBAAkB,GAAG,8BAA8B,GAAG;IAAE,MAAM,CAAC,EAAE,6BAA6B,CAAA;CAAE,CAAA;AAiC5G;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,cACzB,kBAAkB,SAAQ,4BAA4B,KAChE,KAAK,EA8GP,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,cAAe,qBAAqB,SAAQ,0BAA0B,KAAQ,KAAK,EAwBtH,CAAA"}
@@ -0,0 +1,173 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { AuthForbidden } from '@owlmeans/auth';
3
+ import { ConsumerRightsError, DeclarationChannel } from '@owlmeans/payment';
4
+ import { handlers } from '@owlmeans/server-api';
5
+ import { bind } from '@owlmeans/server-entrypoint';
6
+ import { CONSUMER_RIGHTS_SERVICE, GATEWAY_SERVICE } from '../consts.js';
7
+ import { payment } from '../utils.js';
8
+ import { requestOriginOf } from './origin.js';
9
+ import { unlockedProfileView } from './records.js';
10
+ const defaultEntity = (req, _ctx) => req.entity?.id ?? null;
11
+ const DEFAULT_PUBLIC_MIN_MS = 1000;
12
+ const sleep = async (ms) => await new Promise(resolve => setTimeout(resolve, ms));
13
+ /** Answer no sooner than `minMs` after the start — a matched declaration takes as long as an unmatched one. */
14
+ const padded = async (minMs, run) => {
15
+ const started = Date.now();
16
+ try {
17
+ return await run();
18
+ }
19
+ finally {
20
+ const left = minMs - (Date.now() - started);
21
+ if (left > 0)
22
+ await sleep(left);
23
+ }
24
+ };
25
+ /** The public answer: what was declared and when — never a status, an amount or a match. */
26
+ const publicPart = (receipt) => ({
27
+ declarationId: receipt.declarationId, receivedAt: receipt.receivedAt, content: receipt.content, mailed: receipt.mailed,
28
+ });
29
+ /** A form a bot filled (the honeypot) gets the same shape of answer, and nothing is recorded or sent. */
30
+ const decoyReceipt = (content) => ({
31
+ declarationId: randomBytes(12).toString('hex'), receivedAt: new Date(), content, mailed: false,
32
+ });
33
+ const typed = (fields) => Object.fromEntries(Object.entries(fields).filter((entry) => typeof entry[1] === 'string' && entry[1].trim() !== ''));
34
+ /**
35
+ * Server bindings of `makeConsumerRightsProtocols`' tree over the consumer-rights service. The
36
+ * account routes act for the request's organization (`resolveEntity`, default `req.entity.id`);
37
+ * the money-moving acts (consent, start request, withdrawal, cancellation) pass `guardMoney` first
38
+ * (refuse an API key there). The public routes — the statutory functions without a login — need
39
+ * `throttle` (a wiring error otherwise), drop a filled honeypot silently, and answer every
40
+ * declaration with the same receipt shape after at least `publicMinMs`, matched or not. Every hook
41
+ * gets the request's context as its last argument.
42
+ *
43
+ * @throws SyntaxError when the tree has a public subtree and no `throttle` is given
44
+ */
45
+ export const consumerRightsEntrypoints = (protocols, opts = {}) => {
46
+ if (protocols.public != null && opts.throttle == null) {
47
+ throw new SyntaxError('consumer-rights: the public routes need a throttle');
48
+ }
49
+ const api = handlers();
50
+ const serviceOf = (ctx) => ctx.service(opts.serviceAlias ?? CONSUMER_RIGHTS_SERVICE);
51
+ const apiCtx = (ctx) => ctx;
52
+ const entityOf = (req, ctx) => {
53
+ const entityId = (opts.resolveEntity ?? defaultEntity)(req, apiCtx(ctx));
54
+ if (entityId == null || entityId === '') {
55
+ throw new AuthForbidden('entity');
56
+ }
57
+ return entityId;
58
+ };
59
+ const subjectFor = async (req, ctx) => ({
60
+ ...(await opts.subjectOf?.(req, apiCtx(ctx)) ?? {}),
61
+ entityId: entityOf(req, ctx),
62
+ channel: DeclarationChannel.InApp,
63
+ });
64
+ const metaOf = (req, ctx) => opts.metaOf != null ? opts.metaOf(req, apiCtx(ctx)) : requestOriginOf(req);
65
+ const guard = async (req, action, ctx) => {
66
+ await opts.guardMoney?.(req, action, apiCtx(ctx));
67
+ };
68
+ const minMs = opts.publicMinMs ?? DEFAULT_PUBLIC_MIN_MS;
69
+ const bound = [
70
+ bind(protocols.base),
71
+ bind(protocols.profile, api.request(protocols.profile, async (req, ctx) => await serviceOf(ctx).profile(entityOf(req, ctx))
72
+ ?? unlockedProfileView(await payment(ctx).consumerRightsPolicy()))),
73
+ bind(protocols.purchases, api.request(protocols.purchases, async (req, ctx) => ({ purchases: await serviceOf(ctx).purchases(entityOf(req, ctx)) }))),
74
+ bind(protocols.consent, api.request(protocols.consent, async (req, ctx) => await serviceOf(ctx).consentView(entityOf(req, ctx)))),
75
+ bind(protocols.giveConsent, api.body(protocols.giveConsent, async (body, ctx, req) => {
76
+ await guard(req, 'consent', ctx);
77
+ return await serviceOf(ctx).recordConsent(await subjectFor(req, ctx), body, metaOf(req, ctx));
78
+ })),
79
+ bind(protocols.start, api.request(protocols.start, async (req, ctx) => await serviceOf(ctx).startView(entityOf(req, ctx), String(req.query.planSku)))),
80
+ bind(protocols.requestStart, api.body(protocols.requestStart, async (body, ctx, req) => {
81
+ await guard(req, 'start', ctx);
82
+ const plan = await opts.planNameOf?.(body.planSku, body.language, req, apiCtx(ctx));
83
+ return await serviceOf(ctx).recordStartRequest(await subjectFor(req, ctx), body, metaOf(req, ctx), plan != null ? { plan } : {});
84
+ })),
85
+ bind(protocols.withdrawals, api.request(protocols.withdrawals, async (req, ctx) => {
86
+ const subject = await subjectFor(req, ctx);
87
+ return await serviceOf(ctx).withdrawalCandidates(subject.entityId, subject);
88
+ })),
89
+ bind(protocols.withdraw, api.body(protocols.withdraw, async (body, ctx, req) => {
90
+ await guard(req, 'withdraw', ctx);
91
+ return await serviceOf(ctx).withdraw(await subjectFor(req, ctx), body, metaOf(req, ctx));
92
+ })),
93
+ bind(protocols.cancel, api.body(protocols.cancel, async (body, ctx, req) => {
94
+ await guard(req, 'cancel', ctx);
95
+ return await serviceOf(ctx).cancel(await subjectFor(req, ctx), body, metaOf(req, ctx));
96
+ })),
97
+ ];
98
+ const pub = protocols.public;
99
+ if (pub == null) {
100
+ return bound;
101
+ }
102
+ const throttle = opts.throttle;
103
+ return [
104
+ ...bound,
105
+ bind(pub.base),
106
+ bind(pub.policy, api.request(pub.policy, async (_req, ctx) => {
107
+ const policy = await payment(ctx).consumerRightsPolicy();
108
+ if (policy == null) {
109
+ throw new ConsumerRightsError('policy:none');
110
+ }
111
+ const view = {
112
+ mechanisms: { withdrawal: policy.mechanisms.withdrawal, cancellation: policy.mechanisms.cancellation },
113
+ languages: Object.keys(policy.links),
114
+ links: policy.links,
115
+ textVersion: policy.textVersion,
116
+ };
117
+ return view;
118
+ })),
119
+ bind(pub.withdraw, api.body(pub.withdraw, async (body, ctx, req) => {
120
+ const origin = metaOf(req, ctx);
121
+ await throttle(req, { action: 'withdrawal', email: body.email, ...(origin.ip != null ? { ip: origin.ip } : {}) }, apiCtx(ctx));
122
+ return await padded(minMs, async () => {
123
+ const content = typed({ name: body.name, contract: body.contractRef, email: body.email });
124
+ if (body.honeypot != null && body.honeypot !== '') {
125
+ return decoyReceipt(content);
126
+ }
127
+ return publicPart(await serviceOf(ctx).withdraw(null, body, origin));
128
+ });
129
+ })),
130
+ bind(pub.cancel, api.body(pub.cancel, async (body, ctx, req) => {
131
+ const origin = metaOf(req, ctx);
132
+ await throttle(req, { action: 'cancellation', email: body.email, ...(origin.ip != null ? { ip: origin.ip } : {}) }, apiCtx(ctx));
133
+ return await padded(minMs, async () => {
134
+ const content = typed({
135
+ name: body.name, contract: body.contractRef, email: body.email, kind: body.kind, reason: body.reason,
136
+ date: body.effective === 'date' ? body.date : undefined,
137
+ });
138
+ if (body.honeypot != null && body.honeypot !== '') {
139
+ return decoyReceipt(content);
140
+ }
141
+ return publicPart(await serviceOf(ctx).cancel(null, body, origin));
142
+ });
143
+ })),
144
+ ];
145
+ };
146
+ /**
147
+ * Server bindings of `makeCheckoutReadProtocols`' tree: the entity's amount policy as the checkout
148
+ * plugins narrow it now (the same computation the checkout enforces) and the synced plan prices.
149
+ */
150
+ export const checkoutReadEntrypoints = (protocols, opts = {}) => {
151
+ const api = handlers();
152
+ const gatewayOf = (ctx) => ctx.service(opts.gatewayAlias ?? GATEWAY_SERVICE);
153
+ const entityOf = (req, ctx) => {
154
+ const entityId = (opts.resolveEntity ?? defaultEntity)(req, ctx);
155
+ if (entityId == null || entityId === '') {
156
+ throw new AuthForbidden('entity');
157
+ }
158
+ return entityId;
159
+ };
160
+ return [
161
+ bind(protocols.base),
162
+ bind(protocols.amountPolicy, api.request(protocols.amountPolicy, async (req, ctx) => {
163
+ const { productSku, planSku } = req.query;
164
+ return await gatewayOf(ctx).amountPolicy(ctx, entityOf(req, ctx), productSku, planSku ?? undefined);
165
+ })),
166
+ bind(protocols.planPrices, api.request(protocols.planPrices, async (req, ctx) => {
167
+ entityOf(req, ctx);
168
+ const { productSku } = req.query;
169
+ return { prices: await gatewayOf(ctx).planPrices(ctx, productSku) };
170
+ })),
171
+ ];
172
+ };
173
+ //# sourceMappingURL=handlers.js.map