@adriangalilea/utils 0.17.1 → 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 +9 -9
- package/dist/bot/access-control.d.ts.map +1 -1
- package/dist/bot/access-control.js +62 -45
- 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 +93 -41
- package/dist/bot/kit.d.ts.map +1 -1
- package/dist/bot/kit.js +153 -30
- package/dist/bot/kit.js.map +1 -1
- package/dist/bot/menu.d.ts +31 -11
- package/dist/bot/menu.d.ts.map +1 -1
- package/dist/bot/menu.js +34 -9
- 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,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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,EAA0B,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EACN,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAwB,MAAM,aAAa,CAAC;AACxE,OAAO,EACN,uBAAuB,EACvB,6BAA6B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAmB,MAAM,cAAc,CAAC;AAChE,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,eAAe,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAU/D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAsF9D,sEAAsE;AAEtE,MAAM,WAAW,GAAG,CAAC,IAUpB,EAAE,EAAE;IACJ,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAI9E,MAAM,cAAc,GAAG,mBAAmB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAE7D,6DAA6D;IAC7D,MAAM,wBAAwB,GAAG,6BAA6B,CAAC;QAC9D,MAAM;QACN,GAAG;QACH,OAAO;QACP,yEAAyE;QACzE,WAAW,EAAE,cAGZ;KACD,CAAC,CAAC;IACH,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;QACtD,MAAM;QACN,OAAO;QACP,GAAG;KACH,CAAC,CAAC;IACH,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3E,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;QACtD,MAAM;QACN,OAAO;QACP,GAAG;KACH,CAAC,CAAC;IACH,MAAM,oBAAoB,GAAG,yBAAyB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IACzE,MAAM,mBAAmB,GAAG,wBAAwB,EAAE,CAAC;IACvD,MAAM,kBAAkB,GAAG,uBAAuB,EAAE,CAAC;IACrD,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAEtD,OAAO,CACN,IAAI,MAAM,CAGR,mCAAmC,EAAE;QACtC,YAAY,EAAE,CAAC,gCAAgC,CAAC;KAChD,CAAC;QACD,8DAA8D;QAC9D,yDAAyD;SACxD,MAAM,CAAC,aAAa,CAAC;QACtB,uCAAuC;SACtC,MAAM,CACN,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAC7B,cAA4D,CAC5D;QACD,sDAAsD;SACrD,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC7C,MAAM,kBAAkB,CACvB,MAAM,CAA2C,GAAG,CAAC,CACrD,CAAC;QACF,MAAM,IAAI,EAAE,CAAC;IACd,CAAC,CAAC;QACF,+DAA+D;QAC/D,6DAA6D;QAC7D,4CAA4C;SAC3C,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC7C,MAAM,wBAAwB,CAC7B,MAAM,CAAiD,GAAG,CAAC,CAC3D,CAAC;QACF,MAAM,IAAI,EAAE,CAAC;IACd,CAAC,CAAC;QACF,6DAA6D;SAC5D,aAAa,CAAC,eAAe,EAAE,oBAAoB,CAAC;SACpD,aAAa,CAAC,cAAc,EAAE,mBAAmB,CAAC;QACnD,6DAA6D;SAC5D,aAAa,CAAC,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC7C,MAAM,CAAC,GAAG,MAAM,CAA6C,GAAG,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC;SACD,aAAa,CAAC,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC7C,oBAAoB,CACnB,MAAM,CAA6C,GAAG,CAAC,CACvD,CACD;SACA,aAAa,CAAC,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC1C,iBAAiB,CAAC,MAAM,CAA0C,GAAG,CAAC,CAAC,CACvE;SACA,aAAa,CAAC,aAAa,EAAE,kBAAkB,CAAC;QACjD,8CAA8C;SAC7C,OAAO,CACP,YAAY,EACZ,EAAE,WAAW,EAAE,6BAA6B,EAAE,EAC9C,iBAAiB,CACjB,CACF,CAAC;AACH,CAAC,CAAC;AAEF,sEAAsE;AAEtE,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,IAA6B,EACJ,EAAE;IAC3B,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAsB,CAAC;IAEnC,cAAc,CAAC,GAAG,CAAC,CAAC;IACpB,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAG,IAAI,GAAG,EAG3B,CAAC;IAEJ,MAAM,MAAM,GAAG,WAAW,CAAC;QAC1B,GAAG;QACH,OAAO;QACP,aAAa;QACb,OAAO;QACP,MAAM;QACN,cAAc;KACd,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,qBAAqB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,KAAK,GAAkB;QAC5B,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;YAClC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAC5C,CAAC;YACF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAA8B,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;QAC3E,CAAC;QACD,SAAS,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC;KAC/D,CAAC;IAEF,MAAM,WAAW,GAA0C,CAC1D,UAAU,EACV,OAAO,EACN,EAAE;QACH,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/D,KAAK,CAAC,iEAAiE,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YACnC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACjD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClB,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAC1D,CAAC,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refund flow — user-initiated request, admin-approved, executed via
|
|
3
|
+
* `refundStarPayment` on Telegram's side, reversed in our cache via
|
|
4
|
+
* `derive.ts`'s incremental + full-rebuild helpers.
|
|
5
|
+
*
|
|
6
|
+
* The flow mirrors `access-control.ts`'s admin-DM approval pattern:
|
|
7
|
+
*
|
|
8
|
+
* user opens /settings → 💎 VIP → 📜 History → tap a charge
|
|
9
|
+
* │
|
|
10
|
+
* "💸 Request refund" → confirm overlay → tap "✅"
|
|
11
|
+
* │
|
|
12
|
+
* pay:charge.paysupportState = 'opened'
|
|
13
|
+
* DM admin with charge details + [✅ Approve] [❌ Deny]
|
|
14
|
+
* │
|
|
15
|
+
* admin taps
|
|
16
|
+
* │
|
|
17
|
+
* Approve → refundStarPayment → pay:refund + revert state
|
|
18
|
+
* Deny → paysupportState = 'none'
|
|
19
|
+
* │
|
|
20
|
+
* user gets DM with the outcome
|
|
21
|
+
*
|
|
22
|
+
* Callback data carries the chargeId only — everything else is read
|
|
23
|
+
* from the persisted record. Telegram's `telegram_payment_charge_id` is
|
|
24
|
+
* a moderately long string (~40 chars in practice); with our short
|
|
25
|
+
* `payRf*` schema names this stays under the 64-byte callback_data cap.
|
|
26
|
+
*/
|
|
27
|
+
import type { Storage } from "@gramio/storage";
|
|
28
|
+
import type { BotCallbackCtx } from "../ctx.js";
|
|
29
|
+
import type { PaymentsStores } from "./stores.js";
|
|
30
|
+
import type { BotPaymentsConfig, PaymentsSession } from "./types.js";
|
|
31
|
+
/** User tap on a charge entry — opens confirmation overlay then admin DM. */
|
|
32
|
+
export declare const refundRequestCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
|
|
33
|
+
/** Admin tap on the DM notification — approve. */
|
|
34
|
+
export declare const refundApproveCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
|
|
35
|
+
/** Admin tap on the DM notification — deny. */
|
|
36
|
+
export declare const refundDenyCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
|
|
37
|
+
/** Admin tap on the DM notification — close (no action). */
|
|
38
|
+
export declare const refundCloseCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
|
|
39
|
+
/** Strict bot.api surface this module exercises. */
|
|
40
|
+
type RefundBotApi = {
|
|
41
|
+
refundStarPayment: (params: {
|
|
42
|
+
user_id: number;
|
|
43
|
+
telegram_payment_charge_id: string;
|
|
44
|
+
}) => Promise<unknown>;
|
|
45
|
+
editUserStarSubscription: (params: {
|
|
46
|
+
user_id: number;
|
|
47
|
+
telegram_payment_charge_id: string;
|
|
48
|
+
is_canceled: boolean;
|
|
49
|
+
}) => Promise<unknown>;
|
|
50
|
+
sendMessage: (params: {
|
|
51
|
+
chat_id: number;
|
|
52
|
+
message_thread_id?: number;
|
|
53
|
+
text: string;
|
|
54
|
+
reply_markup?: unknown;
|
|
55
|
+
}) => Promise<unknown>;
|
|
56
|
+
};
|
|
57
|
+
type SessionLike = {
|
|
58
|
+
pay?: PaymentsSession;
|
|
59
|
+
language?: string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Callback-query ctx shape this module's handlers receive. Composed
|
|
63
|
+
* from the canonical `BotCallbackCtx` with `{ cid: string }` queryData,
|
|
64
|
+
* intersected with the admin-context derives (`adminId`, `isAdmin`)
|
|
65
|
+
* the handlers depend on for the approve/deny/close paths.
|
|
66
|
+
*/
|
|
67
|
+
type CommonCtx = BotCallbackCtx<SessionLike, {
|
|
68
|
+
cid: string;
|
|
69
|
+
}, RefundBotApi> & {
|
|
70
|
+
adminId: number;
|
|
71
|
+
isAdmin: boolean;
|
|
72
|
+
};
|
|
73
|
+
export type RefundHandlersOptions = {
|
|
74
|
+
stores: PaymentsStores;
|
|
75
|
+
/**
|
|
76
|
+
* Raw storage for cross-user session record reads/writes (the
|
|
77
|
+
* `@gramio/session` keyspace, outside our `pay:*` stores). Used by
|
|
78
|
+
* `applyRefundToUser` + `loadFullRecord` to reach a target user's
|
|
79
|
+
* full session record (preserving fields owned by other plugins).
|
|
80
|
+
*/
|
|
81
|
+
storage: Storage;
|
|
82
|
+
cfg: BotPaymentsConfig<string>;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* User taps "💸 Request refund" on a charge entry inside the menu's
|
|
86
|
+
* History view. The menu plugin's `confirm:` overlay already handled
|
|
87
|
+
* the "Are you sure?" step; this fires on the confirmed tap and:
|
|
88
|
+
*
|
|
89
|
+
* 1. Marks the charge `paysupportState = 'opened'`
|
|
90
|
+
* 2. DMs the admin with [Approve][Deny] buttons
|
|
91
|
+
* 3. Returns a toast acknowledging the request was sent
|
|
92
|
+
*
|
|
93
|
+
* The user's menu remains on the History view (menu's
|
|
94
|
+
* `confirm`-confirmed action returns to root, but the toast confirms
|
|
95
|
+
* the action so the user knows what happened).
|
|
96
|
+
*/
|
|
97
|
+
export declare const buildRefundRequestHandler: (opts: RefundHandlersOptions) => (ctx: CommonCtx & {
|
|
98
|
+
queryData: {
|
|
99
|
+
cid: string;
|
|
100
|
+
};
|
|
101
|
+
}) => Promise<string | undefined>;
|
|
102
|
+
/**
|
|
103
|
+
* Admin taps "✅ Approve". Calls Telegram's `refundStarPayment`,
|
|
104
|
+
* persists the refund record + flips charge state, applies the refund
|
|
105
|
+
* to the target user's session (cross-user write), and notifies the
|
|
106
|
+
* user.
|
|
107
|
+
*/
|
|
108
|
+
export declare const buildRefundApproveHandler: (opts: RefundHandlersOptions) => (ctx: CommonCtx & {
|
|
109
|
+
queryData: {
|
|
110
|
+
cid: string;
|
|
111
|
+
};
|
|
112
|
+
}) => Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Admin taps "❌ Deny". Reverts `paysupportState` to `'none'` so the
|
|
115
|
+
* user can try again or the admin can reconsider later. Notifies the
|
|
116
|
+
* user with the configured paysupport contact for further escalation.
|
|
117
|
+
*/
|
|
118
|
+
export declare const buildRefundDenyHandler: (opts: RefundHandlersOptions) => (ctx: CommonCtx & {
|
|
119
|
+
queryData: {
|
|
120
|
+
cid: string;
|
|
121
|
+
};
|
|
122
|
+
}) => Promise<void>;
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=refund.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refund.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/refund.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACX,iBAAiB,EAEjB,eAAe,EACf,MAAM,YAAY,CAAC;AASpB,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2EAA+C,CAAC;AAC5E,kDAAkD;AAClD,eAAO,MAAM,eAAe,2EAA+C,CAAC;AAC5E,+CAA+C;AAC/C,eAAO,MAAM,YAAY,2EAA4C,CAAC;AACtE,4DAA4D;AAC5D,eAAO,MAAM,aAAa,2EAA8B,CAAC;AAIzD,oDAAoD;AACpD,KAAK,YAAY,GAAG;IACnB,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,0BAA0B,EAAE,MAAM,CAAC;KACnC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,wBAAwB,EAAE,CAAC,MAAM,EAAE;QAClC,OAAO,EAAE,MAAM,CAAC;QAChB,0BAA0B,EAAE,MAAM,CAAC;QACnC,WAAW,EAAE,OAAO,CAAC;KACrB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,WAAW,EAAE,CAAC,MAAM,EAAE;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACvB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB,CAAC;AAEF,KAAK,WAAW,GAAG;IAAE,GAAG,CAAC,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;GAKG;AACH,KAAK,SAAS,GAAG,cAAc,CAAC,WAAW,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,YAAY,CAAC,GAAG;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CACjB,CAAC;AAyGF,MAAM,MAAM,qBAAqB,GAAG;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,qBAAqB,MAE3B,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAC7C,OAAO,CAAC,MAAM,GAAG,SAAS,CA8D5B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,qBAAqB,MACrB,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAgHpE,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,qBAAqB,MACrB,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAwDpE,CAAC"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refund flow — user-initiated request, admin-approved, executed via
|
|
3
|
+
* `refundStarPayment` on Telegram's side, reversed in our cache via
|
|
4
|
+
* `derive.ts`'s incremental + full-rebuild helpers.
|
|
5
|
+
*
|
|
6
|
+
* The flow mirrors `access-control.ts`'s admin-DM approval pattern:
|
|
7
|
+
*
|
|
8
|
+
* user opens /settings → 💎 VIP → 📜 History → tap a charge
|
|
9
|
+
* │
|
|
10
|
+
* "💸 Request refund" → confirm overlay → tap "✅"
|
|
11
|
+
* │
|
|
12
|
+
* pay:charge.paysupportState = 'opened'
|
|
13
|
+
* DM admin with charge details + [✅ Approve] [❌ Deny]
|
|
14
|
+
* │
|
|
15
|
+
* admin taps
|
|
16
|
+
* │
|
|
17
|
+
* Approve → refundStarPayment → pay:refund + revert state
|
|
18
|
+
* Deny → paysupportState = 'none'
|
|
19
|
+
* │
|
|
20
|
+
* user gets DM with the outcome
|
|
21
|
+
*
|
|
22
|
+
* Callback data carries the chargeId only — everything else is read
|
|
23
|
+
* from the persisted record. Telegram's `telegram_payment_charge_id` is
|
|
24
|
+
* a moderately long string (~40 chars in practice); with our short
|
|
25
|
+
* `payRf*` schema names this stays under the 64-byte callback_data cap.
|
|
26
|
+
*/
|
|
27
|
+
import { InlineKeyboard } from "gramio";
|
|
28
|
+
import { say } from "../../say/index.js";
|
|
29
|
+
import { createLogger } from "../../universal/log.js";
|
|
30
|
+
import { callbackNs } from "../callbacks.js";
|
|
31
|
+
import { botStorageKey } from "../kit.js";
|
|
32
|
+
import { rebuildVipAndPerks, revertCreditsForCharge } from "./state.js";
|
|
33
|
+
const FALLBACK_LANG = "en";
|
|
34
|
+
const log = createLogger("bot/payments");
|
|
35
|
+
const cb = callbackNs("pay");
|
|
36
|
+
// ─── callback data schemas ─────────────────────────────────────────
|
|
37
|
+
/** User tap on a charge entry — opens confirmation overlay then admin DM. */
|
|
38
|
+
export const refundRequestCb = cb.data("refund:request", { cid: "string" });
|
|
39
|
+
/** Admin tap on the DM notification — approve. */
|
|
40
|
+
export const refundApproveCb = cb.data("refund:approve", { cid: "string" });
|
|
41
|
+
/** Admin tap on the DM notification — deny. */
|
|
42
|
+
export const refundDenyCb = cb.data("refund:deny", { cid: "string" });
|
|
43
|
+
/** Admin tap on the DM notification — close (no action). */
|
|
44
|
+
export const refundCloseCb = cb.data("refund:close", {});
|
|
45
|
+
const ctxLang = (ctx) => ctx.session?.language ?? FALLBACK_LANG;
|
|
46
|
+
// ─── cross-user session mutation ───────────────────────────────────
|
|
47
|
+
//
|
|
48
|
+
// The user's session record lives in @gramio/session's keyspace
|
|
49
|
+
// (`bot-<id>:<userId>` via `botStorageKey`), not in our `pay:*` stores.
|
|
50
|
+
// We still hit `storage` directly here because the session record holds
|
|
51
|
+
// many fields owned by other plugins (access, language, llm) and we
|
|
52
|
+
// must preserve them via read-modify-write.
|
|
53
|
+
const loadFullRecord = async (storage, ctx, userId) => (await storage.get(botStorageKey(ctx, userId))) ?? {};
|
|
54
|
+
/**
|
|
55
|
+
* Apply the refund's side effect to the target user's session record
|
|
56
|
+
* (which is NOT `ctx.session` when the admin is the actor).
|
|
57
|
+
*
|
|
58
|
+
* - credits: targeted decrement (clamped at zero — see derive.ts)
|
|
59
|
+
* - vip + perks: full rebuild from the post-refund charge log
|
|
60
|
+
*
|
|
61
|
+
* Read-modify-write preserves other plugins' fields in the same record.
|
|
62
|
+
*/
|
|
63
|
+
const applyRefundToUser = async (stores, storage, ctx, userId, refundedCharge) => {
|
|
64
|
+
const full = await loadFullRecord(storage, ctx, userId);
|
|
65
|
+
const session = { pay: full.pay };
|
|
66
|
+
revertCreditsForCharge(session, refundedCharge);
|
|
67
|
+
// charges already include the refunded one with state='refunded'
|
|
68
|
+
const chargeIds = await stores.userCharges(userId).list(ctx);
|
|
69
|
+
const charges = (await Promise.all(chargeIds.map((id) => stores.charges.get(ctx, id)))).filter((c) => c !== undefined);
|
|
70
|
+
rebuildVipAndPerks(session, charges);
|
|
71
|
+
full.pay = session.pay;
|
|
72
|
+
await storage.set(botStorageKey(ctx, userId), full);
|
|
73
|
+
};
|
|
74
|
+
// ─── notification rendering ────────────────────────────────────────
|
|
75
|
+
const fmtAge = (ms) => {
|
|
76
|
+
const s = Math.floor(ms / 1000);
|
|
77
|
+
if (s < 60)
|
|
78
|
+
return `${s}s`;
|
|
79
|
+
const m = Math.floor(s / 60);
|
|
80
|
+
if (m < 60)
|
|
81
|
+
return `${m}min`;
|
|
82
|
+
const h = Math.floor(m / 60);
|
|
83
|
+
if (h < 24)
|
|
84
|
+
return `${h}h`;
|
|
85
|
+
return `${Math.floor(h / 24)}d`;
|
|
86
|
+
};
|
|
87
|
+
const adminNotificationText = (charge, lang) => [
|
|
88
|
+
say({ en: "💸 Refund requested", es: "💸 Reembolso solicitado" }, lang),
|
|
89
|
+
"",
|
|
90
|
+
`👤 ${say({ en: "user", es: "usuario" }, lang)}: ${charge.userId}`,
|
|
91
|
+
`📦 ${charge.productKey}`,
|
|
92
|
+
`⭐ ${charge.xtr}`,
|
|
93
|
+
`🆔 ${charge.chargeId}`,
|
|
94
|
+
`⏰ ${say({ en: "purchased", es: "comprado" }, lang)}: ${fmtAge(Date.now() - charge.receivedAt)} ${say({ en: "ago", es: "atrás" }, lang)}`,
|
|
95
|
+
].join("\n");
|
|
96
|
+
const adminKeyboard = (chargeId, lang) => new InlineKeyboard()
|
|
97
|
+
.text(say({ en: "✅ Approve", es: "✅ Aprobar" }, lang), refundApproveCb.pack({ cid: chargeId }), { style: "success" })
|
|
98
|
+
.text(say({ en: "❌ Deny", es: "❌ Denegar" }, lang), refundDenyCb.pack({ cid: chargeId }), { style: "danger" })
|
|
99
|
+
.row()
|
|
100
|
+
.text(say({ en: "✖️ Close", es: "✖️ Cerrar" }, lang), refundCloseCb.pack({}));
|
|
101
|
+
const adminLangOfUser = async (storage, ctx, userId) => {
|
|
102
|
+
const full = await loadFullRecord(storage, ctx, userId);
|
|
103
|
+
return full.language ?? FALLBACK_LANG;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* User taps "💸 Request refund" on a charge entry inside the menu's
|
|
107
|
+
* History view. The menu plugin's `confirm:` overlay already handled
|
|
108
|
+
* the "Are you sure?" step; this fires on the confirmed tap and:
|
|
109
|
+
*
|
|
110
|
+
* 1. Marks the charge `paysupportState = 'opened'`
|
|
111
|
+
* 2. DMs the admin with [Approve][Deny] buttons
|
|
112
|
+
* 3. Returns a toast acknowledging the request was sent
|
|
113
|
+
*
|
|
114
|
+
* The user's menu remains on the History view (menu's
|
|
115
|
+
* `confirm`-confirmed action returns to root, but the toast confirms
|
|
116
|
+
* the action so the user knows what happened).
|
|
117
|
+
*/
|
|
118
|
+
export const buildRefundRequestHandler = (opts) => async (ctx) => {
|
|
119
|
+
const lang = ctxLang(ctx);
|
|
120
|
+
const userId = ctx.from?.id;
|
|
121
|
+
if (userId === undefined) {
|
|
122
|
+
return say({ en: "Could not identify you.", es: "No se pudo identificarte." }, lang);
|
|
123
|
+
}
|
|
124
|
+
const charge = await opts.stores.charges.get(ctx, ctx.queryData.cid);
|
|
125
|
+
if (!charge) {
|
|
126
|
+
return say({ en: "Charge not found.", es: "Cargo no encontrado." }, lang);
|
|
127
|
+
}
|
|
128
|
+
if (charge.userId !== userId) {
|
|
129
|
+
// Anti-tamper — never let user A request refund of user B's charge.
|
|
130
|
+
return say({ en: "Not your charge.", es: "No es tu cargo." }, lang);
|
|
131
|
+
}
|
|
132
|
+
if (charge.paysupportState !== "none") {
|
|
133
|
+
return say({
|
|
134
|
+
en: "Refund already in progress or completed.",
|
|
135
|
+
es: "Reembolso ya en curso o completado.",
|
|
136
|
+
}, lang);
|
|
137
|
+
}
|
|
138
|
+
charge.paysupportState = "opened";
|
|
139
|
+
await opts.stores.charges.set(ctx, charge.chargeId, charge);
|
|
140
|
+
log.event(`refund requested by user: chargeId=${charge.chargeId} product=${charge.productKey} xtr=${charge.xtr}`);
|
|
141
|
+
const adminLang = await adminLangOfUser(opts.storage, ctx, ctx.adminId);
|
|
142
|
+
try {
|
|
143
|
+
await ctx.bot.api.sendMessage({
|
|
144
|
+
chat_id: ctx.adminId,
|
|
145
|
+
text: adminNotificationText(charge, adminLang),
|
|
146
|
+
reply_markup: adminKeyboard(charge.chargeId, adminLang),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
log.error(`failed to DM admin: ${e}`);
|
|
151
|
+
// Roll back the open state so the user can try again later.
|
|
152
|
+
charge.paysupportState = "none";
|
|
153
|
+
await opts.stores.charges.set(ctx, charge.chargeId, charge);
|
|
154
|
+
return say({
|
|
155
|
+
en: `Couldn't reach the admin. Contact ${opts.cfg.paysupport}.`,
|
|
156
|
+
es: `No se pudo contactar al admin. Contacta con ${opts.cfg.paysupport}.`,
|
|
157
|
+
}, lang);
|
|
158
|
+
}
|
|
159
|
+
return say({
|
|
160
|
+
en: "💸 Refund request sent.",
|
|
161
|
+
es: "💸 Solicitud de reembolso enviada.",
|
|
162
|
+
}, lang);
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Admin taps "✅ Approve". Calls Telegram's `refundStarPayment`,
|
|
166
|
+
* persists the refund record + flips charge state, applies the refund
|
|
167
|
+
* to the target user's session (cross-user write), and notifies the
|
|
168
|
+
* user.
|
|
169
|
+
*/
|
|
170
|
+
export const buildRefundApproveHandler = (opts) => async (ctx) => {
|
|
171
|
+
const aLang = ctxLang(ctx);
|
|
172
|
+
if (!ctx.isAdmin) {
|
|
173
|
+
await ctx.answer({
|
|
174
|
+
text: say({ en: "Admin only.", es: "Solo admin." }, aLang),
|
|
175
|
+
show_alert: true,
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const charge = await opts.stores.charges.get(ctx, ctx.queryData.cid);
|
|
180
|
+
if (!charge) {
|
|
181
|
+
await ctx.answer({
|
|
182
|
+
text: say({ en: "Charge not found.", es: "Cargo no encontrado." }, aLang),
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (charge.paysupportState === "refunded") {
|
|
187
|
+
await ctx.answer({
|
|
188
|
+
text: say({ en: "Already refunded.", es: "Ya reembolsado." }, aLang),
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
log.event(`admin approving refund: chargeId=${charge.chargeId} user=${charge.userId} xtr=${charge.xtr}`);
|
|
193
|
+
try {
|
|
194
|
+
await ctx.bot.api.refundStarPayment({
|
|
195
|
+
user_id: charge.userId,
|
|
196
|
+
telegram_payment_charge_id: charge.chargeId,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
log.error(`refundStarPayment failed: ${e}`);
|
|
201
|
+
await ctx.answer({
|
|
202
|
+
text: say({
|
|
203
|
+
en: `❌ Refund failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
204
|
+
es: `❌ Reembolso falló: ${e instanceof Error ? e.message : String(e)}`,
|
|
205
|
+
}, aLang),
|
|
206
|
+
show_alert: true,
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// If this charge funded a still-active vip subscription, also
|
|
211
|
+
// cancel its auto-renewal so the user isn't charged again at the
|
|
212
|
+
// next period boundary. Best-effort; refund itself already
|
|
213
|
+
// succeeded.
|
|
214
|
+
if (charge.vipRung !== undefined) {
|
|
215
|
+
try {
|
|
216
|
+
await ctx.bot.api.editUserStarSubscription({
|
|
217
|
+
user_id: charge.userId,
|
|
218
|
+
telegram_payment_charge_id: charge.chargeId,
|
|
219
|
+
is_canceled: true,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
log.warn(`post-refund cancel renewal failed (probably already canceled): ${e}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
charge.paysupportState = "refunded";
|
|
227
|
+
charge.refundedAt = Date.now();
|
|
228
|
+
await opts.stores.charges.set(ctx, charge.chargeId, charge);
|
|
229
|
+
await applyRefundToUser(opts.stores, opts.storage, ctx, charge.userId, charge);
|
|
230
|
+
// User notification
|
|
231
|
+
const userFull = await loadFullRecord(opts.storage, ctx, charge.userId);
|
|
232
|
+
const uLang = userFull.language ?? FALLBACK_LANG;
|
|
233
|
+
try {
|
|
234
|
+
await ctx.bot.api.sendMessage({
|
|
235
|
+
chat_id: charge.userId,
|
|
236
|
+
text: say({
|
|
237
|
+
en: `✅ Your refund of ${charge.xtr} ⭐ has been approved.`,
|
|
238
|
+
es: `✅ Tu reembolso de ${charge.xtr} ⭐ ha sido aprobado.`,
|
|
239
|
+
}, uLang),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
log.error(`failed to notify user of approval: ${e}`);
|
|
244
|
+
}
|
|
245
|
+
await ctx.answer({
|
|
246
|
+
text: say({ en: "✅ Refund approved.", es: "✅ Reembolso aprobado." }, aLang),
|
|
247
|
+
});
|
|
248
|
+
if (ctx.editText) {
|
|
249
|
+
try {
|
|
250
|
+
await ctx.editText(`${say({ en: "✅ Refunded", es: "✅ Reembolsado" }, aLang)} · ${charge.chargeId}`);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
/* message too old to edit */
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Admin taps "❌ Deny". Reverts `paysupportState` to `'none'` so the
|
|
259
|
+
* user can try again or the admin can reconsider later. Notifies the
|
|
260
|
+
* user with the configured paysupport contact for further escalation.
|
|
261
|
+
*/
|
|
262
|
+
export const buildRefundDenyHandler = (opts) => async (ctx) => {
|
|
263
|
+
const aLang = ctxLang(ctx);
|
|
264
|
+
if (!ctx.isAdmin) {
|
|
265
|
+
await ctx.answer({
|
|
266
|
+
text: say({ en: "Admin only.", es: "Solo admin." }, aLang),
|
|
267
|
+
show_alert: true,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const charge = await opts.stores.charges.get(ctx, ctx.queryData.cid);
|
|
272
|
+
if (!charge) {
|
|
273
|
+
await ctx.answer({
|
|
274
|
+
text: say({ en: "Charge not found.", es: "Cargo no encontrado." }, aLang),
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
charge.paysupportState = "none";
|
|
279
|
+
await opts.stores.charges.set(ctx, charge.chargeId, charge);
|
|
280
|
+
const userFull = await loadFullRecord(opts.storage, ctx, charge.userId);
|
|
281
|
+
const uLang = userFull.language ?? FALLBACK_LANG;
|
|
282
|
+
try {
|
|
283
|
+
await ctx.bot.api.sendMessage({
|
|
284
|
+
chat_id: charge.userId,
|
|
285
|
+
text: say({
|
|
286
|
+
en: `❌ Your refund request was denied.\n\n` +
|
|
287
|
+
`For further questions, contact ${opts.cfg.paysupport}.`,
|
|
288
|
+
es: `❌ Tu solicitud de reembolso fue denegada.\n\n` +
|
|
289
|
+
`Para más consultas, contacta con ${opts.cfg.paysupport}.`,
|
|
290
|
+
}, uLang),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
log.error("failed to notify user of denial", e);
|
|
295
|
+
}
|
|
296
|
+
await ctx.answer({
|
|
297
|
+
text: say({ en: "❌ Denied", es: "❌ Denegado" }, aLang),
|
|
298
|
+
});
|
|
299
|
+
if (ctx.editText) {
|
|
300
|
+
try {
|
|
301
|
+
await ctx.editText(`${say({ en: "❌ Denied", es: "❌ Denegado" }, aLang)} · ${charge.chargeId}`);
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
/* ignore */
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
//# sourceMappingURL=refund.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refund.js","sourceRoot":"","sources":["../../../src/bot/payments/refund.ts"],"names":[],"mappings":"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"}
|