@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,69 @@
1
+ /**
2
+ * Invoice payload codec. Telegram echoes the `invoice_payload` string
3
+ * back on `pre_checkout_query` and `successful_payment`; we use it to
4
+ * recover the `(productKey, userId)` pair that the in-flight purchase
5
+ * refers to.
6
+ *
7
+ * Constraints from Telegram:
8
+ * - Max 128 bytes.
9
+ * - Must be a string (we use printable ASCII).
10
+ *
11
+ * Format: `${productKey}|${userId}`
12
+ *
13
+ * The `|` delimiter is unambiguous because productKey is one of:
14
+ * - `vip.${number}` — only digits and `.`
15
+ * - `credits.${number}` — only digits and `.`
16
+ * - `perks.${key}` — key restricted to `/^[a-z][a-z0-9_]*$/i`
17
+ * by `config.ts` validation
18
+ *
19
+ * None of those contain `|`, so the first `|` is always the delimiter.
20
+ * Worst case length is ~`perks.${64-char-key}|${20-digit-int}` ≈ 90 bytes
21
+ * — comfortably under the 128-byte cap. We assert the length to fail
22
+ * loud if the cap is ever approached.
23
+ */
24
+ import { panic } from "../../offensive.js";
25
+ const MAX_PAYLOAD_BYTES = 128;
26
+ /**
27
+ * Build the `invoice_payload` string for a `sendInvoice` call. Throws
28
+ * `Panic` if the result would exceed Telegram's 128-byte cap — that's
29
+ * either a bad productKey or a userId out of bounds, both of which are
30
+ * bugs we want to scream about.
31
+ */
32
+ export const encodePayload = (productKey, userId) => {
33
+ if (!productKey.length) {
34
+ panic("bot/payments/payload: encodePayload called with empty productKey");
35
+ }
36
+ if (!Number.isInteger(userId) || userId <= 0) {
37
+ panic(`bot/payments/payload: encodePayload userId must be positive integer (got ${userId})`);
38
+ }
39
+ const payload = `${productKey}|${userId}`;
40
+ // ASCII-only by construction (productKey shape + decimal digits), so
41
+ // .length === byte length. Cheap check.
42
+ if (payload.length > MAX_PAYLOAD_BYTES) {
43
+ panic(`bot/payments/payload: encoded payload ${payload.length}B exceeds Telegram's ` +
44
+ `${MAX_PAYLOAD_BYTES}B cap. productKey="${productKey}" userId=${userId}`);
45
+ }
46
+ return payload;
47
+ };
48
+ /**
49
+ * Decode the `invoice_payload` Telegram echoes back. Returns `undefined`
50
+ * (NOT throws) on malformed input — Telegram could in principle deliver
51
+ * a payload from a different bot version, and we want the pre_checkout
52
+ * handler to be able to reject it gracefully rather than crashing.
53
+ */
54
+ export const decodePayload = (payload) => {
55
+ if (typeof payload !== "string" || payload.length === 0)
56
+ return undefined;
57
+ const idx = payload.indexOf("|");
58
+ if (idx <= 0 || idx === payload.length - 1)
59
+ return undefined;
60
+ const productKey = payload.slice(0, idx);
61
+ const userIdStr = payload.slice(idx + 1);
62
+ if (!/^\d+$/.test(userIdStr))
63
+ return undefined;
64
+ const userId = Number.parseInt(userIdStr, 10);
65
+ if (!Number.isInteger(userId) || userId <= 0)
66
+ return undefined;
67
+ return { productKey, userId };
68
+ };
69
+ //# sourceMappingURL=payload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../../src/bot/payments/payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAO9B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,UAAkB,EAAE,MAAc,EAAU,EAAE;IAC3E,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC9C,KAAK,CACJ,4EAA4E,MAAM,GAAG,CACrF,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,CAAC;IAC1C,qEAAqE;IACrE,wCAAwC;IACxC,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACxC,KAAK,CACJ,yCAAyC,OAAO,CAAC,MAAM,uBAAuB;YAC7E,GAAG,iBAAiB,sBAAsB,UAAU,YAAY,MAAM,EAAE,CACzE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAe,EAA8B,EAAE;IAC5E,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1E,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Fragment payout ledger — the bot author's accounting surface.
3
+ *
4
+ * Fragment Corp redeems your accumulated XTR balance into TON paid to
5
+ * the configured wallet. That payout is the **income event** for
6
+ * Spanish autónomo IRPF purposes (CLAUDE.md §4); the per-charge log on
7
+ * Telegram's side is informational. We persist each payout you receive
8
+ * here, optionally tagged with the Spanish factura number, and provide
9
+ * an `export()` helper that joins charges falling in the payout's time
10
+ * window — that's what your gestor needs.
11
+ *
12
+ * Calling pattern (called by the bot author, NOT from inside an event
13
+ * handler):
14
+ *
15
+ * import { Bot } from 'gramio'
16
+ * const bot = new Bot(token); await bot.start()
17
+ *
18
+ * // After confirming a Fragment payout to your TON wallet:
19
+ * await payments.payouts.record(bot, {
20
+ * ton: 12.34,
21
+ * eurAtReceipt: 47.20,
22
+ * fromMs: Date.parse('2026-05-01'),
23
+ * toMs: Date.parse('2026-06-01'),
24
+ * })
25
+ *
26
+ * // For the monthly gestor export:
27
+ * const data = await payments.payouts.exportForUsers(bot, userIds, {
28
+ * from: Date.parse('2026-05-01'),
29
+ * to: Date.parse('2026-06-01'),
30
+ * })
31
+ *
32
+ * Fragment doesn't tag payouts with specific charges, so the join is
33
+ * **time-windowed**: payouts get `{ fromMs, toMs }` and the export
34
+ * pulls every `ChargeRecord` whose `receivedAt` falls inside.
35
+ */
36
+ import type { AnyBot } from "gramio";
37
+ import type { PaymentsStores } from "./stores.js";
38
+ import type { ChargeRecord, PayoutRecord } from "./types.js";
39
+ export type RecordPayoutInput = {
40
+ /** Amount of TON received from Fragment. */
41
+ ton: number;
42
+ /** EUR value at receipt time (snapshot — exchange rates move). */
43
+ eurAtReceipt: number;
44
+ /** Window covered by this payout, lower bound (ms since epoch). */
45
+ fromMs: number;
46
+ /** Window covered by this payout, upper bound (ms since epoch). */
47
+ toMs: number;
48
+ /** Optional Spanish factura number issued for this payout. */
49
+ facturaNumber?: string;
50
+ /** Override the auto-generated batchId. Default: `payout_${toMs}`. */
51
+ batchId?: string;
52
+ };
53
+ export declare const recordPayout: (bot: AnyBot, stores: PaymentsStores, input: RecordPayoutInput) => Promise<PayoutRecord>;
54
+ export declare const listPayouts: (bot: AnyBot, stores: PaymentsStores) => Promise<PayoutRecord[]>;
55
+ export type ExportInput = {
56
+ /** Window lower bound, ms since epoch. */
57
+ from: number;
58
+ /** Window upper bound, ms since epoch. */
59
+ to: number;
60
+ };
61
+ export type ExportResult = {
62
+ payouts: PayoutRecord[];
63
+ charges: ChargeRecord[];
64
+ };
65
+ /**
66
+ * Build the payout-only dataset for a time window. Returns every
67
+ * `PayoutRecord` whose `[fromMs, toMs]` overlaps the requested window.
68
+ *
69
+ * The `charges` field is intentionally empty — without a global charge
70
+ * index (v2 TODO) we can't enumerate every user's charges from one
71
+ * call. Use `exportPayoutsForUsers` with an explicit user-id list.
72
+ */
73
+ export declare const exportPayouts: (bot: AnyBot, stores: PaymentsStores, input: ExportInput) => Promise<ExportResult>;
74
+ /**
75
+ * Same as `exportPayouts` but with an explicit user-id list to scan.
76
+ * Pulls every NON-refunded `ChargeRecord` whose `receivedAt` falls in
77
+ * the requested window, joined by time rather than payout identity
78
+ * (Fragment doesn't expose that mapping).
79
+ */
80
+ export declare const exportPayoutsForUsers: (bot: AnyBot, stores: PaymentsStores, userIds: ReadonlyArray<number>, input: ExportInput) => Promise<ExportResult>;
81
+ export type PayoutsApi = {
82
+ record: (bot: AnyBot, input: RecordPayoutInput) => Promise<PayoutRecord>;
83
+ list: (bot: AnyBot) => Promise<PayoutRecord[]>;
84
+ export: (bot: AnyBot, input: ExportInput) => Promise<ExportResult>;
85
+ exportForUsers: (bot: AnyBot, userIds: ReadonlyArray<number>, input: ExportInput) => Promise<ExportResult>;
86
+ };
87
+ export declare const buildPayoutsApi: (stores: PaymentsStores) => PayoutsApi;
88
+ //# sourceMappingURL=payouts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payouts.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/payouts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAwB7D,MAAM,MAAM,iBAAiB,GAAG;IAC/B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAyBF,eAAO,MAAM,YAAY,GACxB,KAAK,MAAM,EACX,QAAQ,cAAc,EACtB,OAAO,iBAAiB,KACtB,OAAO,CAAC,YAAY,CAMtB,CAAC;AAEF,eAAO,MAAM,WAAW,GACvB,KAAK,MAAM,EACX,QAAQ,cAAc,KACpB,OAAO,CAAC,YAAY,EAAE,CAOxB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;CACxB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACzB,KAAK,MAAM,EACX,QAAQ,cAAc,EACtB,OAAO,WAAW,KAChB,OAAO,CAAC,YAAY,CAWtB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,KAAK,MAAM,EACX,QAAQ,cAAc,EACtB,SAAS,aAAa,CAAC,MAAM,CAAC,EAC9B,OAAO,WAAW,KAChB,OAAO,CAAC,YAAY,CAqBtB,CAAC;AAIF,MAAM,MAAM,UAAU,GAAG;IACxB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACzE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACnE,cAAc,EAAE,CACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,KAAK,EAAE,WAAW,KACd,OAAO,CAAC,YAAY,CAAC,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,QAAQ,cAAc,KAAG,UAMvD,CAAC"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Fragment payout ledger — the bot author's accounting surface.
3
+ *
4
+ * Fragment Corp redeems your accumulated XTR balance into TON paid to
5
+ * the configured wallet. That payout is the **income event** for
6
+ * Spanish autónomo IRPF purposes (CLAUDE.md §4); the per-charge log on
7
+ * Telegram's side is informational. We persist each payout you receive
8
+ * here, optionally tagged with the Spanish factura number, and provide
9
+ * an `export()` helper that joins charges falling in the payout's time
10
+ * window — that's what your gestor needs.
11
+ *
12
+ * Calling pattern (called by the bot author, NOT from inside an event
13
+ * handler):
14
+ *
15
+ * import { Bot } from 'gramio'
16
+ * const bot = new Bot(token); await bot.start()
17
+ *
18
+ * // After confirming a Fragment payout to your TON wallet:
19
+ * await payments.payouts.record(bot, {
20
+ * ton: 12.34,
21
+ * eurAtReceipt: 47.20,
22
+ * fromMs: Date.parse('2026-05-01'),
23
+ * toMs: Date.parse('2026-06-01'),
24
+ * })
25
+ *
26
+ * // For the monthly gestor export:
27
+ * const data = await payments.payouts.exportForUsers(bot, userIds, {
28
+ * from: Date.parse('2026-05-01'),
29
+ * to: Date.parse('2026-06-01'),
30
+ * })
31
+ *
32
+ * Fragment doesn't tag payouts with specific charges, so the join is
33
+ * **time-windowed**: payouts get `{ fromMs, toMs }` and the export
34
+ * pulls every `ChargeRecord` whose `receivedAt` falls inside.
35
+ */
36
+ import { panic } from "../../offensive.js";
37
+ // ─── adapter for the offline-call shape ────────────────────────────
38
+ /**
39
+ * Construct the `BotIdCtx` shape `bot/storage` needs when called outside
40
+ * an event (just `{ bot: { info: bot.info } }`). Mirrors the
41
+ * `simulateAccessRequest` pattern in `access-control.ts`.
42
+ *
43
+ * Throws if the bot's `info` isn't populated yet — usually because the
44
+ * caller forgot to `await bot.start()` (or `bot.init()`) first.
45
+ */
46
+ const ctxFor = (bot) => {
47
+ const info = bot.info;
48
+ if (!info) {
49
+ panic("bot/payments/payouts: bot.info is undefined — call bot.start() (or bot.init()) first");
50
+ }
51
+ return { bot: { info } };
52
+ };
53
+ const buildPayoutRecord = (input) => {
54
+ if (input.ton <= 0)
55
+ panic(`bot/payments/payouts: ton must be positive (got ${input.ton})`);
56
+ if (input.eurAtReceipt < 0)
57
+ panic(`bot/payments/payouts: eurAtReceipt must be >= 0 (got ${input.eurAtReceipt})`);
58
+ if (input.fromMs >= input.toMs)
59
+ panic(`bot/payments/payouts: fromMs must be < toMs (got ${input.fromMs} / ${input.toMs})`);
60
+ const batchId = input.batchId ?? `payout_${input.toMs}`;
61
+ return {
62
+ batchId,
63
+ fromMs: input.fromMs,
64
+ toMs: input.toMs,
65
+ tonAmount: input.ton,
66
+ eurAtReceipt: input.eurAtReceipt,
67
+ recordedAt: Date.now(),
68
+ facturaNumber: input.facturaNumber,
69
+ };
70
+ };
71
+ export const recordPayout = async (bot, stores, input) => {
72
+ const ctx = ctxFor(bot);
73
+ const record = buildPayoutRecord(input);
74
+ await stores.payouts.set(ctx, record.batchId, record);
75
+ await stores.payoutsIndex.prepend(ctx, record.batchId);
76
+ return record;
77
+ };
78
+ export const listPayouts = async (bot, stores) => {
79
+ const ctx = ctxFor(bot);
80
+ const ids = await stores.payoutsIndex.list(ctx);
81
+ const records = await Promise.all(ids.map((id) => stores.payouts.get(ctx, id)));
82
+ return records.filter((r) => r !== undefined);
83
+ };
84
+ /**
85
+ * Build the payout-only dataset for a time window. Returns every
86
+ * `PayoutRecord` whose `[fromMs, toMs]` overlaps the requested window.
87
+ *
88
+ * The `charges` field is intentionally empty — without a global charge
89
+ * index (v2 TODO) we can't enumerate every user's charges from one
90
+ * call. Use `exportPayoutsForUsers` with an explicit user-id list.
91
+ */
92
+ export const exportPayouts = async (bot, stores, input) => {
93
+ if (input.from >= input.to) {
94
+ panic(`bot/payments/payouts: export from must be < to (got ${input.from} / ${input.to})`);
95
+ }
96
+ const allPayouts = await listPayouts(bot, stores);
97
+ const payouts = allPayouts.filter((p) => !(p.toMs < input.from || p.fromMs > input.to));
98
+ return { payouts, charges: [] };
99
+ };
100
+ /**
101
+ * Same as `exportPayouts` but with an explicit user-id list to scan.
102
+ * Pulls every NON-refunded `ChargeRecord` whose `receivedAt` falls in
103
+ * the requested window, joined by time rather than payout identity
104
+ * (Fragment doesn't expose that mapping).
105
+ */
106
+ export const exportPayoutsForUsers = async (bot, stores, userIds, input) => {
107
+ const ctx = ctxFor(bot);
108
+ const allPayouts = await listPayouts(bot, stores);
109
+ const payouts = allPayouts.filter((p) => !(p.toMs < input.from || p.fromMs > input.to));
110
+ const allCharges = [];
111
+ for (const userId of userIds) {
112
+ const chargeIds = await stores.userCharges(userId).list(ctx);
113
+ for (const cid of chargeIds) {
114
+ const c = await stores.charges.get(ctx, cid);
115
+ if (!c)
116
+ continue;
117
+ if (c.paysupportState === "refunded")
118
+ continue;
119
+ if (c.receivedAt >= input.from && c.receivedAt <= input.to) {
120
+ allCharges.push(c);
121
+ }
122
+ }
123
+ }
124
+ allCharges.sort((a, b) => a.receivedAt - b.receivedAt);
125
+ return { payouts, charges: allCharges };
126
+ };
127
+ export const buildPayoutsApi = (stores) => ({
128
+ record: (bot, input) => recordPayout(bot, stores, input),
129
+ list: (bot) => listPayouts(bot, stores),
130
+ export: (bot, input) => exportPayouts(bot, stores, input),
131
+ exportForUsers: (bot, userIds, input) => exportPayoutsForUsers(bot, stores, userIds, input),
132
+ });
133
+ //# sourceMappingURL=payouts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payouts.js","sourceRoot":"","sources":["../../../src/bot/payments/payouts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * `botPayments({...})` — the top-level factory + GramIO plugin
3
+ * assembly.
4
+ *
5
+ * This file is **wiring only** per `bot/CLAUDE.md` § plugin file
6
+ * convention. Every handler body lives in its concern-specific
7
+ * neighbour (`derive.ts` / `handlers.ts` / `callbacks.ts` /
8
+ * `commands.ts` / `refund.ts`); this file's job is to stitch them
9
+ * into a `Plugin` chain.
10
+ *
11
+ * What gets wired:
12
+ *
13
+ * - `ctx.payments.*` surface via `.derive()` → `derive.ts`
14
+ * - `pre_checkout_query` handler → `handlers.ts`
15
+ * - `successful_payment` (dedicated event) → `handlers.ts`
16
+ * - Waiver consent / cancel callbacks → `callbacks.ts` + `waiver.ts`
17
+ * - Refund request / approve / deny / close → `callbacks.ts` + `refund.ts`
18
+ * - `/paysupport` slash command → `commands.ts`
19
+ *
20
+ * Returned alongside the plugin: `menuItem` (drop-in for `botMenu`),
21
+ * `payouts` (Fragment ledger), `admin` (read helpers for custom
22
+ * admin commands), and `onFulfilled` (event hook registration).
23
+ *
24
+ * See `CLAUDE.md` in this folder for the design rationale and flows.
25
+ */
26
+ import type { session } from "@gramio/session";
27
+ import type { Storage } from "@gramio/storage";
28
+ import { type DeriveDefinitions, Plugin } from "gramio";
29
+ import { buildCatalog } from "./config.js";
30
+ import { type PaymentsDerived } from "./derive.js";
31
+ import { buildPaymentsMenuItem } from "./menu-item.js";
32
+ import { type PayoutsApi } from "./payouts.js";
33
+ import { type PaymentsStores } from "./stores.js";
34
+ import type { AtLeastKey, BotPaymentsConfig, ChargeRecord, FulfillmentEvent, PaymentsSession, ProductKey, TierKey } from "./types.js";
35
+ type SessionLike = {
36
+ pay?: PaymentsSession;
37
+ language?: string;
38
+ };
39
+ type PaySessionPluginRef = ReturnType<typeof session<SessionLike, "session">>;
40
+ type TierFn<Cfg extends BotPaymentsConfig<string>> = (() => TierKey<Cfg>) & {
41
+ level: () => number;
42
+ /**
43
+ * Resolved display label of the current rung in `ctx.session.language`,
44
+ * or `undefined` on free tier. Named `label` (not `name`) because
45
+ * `Function.prototype.name` is read-only in strict mode and we attach
46
+ * this onto a callable.
47
+ */
48
+ label: () => string | undefined;
49
+ };
50
+ export type PaymentsCtx<Cfg extends BotPaymentsConfig<string>> = {
51
+ atLeast: (id: AtLeastKey<Cfg>) => boolean;
52
+ tier: TierFn<Cfg>;
53
+ has: (perkId: string) => boolean;
54
+ credits: PaymentsDerived["credits"];
55
+ require: (id: AtLeastKey<Cfg>, opts?: {
56
+ feature?: {
57
+ en: string;
58
+ es: string;
59
+ } | string;
60
+ }) => Promise<boolean>;
61
+ invoice: (productKey: ProductKey<Cfg>) => Promise<"invoice_sent" | "waiver_prompt_sent" | "unknown_product">;
62
+ };
63
+ /**
64
+ * Admin-side read helpers exposed on `payments.admin.*`. Bot authors
65
+ * use these when writing custom admin commands (e.g. `/refunds`) that
66
+ * bypass the user-initiated refund-request flow. ctx only needs
67
+ * `.bot.info.id` for namespacing — pass any real gramio ctx or a
68
+ * synthetic `{ bot: { info: bot.info } }` for offline scripts.
69
+ */
70
+ export type PaymentsAdmin = {
71
+ /** Every non-pruned charge for a user, newest-first. */
72
+ listCharges: (ctx: {
73
+ bot: {
74
+ info: {
75
+ id: number;
76
+ };
77
+ };
78
+ }, userId: number) => Promise<ReadonlyArray<ChargeRecord>>;
79
+ /** One charge by id; `undefined` if not found. */
80
+ getCharge: (ctx: {
81
+ bot: {
82
+ info: {
83
+ id: number;
84
+ };
85
+ };
86
+ }, chargeId: string) => Promise<ChargeRecord | undefined>;
87
+ };
88
+ export type BotPaymentsResult<Cfg extends BotPaymentsConfig<string>> = {
89
+ plugin: ReturnType<typeof buildPlugin>;
90
+ menuItem: ReturnType<typeof buildPaymentsMenuItem>;
91
+ payouts: PayoutsApi;
92
+ admin: PaymentsAdmin;
93
+ onFulfilled: (productKey: ProductKey<Cfg> | "*", handler: (event: FulfillmentEvent, ctx: unknown) => void) => void;
94
+ };
95
+ export type BotPaymentsOptions<Cfg extends BotPaymentsConfig<string>> = Cfg & {
96
+ /** Shared session plugin. Required — see CLAUDE.md §"Storage layout". */
97
+ session: PaySessionPluginRef;
98
+ /**
99
+ * Storage backend for the global ledger (charges, payouts, refunds,
100
+ * idempotency). MUST be the same instance backing `session`.
101
+ */
102
+ storage: Storage;
103
+ };
104
+ declare const buildPlugin: (args: {
105
+ cfg: BotPaymentsConfig<string>;
106
+ catalog: ReturnType<typeof buildCatalog>;
107
+ sessionPlugin: PaySessionPluginRef;
108
+ storage: Storage;
109
+ stores: PaymentsStores;
110
+ onFulfilledMap: Map<string, Array<(event: FulfillmentEvent, ctx: unknown) => void>>;
111
+ }) => Plugin<Record<string, never>, DeriveDefinitions & {
112
+ global: {
113
+ payments: PaymentsCtx<BotPaymentsConfig<string>>;
114
+ };
115
+ } & {
116
+ message: {
117
+ session: SessionLike & {
118
+ $clear: () => Promise<void>;
119
+ };
120
+ };
121
+ channel_post: {
122
+ session: SessionLike & {
123
+ $clear: () => Promise<void>;
124
+ };
125
+ };
126
+ inline_query: {
127
+ session: SessionLike & {
128
+ $clear: () => Promise<void>;
129
+ };
130
+ };
131
+ chosen_inline_result: {
132
+ session: SessionLike & {
133
+ $clear: () => Promise<void>;
134
+ };
135
+ };
136
+ callback_query: {
137
+ session: SessionLike & {
138
+ $clear: () => Promise<void>;
139
+ };
140
+ };
141
+ shipping_query: {
142
+ session: SessionLike & {
143
+ $clear: () => Promise<void>;
144
+ };
145
+ };
146
+ pre_checkout_query: {
147
+ session: SessionLike & {
148
+ $clear: () => Promise<void>;
149
+ };
150
+ };
151
+ poll_answer: {
152
+ session: SessionLike & {
153
+ $clear: () => Promise<void>;
154
+ };
155
+ };
156
+ chat_join_request: {
157
+ session: SessionLike & {
158
+ $clear: () => Promise<void>;
159
+ };
160
+ };
161
+ new_chat_members: {
162
+ session: SessionLike & {
163
+ $clear: () => Promise<void>;
164
+ };
165
+ };
166
+ new_chat_title: {
167
+ session: SessionLike & {
168
+ $clear: () => Promise<void>;
169
+ };
170
+ };
171
+ new_chat_photo: {
172
+ session: SessionLike & {
173
+ $clear: () => Promise<void>;
174
+ };
175
+ };
176
+ delete_chat_photo: {
177
+ session: SessionLike & {
178
+ $clear: () => Promise<void>;
179
+ };
180
+ };
181
+ group_chat_created: {
182
+ session: SessionLike & {
183
+ $clear: () => Promise<void>;
184
+ };
185
+ };
186
+ message_auto_delete_timer_changed: {
187
+ session: SessionLike & {
188
+ $clear: () => Promise<void>;
189
+ };
190
+ };
191
+ migrate_to_chat_id: {
192
+ session: SessionLike & {
193
+ $clear: () => Promise<void>;
194
+ };
195
+ };
196
+ migrate_from_chat_id: {
197
+ session: SessionLike & {
198
+ $clear: () => Promise<void>;
199
+ };
200
+ };
201
+ pinned_message: {
202
+ session: SessionLike & {
203
+ $clear: () => Promise<void>;
204
+ };
205
+ };
206
+ invoice: {
207
+ session: SessionLike & {
208
+ $clear: () => Promise<void>;
209
+ };
210
+ };
211
+ successful_payment: {
212
+ session: SessionLike & {
213
+ $clear: () => Promise<void>;
214
+ };
215
+ };
216
+ chat_shared: {
217
+ session: SessionLike & {
218
+ $clear: () => Promise<void>;
219
+ };
220
+ };
221
+ proximity_alert_triggered: {
222
+ session: SessionLike & {
223
+ $clear: () => Promise<void>;
224
+ };
225
+ };
226
+ video_chat_scheduled: {
227
+ session: SessionLike & {
228
+ $clear: () => Promise<void>;
229
+ };
230
+ };
231
+ video_chat_started: {
232
+ session: SessionLike & {
233
+ $clear: () => Promise<void>;
234
+ };
235
+ };
236
+ video_chat_ended: {
237
+ session: SessionLike & {
238
+ $clear: () => Promise<void>;
239
+ };
240
+ };
241
+ video_chat_participants_invited: {
242
+ session: SessionLike & {
243
+ $clear: () => Promise<void>;
244
+ };
245
+ };
246
+ web_app_data: {
247
+ session: SessionLike & {
248
+ $clear: () => Promise<void>;
249
+ };
250
+ };
251
+ location: {
252
+ session: SessionLike & {
253
+ $clear: () => Promise<void>;
254
+ };
255
+ };
256
+ passport_data: {
257
+ session: SessionLike & {
258
+ $clear: () => Promise<void>;
259
+ };
260
+ };
261
+ } & {
262
+ message: {
263
+ payments: PaymentsCtx<BotPaymentsConfig<string>>;
264
+ };
265
+ callback_query: {
266
+ payments: PaymentsCtx<BotPaymentsConfig<string>>;
267
+ };
268
+ }, {}>;
269
+ export declare const botPayments: <const Cfg extends BotPaymentsConfig<string>>(opts: BotPaymentsOptions<Cfg>) => BotPaymentsResult<Cfg>;
270
+ export {};
271
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/plugin.ts"],"names":[],"mappings":"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"}