@adriangalilea/utils 0.18.0 → 0.19.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.
- package/README.md +2 -1
- package/dist/bot/access-control.d.ts.map +1 -1
- package/dist/bot/access-control.js +17 -1
- package/dist/bot/access-control.js.map +1 -1
- package/dist/bot/callbacks.d.ts +89 -0
- package/dist/bot/callbacks.d.ts.map +1 -0
- package/dist/bot/callbacks.js +128 -0
- package/dist/bot/callbacks.js.map +1 -0
- package/dist/bot/ctx.d.ts +219 -0
- package/dist/bot/ctx.d.ts.map +1 -0
- package/dist/bot/ctx.js +52 -0
- package/dist/bot/ctx.js.map +1 -0
- package/dist/bot/index.d.ts +4 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +4 -0
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/kit.d.ts +1 -1
- package/dist/bot/kit.d.ts.map +1 -1
- package/dist/bot/kit.js +4 -2
- package/dist/bot/kit.js.map +1 -1
- package/dist/bot/menu.d.ts +23 -1
- package/dist/bot/menu.d.ts.map +1 -1
- package/dist/bot/menu.js +30 -1
- package/dist/bot/menu.js.map +1 -1
- package/dist/bot/payments/callbacks.d.ts +28 -0
- package/dist/bot/payments/callbacks.d.ts.map +1 -0
- package/dist/bot/payments/callbacks.js +102 -0
- package/dist/bot/payments/callbacks.js.map +1 -0
- package/dist/bot/payments/commands.d.ts +26 -0
- package/dist/bot/payments/commands.d.ts.map +1 -0
- package/dist/bot/payments/commands.js +41 -0
- package/dist/bot/payments/commands.js.map +1 -0
- package/dist/bot/payments/config.d.ts +30 -0
- package/dist/bot/payments/config.d.ts.map +1 -0
- package/dist/bot/payments/config.js +166 -0
- package/dist/bot/payments/config.js.map +1 -0
- package/dist/bot/payments/derive.d.ts +75 -0
- package/dist/bot/payments/derive.d.ts.map +1 -0
- package/dist/bot/payments/derive.js +151 -0
- package/dist/bot/payments/derive.js.map +1 -0
- package/dist/bot/payments/handlers.d.ts +113 -0
- package/dist/bot/payments/handlers.d.ts.map +1 -0
- package/dist/bot/payments/handlers.js +284 -0
- package/dist/bot/payments/handlers.js.map +1 -0
- package/dist/bot/payments/index.d.ts +17 -0
- package/dist/bot/payments/index.d.ts.map +1 -0
- package/dist/bot/payments/index.js +14 -0
- package/dist/bot/payments/index.js.map +1 -0
- package/dist/bot/payments/invoice.d.ts +101 -0
- package/dist/bot/payments/invoice.d.ts.map +1 -0
- package/dist/bot/payments/invoice.js +213 -0
- package/dist/bot/payments/invoice.js.map +1 -0
- package/dist/bot/payments/menu-item.d.ts +41 -0
- package/dist/bot/payments/menu-item.d.ts.map +1 -0
- package/dist/bot/payments/menu-item.js +259 -0
- package/dist/bot/payments/menu-item.js.map +1 -0
- package/dist/bot/payments/payload.d.ts +42 -0
- package/dist/bot/payments/payload.d.ts.map +1 -0
- package/dist/bot/payments/payload.js +69 -0
- package/dist/bot/payments/payload.js.map +1 -0
- package/dist/bot/payments/payouts.d.ts +88 -0
- package/dist/bot/payments/payouts.d.ts.map +1 -0
- package/dist/bot/payments/payouts.js +133 -0
- package/dist/bot/payments/payouts.js.map +1 -0
- package/dist/bot/payments/plugin.d.ts +271 -0
- package/dist/bot/payments/plugin.d.ts.map +1 -0
- package/dist/bot/payments/plugin.js +141 -0
- package/dist/bot/payments/plugin.js.map +1 -0
- package/dist/bot/payments/refund.d.ts +124 -0
- package/dist/bot/payments/refund.d.ts.map +1 -0
- package/dist/bot/payments/refund.js +308 -0
- package/dist/bot/payments/refund.js.map +1 -0
- package/dist/bot/payments/schemas.d.ts +58 -0
- package/dist/bot/payments/schemas.d.ts.map +1 -0
- package/dist/bot/payments/schemas.js +55 -0
- package/dist/bot/payments/schemas.js.map +1 -0
- package/dist/bot/payments/state.d.ts +90 -0
- package/dist/bot/payments/state.d.ts.map +1 -0
- package/dist/bot/payments/state.js +149 -0
- package/dist/bot/payments/state.js.map +1 -0
- package/dist/bot/payments/stores.d.ts +44 -0
- package/dist/bot/payments/stores.d.ts.map +1 -0
- package/dist/bot/payments/stores.js +39 -0
- package/dist/bot/payments/stores.js.map +1 -0
- package/dist/bot/payments/types.d.ts +299 -0
- package/dist/bot/payments/types.d.ts.map +1 -0
- package/dist/bot/payments/types.js +39 -0
- package/dist/bot/payments/types.js.map +1 -0
- package/dist/bot/payments/waiver.d.ts +81 -0
- package/dist/bot/payments/waiver.d.ts.map +1 -0
- package/dist/bot/payments/waiver.js +145 -0
- package/dist/bot/payments/waiver.js.map +1 -0
- package/dist/bot/storage.d.ts +154 -0
- package/dist/bot/storage.d.ts.map +1 -0
- package/dist/bot/storage.js +222 -0
- package/dist/bot/storage.js.map +1 -0
- package/package.json +24 -3
|
@@ -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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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
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 { Plugin } from "gramio";
|
|
27
|
+
import { panic } from "../../offensive.js";
|
|
28
|
+
import { narrow } from "../ctx.js";
|
|
29
|
+
import { buildRefundCloseHandler, buildWaiverCancelHandler, buildWaiverConsentHandler, } from "./callbacks.js";
|
|
30
|
+
import { buildPaysupportCommand } from "./commands.js";
|
|
31
|
+
import { buildCatalog, validateConfig } from "./config.js";
|
|
32
|
+
import { buildPaymentsDerive } from "./derive.js";
|
|
33
|
+
import { buildPreCheckoutHandler, buildSuccessfulPaymentHandler, } from "./handlers.js";
|
|
34
|
+
import { buildPaymentsMenuItem } from "./menu-item.js";
|
|
35
|
+
import { buildPayoutsApi } from "./payouts.js";
|
|
36
|
+
import { buildRefundApproveHandler, buildRefundDenyHandler, buildRefundRequestHandler, refundApproveCb, refundCloseCb, refundDenyCb, refundRequestCb, } from "./refund.js";
|
|
37
|
+
import { buildStores } from "./stores.js";
|
|
38
|
+
import { waiverCancelCb, waiverConsentCb } from "./waiver.js";
|
|
39
|
+
// ─── plugin assembly ───────────────────────────────────────────────
|
|
40
|
+
const buildPlugin = (args) => {
|
|
41
|
+
const { cfg, catalog, sessionPlugin, storage, stores, onFulfilledMap } = args;
|
|
42
|
+
const paymentsDerive = buildPaymentsDerive({ cfg, catalog });
|
|
43
|
+
// handler factories (all bodies live in their concern files)
|
|
44
|
+
const successfulPaymentHandler = buildSuccessfulPaymentHandler({
|
|
45
|
+
stores,
|
|
46
|
+
cfg,
|
|
47
|
+
catalog,
|
|
48
|
+
// internals iterate this map per fulfillment, including `"*"` catch-all.
|
|
49
|
+
onFulfilled: onFulfilledMap,
|
|
50
|
+
});
|
|
51
|
+
const preCheckoutHandler = buildPreCheckoutHandler(catalog);
|
|
52
|
+
const refundApproveHandler = buildRefundApproveHandler({
|
|
53
|
+
stores,
|
|
54
|
+
storage,
|
|
55
|
+
cfg,
|
|
56
|
+
});
|
|
57
|
+
const refundDenyHandler = buildRefundDenyHandler({ stores, storage, cfg });
|
|
58
|
+
const refundRequestHandler = buildRefundRequestHandler({
|
|
59
|
+
stores,
|
|
60
|
+
storage,
|
|
61
|
+
cfg,
|
|
62
|
+
});
|
|
63
|
+
const waiverConsentHandler = buildWaiverConsentHandler({ cfg, catalog });
|
|
64
|
+
const waiverCancelHandler = buildWaiverCancelHandler();
|
|
65
|
+
const refundCloseHandler = buildRefundCloseHandler();
|
|
66
|
+
const paysupportCommand = buildPaysupportCommand(cfg);
|
|
67
|
+
return (new Plugin("@adriangalilea/utils/bot/payments", {
|
|
68
|
+
dependencies: ["@adriangalilea/utils/bot/admin"],
|
|
69
|
+
})
|
|
70
|
+
// Declare the shared session as a dep so types flow + runtime
|
|
71
|
+
// dedup ensures the session derive runs once per update.
|
|
72
|
+
.extend(sessionPlugin)
|
|
73
|
+
// ctx.payments builder — see derive.ts
|
|
74
|
+
.derive(["message", "callback_query"], paymentsDerive)
|
|
75
|
+
// pre_checkout_query — synchronous gate, 10s deadline
|
|
76
|
+
.on("pre_checkout_query", async (ctx, next) => {
|
|
77
|
+
await preCheckoutHandler(narrow(ctx));
|
|
78
|
+
await next();
|
|
79
|
+
})
|
|
80
|
+
// successful_payment — dedicated event (NOT a `message` rider;
|
|
81
|
+
// gramio puts it in `allowed_updates` only via the dedicated
|
|
82
|
+
// subscription, see CLAUDE.md §"foot-guns")
|
|
83
|
+
.on("successful_payment", async (ctx, next) => {
|
|
84
|
+
await successfulPaymentHandler(narrow(ctx));
|
|
85
|
+
await next();
|
|
86
|
+
})
|
|
87
|
+
// Waiver consent + cancel callbacks (bodies in callbacks.ts)
|
|
88
|
+
.callbackQuery(waiverConsentCb, waiverConsentHandler)
|
|
89
|
+
.callbackQuery(waiverCancelCb, waiverCancelHandler)
|
|
90
|
+
// Refund flow callbacks (bodies in refund.ts + callbacks.ts)
|
|
91
|
+
.callbackQuery(refundRequestCb, async (ctx) => {
|
|
92
|
+
const c = narrow(ctx);
|
|
93
|
+
const toast = await refundRequestHandler(c);
|
|
94
|
+
await c.answer(toast !== undefined ? { text: toast } : {});
|
|
95
|
+
})
|
|
96
|
+
.callbackQuery(refundApproveCb, async (ctx) => refundApproveHandler(narrow(ctx)))
|
|
97
|
+
.callbackQuery(refundDenyCb, async (ctx) => refundDenyHandler(narrow(ctx)))
|
|
98
|
+
.callbackQuery(refundCloseCb, refundCloseHandler)
|
|
99
|
+
// /paysupport (ToS §6.5; body in commands.ts)
|
|
100
|
+
.command("paysupport", { description: "Help & refunds for payments" }, paysupportCommand));
|
|
101
|
+
};
|
|
102
|
+
// ─── public factory ────────────────────────────────────────────────
|
|
103
|
+
export const botPayments = (opts) => {
|
|
104
|
+
const { session: sessionPlugin, storage, ...rest } = opts;
|
|
105
|
+
const cfg = rest;
|
|
106
|
+
validateConfig(cfg);
|
|
107
|
+
const catalog = buildCatalog(cfg);
|
|
108
|
+
const stores = buildStores(storage);
|
|
109
|
+
const onFulfilledMap = new Map();
|
|
110
|
+
const plugin = buildPlugin({
|
|
111
|
+
cfg,
|
|
112
|
+
catalog,
|
|
113
|
+
sessionPlugin,
|
|
114
|
+
storage,
|
|
115
|
+
stores,
|
|
116
|
+
onFulfilledMap,
|
|
117
|
+
});
|
|
118
|
+
const menuItem = buildPaymentsMenuItem({ cfg, catalog });
|
|
119
|
+
const payouts = buildPayoutsApi(stores);
|
|
120
|
+
const admin = {
|
|
121
|
+
listCharges: async (ctx, userId) => {
|
|
122
|
+
const ids = await stores.userCharges(userId).list(ctx);
|
|
123
|
+
const charges = await Promise.all(ids.map((id) => stores.charges.get(ctx, id)));
|
|
124
|
+
return charges.filter((c) => c !== undefined);
|
|
125
|
+
},
|
|
126
|
+
getCharge: (ctx, chargeId) => stores.charges.get(ctx, chargeId),
|
|
127
|
+
};
|
|
128
|
+
const onFulfilled = (productKey, handler) => {
|
|
129
|
+
if (typeof productKey !== "string" || productKey.length === 0) {
|
|
130
|
+
panic("bot/payments: onFulfilled productKey must be a non-empty string");
|
|
131
|
+
}
|
|
132
|
+
if (typeof handler !== "function") {
|
|
133
|
+
panic("bot/payments: onFulfilled handler must be a function");
|
|
134
|
+
}
|
|
135
|
+
const arr = onFulfilledMap.get(productKey) ?? [];
|
|
136
|
+
arr.push(handler);
|
|
137
|
+
onFulfilledMap.set(productKey, arr);
|
|
138
|
+
};
|
|
139
|
+
return { plugin, menuItem, payouts, admin, onFulfilled };
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/bot/payments/plugin.ts"],"names":[],"mappings":"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"}
|