@adriangalilea/utils 0.18.0 → 0.20.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 (101) hide show
  1. package/README.md +3 -1
  2. package/dist/bot/access-control.d.ts.map +1 -1
  3. package/dist/bot/access-control.js +17 -1
  4. package/dist/bot/access-control.js.map +1 -1
  5. package/dist/bot/allow-list.d.ts +38 -0
  6. package/dist/bot/allow-list.d.ts.map +1 -0
  7. package/dist/bot/allow-list.js +41 -0
  8. package/dist/bot/allow-list.js.map +1 -0
  9. package/dist/bot/callbacks.d.ts +89 -0
  10. package/dist/bot/callbacks.d.ts.map +1 -0
  11. package/dist/bot/callbacks.js +128 -0
  12. package/dist/bot/callbacks.js.map +1 -0
  13. package/dist/bot/ctx.d.ts +219 -0
  14. package/dist/bot/ctx.d.ts.map +1 -0
  15. package/dist/bot/ctx.js +52 -0
  16. package/dist/bot/ctx.js.map +1 -0
  17. package/dist/bot/index.d.ts +4 -0
  18. package/dist/bot/index.d.ts.map +1 -1
  19. package/dist/bot/index.js +4 -0
  20. package/dist/bot/index.js.map +1 -1
  21. package/dist/bot/kit.d.ts +1 -1
  22. package/dist/bot/kit.d.ts.map +1 -1
  23. package/dist/bot/kit.js +4 -2
  24. package/dist/bot/kit.js.map +1 -1
  25. package/dist/bot/menu.d.ts +23 -1
  26. package/dist/bot/menu.d.ts.map +1 -1
  27. package/dist/bot/menu.js +30 -1
  28. package/dist/bot/menu.js.map +1 -1
  29. package/dist/bot/payments/callbacks.d.ts +28 -0
  30. package/dist/bot/payments/callbacks.d.ts.map +1 -0
  31. package/dist/bot/payments/callbacks.js +102 -0
  32. package/dist/bot/payments/callbacks.js.map +1 -0
  33. package/dist/bot/payments/commands.d.ts +26 -0
  34. package/dist/bot/payments/commands.d.ts.map +1 -0
  35. package/dist/bot/payments/commands.js +41 -0
  36. package/dist/bot/payments/commands.js.map +1 -0
  37. package/dist/bot/payments/config.d.ts +30 -0
  38. package/dist/bot/payments/config.d.ts.map +1 -0
  39. package/dist/bot/payments/config.js +166 -0
  40. package/dist/bot/payments/config.js.map +1 -0
  41. package/dist/bot/payments/derive.d.ts +75 -0
  42. package/dist/bot/payments/derive.d.ts.map +1 -0
  43. package/dist/bot/payments/derive.js +151 -0
  44. package/dist/bot/payments/derive.js.map +1 -0
  45. package/dist/bot/payments/handlers.d.ts +113 -0
  46. package/dist/bot/payments/handlers.d.ts.map +1 -0
  47. package/dist/bot/payments/handlers.js +284 -0
  48. package/dist/bot/payments/handlers.js.map +1 -0
  49. package/dist/bot/payments/index.d.ts +17 -0
  50. package/dist/bot/payments/index.d.ts.map +1 -0
  51. package/dist/bot/payments/index.js +14 -0
  52. package/dist/bot/payments/index.js.map +1 -0
  53. package/dist/bot/payments/invoice.d.ts +101 -0
  54. package/dist/bot/payments/invoice.d.ts.map +1 -0
  55. package/dist/bot/payments/invoice.js +213 -0
  56. package/dist/bot/payments/invoice.js.map +1 -0
  57. package/dist/bot/payments/menu-item.d.ts +41 -0
  58. package/dist/bot/payments/menu-item.d.ts.map +1 -0
  59. package/dist/bot/payments/menu-item.js +259 -0
  60. package/dist/bot/payments/menu-item.js.map +1 -0
  61. package/dist/bot/payments/payload.d.ts +42 -0
  62. package/dist/bot/payments/payload.d.ts.map +1 -0
  63. package/dist/bot/payments/payload.js +69 -0
  64. package/dist/bot/payments/payload.js.map +1 -0
  65. package/dist/bot/payments/payouts.d.ts +88 -0
  66. package/dist/bot/payments/payouts.d.ts.map +1 -0
  67. package/dist/bot/payments/payouts.js +133 -0
  68. package/dist/bot/payments/payouts.js.map +1 -0
  69. package/dist/bot/payments/plugin.d.ts +271 -0
  70. package/dist/bot/payments/plugin.d.ts.map +1 -0
  71. package/dist/bot/payments/plugin.js +141 -0
  72. package/dist/bot/payments/plugin.js.map +1 -0
  73. package/dist/bot/payments/refund.d.ts +124 -0
  74. package/dist/bot/payments/refund.d.ts.map +1 -0
  75. package/dist/bot/payments/refund.js +308 -0
  76. package/dist/bot/payments/refund.js.map +1 -0
  77. package/dist/bot/payments/schemas.d.ts +58 -0
  78. package/dist/bot/payments/schemas.d.ts.map +1 -0
  79. package/dist/bot/payments/schemas.js +55 -0
  80. package/dist/bot/payments/schemas.js.map +1 -0
  81. package/dist/bot/payments/state.d.ts +90 -0
  82. package/dist/bot/payments/state.d.ts.map +1 -0
  83. package/dist/bot/payments/state.js +149 -0
  84. package/dist/bot/payments/state.js.map +1 -0
  85. package/dist/bot/payments/stores.d.ts +44 -0
  86. package/dist/bot/payments/stores.d.ts.map +1 -0
  87. package/dist/bot/payments/stores.js +39 -0
  88. package/dist/bot/payments/stores.js.map +1 -0
  89. package/dist/bot/payments/types.d.ts +299 -0
  90. package/dist/bot/payments/types.d.ts.map +1 -0
  91. package/dist/bot/payments/types.js +39 -0
  92. package/dist/bot/payments/types.js.map +1 -0
  93. package/dist/bot/payments/waiver.d.ts +81 -0
  94. package/dist/bot/payments/waiver.d.ts.map +1 -0
  95. package/dist/bot/payments/waiver.js +145 -0
  96. package/dist/bot/payments/waiver.js.map +1 -0
  97. package/dist/bot/storage.d.ts +154 -0
  98. package/dist/bot/storage.d.ts.map +1 -0
  99. package/dist/bot/storage.js +222 -0
  100. package/dist/bot/storage.js.map +1 -0
  101. package/package.json +28 -3
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Type surface for `bot/payments`. Two layers:
3
+ *
4
+ * 1. **Config shapes** the consumer authors (`BotPaymentsConfig`).
5
+ * 2. **Derived types** the consumer reads through `ctx.payments`
6
+ * (`TierKey`, `AtLeastKey`, `ProductKey`) — narrowed from their
7
+ * config so e.g. `atLeast('vip.5')` is a TS error when only 2
8
+ * rungs are declared.
9
+ *
10
+ * Internal types (the ledger records, the catalog) live below and aren't
11
+ * exported through the package entrypoint.
12
+ *
13
+ * See `CLAUDE.md` in this folder for the design rationale.
14
+ */
15
+ import type { Polyglot } from "../../say/index.js";
16
+ /**
17
+ * The only Telegram-supported subscription period as of 2026 is exactly
18
+ * 2,592,000 seconds (30 days). Modeled as a literal so misconfigurations
19
+ * fail at compile time. See `sendInvoice` /
20
+ * `createInvoiceLink` docs at https://core.telegram.org/bots/api.
21
+ */
22
+ export type SubscriptionPeriod = "30d";
23
+ /** Internal value Telegram expects on the wire (seconds). */
24
+ export declare const SUBSCRIPTION_PERIOD_SECONDS: Readonly<Record<SubscriptionPeriod, number>>;
25
+ /**
26
+ * One rung of the VIP ladder. Position in the array is the rank (1-based
27
+ * id at the call site: `vip.1`, `vip.2`, …). Insert a rung at position 2
28
+ * → existing subscribers on rung 2+ shift by one, plus a trivial storage
29
+ * migration; no string-id churn.
30
+ *
31
+ * `name` is display-only Polyglot — renaming a rung is a config edit; the
32
+ * call sites (`atLeast('vip.2')`) are positional and untouched.
33
+ *
34
+ * `grants.credits` is applied **on every renewal** of this rung (not just
35
+ * the initial purchase). That's the canonical SaaS shape: "VIP gets 1000
36
+ * messages per month."
37
+ */
38
+ export type VipRung<L extends string = string> = {
39
+ readonly xtr: number;
40
+ readonly period: SubscriptionPeriod;
41
+ readonly name: Polyglot<L>;
42
+ readonly grants?: {
43
+ readonly credits?: number;
44
+ };
45
+ };
46
+ /**
47
+ * A consumable credit pack the user buys one-shot to top up their
48
+ * `session.pay.credits` balance. Like VIP rungs, packs are positionally
49
+ * identified (`credits.1`, `credits.2`, …) so the call site doesn't carry
50
+ * a name. Adding/removing packs shifts ids; storage is the charge log
51
+ * (which records the exact productKey at purchase time), so historical
52
+ * charges stay valid even if you remove a pack later.
53
+ */
54
+ export type CreditsPack<L extends string = string> = {
55
+ readonly xtr: number;
56
+ readonly grants: {
57
+ readonly credits: number;
58
+ };
59
+ readonly name?: Polyglot<L>;
60
+ };
61
+ /**
62
+ * Credits axis config. `unit` is the user-facing word for one credit
63
+ * (the menu says e.g. "1234 mensajes"). `packs` is the catalogue of
64
+ * top-up bundles surfaced in the menu and via `invoice('credits.N')`.
65
+ */
66
+ export type CreditsConfig<L extends string = string> = {
67
+ readonly unit: Polyglot<L>;
68
+ readonly packs: ReadonlyArray<CreditsPack<L>>;
69
+ };
70
+ /**
71
+ * Orthogonal one-shot unlock (a "perk"). Named via the user's own key in
72
+ * the config (`perks: { voice_mode: { … } }`), surfaced as
73
+ * `perks.voice_mode` everywhere.
74
+ */
75
+ export type Perk<L extends string = string> = {
76
+ readonly xtr: number;
77
+ readonly name: Polyglot<L>;
78
+ };
79
+ export type PerksConfig<L extends string = string> = Readonly<Record<string, Perk<L>>>;
80
+ export type LegalConfig = {
81
+ readonly sellerName: string;
82
+ readonly nif: string;
83
+ /**
84
+ * Optional terms-of-service URL. When omitted, the consent prompt's
85
+ * 📖 Terms button is hidden — there's no Telegram-side standard ToS
86
+ * to default to (unlike `privacyUrl`).
87
+ */
88
+ readonly termsUrl?: string;
89
+ /**
90
+ * Optional privacy policy URL. When omitted, defaults to Telegram's
91
+ * [Standard Bot Privacy Policy](https://telegram.org/privacy-tpa),
92
+ * which covers what this library's plugins retain (session record,
93
+ * waiver consent, charge log) under "data necessary to function".
94
+ * Override only when your bot retains data beyond what the standard
95
+ * covers.
96
+ */
97
+ readonly privacyUrl?: string;
98
+ };
99
+ /**
100
+ * Fallback when `legal.privacyUrl` is omitted. Identical to the menu
101
+ * plugin's default — both surface the same policy text.
102
+ */
103
+ export declare const DEFAULT_PRIVACY_URL = "https://telegram.org/privacy-tpa";
104
+ /**
105
+ * Art. 103(m) TRLGDCU waiver text. Versioned so a wording change forces
106
+ * re-consent; the version string is stored alongside each charge for audit.
107
+ *
108
+ * The Polyglot covers every locale the bot supports — the plugin
109
+ * validates this at construction.
110
+ */
111
+ export type WaiverConfig<L extends string = string> = {
112
+ readonly version: string;
113
+ readonly text: Polyglot<L>;
114
+ };
115
+ /**
116
+ * What the bot author passes to `botPayments({...})`. The generic `L`
117
+ * carries the bot's Polyglot language union forward into every nested
118
+ * `Polyglot<L>`, so TS catches a missing locale in any sub-config.
119
+ */
120
+ export type BotPaymentsConfig<L extends string = string> = {
121
+ readonly paysupport: string;
122
+ readonly legal: LegalConfig;
123
+ readonly waiver: WaiverConfig<L>;
124
+ readonly vip?: ReadonlyArray<VipRung<L>>;
125
+ readonly credits?: CreditsConfig<L>;
126
+ readonly perks?: PerksConfig<L>;
127
+ };
128
+ type _Range1To<N extends number, Acc extends unknown[] = [unknown]> = Acc["length"] extends N ? Acc["length"] : Acc["length"] | _Range1To<N, [...Acc, unknown]>;
129
+ /**
130
+ * Positional 1-based ids for a tuple. `[a,b,c]` → `1 | 2 | 3`.
131
+ *
132
+ * Empty tuple → `never` (no ids exist).
133
+ */
134
+ export type Indices1<V extends ReadonlyArray<unknown>> = V["length"] extends 0 ? never : V["length"] extends number ? _Range1To<V["length"]> : never;
135
+ /**
136
+ * Tier ids derivable from a `vip` config array. Bare `'vip'` is also
137
+ * accepted as "any rung" by `atLeast` (see `AtLeastKey`).
138
+ */
139
+ export type VipPositionalKeys<V extends ReadonlyArray<unknown> | undefined> = V extends ReadonlyArray<unknown> ? `vip.${Indices1<V>}` : never;
140
+ export type CreditsPackKeys<C extends CreditsConfig<string> | undefined> = C extends {
141
+ packs: infer P;
142
+ } ? P extends ReadonlyArray<unknown> ? `credits.${Indices1<P>}` : never : never;
143
+ export type PerkKeys<P extends PerksConfig<string> | undefined> = P extends PerksConfig<string> ? `perks.${keyof P & string}` : never;
144
+ /**
145
+ * Every product the user can purchase. Discriminated by prefix. Note
146
+ * `perks.X` uses the **author-supplied key** verbatim (e.g.
147
+ * `perks.voice_mode`) while `vip.N` / `credits.N` are positional.
148
+ */
149
+ export type ProductKey<Cfg extends BotPaymentsConfig<string>> = VipPositionalKeys<Cfg["vip"]> | CreditsPackKeys<Cfg["credits"]> | PerkKeys<Cfg["perks"]>;
150
+ /**
151
+ * What `ctx.payments.tier()` can return. `'free'` is the bottom rung;
152
+ * `vip.N` follows the ladder.
153
+ */
154
+ export type TierKey<Cfg extends BotPaymentsConfig<string>> = "free" | VipPositionalKeys<Cfg["vip"]>;
155
+ /**
156
+ * What `ctx.payments.atLeast(...)` accepts. Same as `TierKey` minus
157
+ * `'free'` (atLeast('free') is trivially always true and unhelpful as a
158
+ * gate) plus the bare `'vip'` synonym for "any rung in the namespace".
159
+ */
160
+ export type AtLeastKey<Cfg extends BotPaymentsConfig<string>> = "vip" | VipPositionalKeys<Cfg["vip"]>;
161
+ export type VipRungResolved = {
162
+ readonly id: `vip.${number}`;
163
+ readonly rank: number;
164
+ readonly xtr: number;
165
+ readonly periodSeconds: number;
166
+ readonly name: Polyglot<string>;
167
+ readonly creditsGranted: number;
168
+ };
169
+ export type CreditsPackResolved = {
170
+ readonly id: `credits.${number}`;
171
+ readonly xtr: number;
172
+ readonly creditsGranted: number;
173
+ readonly name?: Polyglot<string>;
174
+ };
175
+ export type PerkResolved = {
176
+ readonly id: `perks.${string}`;
177
+ readonly key: string;
178
+ readonly xtr: number;
179
+ readonly name: Polyglot<string>;
180
+ };
181
+ export type ProductCatalog = {
182
+ readonly vip: ReadonlyArray<VipRungResolved>;
183
+ readonly creditsUnit: Polyglot<string> | undefined;
184
+ readonly creditsPacks: ReadonlyArray<CreditsPackResolved>;
185
+ readonly perks: ReadonlyArray<PerkResolved>;
186
+ /** O(1) lookup by ProductKey. */
187
+ readonly byKey: ReadonlyMap<string, VipRungResolved | CreditsPackResolved | PerkResolved>;
188
+ };
189
+ export type WaiverRecord = {
190
+ readonly at: number;
191
+ readonly version: string;
192
+ readonly locale: string;
193
+ };
194
+ export type VipState = {
195
+ /** 1-based rank — index into the resolved vip ladder + 1. */
196
+ readonly rung: number;
197
+ /** Original `telegram_payment_charge_id` of the active subscription. */
198
+ readonly chargeId: string;
199
+ /** Telegram's `subscription_expiration_date` — unix seconds. */
200
+ readonly expiresAt: number;
201
+ /** True after the user toggled cancel-renewal; access keeps until `expiresAt`. */
202
+ readonly canceled: boolean;
203
+ };
204
+ export type PerkState = {
205
+ readonly chargeId: string;
206
+ readonly at: number;
207
+ };
208
+ export type PaymentsSession = {
209
+ waiver?: WaiverRecord;
210
+ credits?: number;
211
+ vip?: VipState;
212
+ perks?: Record<string, PerkState>;
213
+ };
214
+ export type PaysupportState = "none" | "opened" | "refunded";
215
+ export type ChargeRecord = {
216
+ readonly chargeId: string;
217
+ readonly userId: number;
218
+ readonly productKey: string;
219
+ readonly xtr: number;
220
+ readonly receivedAt: number;
221
+ /** Decoded invoice payload echoed back from Telegram. */
222
+ readonly payload: string;
223
+ /**
224
+ * Waiver snapshot at purchase time. Captured even if the user
225
+ * already consented earlier — defensive forensics.
226
+ */
227
+ readonly waiverSnapshot: WaiverRecord;
228
+ /**
229
+ * Telegram's `subscription_expiration_date` (unix seconds) if this
230
+ * charge was a subscription purchase. Used to compute tier expiry.
231
+ */
232
+ readonly subscriptionExpiresAt?: number;
233
+ /**
234
+ * Credits this charge grants at fulfillment / renewal. Denormalized
235
+ * onto the record so derivation works even if the catalog config
236
+ * later changes (rare, but the charge log must remain authoritative
237
+ * regardless of source-code edits). Zero for products that don't
238
+ * grant credits.
239
+ */
240
+ readonly creditsGranted: number;
241
+ /**
242
+ * 1-based vip rung this charge subscribed to, denormalized for the
243
+ * same reason as `creditsGranted`. Only set for `vip.*` charges.
244
+ */
245
+ readonly vipRung?: number;
246
+ /**
247
+ * Perk key this charge unlocked, denormalized. Only set for
248
+ * `perks.*` charges.
249
+ */
250
+ readonly perkKey?: string;
251
+ paysupportState: PaysupportState;
252
+ refundedAt?: number | null;
253
+ /** Set after `record()` claims this charge into a payout batch. */
254
+ payoutBatchId?: string | null;
255
+ };
256
+ export type PayoutRecord = {
257
+ readonly batchId: string;
258
+ readonly fromMs: number;
259
+ readonly toMs: number;
260
+ readonly tonAmount: number;
261
+ readonly eurAtReceipt: number;
262
+ readonly recordedAt: number;
263
+ /** Optional Spanish factura number issued for this payout. */
264
+ facturaNumber?: string;
265
+ };
266
+ /**
267
+ * Snapshot returned by `derive.ts` — pure function of the charge log
268
+ * for a given user. The plugin writes this into `session.pay.*` so
269
+ * runtime checks are O(1), but the log is always the truth.
270
+ */
271
+ export type DerivedPaymentsState = {
272
+ readonly credits: number;
273
+ readonly vip: VipState | undefined;
274
+ readonly perks: Record<string, PerkState>;
275
+ };
276
+ /**
277
+ * What `payments.onFulfilled(productKey, handler)` receives. Sync
278
+ * signature on purpose — the plugin always answers the
279
+ * `successful_payment` event itself; the handler does its own async work
280
+ * fire-and-forget.
281
+ */
282
+ export type FulfillmentEvent = {
283
+ readonly productKey: string;
284
+ readonly userId: number;
285
+ readonly chargeId: string;
286
+ readonly xtr: number;
287
+ readonly receivedAt: number;
288
+ };
289
+ /**
290
+ * Thrown by `credits.consume(n)` when the balance is below `n`.
291
+ * `tryConsume(n)` returns `false` instead.
292
+ */
293
+ export declare class InsufficientCredits extends Error {
294
+ readonly requested: number;
295
+ readonly available: number;
296
+ constructor(requested: number, available: number);
297
+ }
298
+ export {};
299
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Type surface for `bot/payments`. Two layers:
3
+ *
4
+ * 1. **Config shapes** the consumer authors (`BotPaymentsConfig`).
5
+ * 2. **Derived types** the consumer reads through `ctx.payments`
6
+ * (`TierKey`, `AtLeastKey`, `ProductKey`) — narrowed from their
7
+ * config so e.g. `atLeast('vip.5')` is a TS error when only 2
8
+ * rungs are declared.
9
+ *
10
+ * Internal types (the ledger records, the catalog) live below and aren't
11
+ * exported through the package entrypoint.
12
+ *
13
+ * See `CLAUDE.md` in this folder for the design rationale.
14
+ */
15
+ /** Internal value Telegram expects on the wire (seconds). */
16
+ export const SUBSCRIPTION_PERIOD_SECONDS = {
17
+ "30d": 2_592_000,
18
+ };
19
+ /**
20
+ * Fallback when `legal.privacyUrl` is omitted. Identical to the menu
21
+ * plugin's default — both surface the same policy text.
22
+ */
23
+ export const DEFAULT_PRIVACY_URL = "https://telegram.org/privacy-tpa";
24
+ // ─── error sentinel ────────────────────────────────────────────────
25
+ /**
26
+ * Thrown by `credits.consume(n)` when the balance is below `n`.
27
+ * `tryConsume(n)` returns `false` instead.
28
+ */
29
+ export class InsufficientCredits extends Error {
30
+ requested;
31
+ available;
32
+ constructor(requested, available) {
33
+ super(`InsufficientCredits: requested ${requested}, available ${available}`);
34
+ this.name = "InsufficientCredits";
35
+ this.requested = requested;
36
+ this.available = available;
37
+ }
38
+ }
39
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/bot/payments/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,2BAA2B,GAEpC;IACH,KAAK,EAAE,SAAS;CAChB,CAAC;AAwFF;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,kCAAkC,CAAC;AAoQtE,sEAAsE;AAEtE;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACpC,SAAS,CAAS;IAClB,SAAS,CAAS;IAC3B,YAAY,SAAiB,EAAE,SAAiB;QAC/C,KAAK,CACJ,kCAAkC,SAAS,eAAe,SAAS,EAAE,CACrE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC5B,CAAC;CACD"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Art. 103(m) TRLGDCU waiver flow.
3
+ *
4
+ * Three things have to happen together (see CLAUDE.md §5):
5
+ *
6
+ * 1. The user expresses *prior express consent* to begin execution.
7
+ * 2. They acknowledge they thereby lose the 14-day right of withdrawal.
8
+ * 3. The contract is confirmed on a durable medium.
9
+ *
10
+ * This module owns #1 and #2 via a separate, unbundled consent prompt
11
+ * — one inline message with the waiver text plus a single affirmative
12
+ * button. Bundling this into ToS acceptance voids the waiver under
13
+ * Spanish consumer law. #3 is satisfied implicitly by Telegram: every
14
+ * outgoing message the bot sends is preserved in the chat (durable).
15
+ *
16
+ * The bot author supplies the wording (Polyglot text + version string).
17
+ * Bumping `waiver.version` forces re-consent on the next purchase
18
+ * attempt; old consents stay attached to old charges for audit (see
19
+ * `ChargeRecord.waiverSnapshot`).
20
+ */
21
+ import type { Storage } from "@gramio/storage";
22
+ import { InlineKeyboard } from "gramio";
23
+ import { type BotPaymentsConfig, type PaymentsSession, type WaiverRecord } from "./types.js";
24
+ /**
25
+ * Callback fired when the user taps "✅ Consiento y comprendo" in the
26
+ * waiver consent prompt. Carries the productKey of the in-flight
27
+ * purchase so we can resume after persisting consent.
28
+ *
29
+ * `pk` (productKey) follows the shape validated by `payload.ts` —
30
+ * `vip.N` / `credits.N` / `perks.X` — all ASCII-safe.
31
+ */
32
+ export declare const waiverConsentCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
33
+ /** Fired when the user taps "❌ Cancelar" in the consent prompt. */
34
+ export declare const waiverCancelCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
35
+ /**
36
+ * Returns true when the stored consent matches the current waiver
37
+ * version — i.e. no re-consent needed before a purchase.
38
+ */
39
+ export declare const isWaiverFresh: (stored: WaiverRecord | undefined, currentVersion: string) => boolean;
40
+ /**
41
+ * Build the inline keyboard that accompanies the consent prompt. Three
42
+ * buttons:
43
+ *
44
+ * 📖 Términos — URL to `legal.termsUrl` (informational, not the
45
+ * affirmative action)
46
+ * 🔒 Privacidad — URL to `legal.privacyUrl` (same)
47
+ * ✅ Consiento y comprendo — the single affirmative action
48
+ * ❌ Cancelar — back out without consenting
49
+ *
50
+ * The "✅" button carries the productKey the user was about to buy, so
51
+ * the consent handler can resume the invoice flow on tap.
52
+ */
53
+ export declare const waiverKeyboard: (cfg: BotPaymentsConfig<string>, productKey: string, lang: string) => InlineKeyboard;
54
+ /**
55
+ * Render the consent body the user sees. Includes a short, neutral
56
+ * header above the wording supplied by the bot author. The header is
57
+ * separated by a blank line so reading order matches what consumer
58
+ * authorities expect (waiver text is unambiguous and prominent).
59
+ */
60
+ export declare const renderConsentBody: (cfg: BotPaymentsConfig<string>, lang: string) => string;
61
+ /**
62
+ * Persist a fresh waiver record onto the current ctx's session. Callers
63
+ * own the session shape via a structural narrow — this avoids a generic
64
+ * type bloom across the plugin.
65
+ */
66
+ export declare const persistWaiverOnSession: (ctx: {
67
+ session: {
68
+ pay?: PaymentsSession;
69
+ };
70
+ }, version: string, locale: string) => WaiverRecord;
71
+ /**
72
+ * Persist a waiver record onto a SPECIFIC user's stored session (used
73
+ * when the consent tap arrives on a different ctx than the original
74
+ * `invoice()` call — rare, but possible if the user wedges multiple
75
+ * flows). The cross-user write is via storage directly, mirroring the
76
+ * pattern in `access-control.ts`.
77
+ */
78
+ export declare const persistWaiverForUser: (storage: Storage, ctx: {
79
+ bot: unknown;
80
+ }, userId: number, version: string, locale: string) => Promise<WaiverRecord>;
81
+ //# sourceMappingURL=waiver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waiver.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/waiver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAMxC,OAAO,EACN,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,MAAM,YAAY,CAAC;AAOpB;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,2EAA8C,CAAC;AAE3E,mEAAmE;AACnE,eAAO,MAAM,cAAc,2EAA+B,CAAC;AAI3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GACzB,QAAQ,YAAY,GAAG,SAAS,EAChC,gBAAgB,MAAM,KACpB,OAGF,CAAC;AAiBF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAC1B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,YAAY,MAAM,EAClB,MAAM,MAAM,KACV,cA8BF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC7B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,MAAM,MAAM,KACV,MAWU,CAAC;AAId;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAClC,KAAK;IAAE,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,eAAe,CAAA;KAAE,CAAA;CAAE,EAC3C,SAAS,MAAM,EACf,QAAQ,MAAM,KACZ,YAUF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,OAAO,EAChB,KAAK;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,EACrB,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,QAAQ,MAAM,KACZ,OAAO,CAAC,YAAY,CAatB,CAAC"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Art. 103(m) TRLGDCU waiver flow.
3
+ *
4
+ * Three things have to happen together (see CLAUDE.md §5):
5
+ *
6
+ * 1. The user expresses *prior express consent* to begin execution.
7
+ * 2. They acknowledge they thereby lose the 14-day right of withdrawal.
8
+ * 3. The contract is confirmed on a durable medium.
9
+ *
10
+ * This module owns #1 and #2 via a separate, unbundled consent prompt
11
+ * — one inline message with the waiver text plus a single affirmative
12
+ * button. Bundling this into ToS acceptance voids the waiver under
13
+ * Spanish consumer law. #3 is satisfied implicitly by Telegram: every
14
+ * outgoing message the bot sends is preserved in the chat (durable).
15
+ *
16
+ * The bot author supplies the wording (Polyglot text + version string).
17
+ * Bumping `waiver.version` forces re-consent on the next purchase
18
+ * attempt; old consents stay attached to old charges for audit (see
19
+ * `ChargeRecord.waiverSnapshot`).
20
+ */
21
+ import { InlineKeyboard } from "gramio";
22
+ import { say } from "../../say/index.js";
23
+ import { createLogger } from "../../universal/log.js";
24
+ import { callbackNs } from "../callbacks.js";
25
+ import { botStorageKey } from "../kit.js";
26
+ import { DEFAULT_PRIVACY_URL, } from "./types.js";
27
+ const FALLBACK_LANG = "en";
28
+ const log = createLogger("bot/payments");
29
+ const cb = callbackNs("pay");
30
+ /**
31
+ * Callback fired when the user taps "✅ Consiento y comprendo" in the
32
+ * waiver consent prompt. Carries the productKey of the in-flight
33
+ * purchase so we can resume after persisting consent.
34
+ *
35
+ * `pk` (productKey) follows the shape validated by `payload.ts` —
36
+ * `vip.N` / `credits.N` / `perks.X` — all ASCII-safe.
37
+ */
38
+ export const waiverConsentCb = cb.data("waiver:consent", { pk: "string" });
39
+ /** Fired when the user taps "❌ Cancelar" in the consent prompt. */
40
+ export const waiverCancelCb = cb.data("waiver:cancel", {});
41
+ // ─── pure helpers ──────────────────────────────────────────────────
42
+ /**
43
+ * Returns true when the stored consent matches the current waiver
44
+ * version — i.e. no re-consent needed before a purchase.
45
+ */
46
+ export const isWaiverFresh = (stored, currentVersion) => {
47
+ if (!stored)
48
+ return false;
49
+ return stored.version === currentVersion;
50
+ };
51
+ /**
52
+ * Resolve the waiver text Polyglot to a single string for the recipient's
53
+ * locale, with `'en'` fallback. Matches `say()`'s semantics for the
54
+ * dynamic-string escape hatch.
55
+ */
56
+ const resolveWaiverText = (cfg, lang) => {
57
+ const text = cfg.waiver.text;
58
+ return text[lang] ?? text[FALLBACK_LANG] ?? Object.values(text)[0] ?? "";
59
+ };
60
+ // ─── consent prompt rendering ──────────────────────────────────────
61
+ /**
62
+ * Build the inline keyboard that accompanies the consent prompt. Three
63
+ * buttons:
64
+ *
65
+ * 📖 Términos — URL to `legal.termsUrl` (informational, not the
66
+ * affirmative action)
67
+ * 🔒 Privacidad — URL to `legal.privacyUrl` (same)
68
+ * ✅ Consiento y comprendo — the single affirmative action
69
+ * ❌ Cancelar — back out without consenting
70
+ *
71
+ * The "✅" button carries the productKey the user was about to buy, so
72
+ * the consent handler can resume the invoice flow on tap.
73
+ */
74
+ export const waiverKeyboard = (cfg, productKey, lang) => {
75
+ const kb = new InlineKeyboard();
76
+ const termsUrl = cfg.legal.termsUrl;
77
+ const privacyUrl = cfg.legal.privacyUrl ?? DEFAULT_PRIVACY_URL;
78
+ // Terms button only renders if the bot configured a termsUrl —
79
+ // there's no Telegram-side standard ToS to default to. Privacy
80
+ // always renders; it falls back to Telegram's Standard Bot Privacy
81
+ // Policy when unset (covers what this library retains by default).
82
+ if (termsUrl) {
83
+ kb.url(say({ en: "📖 Terms", es: "📖 Términos" }, lang), termsUrl);
84
+ }
85
+ kb.url(say({ en: "🔒 Privacy", es: "🔒 Privacidad" }, lang), privacyUrl);
86
+ kb.row();
87
+ kb.text(say({
88
+ en: "✅ I consent and understand",
89
+ es: "✅ Consiento y comprendo",
90
+ }, lang), waiverConsentCb.pack({ pk: productKey }), { style: "primary" });
91
+ kb.row();
92
+ kb.text(say({ en: "❌ Cancel", es: "❌ Cancelar" }, lang), waiverCancelCb.pack({}));
93
+ return kb;
94
+ };
95
+ /**
96
+ * Render the consent body the user sees. Includes a short, neutral
97
+ * header above the wording supplied by the bot author. The header is
98
+ * separated by a blank line so reading order matches what consumer
99
+ * authorities expect (waiver text is unambiguous and prominent).
100
+ */
101
+ export const renderConsentBody = (cfg, lang) => [
102
+ say({
103
+ en: "Before continuing, please read and consent:",
104
+ es: "Antes de continuar, lee y consiente:",
105
+ }, lang),
106
+ "",
107
+ resolveWaiverText(cfg, lang),
108
+ ].join("\n");
109
+ // ─── persistence ───────────────────────────────────────────────────
110
+ /**
111
+ * Persist a fresh waiver record onto the current ctx's session. Callers
112
+ * own the session shape via a structural narrow — this avoids a generic
113
+ * type bloom across the plugin.
114
+ */
115
+ export const persistWaiverOnSession = (ctx, version, locale) => {
116
+ const record = {
117
+ at: Date.now(),
118
+ version,
119
+ locale,
120
+ };
121
+ ctx.session.pay ??= {};
122
+ ctx.session.pay.waiver = record;
123
+ log.success(`waiver consent persisted: version=${version} locale=${locale}`);
124
+ return record;
125
+ };
126
+ /**
127
+ * Persist a waiver record onto a SPECIFIC user's stored session (used
128
+ * when the consent tap arrives on a different ctx than the original
129
+ * `invoice()` call — rare, but possible if the user wedges multiple
130
+ * flows). The cross-user write is via storage directly, mirroring the
131
+ * pattern in `access-control.ts`.
132
+ */
133
+ export const persistWaiverForUser = async (storage, ctx, userId, version, locale) => {
134
+ const key = botStorageKey(ctx, userId);
135
+ const full = ((await storage.get(key)) ?? {});
136
+ const record = {
137
+ at: Date.now(),
138
+ version,
139
+ locale,
140
+ };
141
+ full.pay = { ...(full.pay ?? {}), waiver: record };
142
+ await storage.set(key, full);
143
+ return record;
144
+ };
145
+ //# sourceMappingURL=waiver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waiver.js","sourceRoot":"","sources":["../../../src/bot/payments/waiver.ts"],"names":[],"mappings":"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"}