@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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure state computation over the charge log.
|
|
3
|
+
*
|
|
4
|
+
* This file is `state.ts` (not `derive.ts`) because gramio's `derive`
|
|
5
|
+
* concept is reserved for ctx decoration (see `derive.ts` alongside,
|
|
6
|
+
* which builds `ctx.payments`). The reducers here are bot-id and ctx
|
|
7
|
+
* agnostic — they take `ChargeRecord[]` and a session-shaped object
|
|
8
|
+
* and mutate the session in place.
|
|
9
|
+
*
|
|
10
|
+
* The charge log (`pay:charge:{chargeId}`) is the single source of truth
|
|
11
|
+
* (CLAUDE.md §"Storage layout"). Session state is a cache for O(1)
|
|
12
|
+
* runtime checks. This module owns the function that rebuilds the cache
|
|
13
|
+
* from the log.
|
|
14
|
+
*
|
|
15
|
+
* Two surfaces:
|
|
16
|
+
*
|
|
17
|
+
* `deriveState(charges)` — pure, reduces a chronological list of
|
|
18
|
+
* NON-refunded charges into the cache
|
|
19
|
+
* shape. Used by reconcile and refund
|
|
20
|
+
* (after marking the refunded charge).
|
|
21
|
+
*
|
|
22
|
+
* `applyCharge(session, …)` — incremental, mutates one session record
|
|
23
|
+
* for a single new charge. Used by
|
|
24
|
+
* `handlers.ts` on `successful_payment`.
|
|
25
|
+
*
|
|
26
|
+
* `revertCreditsForCharge(session, charge)` — targeted decrement
|
|
27
|
+
* used by refund (vip/perks are full
|
|
28
|
+
* re-derive territory; credits is not,
|
|
29
|
+
* because consumption isn't in the log).
|
|
30
|
+
*
|
|
31
|
+
* Credits balance is **not fully derivable** from the log alone — the
|
|
32
|
+
* log records grants, not consumption. `deriveState` therefore returns
|
|
33
|
+
* `totalCreditsGranted` (sum) and callers decide whether to use it as
|
|
34
|
+
* the balance (reconcile of fresh state) or to ignore (incremental).
|
|
35
|
+
*/
|
|
36
|
+
import type { ChargeRecord, DerivedPaymentsState, PaymentsSession } from "./types.js";
|
|
37
|
+
/**
|
|
38
|
+
* Result of `deriveState`. `totalCreditsGranted` is informational —
|
|
39
|
+
* subtract consumption (which isn't logged) for the real balance.
|
|
40
|
+
*/
|
|
41
|
+
export type DeriveResult = DerivedPaymentsState & {
|
|
42
|
+
readonly totalCreditsGranted: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Pure reducer over a chronological list of charges. Caller is
|
|
46
|
+
* responsible for filtering out refunded ones if they want
|
|
47
|
+
* "current state after refunds" semantics.
|
|
48
|
+
*/
|
|
49
|
+
export declare const deriveState: (charges: ReadonlyArray<ChargeRecord>) => DeriveResult;
|
|
50
|
+
/**
|
|
51
|
+
* Apply a single new charge to a session in place. Idempotent across
|
|
52
|
+
* `chargeId` because the caller (handlers.ts) guards via the
|
|
53
|
+
* `pay:idempotency:{chargeId}` sentinel before this fires; we don't
|
|
54
|
+
* re-check here.
|
|
55
|
+
*
|
|
56
|
+
* - vip: replaces `session.pay.vip` (renewals update expiresAt;
|
|
57
|
+
* tier upgrades replace rung)
|
|
58
|
+
* - credits: increments `session.pay.credits` by the granted amount
|
|
59
|
+
* (covers both pack purchases AND vip renewal grants)
|
|
60
|
+
* - perks: set-if-absent on `session.pay.perks[key]`
|
|
61
|
+
*/
|
|
62
|
+
export declare const applyCharge: (session: {
|
|
63
|
+
pay?: PaymentsSession;
|
|
64
|
+
}, charge: ChargeRecord) => void;
|
|
65
|
+
/**
|
|
66
|
+
* Subtract a refunded charge's `creditsGranted` from the session
|
|
67
|
+
* balance, clamping at zero. The "clamp at zero" semantics are
|
|
68
|
+
* deliberate: a user may have already consumed credits when the refund
|
|
69
|
+
* arrives, and going negative would let them buy back into a debt — we
|
|
70
|
+
* refuse that and accept the asymmetry (the refunded Stars are already
|
|
71
|
+
* leaving the bot's balance via `refundStarPayment` either way).
|
|
72
|
+
*/
|
|
73
|
+
export declare const revertCreditsForCharge: (session: {
|
|
74
|
+
pay?: PaymentsSession;
|
|
75
|
+
}, charge: ChargeRecord) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Rebuild vip + perks (the fully-derivable parts of session.pay.*)
|
|
78
|
+
* from a fresh chronological view of the charge log, **after** the
|
|
79
|
+
* caller has marked any newly-refunded charges. Credits balance is
|
|
80
|
+
* NOT touched — caller handles that explicitly via
|
|
81
|
+
* `revertCreditsForCharge` for the targeted decrement semantics.
|
|
82
|
+
*
|
|
83
|
+
* Used by:
|
|
84
|
+
* - refund.ts after `refundStarPayment` succeeds
|
|
85
|
+
* - reconcile (admin command) for full state recovery
|
|
86
|
+
*/
|
|
87
|
+
export declare const rebuildVipAndPerks: (session: {
|
|
88
|
+
pay?: PaymentsSession;
|
|
89
|
+
}, charges: ReadonlyArray<ChargeRecord>) => void;
|
|
90
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EACX,YAAY,EACZ,oBAAoB,EACpB,eAAe,EAGf,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG;IACjD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GACvB,SAAS,aAAa,CAAC,YAAY,CAAC,KAClC,YAwCF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,GACvB,SAAS;IAAE,GAAG,CAAC,EAAE,eAAe,CAAA;CAAE,EAClC,QAAQ,YAAY,KAClB,IA0BF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAClC,SAAS;IAAE,GAAG,CAAC,EAAE,eAAe,CAAA;CAAE,EAClC,QAAQ,YAAY,KAClB,IAMF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAC9B,SAAS;IAAE,GAAG,CAAC,EAAE,eAAe,CAAA;CAAE,EAClC,SAAS,aAAa,CAAC,YAAY,CAAC,KAClC,IAMF,CAAC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure state computation over the charge log.
|
|
3
|
+
*
|
|
4
|
+
* This file is `state.ts` (not `derive.ts`) because gramio's `derive`
|
|
5
|
+
* concept is reserved for ctx decoration (see `derive.ts` alongside,
|
|
6
|
+
* which builds `ctx.payments`). The reducers here are bot-id and ctx
|
|
7
|
+
* agnostic — they take `ChargeRecord[]` and a session-shaped object
|
|
8
|
+
* and mutate the session in place.
|
|
9
|
+
*
|
|
10
|
+
* The charge log (`pay:charge:{chargeId}`) is the single source of truth
|
|
11
|
+
* (CLAUDE.md §"Storage layout"). Session state is a cache for O(1)
|
|
12
|
+
* runtime checks. This module owns the function that rebuilds the cache
|
|
13
|
+
* from the log.
|
|
14
|
+
*
|
|
15
|
+
* Two surfaces:
|
|
16
|
+
*
|
|
17
|
+
* `deriveState(charges)` — pure, reduces a chronological list of
|
|
18
|
+
* NON-refunded charges into the cache
|
|
19
|
+
* shape. Used by reconcile and refund
|
|
20
|
+
* (after marking the refunded charge).
|
|
21
|
+
*
|
|
22
|
+
* `applyCharge(session, …)` — incremental, mutates one session record
|
|
23
|
+
* for a single new charge. Used by
|
|
24
|
+
* `handlers.ts` on `successful_payment`.
|
|
25
|
+
*
|
|
26
|
+
* `revertCreditsForCharge(session, charge)` — targeted decrement
|
|
27
|
+
* used by refund (vip/perks are full
|
|
28
|
+
* re-derive territory; credits is not,
|
|
29
|
+
* because consumption isn't in the log).
|
|
30
|
+
*
|
|
31
|
+
* Credits balance is **not fully derivable** from the log alone — the
|
|
32
|
+
* log records grants, not consumption. `deriveState` therefore returns
|
|
33
|
+
* `totalCreditsGranted` (sum) and callers decide whether to use it as
|
|
34
|
+
* the balance (reconcile of fresh state) or to ignore (incremental).
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Pure reducer over a chronological list of charges. Caller is
|
|
38
|
+
* responsible for filtering out refunded ones if they want
|
|
39
|
+
* "current state after refunds" semantics.
|
|
40
|
+
*/
|
|
41
|
+
export const deriveState = (charges) => {
|
|
42
|
+
let vip;
|
|
43
|
+
const perks = {};
|
|
44
|
+
let totalCreditsGranted = 0;
|
|
45
|
+
// Charges are oldest-first by convention. Vip latest-wins by
|
|
46
|
+
// receivedAt; perks first-wins (an idempotent unlock — buying twice
|
|
47
|
+
// shouldn't change the unlock date).
|
|
48
|
+
for (const c of charges) {
|
|
49
|
+
if (c.paysupportState === "refunded")
|
|
50
|
+
continue;
|
|
51
|
+
totalCreditsGranted += c.creditsGranted;
|
|
52
|
+
if (c.vipRung !== undefined && c.subscriptionExpiresAt !== undefined) {
|
|
53
|
+
// Replace the active subscription only when this charge is newer
|
|
54
|
+
// or this is the first vip charge encountered.
|
|
55
|
+
if (!vip || c.receivedAt >= (vip.expiresAt ?? 0) || c.receivedAt > 0) {
|
|
56
|
+
vip = {
|
|
57
|
+
rung: c.vipRung,
|
|
58
|
+
chargeId: c.chargeId,
|
|
59
|
+
expiresAt: c.subscriptionExpiresAt,
|
|
60
|
+
canceled: false,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (c.perkKey && !perks[c.perkKey]) {
|
|
65
|
+
perks[c.perkKey] = {
|
|
66
|
+
chargeId: c.chargeId,
|
|
67
|
+
at: c.receivedAt,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
credits: totalCreditsGranted,
|
|
73
|
+
vip,
|
|
74
|
+
perks,
|
|
75
|
+
totalCreditsGranted,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Apply a single new charge to a session in place. Idempotent across
|
|
80
|
+
* `chargeId` because the caller (handlers.ts) guards via the
|
|
81
|
+
* `pay:idempotency:{chargeId}` sentinel before this fires; we don't
|
|
82
|
+
* re-check here.
|
|
83
|
+
*
|
|
84
|
+
* - vip: replaces `session.pay.vip` (renewals update expiresAt;
|
|
85
|
+
* tier upgrades replace rung)
|
|
86
|
+
* - credits: increments `session.pay.credits` by the granted amount
|
|
87
|
+
* (covers both pack purchases AND vip renewal grants)
|
|
88
|
+
* - perks: set-if-absent on `session.pay.perks[key]`
|
|
89
|
+
*/
|
|
90
|
+
export const applyCharge = (session, charge) => {
|
|
91
|
+
session.pay ??= {};
|
|
92
|
+
const pay = session.pay;
|
|
93
|
+
if (charge.creditsGranted > 0) {
|
|
94
|
+
pay.credits = (pay.credits ?? 0) + charge.creditsGranted;
|
|
95
|
+
}
|
|
96
|
+
if (charge.vipRung !== undefined &&
|
|
97
|
+
charge.subscriptionExpiresAt !== undefined) {
|
|
98
|
+
pay.vip = {
|
|
99
|
+
rung: charge.vipRung,
|
|
100
|
+
chargeId: charge.chargeId,
|
|
101
|
+
expiresAt: charge.subscriptionExpiresAt,
|
|
102
|
+
canceled: false,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (charge.perkKey) {
|
|
106
|
+
pay.perks ??= {};
|
|
107
|
+
if (!pay.perks[charge.perkKey]) {
|
|
108
|
+
pay.perks[charge.perkKey] = {
|
|
109
|
+
chargeId: charge.chargeId,
|
|
110
|
+
at: charge.receivedAt,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Subtract a refunded charge's `creditsGranted` from the session
|
|
117
|
+
* balance, clamping at zero. The "clamp at zero" semantics are
|
|
118
|
+
* deliberate: a user may have already consumed credits when the refund
|
|
119
|
+
* arrives, and going negative would let them buy back into a debt — we
|
|
120
|
+
* refuse that and accept the asymmetry (the refunded Stars are already
|
|
121
|
+
* leaving the bot's balance via `refundStarPayment` either way).
|
|
122
|
+
*/
|
|
123
|
+
export const revertCreditsForCharge = (session, charge) => {
|
|
124
|
+
if (!charge.creditsGranted)
|
|
125
|
+
return;
|
|
126
|
+
session.pay ??= {};
|
|
127
|
+
const pay = session.pay;
|
|
128
|
+
const current = pay.credits ?? 0;
|
|
129
|
+
pay.credits = Math.max(0, current - charge.creditsGranted);
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Rebuild vip + perks (the fully-derivable parts of session.pay.*)
|
|
133
|
+
* from a fresh chronological view of the charge log, **after** the
|
|
134
|
+
* caller has marked any newly-refunded charges. Credits balance is
|
|
135
|
+
* NOT touched — caller handles that explicitly via
|
|
136
|
+
* `revertCreditsForCharge` for the targeted decrement semantics.
|
|
137
|
+
*
|
|
138
|
+
* Used by:
|
|
139
|
+
* - refund.ts after `refundStarPayment` succeeds
|
|
140
|
+
* - reconcile (admin command) for full state recovery
|
|
141
|
+
*/
|
|
142
|
+
export const rebuildVipAndPerks = (session, charges) => {
|
|
143
|
+
const derived = deriveState(charges);
|
|
144
|
+
session.pay ??= {};
|
|
145
|
+
const pay = session.pay;
|
|
146
|
+
pay.vip = derived.vip;
|
|
147
|
+
pay.perks = derived.perks;
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/bot/payments/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin's storage layout, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the scattered `chargeKey` / `userChargesIndexKey` /
|
|
5
|
+
* `idempotencyKey` / `payoutKey` / `payoutsIndexKey` helpers that used
|
|
6
|
+
* to live in `handlers.ts`, `refund.ts`, `payouts.ts`. Every storage
|
|
7
|
+
* operation in the plugin goes through one of these handles.
|
|
8
|
+
*
|
|
9
|
+
* Built once at plugin construction (in `plugin.ts`) and threaded
|
|
10
|
+
* through every concern that needs it.
|
|
11
|
+
*/
|
|
12
|
+
import type { Storage } from "@gramio/storage";
|
|
13
|
+
import { botIndex, botRecord, botSentinel } from "../storage.js";
|
|
14
|
+
import type { ChargeRecord, PayoutRecord } from "./types.js";
|
|
15
|
+
export type PaymentsStores = {
|
|
16
|
+
/** `pay:charge:<chargeId>` — authoritative per-charge record. */
|
|
17
|
+
readonly charges: ReturnType<typeof botRecord<ChargeRecord>>;
|
|
18
|
+
/** `pay:idem:<chargeId>` — fulfillment idempotency sentinel. */
|
|
19
|
+
readonly idempotency: ReturnType<typeof botSentinel>;
|
|
20
|
+
/**
|
|
21
|
+
* `pay:user:<userId>:charges` — newest-first cap of chargeIds for
|
|
22
|
+
* `/refunds` listing + menu history (v2). Factory because the key
|
|
23
|
+
* is per-user.
|
|
24
|
+
*/
|
|
25
|
+
readonly userCharges: (userId: number) => ReturnType<typeof botIndex>;
|
|
26
|
+
/** `pay:payout:<batchId>` — Fragment payout record. */
|
|
27
|
+
readonly payouts: ReturnType<typeof botRecord<PayoutRecord>>;
|
|
28
|
+
/** `pay:payouts:index` — newest-first batchIds for export. */
|
|
29
|
+
readonly payoutsIndex: ReturnType<typeof botIndex>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Construct the plugin's storage handles. Called once at plugin
|
|
33
|
+
* construction; the result is passed into every concern that reads or
|
|
34
|
+
* writes plugin state.
|
|
35
|
+
*
|
|
36
|
+
* Schemas are wired in — every `.get()` on `charges` / `payouts` runs
|
|
37
|
+
* the corresponding zod schema's `.parse()` and throws a clear
|
|
38
|
+
* `SourcedError({ source: 'storage', operation: 'validate' })` on a
|
|
39
|
+
* corrupted record (instead of NaN-ing downstream when the consumer
|
|
40
|
+
* reads a now-missing field). `idempotency` is a presence-only sentinel
|
|
41
|
+
* so no schema applies; `*Index` stores `string[]` so likewise.
|
|
42
|
+
*/
|
|
43
|
+
export declare const buildStores: (storage: Storage) => PaymentsStores;
|
|
44
|
+
//# sourceMappingURL=stores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/stores.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO7D,MAAM,MAAM,cAAc,GAAG;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IAC7D,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;IACtE,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IAC7D,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;CACnD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,OAAO,KAAG,cAS7C,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin's storage layout, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the scattered `chargeKey` / `userChargesIndexKey` /
|
|
5
|
+
* `idempotencyKey` / `payoutKey` / `payoutsIndexKey` helpers that used
|
|
6
|
+
* to live in `handlers.ts`, `refund.ts`, `payouts.ts`. Every storage
|
|
7
|
+
* operation in the plugin goes through one of these handles.
|
|
8
|
+
*
|
|
9
|
+
* Built once at plugin construction (in `plugin.ts`) and threaded
|
|
10
|
+
* through every concern that needs it.
|
|
11
|
+
*/
|
|
12
|
+
import { botIndex, botRecord, botSentinel } from "../storage.js";
|
|
13
|
+
import { chargeRecordSchema, payoutRecordSchema } from "./schemas.js";
|
|
14
|
+
/** Cap on the per-user `pay:user:<id>:charges` index. Older charges
|
|
15
|
+
* drop off the list but remain in `pay:charge:<id>` for the gestor
|
|
16
|
+
* export (which scans charges directly, not the index). */
|
|
17
|
+
const USER_CHARGES_CAP = 100;
|
|
18
|
+
/**
|
|
19
|
+
* Construct the plugin's storage handles. Called once at plugin
|
|
20
|
+
* construction; the result is passed into every concern that reads or
|
|
21
|
+
* writes plugin state.
|
|
22
|
+
*
|
|
23
|
+
* Schemas are wired in — every `.get()` on `charges` / `payouts` runs
|
|
24
|
+
* the corresponding zod schema's `.parse()` and throws a clear
|
|
25
|
+
* `SourcedError({ source: 'storage', operation: 'validate' })` on a
|
|
26
|
+
* corrupted record (instead of NaN-ing downstream when the consumer
|
|
27
|
+
* reads a now-missing field). `idempotency` is a presence-only sentinel
|
|
28
|
+
* so no schema applies; `*Index` stores `string[]` so likewise.
|
|
29
|
+
*/
|
|
30
|
+
export const buildStores = (storage) => ({
|
|
31
|
+
charges: botRecord(storage, "pay:charge", chargeRecordSchema),
|
|
32
|
+
idempotency: botSentinel(storage, "pay:idem"),
|
|
33
|
+
userCharges: (userId) => botIndex(storage, `pay:user:${userId}:charges`, {
|
|
34
|
+
capacity: USER_CHARGES_CAP,
|
|
35
|
+
}),
|
|
36
|
+
payouts: botRecord(storage, "pay:payout", payoutRecordSchema),
|
|
37
|
+
payoutsIndex: botIndex(storage, "pay:payouts:index"),
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../../../src/bot/payments/stores.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGtE;;4DAE4D;AAC5D,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAmB7B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAkB,EAAE,CAAC,CAAC;IACjE,OAAO,EAAE,SAAS,CAAe,OAAO,EAAE,YAAY,EAAE,kBAAkB,CAAC;IAC3E,WAAW,EAAE,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7C,WAAW,EAAE,CAAC,MAAc,EAAE,EAAE,CAC/B,QAAQ,CAAC,OAAO,EAAE,YAAY,MAAM,UAAU,EAAE;QAC/C,QAAQ,EAAE,gBAAgB;KAC1B,CAAC;IACH,OAAO,EAAE,SAAS,CAAe,OAAO,EAAE,YAAY,EAAE,kBAAkB,CAAC;IAC3E,YAAY,EAAE,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;CACpD,CAAC,CAAC"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type surface for `bot/payments`. Two layers:
|
|
3
|
+
*
|
|
4
|
+
* 1. **Config shapes** the consumer authors (`BotPaymentsConfig`).
|
|
5
|
+
* 2. **Derived types** the consumer reads through `ctx.payments`
|
|
6
|
+
* (`TierKey`, `AtLeastKey`, `ProductKey`) — narrowed from their
|
|
7
|
+
* config so e.g. `atLeast('vip.5')` is a TS error when only 2
|
|
8
|
+
* rungs are declared.
|
|
9
|
+
*
|
|
10
|
+
* Internal types (the ledger records, the catalog) live below and aren't
|
|
11
|
+
* exported through the package entrypoint.
|
|
12
|
+
*
|
|
13
|
+
* See `CLAUDE.md` in this folder for the design rationale.
|
|
14
|
+
*/
|
|
15
|
+
import type { Polyglot } from "../../say/index.js";
|
|
16
|
+
/**
|
|
17
|
+
* The only Telegram-supported subscription period as of 2026 is exactly
|
|
18
|
+
* 2,592,000 seconds (30 days). Modeled as a literal so misconfigurations
|
|
19
|
+
* fail at compile time. See `sendInvoice` /
|
|
20
|
+
* `createInvoiceLink` docs at https://core.telegram.org/bots/api.
|
|
21
|
+
*/
|
|
22
|
+
export type SubscriptionPeriod = "30d";
|
|
23
|
+
/** Internal value Telegram expects on the wire (seconds). */
|
|
24
|
+
export declare const SUBSCRIPTION_PERIOD_SECONDS: Readonly<Record<SubscriptionPeriod, number>>;
|
|
25
|
+
/**
|
|
26
|
+
* One rung of the VIP ladder. Position in the array is the rank (1-based
|
|
27
|
+
* id at the call site: `vip.1`, `vip.2`, …). Insert a rung at position 2
|
|
28
|
+
* → existing subscribers on rung 2+ shift by one, plus a trivial storage
|
|
29
|
+
* migration; no string-id churn.
|
|
30
|
+
*
|
|
31
|
+
* `name` is display-only Polyglot — renaming a rung is a config edit; the
|
|
32
|
+
* call sites (`atLeast('vip.2')`) are positional and untouched.
|
|
33
|
+
*
|
|
34
|
+
* `grants.credits` is applied **on every renewal** of this rung (not just
|
|
35
|
+
* the initial purchase). That's the canonical SaaS shape: "VIP gets 1000
|
|
36
|
+
* messages per month."
|
|
37
|
+
*/
|
|
38
|
+
export type VipRung<L extends string = string> = {
|
|
39
|
+
readonly xtr: number;
|
|
40
|
+
readonly period: SubscriptionPeriod;
|
|
41
|
+
readonly name: Polyglot<L>;
|
|
42
|
+
readonly grants?: {
|
|
43
|
+
readonly credits?: number;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A consumable credit pack the user buys one-shot to top up their
|
|
48
|
+
* `session.pay.credits` balance. Like VIP rungs, packs are positionally
|
|
49
|
+
* identified (`credits.1`, `credits.2`, …) so the call site doesn't carry
|
|
50
|
+
* a name. Adding/removing packs shifts ids; storage is the charge log
|
|
51
|
+
* (which records the exact productKey at purchase time), so historical
|
|
52
|
+
* charges stay valid even if you remove a pack later.
|
|
53
|
+
*/
|
|
54
|
+
export type CreditsPack<L extends string = string> = {
|
|
55
|
+
readonly xtr: number;
|
|
56
|
+
readonly grants: {
|
|
57
|
+
readonly credits: number;
|
|
58
|
+
};
|
|
59
|
+
readonly name?: Polyglot<L>;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Credits axis config. `unit` is the user-facing word for one credit
|
|
63
|
+
* (the menu says e.g. "1234 mensajes"). `packs` is the catalogue of
|
|
64
|
+
* top-up bundles surfaced in the menu and via `invoice('credits.N')`.
|
|
65
|
+
*/
|
|
66
|
+
export type CreditsConfig<L extends string = string> = {
|
|
67
|
+
readonly unit: Polyglot<L>;
|
|
68
|
+
readonly packs: ReadonlyArray<CreditsPack<L>>;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Orthogonal one-shot unlock (a "perk"). Named via the user's own key in
|
|
72
|
+
* the config (`perks: { voice_mode: { … } }`), surfaced as
|
|
73
|
+
* `perks.voice_mode` everywhere.
|
|
74
|
+
*/
|
|
75
|
+
export type Perk<L extends string = string> = {
|
|
76
|
+
readonly xtr: number;
|
|
77
|
+
readonly name: Polyglot<L>;
|
|
78
|
+
};
|
|
79
|
+
export type PerksConfig<L extends string = string> = Readonly<Record<string, Perk<L>>>;
|
|
80
|
+
export type LegalConfig = {
|
|
81
|
+
readonly sellerName: string;
|
|
82
|
+
readonly nif: string;
|
|
83
|
+
/**
|
|
84
|
+
* Optional terms-of-service URL. When omitted, the consent prompt's
|
|
85
|
+
* 📖 Terms button is hidden — there's no Telegram-side standard ToS
|
|
86
|
+
* to default to (unlike `privacyUrl`).
|
|
87
|
+
*/
|
|
88
|
+
readonly termsUrl?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Optional privacy policy URL. When omitted, defaults to Telegram's
|
|
91
|
+
* [Standard Bot Privacy Policy](https://telegram.org/privacy-tpa),
|
|
92
|
+
* which covers what this library's plugins retain (session record,
|
|
93
|
+
* waiver consent, charge log) under "data necessary to function".
|
|
94
|
+
* Override only when your bot retains data beyond what the standard
|
|
95
|
+
* covers.
|
|
96
|
+
*/
|
|
97
|
+
readonly privacyUrl?: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Fallback when `legal.privacyUrl` is omitted. Identical to the menu
|
|
101
|
+
* plugin's default — both surface the same policy text.
|
|
102
|
+
*/
|
|
103
|
+
export declare const DEFAULT_PRIVACY_URL = "https://telegram.org/privacy-tpa";
|
|
104
|
+
/**
|
|
105
|
+
* Art. 103(m) TRLGDCU waiver text. Versioned so a wording change forces
|
|
106
|
+
* re-consent; the version string is stored alongside each charge for audit.
|
|
107
|
+
*
|
|
108
|
+
* The Polyglot covers every locale the bot supports — the plugin
|
|
109
|
+
* validates this at construction.
|
|
110
|
+
*/
|
|
111
|
+
export type WaiverConfig<L extends string = string> = {
|
|
112
|
+
readonly version: string;
|
|
113
|
+
readonly text: Polyglot<L>;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* What the bot author passes to `botPayments({...})`. The generic `L`
|
|
117
|
+
* carries the bot's Polyglot language union forward into every nested
|
|
118
|
+
* `Polyglot<L>`, so TS catches a missing locale in any sub-config.
|
|
119
|
+
*/
|
|
120
|
+
export type BotPaymentsConfig<L extends string = string> = {
|
|
121
|
+
readonly paysupport: string;
|
|
122
|
+
readonly legal: LegalConfig;
|
|
123
|
+
readonly waiver: WaiverConfig<L>;
|
|
124
|
+
readonly vip?: ReadonlyArray<VipRung<L>>;
|
|
125
|
+
readonly credits?: CreditsConfig<L>;
|
|
126
|
+
readonly perks?: PerksConfig<L>;
|
|
127
|
+
};
|
|
128
|
+
type _Range1To<N extends number, Acc extends unknown[] = [unknown]> = Acc["length"] extends N ? Acc["length"] : Acc["length"] | _Range1To<N, [...Acc, unknown]>;
|
|
129
|
+
/**
|
|
130
|
+
* Positional 1-based ids for a tuple. `[a,b,c]` → `1 | 2 | 3`.
|
|
131
|
+
*
|
|
132
|
+
* Empty tuple → `never` (no ids exist).
|
|
133
|
+
*/
|
|
134
|
+
export type Indices1<V extends ReadonlyArray<unknown>> = V["length"] extends 0 ? never : V["length"] extends number ? _Range1To<V["length"]> : never;
|
|
135
|
+
/**
|
|
136
|
+
* Tier ids derivable from a `vip` config array. Bare `'vip'` is also
|
|
137
|
+
* accepted as "any rung" by `atLeast` (see `AtLeastKey`).
|
|
138
|
+
*/
|
|
139
|
+
export type VipPositionalKeys<V extends ReadonlyArray<unknown> | undefined> = V extends ReadonlyArray<unknown> ? `vip.${Indices1<V>}` : never;
|
|
140
|
+
export type CreditsPackKeys<C extends CreditsConfig<string> | undefined> = C extends {
|
|
141
|
+
packs: infer P;
|
|
142
|
+
} ? P extends ReadonlyArray<unknown> ? `credits.${Indices1<P>}` : never : never;
|
|
143
|
+
export type PerkKeys<P extends PerksConfig<string> | undefined> = P extends PerksConfig<string> ? `perks.${keyof P & string}` : never;
|
|
144
|
+
/**
|
|
145
|
+
* Every product the user can purchase. Discriminated by prefix. Note
|
|
146
|
+
* `perks.X` uses the **author-supplied key** verbatim (e.g.
|
|
147
|
+
* `perks.voice_mode`) while `vip.N` / `credits.N` are positional.
|
|
148
|
+
*/
|
|
149
|
+
export type ProductKey<Cfg extends BotPaymentsConfig<string>> = VipPositionalKeys<Cfg["vip"]> | CreditsPackKeys<Cfg["credits"]> | PerkKeys<Cfg["perks"]>;
|
|
150
|
+
/**
|
|
151
|
+
* What `ctx.payments.tier()` can return. `'free'` is the bottom rung;
|
|
152
|
+
* `vip.N` follows the ladder.
|
|
153
|
+
*/
|
|
154
|
+
export type TierKey<Cfg extends BotPaymentsConfig<string>> = "free" | VipPositionalKeys<Cfg["vip"]>;
|
|
155
|
+
/**
|
|
156
|
+
* What `ctx.payments.atLeast(...)` accepts. Same as `TierKey` minus
|
|
157
|
+
* `'free'` (atLeast('free') is trivially always true and unhelpful as a
|
|
158
|
+
* gate) plus the bare `'vip'` synonym for "any rung in the namespace".
|
|
159
|
+
*/
|
|
160
|
+
export type AtLeastKey<Cfg extends BotPaymentsConfig<string>> = "vip" | VipPositionalKeys<Cfg["vip"]>;
|
|
161
|
+
export type VipRungResolved = {
|
|
162
|
+
readonly id: `vip.${number}`;
|
|
163
|
+
readonly rank: number;
|
|
164
|
+
readonly xtr: number;
|
|
165
|
+
readonly periodSeconds: number;
|
|
166
|
+
readonly name: Polyglot<string>;
|
|
167
|
+
readonly creditsGranted: number;
|
|
168
|
+
};
|
|
169
|
+
export type CreditsPackResolved = {
|
|
170
|
+
readonly id: `credits.${number}`;
|
|
171
|
+
readonly xtr: number;
|
|
172
|
+
readonly creditsGranted: number;
|
|
173
|
+
readonly name?: Polyglot<string>;
|
|
174
|
+
};
|
|
175
|
+
export type PerkResolved = {
|
|
176
|
+
readonly id: `perks.${string}`;
|
|
177
|
+
readonly key: string;
|
|
178
|
+
readonly xtr: number;
|
|
179
|
+
readonly name: Polyglot<string>;
|
|
180
|
+
};
|
|
181
|
+
export type ProductCatalog = {
|
|
182
|
+
readonly vip: ReadonlyArray<VipRungResolved>;
|
|
183
|
+
readonly creditsUnit: Polyglot<string> | undefined;
|
|
184
|
+
readonly creditsPacks: ReadonlyArray<CreditsPackResolved>;
|
|
185
|
+
readonly perks: ReadonlyArray<PerkResolved>;
|
|
186
|
+
/** O(1) lookup by ProductKey. */
|
|
187
|
+
readonly byKey: ReadonlyMap<string, VipRungResolved | CreditsPackResolved | PerkResolved>;
|
|
188
|
+
};
|
|
189
|
+
export type WaiverRecord = {
|
|
190
|
+
readonly at: number;
|
|
191
|
+
readonly version: string;
|
|
192
|
+
readonly locale: string;
|
|
193
|
+
};
|
|
194
|
+
export type VipState = {
|
|
195
|
+
/** 1-based rank — index into the resolved vip ladder + 1. */
|
|
196
|
+
readonly rung: number;
|
|
197
|
+
/** Original `telegram_payment_charge_id` of the active subscription. */
|
|
198
|
+
readonly chargeId: string;
|
|
199
|
+
/** Telegram's `subscription_expiration_date` — unix seconds. */
|
|
200
|
+
readonly expiresAt: number;
|
|
201
|
+
/** True after the user toggled cancel-renewal; access keeps until `expiresAt`. */
|
|
202
|
+
readonly canceled: boolean;
|
|
203
|
+
};
|
|
204
|
+
export type PerkState = {
|
|
205
|
+
readonly chargeId: string;
|
|
206
|
+
readonly at: number;
|
|
207
|
+
};
|
|
208
|
+
export type PaymentsSession = {
|
|
209
|
+
waiver?: WaiverRecord;
|
|
210
|
+
credits?: number;
|
|
211
|
+
vip?: VipState;
|
|
212
|
+
perks?: Record<string, PerkState>;
|
|
213
|
+
};
|
|
214
|
+
export type PaysupportState = "none" | "opened" | "refunded";
|
|
215
|
+
export type ChargeRecord = {
|
|
216
|
+
readonly chargeId: string;
|
|
217
|
+
readonly userId: number;
|
|
218
|
+
readonly productKey: string;
|
|
219
|
+
readonly xtr: number;
|
|
220
|
+
readonly receivedAt: number;
|
|
221
|
+
/** Decoded invoice payload echoed back from Telegram. */
|
|
222
|
+
readonly payload: string;
|
|
223
|
+
/**
|
|
224
|
+
* Waiver snapshot at purchase time. Captured even if the user
|
|
225
|
+
* already consented earlier — defensive forensics.
|
|
226
|
+
*/
|
|
227
|
+
readonly waiverSnapshot: WaiverRecord;
|
|
228
|
+
/**
|
|
229
|
+
* Telegram's `subscription_expiration_date` (unix seconds) if this
|
|
230
|
+
* charge was a subscription purchase. Used to compute tier expiry.
|
|
231
|
+
*/
|
|
232
|
+
readonly subscriptionExpiresAt?: number;
|
|
233
|
+
/**
|
|
234
|
+
* Credits this charge grants at fulfillment / renewal. Denormalized
|
|
235
|
+
* onto the record so derivation works even if the catalog config
|
|
236
|
+
* later changes (rare, but the charge log must remain authoritative
|
|
237
|
+
* regardless of source-code edits). Zero for products that don't
|
|
238
|
+
* grant credits.
|
|
239
|
+
*/
|
|
240
|
+
readonly creditsGranted: number;
|
|
241
|
+
/**
|
|
242
|
+
* 1-based vip rung this charge subscribed to, denormalized for the
|
|
243
|
+
* same reason as `creditsGranted`. Only set for `vip.*` charges.
|
|
244
|
+
*/
|
|
245
|
+
readonly vipRung?: number;
|
|
246
|
+
/**
|
|
247
|
+
* Perk key this charge unlocked, denormalized. Only set for
|
|
248
|
+
* `perks.*` charges.
|
|
249
|
+
*/
|
|
250
|
+
readonly perkKey?: string;
|
|
251
|
+
paysupportState: PaysupportState;
|
|
252
|
+
refundedAt?: number | null;
|
|
253
|
+
/** Set after `record()` claims this charge into a payout batch. */
|
|
254
|
+
payoutBatchId?: string | null;
|
|
255
|
+
};
|
|
256
|
+
export type PayoutRecord = {
|
|
257
|
+
readonly batchId: string;
|
|
258
|
+
readonly fromMs: number;
|
|
259
|
+
readonly toMs: number;
|
|
260
|
+
readonly tonAmount: number;
|
|
261
|
+
readonly eurAtReceipt: number;
|
|
262
|
+
readonly recordedAt: number;
|
|
263
|
+
/** Optional Spanish factura number issued for this payout. */
|
|
264
|
+
facturaNumber?: string;
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Snapshot returned by `derive.ts` — pure function of the charge log
|
|
268
|
+
* for a given user. The plugin writes this into `session.pay.*` so
|
|
269
|
+
* runtime checks are O(1), but the log is always the truth.
|
|
270
|
+
*/
|
|
271
|
+
export type DerivedPaymentsState = {
|
|
272
|
+
readonly credits: number;
|
|
273
|
+
readonly vip: VipState | undefined;
|
|
274
|
+
readonly perks: Record<string, PerkState>;
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* What `payments.onFulfilled(productKey, handler)` receives. Sync
|
|
278
|
+
* signature on purpose — the plugin always answers the
|
|
279
|
+
* `successful_payment` event itself; the handler does its own async work
|
|
280
|
+
* fire-and-forget.
|
|
281
|
+
*/
|
|
282
|
+
export type FulfillmentEvent = {
|
|
283
|
+
readonly productKey: string;
|
|
284
|
+
readonly userId: number;
|
|
285
|
+
readonly chargeId: string;
|
|
286
|
+
readonly xtr: number;
|
|
287
|
+
readonly receivedAt: number;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Thrown by `credits.consume(n)` when the balance is below `n`.
|
|
291
|
+
* `tryConsume(n)` returns `false` instead.
|
|
292
|
+
*/
|
|
293
|
+
export declare class InsufficientCredits extends Error {
|
|
294
|
+
readonly requested: number;
|
|
295
|
+
readonly available: number;
|
|
296
|
+
constructor(requested: number, available: number);
|
|
297
|
+
}
|
|
298
|
+
export {};
|
|
299
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/types.ts"],"names":[],"mappings":"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"}
|