responses-proxy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +56 -0
  2. package/cli.js +118 -0
  3. package/dist/anthropic-messages.js +383 -0
  4. package/dist/anthropic-messages.test.js +209 -0
  5. package/dist/audit-log.js +138 -0
  6. package/dist/audit-log.test.js +480 -0
  7. package/dist/billing-expiration.js +70 -0
  8. package/dist/billing-expiration.test.js +114 -0
  9. package/dist/billing.js +716 -0
  10. package/dist/billing.test.js +228 -0
  11. package/dist/chatgpt-oauth-store.js +240 -0
  12. package/dist/chatgpt-oauth-store.test.js +88 -0
  13. package/dist/chatgpt-oauth.js +118 -0
  14. package/dist/chatgpt-oauth.test.js +63 -0
  15. package/dist/chatgpt-provider-auth.js +60 -0
  16. package/dist/chatgpt-provider-auth.test.js +101 -0
  17. package/dist/client/app-icon.svg +17 -0
  18. package/dist/client/assets/index-C7Vvhst8.js +14 -0
  19. package/dist/client/assets/index-DpqgYK3L.css +1 -0
  20. package/dist/client/favicon.svg +17 -0
  21. package/dist/client/index.html +31 -0
  22. package/dist/client-config-apply.js +345 -0
  23. package/dist/client-config-apply.test.js +185 -0
  24. package/dist/client-token-limits.js +111 -0
  25. package/dist/client-token-limits.test.js +129 -0
  26. package/dist/codex-config.js +47 -0
  27. package/dist/codex-setup.js +87 -0
  28. package/dist/codex-setup.test.js +30 -0
  29. package/dist/config.js +314 -0
  30. package/dist/cost-analytics.js +31 -0
  31. package/dist/cost-analytics.test.js +38 -0
  32. package/dist/customer-key-access.js +126 -0
  33. package/dist/customer-key-access.test.js +178 -0
  34. package/dist/customer-keys.js +209 -0
  35. package/dist/customer-keys.test.js +68 -0
  36. package/dist/customer-usage.js +18 -0
  37. package/dist/customer-usage.test.js +55 -0
  38. package/dist/dashboard-auth.js +318 -0
  39. package/dist/dashboard-auth.test.js +133 -0
  40. package/dist/dashboard-serving.test.js +235 -0
  41. package/dist/error-response.js +174 -0
  42. package/dist/error-response.test.js +88 -0
  43. package/dist/forward.js +357 -0
  44. package/dist/health-websocket-manager.js +174 -0
  45. package/dist/http-rate-limit.js +36 -0
  46. package/dist/http-rate-limit.test.js +62 -0
  47. package/dist/kiro-auth.js +136 -0
  48. package/dist/kiro-auth.test.js +234 -0
  49. package/dist/kiro-codewhisperer.js +646 -0
  50. package/dist/kiro-codewhisperer.test.js +219 -0
  51. package/dist/kiro-device-login.js +338 -0
  52. package/dist/kiro-eventstream.js +219 -0
  53. package/dist/kiro-eventstream.test.js +79 -0
  54. package/dist/kiro-forward.js +401 -0
  55. package/dist/kiro-import-cli.js +69 -0
  56. package/dist/kiro-import.js +94 -0
  57. package/dist/kiro-import.test.js +125 -0
  58. package/dist/kiro-token-store.js +196 -0
  59. package/dist/kiro-token-store.test.js +207 -0
  60. package/dist/krouter-usage.js +243 -0
  61. package/dist/model-combo-repository.js +147 -0
  62. package/dist/model-routing.js +69 -0
  63. package/dist/model-routing.test.js +41 -0
  64. package/dist/normalize-request.js +531 -0
  65. package/dist/normalize-request.test.js +277 -0
  66. package/dist/omv-public-firewall.test.js +11 -0
  67. package/dist/package.json +17 -0
  68. package/dist/prompt-cache-state.js +146 -0
  69. package/dist/prompt-cache-state.test.js +71 -0
  70. package/dist/prompt-cache.js +229 -0
  71. package/dist/provider-health-service.js +404 -0
  72. package/dist/provider-request-parameters.js +107 -0
  73. package/dist/provider-request-parameters.test.js +26 -0
  74. package/dist/provider-routing.js +114 -0
  75. package/dist/provider-routing.test.js +64 -0
  76. package/dist/provider-usage.js +314 -0
  77. package/dist/request-timeout-policy.js +61 -0
  78. package/dist/request-timeout-policy.test.js +40 -0
  79. package/dist/response-cache.js +69 -0
  80. package/dist/response-cache.test.js +28 -0
  81. package/dist/routing-combo-repository.js +300 -0
  82. package/dist/routing-engine.js +377 -0
  83. package/dist/routing-integration.js +155 -0
  84. package/dist/routing-simulation-engine.js +326 -0
  85. package/dist/rtk-layer.js +483 -0
  86. package/dist/rtk-layer.test.js +198 -0
  87. package/dist/runtime-provider-repository.js +1742 -0
  88. package/dist/runtime-provider-repository.test.js +1177 -0
  89. package/dist/schema.js +118 -0
  90. package/dist/schema.test.js +16 -0
  91. package/dist/sepay-webhook.js +87 -0
  92. package/dist/sepay-webhook.test.js +142 -0
  93. package/dist/server-body-limit.test.js +35 -0
  94. package/dist/server-client-token-limits.test.js +161 -0
  95. package/dist/server-codex-config-setup.test.js +76 -0
  96. package/dist/server-http-rate-limit.test.js +80 -0
  97. package/dist/server-response-cache.test.js +105 -0
  98. package/dist/server-routes-alias.test.js +39 -0
  99. package/dist/server-sepay-webhook-security.test.js +59 -0
  100. package/dist/server.js +5906 -0
  101. package/dist/session-log.js +178 -0
  102. package/dist/tailnet-funnel-script.test.js +33 -0
  103. package/dist/telegram-bot/actions.js +118 -0
  104. package/dist/telegram-bot/admin-actions.js +103 -0
  105. package/dist/telegram-bot/auth.js +46 -0
  106. package/dist/telegram-bot/auth.test.js +1 -0
  107. package/dist/telegram-bot/bot-identity-repository.js +189 -0
  108. package/dist/telegram-bot/bot-identity-repository.test.js +78 -0
  109. package/dist/telegram-bot/callbacks.js +30 -0
  110. package/dist/telegram-bot/codex-config-delivery.js +38 -0
  111. package/dist/telegram-bot/codex-config-delivery.test.js +75 -0
  112. package/dist/telegram-bot/commands/accounts.js +140 -0
  113. package/dist/telegram-bot/commands/apikey.js +737 -0
  114. package/dist/telegram-bot/commands/apply.js +265 -0
  115. package/dist/telegram-bot/commands/clients.js +13 -0
  116. package/dist/telegram-bot/commands/customer-billing.test.js +271 -0
  117. package/dist/telegram-bot/commands/grant.js +138 -0
  118. package/dist/telegram-bot/commands/grant.test.js +217 -0
  119. package/dist/telegram-bot/commands/help.js +52 -0
  120. package/dist/telegram-bot/commands/me.js +53 -0
  121. package/dist/telegram-bot/commands/models.js +6 -0
  122. package/dist/telegram-bot/commands/oauth.js +64 -0
  123. package/dist/telegram-bot/commands/plans.js +96 -0
  124. package/dist/telegram-bot/commands/providers.js +27 -0
  125. package/dist/telegram-bot/commands/quota.js +10 -0
  126. package/dist/telegram-bot/commands/renew-user.js +139 -0
  127. package/dist/telegram-bot/commands/renew-user.test.js +184 -0
  128. package/dist/telegram-bot/commands/renew.js +1369 -0
  129. package/dist/telegram-bot/commands/renew.test.js +1633 -0
  130. package/dist/telegram-bot/commands/start.js +212 -0
  131. package/dist/telegram-bot/commands/start.test.js +280 -0
  132. package/dist/telegram-bot/commands/status.js +6 -0
  133. package/dist/telegram-bot/commands/tailscale.js +15 -0
  134. package/dist/telegram-bot/commands/tailscale.test.js +76 -0
  135. package/dist/telegram-bot/commands/test.js +51 -0
  136. package/dist/telegram-bot/commands/test.test.js +14 -0
  137. package/dist/telegram-bot/commands/usage.js +10 -0
  138. package/dist/telegram-bot/config.js +98 -0
  139. package/dist/telegram-bot/config.test.js +42 -0
  140. package/dist/telegram-bot/customer-actions.js +160 -0
  141. package/dist/telegram-bot/customer-api-keys.js +68 -0
  142. package/dist/telegram-bot/customer-billing.js +72 -0
  143. package/dist/telegram-bot/customer-workspace-repository.js +134 -0
  144. package/dist/telegram-bot/customer-workspace-repository.test.js +47 -0
  145. package/dist/telegram-bot/dashboard-login.js +39 -0
  146. package/dist/telegram-bot/format.js +140 -0
  147. package/dist/telegram-bot/grants.js +370 -0
  148. package/dist/telegram-bot/grants.test.js +290 -0
  149. package/dist/telegram-bot/index.js +85 -0
  150. package/dist/telegram-bot/message-cleanup.js +55 -0
  151. package/dist/telegram-bot/message-cleanup.test.js +77 -0
  152. package/dist/telegram-bot/message-format.js +45 -0
  153. package/dist/telegram-bot/message-format.test.js +10 -0
  154. package/dist/telegram-bot/proxy-client.js +174 -0
  155. package/dist/telegram-bot/rate-limit.js +95 -0
  156. package/dist/telegram-bot/rate-limit.test.js +58 -0
  157. package/dist/telegram-bot/sessions.js +171 -0
  158. package/dist/telegram-bot/sessions.test.js +107 -0
  159. package/dist/telegram-bot/telegram-adapter.js +126 -0
  160. package/dist/telegram-bot/worker.js +63 -0
  161. package/package.json +39 -0
@@ -0,0 +1,1633 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+ import { BillingRepository } from "../../billing.js";
7
+ import { CustomerKeyRepository } from "../../customer-keys.js";
8
+ import { AuditLogRepository } from "../../audit-log.js";
9
+ import { BotIdentityRepository } from "../bot-identity-repository.js";
10
+ import { CustomerWorkspaceRepository } from "../customer-workspace-repository.js";
11
+ import { SqliteSessionStore } from "../sessions.js";
12
+ import { registerRenewCommand } from "./renew.js";
13
+ function createConfig(overrides = {}) {
14
+ return {
15
+ telegramBotToken: "token",
16
+ allowedUserIds: new Set(),
17
+ allowedChatIds: new Set(),
18
+ ownerUserIds: new Set(["1"]),
19
+ adminUserIds: new Set(),
20
+ botMode: "polling",
21
+ proxyAdminBaseUrl: "http://127.0.0.1:8318",
22
+ defaultModel: "gpt-5.5",
23
+ publicSignupEnabled: true,
24
+ requireAdminApproval: false,
25
+ defaultCustomerRoute: "customers",
26
+ publicResponsesBaseUrl: "http://127.0.0.1:8318/v1",
27
+ proxyRequestTimeoutMs: 30_000,
28
+ sessionDbPath: ":memory:",
29
+ sessionTtlMs: 900_000,
30
+ rateLimitWindowMs: 60_000,
31
+ rateLimitMaxRequests: 12,
32
+ logLevel: "info",
33
+ sepayAccountNumber: "8300138258001",
34
+ sepayBankCode: "MBBank",
35
+ sepayTemplate: "compact",
36
+ sepayDownload: false,
37
+ ...overrides,
38
+ };
39
+ }
40
+ function createMockProxyClient() {
41
+ let routeKeys = [];
42
+ return {
43
+ client: {
44
+ async getClientConfigs() {
45
+ return {
46
+ clientRoutes: [{ key: "customers", apiKeys: [...routeKeys] }],
47
+ };
48
+ },
49
+ async setClientRouteApiKeys(input) {
50
+ if (input.client === "customers") {
51
+ routeKeys = [...input.apiKeys];
52
+ }
53
+ return { ok: true };
54
+ },
55
+ },
56
+ };
57
+ }
58
+ function createBotHarness() {
59
+ const commandHandlers = new Map();
60
+ const messageTextHandlers = [];
61
+ const callbackHandlers = [];
62
+ return {
63
+ bot: {
64
+ command(name, handler) {
65
+ commandHandlers.set(name, handler);
66
+ },
67
+ callbackQuery(pattern, handler) {
68
+ callbackHandlers.push({ pattern, handler });
69
+ },
70
+ on(event, handler) {
71
+ if (event === "message:text") {
72
+ messageTextHandlers.push(handler);
73
+ }
74
+ },
75
+ },
76
+ handler(name) {
77
+ const handler = commandHandlers.get(name);
78
+ assert.ok(handler);
79
+ return handler;
80
+ },
81
+ callbackHandler(data) {
82
+ for (const entry of callbackHandlers) {
83
+ if (typeof entry.pattern === "string") {
84
+ if (entry.pattern === data) {
85
+ return { handler: entry.handler, match: [data] };
86
+ }
87
+ continue;
88
+ }
89
+ const match = data.match(entry.pattern);
90
+ if (match) {
91
+ return { handler: entry.handler, match };
92
+ }
93
+ }
94
+ assert.fail(`missing callback handler for ${data}`);
95
+ },
96
+ async runText(ctx) {
97
+ let index = 0;
98
+ const next = async () => {
99
+ const handler = messageTextHandlers[index];
100
+ index += 1;
101
+ if (handler) {
102
+ await handler(ctx, next);
103
+ }
104
+ };
105
+ await next();
106
+ },
107
+ };
108
+ }
109
+ function createContext(input) {
110
+ const replies = [];
111
+ const sentMessages = [];
112
+ const sentDocuments = [];
113
+ const editedTexts = [];
114
+ const replyMarkups = [];
115
+ const editedMarkups = [];
116
+ return {
117
+ from: { id: input.fromId, is_bot: false, first_name: "User" },
118
+ chat: input.chatType === "private"
119
+ ? { id: input.chatId, type: "private", first_name: "User" }
120
+ : { id: input.chatId, type: "group", title: "Ops" },
121
+ message: {
122
+ message_id: 1,
123
+ date: 0,
124
+ chat: input.chatType === "private"
125
+ ? { id: input.chatId, type: "private", first_name: "User" }
126
+ : { id: input.chatId, type: "group", title: "Ops" },
127
+ text: "/renew",
128
+ },
129
+ callbackQuery: input.callbackData
130
+ ? {
131
+ id: "callback-1",
132
+ from: { id: input.fromId, is_bot: false, first_name: "User" },
133
+ chat_instance: "chat",
134
+ data: input.callbackData,
135
+ message: {
136
+ message_id: 99,
137
+ date: 0,
138
+ chat: input.chatType === "private"
139
+ ? { id: input.chatId, type: "private", first_name: "User" }
140
+ : { id: input.chatId, type: "group", title: "Ops" },
141
+ },
142
+ }
143
+ : undefined,
144
+ match: input.match,
145
+ replies,
146
+ sentMessages,
147
+ sentDocuments,
148
+ editedTexts,
149
+ replyMarkups,
150
+ editedMarkups,
151
+ reply(text, options) {
152
+ replies.push(text);
153
+ replyMarkups.push(options?.reply_markup);
154
+ return Promise.resolve({});
155
+ },
156
+ answerCallbackQuery() {
157
+ return Promise.resolve(true);
158
+ },
159
+ editMessageReplyMarkup(options) {
160
+ editedMarkups.push(options?.reply_markup);
161
+ return Promise.resolve({});
162
+ },
163
+ editMessageText(text, options) {
164
+ editedTexts.push(text);
165
+ editedMarkups.push(options?.reply_markup);
166
+ return Promise.resolve({});
167
+ },
168
+ api: {
169
+ async sendMessage(chatId, text) {
170
+ if (input.sendMessageImpl) {
171
+ await input.sendMessageImpl(chatId, text);
172
+ }
173
+ sentMessages.push({ chatId, text });
174
+ return {};
175
+ },
176
+ async sendDocument(chatId, document) {
177
+ sentDocuments.push({
178
+ chatId,
179
+ filename: document.filename,
180
+ content: document.fileData ? Buffer.from(document.fileData).toString("utf8") : "",
181
+ });
182
+ return {};
183
+ },
184
+ async editMessageText(_chatId, _messageId, text) {
185
+ editedTexts.push(text);
186
+ return {};
187
+ },
188
+ },
189
+ };
190
+ }
191
+ async function withRepos(fn) {
192
+ const dir = mkdtempSync(path.join(os.tmpdir(), "renew-command-"));
193
+ try {
194
+ const dbFile = path.join(dir, "bot.sqlite");
195
+ const proxy = createMockProxyClient();
196
+ await fn({
197
+ identities: BotIdentityRepository.create(dbFile),
198
+ workspaces: CustomerWorkspaceRepository.create(dbFile),
199
+ customerKeys: CustomerKeyRepository.create(dbFile),
200
+ billing: BillingRepository.create(dbFile),
201
+ auditLog: AuditLogRepository.create(dbFile),
202
+ sessions: SqliteSessionStore.create(dbFile, 60_000),
203
+ deps: {
204
+ config: createConfig({ sessionDbPath: dbFile }),
205
+ proxyClient: proxy.client,
206
+ },
207
+ });
208
+ }
209
+ finally {
210
+ rmSync(dir, { recursive: true, force: true });
211
+ }
212
+ }
213
+ test("customer /renew without args creates a 24h purchase request", async () => {
214
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
215
+ identities.upsertUser({
216
+ telegramUserId: "42",
217
+ defaultRole: "customer",
218
+ defaultStatus: "active",
219
+ });
220
+ workspaces.ensureDefaultWorkspace({
221
+ ownerTelegramUserId: "42",
222
+ defaultClientRoute: "customers",
223
+ status: "active",
224
+ });
225
+ const harness = createBotHarness();
226
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
227
+ const ctx = createContext({
228
+ fromId: 42,
229
+ chatId: 42,
230
+ chatType: "private",
231
+ match: "",
232
+ });
233
+ await harness.handler("renew")(ctx);
234
+ const request = billing.listRenewalRequests("open")[0];
235
+ assert.equal(request?.requestedDays, 1);
236
+ assert.equal(request?.requestedPlanId, "basic");
237
+ assert.equal(ctx.replies[0]?.includes("API key purchase request submitted."), true);
238
+ assert.equal(ctx.replies[0]?.includes("plan: 5000 VND -> 10000000 tokens"), true);
239
+ assert.equal(ctx.replies[0]?.includes("requested_plan_id: basic"), true);
240
+ assert.equal(ctx.replies[0]?.includes("requested_days: 1"), true);
241
+ });
242
+ });
243
+ test("customer can request 24h renewal from the start button", async () => {
244
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
245
+ identities.upsertUser({
246
+ telegramUserId: "42",
247
+ defaultRole: "customer",
248
+ defaultStatus: "active",
249
+ });
250
+ workspaces.ensureDefaultWorkspace({
251
+ ownerTelegramUserId: "42",
252
+ defaultClientRoute: "customers",
253
+ status: "active",
254
+ });
255
+ const harness = createBotHarness();
256
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
257
+ const found = harness.callbackHandler("v1:renew:open");
258
+ const ctx = createContext({
259
+ fromId: 42,
260
+ chatId: 42,
261
+ chatType: "private",
262
+ match: "",
263
+ callbackData: "v1:renew:open",
264
+ });
265
+ ctx.match = found.match;
266
+ await found.handler(ctx);
267
+ const request = billing.listRenewalRequests("open")[0];
268
+ assert.equal(request?.requestedDays, 1);
269
+ assert.equal(request?.requestedPlanId, "basic");
270
+ assert.equal((ctx.replies[0] ?? ctx.editedTexts[0])?.includes("API key purchase request submitted."), true);
271
+ });
272
+ });
273
+ test("customer can request token top-up from the dashboard button", async () => {
274
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
275
+ identities.upsertUser({
276
+ telegramUserId: "42",
277
+ defaultRole: "customer",
278
+ defaultStatus: "active",
279
+ });
280
+ const workspace = workspaces.ensureDefaultWorkspace({
281
+ ownerTelegramUserId: "42",
282
+ defaultClientRoute: "customers",
283
+ status: "active",
284
+ });
285
+ customerKeys.createKey({
286
+ workspaceId: workspace.id,
287
+ telegramUserId: "42",
288
+ clientRoute: "customers",
289
+ status: "active",
290
+ });
291
+ billing.grantSubscription({
292
+ workspaceId: workspace.id,
293
+ planId: "basic",
294
+ days: 1,
295
+ });
296
+ const harness = createBotHarness();
297
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
298
+ const found = harness.callbackHandler("v1:topup:open");
299
+ const ctx = createContext({
300
+ fromId: 42,
301
+ chatId: 42,
302
+ chatType: "private",
303
+ match: "",
304
+ callbackData: "v1:topup:open",
305
+ });
306
+ await found.handler(ctx);
307
+ const request = billing.listRenewalRequests("open")[0];
308
+ assert.equal(request?.kind, "token_topup");
309
+ assert.equal(request?.requestedTokenDelta, 10_000_000);
310
+ assert.equal(request?.requestedTokenLotDays, 1);
311
+ assert.equal((ctx.replies[0] ?? ctx.editedTexts[0])?.includes("Token top-up request submitted."), true);
312
+ });
313
+ });
314
+ test("customer /renew creates a renewal request and notifies admin", async () => {
315
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
316
+ identities.upsertUser({
317
+ telegramUserId: "42",
318
+ firstName: "Atger",
319
+ defaultRole: "customer",
320
+ defaultStatus: "active",
321
+ });
322
+ const workspace = workspaces.ensureDefaultWorkspace({
323
+ ownerTelegramUserId: "42",
324
+ defaultClientRoute: "customers",
325
+ status: "active",
326
+ });
327
+ customerKeys.createKey({
328
+ workspaceId: workspace.id,
329
+ telegramUserId: "42",
330
+ clientRoute: "customers",
331
+ });
332
+ billing.grantSubscription({
333
+ workspaceId: workspace.id,
334
+ planId: "basic",
335
+ days: 30,
336
+ now: new Date("2026-04-27T00:00:00.000Z"),
337
+ });
338
+ const notified = [];
339
+ const harness = createBotHarness();
340
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
341
+ const ctx = createContext({
342
+ fromId: 42,
343
+ chatId: 42,
344
+ chatType: "private",
345
+ match: "",
346
+ sendMessageImpl: async (chatId, text) => {
347
+ notified.push({ chatId, text });
348
+ },
349
+ });
350
+ await harness.handler("renew")(ctx);
351
+ assert.equal(billing.listRenewalRequests("open").length, 1);
352
+ assert.equal(ctx.replies[0]?.includes("Renewal request submitted."), true);
353
+ assert.equal(ctx.replies[0]?.includes("plan: 5000 VND -> 10000000 tokens"), true);
354
+ assert.equal(ctx.replies[0]?.includes("Payment"), true);
355
+ assert.equal(ctx.replies[0]?.includes("• Amount: 5,000 VND"), true);
356
+ assert.equal(ctx.replies[0]?.includes("• Transfer note: Chuc ngon mieng ma "), true);
357
+ assert.equal(ctx.replies[0]?.includes("• Scan QR: https://qr.sepay.vn/img?"), true);
358
+ assert.equal(notified.length, 1);
359
+ assert.equal(notified[0]?.chatId, 1);
360
+ assert.equal(notified[0]?.text.includes("Renewal request"), true);
361
+ assert.equal(notified[0]?.text.includes("• Telegram user: Atger | id=42"), true);
362
+ assert.equal(notified[0]?.text.includes("• Requested plan: basic (Basic)"), true);
363
+ assert.equal(notified[0]?.text.includes("• Requested days: 1"), true);
364
+ assert.equal(notified[0]?.text.includes("• Current expiry:"), true);
365
+ });
366
+ });
367
+ test("customer sees a warning when admin notification fails", async () => {
368
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
369
+ identities.upsertUser({
370
+ telegramUserId: "42",
371
+ defaultRole: "customer",
372
+ defaultStatus: "active",
373
+ });
374
+ const workspace = workspaces.ensureDefaultWorkspace({
375
+ ownerTelegramUserId: "42",
376
+ defaultClientRoute: "customers",
377
+ status: "active",
378
+ });
379
+ customerKeys.createKey({
380
+ workspaceId: workspace.id,
381
+ telegramUserId: "42",
382
+ clientRoute: "customers",
383
+ });
384
+ const harness = createBotHarness();
385
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
386
+ const ctx = createContext({
387
+ fromId: 42,
388
+ chatId: 42,
389
+ chatType: "private",
390
+ match: "",
391
+ sendMessageImpl: async () => {
392
+ throw new Error("telegram send failed");
393
+ },
394
+ });
395
+ await harness.handler("renew")(ctx);
396
+ assert.equal(billing.listRenewalRequests("open").length, 1);
397
+ assert.equal(ctx.replies[0]?.includes("Renewal request submitted."), true);
398
+ assert.equal(ctx.replies[0]?.includes("admin_notification: pending_manual_follow_up"), true);
399
+ });
400
+ });
401
+ test("admin notification marks users without an active token as new access", async () => {
402
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
403
+ identities.upsertUser({
404
+ telegramUserId: "42",
405
+ firstName: "Atger",
406
+ defaultRole: "customer",
407
+ defaultStatus: "active",
408
+ });
409
+ workspaces.ensureDefaultWorkspace({
410
+ ownerTelegramUserId: "42",
411
+ defaultClientRoute: "customers",
412
+ status: "active",
413
+ });
414
+ const notified = [];
415
+ const harness = createBotHarness();
416
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
417
+ const ctx = createContext({
418
+ fromId: 42,
419
+ chatId: 42,
420
+ chatType: "private",
421
+ match: "",
422
+ sendMessageImpl: async (chatId, text) => {
423
+ notified.push({ chatId, text });
424
+ },
425
+ });
426
+ await harness.handler("renew")(ctx);
427
+ assert.equal(billing.listRenewalRequests("open").length, 1);
428
+ assert.equal(notified.length, 1);
429
+ assert.equal(notified[0]?.text.includes("New access request"), true);
430
+ assert.equal(notified[0]?.text.includes("Key preview:"), false);
431
+ });
432
+ });
433
+ test("admin can mark a renewal request as paid from command", async () => {
434
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
435
+ identities.upsertUser({
436
+ telegramUserId: "42",
437
+ defaultRole: "customer",
438
+ defaultStatus: "active",
439
+ });
440
+ const workspace = workspaces.ensureDefaultWorkspace({
441
+ ownerTelegramUserId: "42",
442
+ defaultClientRoute: "customers",
443
+ status: "active",
444
+ });
445
+ const request = billing.createRenewalRequest({
446
+ workspaceId: workspace.id,
447
+ telegramUserId: "42",
448
+ requestedPlanId: "basic",
449
+ requestedDays: 15,
450
+ now: new Date("2026-04-28T00:00:00.000Z"),
451
+ });
452
+ const harness = createBotHarness();
453
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
454
+ const ctx = createContext({
455
+ fromId: 1,
456
+ chatId: 1,
457
+ chatType: "private",
458
+ match: `paid ${request.request.id}`,
459
+ });
460
+ await harness.handler("renew")(ctx);
461
+ const updatedRequest = billing.getRenewalRequest(request.request.id);
462
+ assert.equal(updatedRequest?.status, "payment_confirmed");
463
+ assert.equal(ctx.replies[0]?.includes("Payment confirmed manually."), true);
464
+ assert.equal(ctx.replies[0]?.includes("• Amount: 5,000 VND"), true);
465
+ assert.equal(JSON.stringify(ctx.replyMarkups[0]).includes("v1:renew:approve:"), true);
466
+ });
467
+ });
468
+ test("admin can mark a renewal request as paid from callback button", async () => {
469
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
470
+ identities.upsertUser({
471
+ telegramUserId: "42",
472
+ defaultRole: "customer",
473
+ defaultStatus: "active",
474
+ });
475
+ const workspace = workspaces.ensureDefaultWorkspace({
476
+ ownerTelegramUserId: "42",
477
+ defaultClientRoute: "customers",
478
+ status: "active",
479
+ });
480
+ const request = billing.createRenewalRequest({
481
+ workspaceId: workspace.id,
482
+ telegramUserId: "42",
483
+ requestedPlanId: "basic",
484
+ requestedDays: 1,
485
+ now: new Date("2026-04-28T00:00:00.000Z"),
486
+ });
487
+ const token = sessions.issueCallbackToken({
488
+ kind: "renewal_request_action",
489
+ action: "confirm_payment",
490
+ requestId: request.request.id,
491
+ });
492
+ const harness = createBotHarness();
493
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
494
+ const found = harness.callbackHandler(`v1:renew:confirm-payment:${token}`);
495
+ const ctx = createContext({
496
+ fromId: 1,
497
+ chatId: 1,
498
+ chatType: "private",
499
+ match: "",
500
+ callbackData: `v1:renew:confirm-payment:${token}`,
501
+ });
502
+ ctx.match = found.match;
503
+ await found.handler(ctx);
504
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "payment_confirmed");
505
+ assert.equal(ctx.editedTexts[0]?.includes("Payment confirmed manually."), true);
506
+ assert.equal(ctx.editedTexts[0]?.includes("• Amount: 5,000 VND"), true);
507
+ assert.ok(ctx.editedMarkups[0]);
508
+ assert.equal(JSON.stringify(ctx.editedMarkups[0]).includes("v1:renew:approve:"), true);
509
+ });
510
+ });
511
+ test("duplicate open /renew returns the existing request", async () => {
512
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
513
+ identities.upsertUser({
514
+ telegramUserId: "42",
515
+ defaultRole: "customer",
516
+ defaultStatus: "active",
517
+ });
518
+ workspaces.ensureDefaultWorkspace({
519
+ ownerTelegramUserId: "42",
520
+ defaultClientRoute: "customers",
521
+ status: "active",
522
+ });
523
+ const harness = createBotHarness();
524
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
525
+ const first = createContext({
526
+ fromId: 42,
527
+ chatId: 42,
528
+ chatType: "private",
529
+ match: "",
530
+ });
531
+ const second = createContext({
532
+ fromId: 42,
533
+ chatId: 42,
534
+ chatType: "private",
535
+ match: "",
536
+ });
537
+ await harness.handler("renew")(first);
538
+ await harness.handler("renew")(second);
539
+ assert.equal(billing.listRenewalRequests("open").length, 1);
540
+ assert.equal(second.replies[0]?.includes("You already have an open renewal request."), true);
541
+ assert.equal(second.replies[0]?.includes("admin_notification: reminder_sent"), true);
542
+ });
543
+ });
544
+ test("admin approve request extends subscription", async () => {
545
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
546
+ identities.upsertUser({
547
+ telegramUserId: "42",
548
+ defaultRole: "customer",
549
+ defaultStatus: "active",
550
+ });
551
+ const workspace = workspaces.ensureDefaultWorkspace({
552
+ ownerTelegramUserId: "42",
553
+ defaultClientRoute: "customers",
554
+ status: "active",
555
+ now: new Date("2026-04-27T00:00:00.000Z"),
556
+ });
557
+ customerKeys.createKey({
558
+ workspaceId: workspace.id,
559
+ telegramUserId: "42",
560
+ clientRoute: "customers",
561
+ now: new Date("2026-04-27T00:00:00.000Z"),
562
+ });
563
+ billing.grantSubscription({
564
+ workspaceId: workspace.id,
565
+ planId: "basic",
566
+ days: 30,
567
+ now: new Date("2026-04-27T00:00:00.000Z"),
568
+ });
569
+ const request = billing.createRenewalRequest({
570
+ workspaceId: workspace.id,
571
+ telegramUserId: "42",
572
+ requestedPlanId: "basic",
573
+ requestedDays: 15,
574
+ now: new Date("2026-04-28T00:00:00.000Z"),
575
+ });
576
+ const before = billing.getLatestSubscriptionForWorkspace(workspace.id);
577
+ assert.ok(before);
578
+ billing.confirmRenewalPayment({ id: request.request.id });
579
+ const harness = createBotHarness();
580
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
581
+ const ctx = createContext({
582
+ fromId: 1,
583
+ chatId: 1,
584
+ chatType: "private",
585
+ match: `approve ${request.request.id} basic 15`,
586
+ });
587
+ await harness.handler("renew")(ctx);
588
+ const after = billing.getLatestSubscriptionForWorkspace(workspace.id);
589
+ const updatedRequest = billing.getRenewalRequest(request.request.id);
590
+ const approvedEvent = auditLog.listEvents({ event: "renewal.approved", subjectId: request.request.id, limit: 1 })[0];
591
+ assert.ok(after);
592
+ assert.ok(approvedEvent);
593
+ assert.ok(updatedRequest);
594
+ assert.equal(updatedRequest?.status, "approved");
595
+ assert.ok(new Date(after.currentPeriodEnd).getTime() > new Date(before.currentPeriodEnd).getTime());
596
+ assert.equal(ctx.replies[0]?.includes("Renewal request approved"), true);
597
+ });
598
+ });
599
+ test("admin close request records an audit event", async () => {
600
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
601
+ const workspace = workspaces.ensureDefaultWorkspace({
602
+ ownerTelegramUserId: "42",
603
+ defaultClientRoute: "customers",
604
+ status: "active",
605
+ });
606
+ const request = billing.createRenewalRequest({
607
+ workspaceId: workspace.id,
608
+ telegramUserId: "42",
609
+ requestedPlanId: "basic",
610
+ requestedDays: 15,
611
+ now: new Date("2026-04-28T00:00:00.000Z"),
612
+ });
613
+ const harness = createBotHarness();
614
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
615
+ const ctx = createContext({
616
+ fromId: 1,
617
+ chatId: 1,
618
+ chatType: "private",
619
+ match: `close ${request.request.id} paid_offline`,
620
+ });
621
+ await harness.handler("renew")(ctx);
622
+ const updatedRequest = billing.getRenewalRequest(request.request.id);
623
+ const closedEvent = auditLog.listEvents({ event: "renewal.closed", subjectId: request.request.id, limit: 1 })[0];
624
+ assert.ok(updatedRequest);
625
+ assert.ok(closedEvent);
626
+ assert.equal(updatedRequest?.status, "closed");
627
+ assert.equal(updatedRequest?.resolution, "paid_offline");
628
+ assert.equal(ctx.replies[0]?.includes("Renewal request closed."), true);
629
+ });
630
+ });
631
+ test("admin can approve a renewal request from callback button", async () => {
632
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
633
+ identities.upsertUser({
634
+ telegramUserId: "42",
635
+ defaultRole: "customer",
636
+ defaultStatus: "active",
637
+ });
638
+ const workspace = workspaces.ensureDefaultWorkspace({
639
+ ownerTelegramUserId: "42",
640
+ defaultClientRoute: "customers",
641
+ status: "active",
642
+ now: new Date("2026-04-27T00:00:00.000Z"),
643
+ });
644
+ customerKeys.createKey({
645
+ workspaceId: workspace.id,
646
+ telegramUserId: "42",
647
+ clientRoute: "customers",
648
+ now: new Date("2026-04-27T00:00:00.000Z"),
649
+ });
650
+ billing.grantSubscription({
651
+ workspaceId: workspace.id,
652
+ planId: "basic",
653
+ days: 30,
654
+ now: new Date("2026-04-27T00:00:00.000Z"),
655
+ });
656
+ const request = billing.createRenewalRequest({
657
+ workspaceId: workspace.id,
658
+ telegramUserId: "42",
659
+ requestedPlanId: "basic",
660
+ requestedDays: 15,
661
+ now: new Date("2026-04-28T00:00:00.000Z"),
662
+ });
663
+ billing.confirmRenewalPayment({ id: request.request.id });
664
+ const token = sessions.issueCallbackToken({
665
+ kind: "renewal_request_action",
666
+ action: "approve",
667
+ requestId: request.request.id,
668
+ });
669
+ const harness = createBotHarness();
670
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
671
+ const found = harness.callbackHandler(`v1:renew:approve:${token}`);
672
+ const ctx = createContext({
673
+ fromId: 1,
674
+ chatId: 1,
675
+ chatType: "private",
676
+ match: "",
677
+ callbackData: `v1:renew:approve:${token}`,
678
+ });
679
+ ctx.match = found.match;
680
+ await found.handler(ctx);
681
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
682
+ assert.equal(ctx.editedTexts[0]?.includes("Renewal request approved"), true);
683
+ assert.equal(ctx.editedTexts[0]?.includes("• Value: approved"), true);
684
+ assert.equal(ctx.editedMarkups[0], undefined);
685
+ });
686
+ });
687
+ test("admin approval of a new access request sends the full key to the customer", async () => {
688
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
689
+ identities.upsertUser({
690
+ telegramUserId: "42",
691
+ defaultRole: "customer",
692
+ defaultStatus: "active",
693
+ });
694
+ const workspace = workspaces.ensureDefaultWorkspace({
695
+ ownerTelegramUserId: "42",
696
+ defaultClientRoute: "customers",
697
+ status: "active",
698
+ });
699
+ const request = billing.createRenewalRequest({
700
+ workspaceId: workspace.id,
701
+ telegramUserId: "42",
702
+ requestedPlanId: "basic",
703
+ requestedDays: 15,
704
+ now: new Date("2026-04-28T00:00:00.000Z"),
705
+ });
706
+ billing.confirmRenewalPayment({ id: request.request.id });
707
+ const token = sessions.issueCallbackToken({
708
+ kind: "renewal_request_action",
709
+ action: "approve",
710
+ requestId: request.request.id,
711
+ });
712
+ const harness = createBotHarness();
713
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
714
+ const found = harness.callbackHandler(`v1:renew:approve:${token}`);
715
+ const ctx = createContext({
716
+ fromId: 1,
717
+ chatId: 1,
718
+ chatType: "private",
719
+ match: "",
720
+ callbackData: `v1:renew:approve:${token}`,
721
+ });
722
+ ctx.match = found.match;
723
+ await found.handler(ctx);
724
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
725
+ assert.equal(customerKeys.getActiveKeyForUser("42")?.status, "active");
726
+ assert.equal(ctx.sentMessages[0]?.chatId, 42);
727
+ assert.equal(ctx.sentMessages[0]?.text.includes("Your access has been approved"), true);
728
+ assert.equal(ctx.sentMessages[0]?.text.includes("api_key:"), false);
729
+ assert.deepEqual(ctx.sentDocuments.map((document) => document.filename), ["config.toml", "auth.json"]);
730
+ assert.match(ctx.sentDocuments[0]?.content ?? "", /base_url = "http:\/\/127\.0\.0\.1:8318\/v1"/);
731
+ assert.match(ctx.sentDocuments[0]?.content ?? "", /api_key = "sk-/);
732
+ assert.match(ctx.sentDocuments[1]?.content ?? "", /"OPENAI_API_KEY": "sk-/);
733
+ assert.equal(auditLog.listEvents({ event: "api_key.revealed", limit: 5 }).length, 2);
734
+ });
735
+ });
736
+ test("admin can approve a token top-up request and create a new lot", async () => {
737
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
738
+ identities.upsertUser({
739
+ telegramUserId: "42",
740
+ defaultRole: "customer",
741
+ defaultStatus: "active",
742
+ });
743
+ const workspace = workspaces.ensureDefaultWorkspace({
744
+ ownerTelegramUserId: "42",
745
+ defaultClientRoute: "customers",
746
+ status: "active",
747
+ });
748
+ customerKeys.createKey({
749
+ workspaceId: workspace.id,
750
+ telegramUserId: "42",
751
+ clientRoute: "customers",
752
+ status: "active",
753
+ });
754
+ billing.grantSubscription({
755
+ workspaceId: workspace.id,
756
+ planId: "basic",
757
+ days: 1,
758
+ });
759
+ const request = billing.createRenewalRequest({
760
+ workspaceId: workspace.id,
761
+ telegramUserId: "42",
762
+ kind: "token_topup",
763
+ requestedTokenDelta: 1_000_000,
764
+ requestedTokenLotDays: 7,
765
+ priceVnd: 5_000,
766
+ });
767
+ billing.confirmRenewalPayment({ id: request.request.id });
768
+ const token = sessions.issueCallbackToken({
769
+ kind: "renewal_request_action",
770
+ action: "approve",
771
+ requestId: request.request.id,
772
+ });
773
+ const harness = createBotHarness();
774
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
775
+ const found = harness.callbackHandler(`v1:renew:approve:${token}`);
776
+ const ctx = createContext({
777
+ fromId: 1,
778
+ chatId: 1,
779
+ chatType: "private",
780
+ match: "",
781
+ callbackData: `v1:renew:approve:${token}`,
782
+ });
783
+ ctx.match = found.match;
784
+ await found.handler(ctx);
785
+ const lots = billing.getActiveEntitlementLotsForWorkspace(workspace.id);
786
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
787
+ assert.equal(lots.length >= 2, true);
788
+ assert.equal(ctx.editedTexts[0]?.includes("Token top-up request approved"), true);
789
+ assert.equal(ctx.editedTexts[0]?.includes("• Value: approved"), true);
790
+ });
791
+ });
792
+ test("failed renewal approval closes request so customer can buy again", async () => {
793
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
794
+ identities.upsertUser({
795
+ telegramUserId: "42",
796
+ defaultRole: "customer",
797
+ defaultStatus: "active",
798
+ });
799
+ const workspace = workspaces.ensureDefaultWorkspace({
800
+ ownerTelegramUserId: "42",
801
+ defaultClientRoute: "customers",
802
+ status: "active",
803
+ });
804
+ const first = billing.createRenewalRequest({
805
+ workspaceId: workspace.id,
806
+ telegramUserId: "42",
807
+ requestedPlanId: "basic",
808
+ requestedDays: 1,
809
+ });
810
+ billing.confirmRenewalPayment({ id: first.request.id });
811
+ const token = sessions.issueCallbackToken({
812
+ kind: "renewal_request_action",
813
+ action: "approve",
814
+ requestId: first.request.id,
815
+ });
816
+ const proxy = deps.proxyClient;
817
+ proxy.getClientConfigs = async () => {
818
+ throw new Error("Dashboard login required");
819
+ };
820
+ const harness = createBotHarness();
821
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
822
+ const approveFound = harness.callbackHandler(`v1:renew:approve:${token}`);
823
+ const approveCtx = createContext({
824
+ fromId: 1,
825
+ chatId: 1,
826
+ chatType: "private",
827
+ match: "",
828
+ callbackData: `v1:renew:approve:${token}`,
829
+ });
830
+ approveCtx.match = approveFound.match;
831
+ await approveFound.handler(approveCtx);
832
+ const failedRequest = billing.getRenewalRequest(first.request.id);
833
+ assert.equal(failedRequest?.status, "closed");
834
+ assert.equal(failedRequest?.resolution, "approval_failed");
835
+ assert.equal(approveCtx.sentMessages.some((message) => message.chatId === 42), true);
836
+ assert.equal(approveCtx.editedTexts[0]?.includes("Renewal approval failed."), true);
837
+ assert.equal(approveCtx.editedTexts[0]?.includes("status: closed"), true);
838
+ assert.equal(approveCtx.editedMarkups[0], undefined);
839
+ const renewCtx = createContext({
840
+ fromId: 42,
841
+ chatId: 42,
842
+ chatType: "private",
843
+ match: "",
844
+ });
845
+ await harness.handler("renew")(renewCtx);
846
+ const renewalRequests = billing.listRenewalRequests();
847
+ assert.equal(renewalRequests.filter((request) => request.workspaceId === workspace.id).length, 2);
848
+ assert.equal(renewalRequests[0]?.status, "open");
849
+ assert.notEqual(renewalRequests[0]?.id, first.request.id);
850
+ assert.equal(renewCtx.replies[0]?.includes("API key purchase request submitted."), true);
851
+ });
852
+ });
853
+ test("admin approval falls back to default purchase plan for legacy 24h requests", async () => {
854
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
855
+ identities.upsertUser({
856
+ telegramUserId: "42",
857
+ defaultRole: "customer",
858
+ defaultStatus: "active",
859
+ });
860
+ const workspace = workspaces.ensureDefaultWorkspace({
861
+ ownerTelegramUserId: "42",
862
+ defaultClientRoute: "customers",
863
+ status: "active",
864
+ });
865
+ const request = billing.createRenewalRequest({
866
+ workspaceId: workspace.id,
867
+ telegramUserId: "42",
868
+ requestedDays: 1,
869
+ now: new Date("2026-04-28T00:00:00.000Z"),
870
+ });
871
+ billing.confirmRenewalPayment({ id: request.request.id });
872
+ const token = sessions.issueCallbackToken({
873
+ kind: "renewal_request_action",
874
+ action: "approve",
875
+ requestId: request.request.id,
876
+ });
877
+ const harness = createBotHarness();
878
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
879
+ const found = harness.callbackHandler(`v1:renew:approve:${token}`);
880
+ const ctx = createContext({
881
+ fromId: 1,
882
+ chatId: 1,
883
+ chatType: "private",
884
+ match: "",
885
+ callbackData: `v1:renew:approve:${token}`,
886
+ });
887
+ ctx.match = found.match;
888
+ await found.handler(ctx);
889
+ const approved = billing.getRenewalRequest(request.request.id);
890
+ assert.equal(approved?.status, "approved");
891
+ assert.equal(approved?.approvedPlanId, "basic");
892
+ assert.equal(customerKeys.getActiveKeyForUser("42")?.status, "active");
893
+ assert.equal(ctx.editedTexts[0]?.includes("• Plan ID: basic"), true);
894
+ });
895
+ });
896
+ test("admin can approve and rotate key from callback button", async () => {
897
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
898
+ identities.upsertUser({
899
+ telegramUserId: "42",
900
+ defaultRole: "customer",
901
+ defaultStatus: "active",
902
+ });
903
+ const workspace = workspaces.ensureDefaultWorkspace({
904
+ ownerTelegramUserId: "42",
905
+ defaultClientRoute: "customers",
906
+ status: "active",
907
+ now: new Date("2026-04-27T00:00:00.000Z"),
908
+ });
909
+ const firstKey = customerKeys.createKey({
910
+ workspaceId: workspace.id,
911
+ telegramUserId: "42",
912
+ clientRoute: "customers",
913
+ now: new Date("2026-04-27T00:00:00.000Z"),
914
+ });
915
+ billing.grantSubscription({
916
+ workspaceId: workspace.id,
917
+ planId: "basic",
918
+ days: 30,
919
+ now: new Date("2026-04-27T00:00:00.000Z"),
920
+ });
921
+ const request = billing.createRenewalRequest({
922
+ workspaceId: workspace.id,
923
+ telegramUserId: "42",
924
+ requestedPlanId: "basic",
925
+ requestedDays: 15,
926
+ now: new Date("2026-04-28T00:00:00.000Z"),
927
+ });
928
+ billing.confirmRenewalPayment({ id: request.request.id });
929
+ const token = sessions.issueCallbackToken({
930
+ kind: "renewal_request_action",
931
+ action: "approve_rotate",
932
+ requestId: request.request.id,
933
+ });
934
+ const harness = createBotHarness();
935
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
936
+ const found = harness.callbackHandler(`v1:renew:approve-rotate:${token}`);
937
+ const ctx = createContext({
938
+ fromId: 1,
939
+ chatId: 1,
940
+ chatType: "private",
941
+ match: "",
942
+ callbackData: `v1:renew:approve-rotate:${token}`,
943
+ });
944
+ ctx.match = found.match;
945
+ await found.handler(ctx);
946
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
947
+ assert.equal(customerKeys.getById(firstKey.record.id)?.status, "revoked");
948
+ assert.equal(customerKeys.getActiveKeyForUser("42")?.id === firstKey.record.id, false);
949
+ assert.equal(ctx.sentMessages[0]?.chatId, 42);
950
+ assert.equal(ctx.sentMessages[0]?.text.includes("Your access has been approved"), true);
951
+ assert.equal(ctx.sentMessages[0]?.text.includes("api_key:"), false);
952
+ assert.deepEqual(ctx.sentDocuments.map((document) => document.filename), ["config.toml", "auth.json"]);
953
+ assert.match(ctx.sentDocuments[0]?.content ?? "", /base_url = "http:\/\/127\.0\.0\.1:8318\/v1"/);
954
+ assert.match(ctx.sentDocuments[0]?.content ?? "", /api_key = "sk-/);
955
+ assert.equal(ctx.editedTexts[0]?.includes("Renewal request approved"), true);
956
+ });
957
+ });
958
+ test("admin can approve a renewal request with 90 day override", async () => {
959
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
960
+ identities.upsertUser({
961
+ telegramUserId: "42",
962
+ defaultRole: "customer",
963
+ defaultStatus: "active",
964
+ });
965
+ const workspace = workspaces.ensureDefaultWorkspace({
966
+ ownerTelegramUserId: "42",
967
+ defaultClientRoute: "customers",
968
+ status: "active",
969
+ now: new Date("2026-04-27T00:00:00.000Z"),
970
+ });
971
+ customerKeys.createKey({
972
+ workspaceId: workspace.id,
973
+ telegramUserId: "42",
974
+ clientRoute: "customers",
975
+ now: new Date("2026-04-27T00:00:00.000Z"),
976
+ });
977
+ billing.grantSubscription({
978
+ workspaceId: workspace.id,
979
+ planId: "basic",
980
+ days: 30,
981
+ now: new Date("2026-04-27T00:00:00.000Z"),
982
+ });
983
+ const request = billing.createRenewalRequest({
984
+ workspaceId: workspace.id,
985
+ telegramUserId: "42",
986
+ requestedPlanId: "basic",
987
+ requestedDays: 15,
988
+ now: new Date("2026-04-28T00:00:00.000Z"),
989
+ });
990
+ const before = billing.getLatestSubscriptionForWorkspace(workspace.id);
991
+ assert.ok(before);
992
+ billing.confirmRenewalPayment({ id: request.request.id });
993
+ const token = sessions.issueCallbackToken({
994
+ kind: "renewal_request_action",
995
+ action: "approve_override",
996
+ requestId: request.request.id,
997
+ overrideDays: 90,
998
+ });
999
+ const harness = createBotHarness();
1000
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1001
+ const found = harness.callbackHandler(`v1:renew:approve-90:${token}`);
1002
+ const ctx = createContext({
1003
+ fromId: 1,
1004
+ chatId: 1,
1005
+ chatType: "private",
1006
+ match: "",
1007
+ callbackData: `v1:renew:approve-90:${token}`,
1008
+ });
1009
+ ctx.match = found.match;
1010
+ await found.handler(ctx);
1011
+ const after = billing.getLatestSubscriptionForWorkspace(workspace.id);
1012
+ assert.ok(after);
1013
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
1014
+ assert.ok(new Date(after.currentPeriodEnd).getTime() > new Date(before.currentPeriodEnd).getTime());
1015
+ assert.equal(ctx.editedTexts[0]?.includes("• Days: 90"), true);
1016
+ });
1017
+ });
1018
+ test("admin can reject a renewal request with a canned reason", async () => {
1019
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1020
+ identities.upsertUser({
1021
+ telegramUserId: "42",
1022
+ defaultRole: "customer",
1023
+ defaultStatus: "active",
1024
+ });
1025
+ const workspace = workspaces.ensureDefaultWorkspace({
1026
+ ownerTelegramUserId: "42",
1027
+ defaultClientRoute: "customers",
1028
+ status: "active",
1029
+ });
1030
+ const request = billing.createRenewalRequest({
1031
+ workspaceId: workspace.id,
1032
+ telegramUserId: "42",
1033
+ requestedPlanId: "basic",
1034
+ requestedDays: 15,
1035
+ now: new Date("2026-04-28T00:00:00.000Z"),
1036
+ });
1037
+ billing.confirmRenewalPayment({ id: request.request.id });
1038
+ const token = sessions.issueCallbackToken({
1039
+ kind: "renewal_request_action",
1040
+ action: "reject_reason",
1041
+ requestId: request.request.id,
1042
+ resolution: "rejected_unpaid",
1043
+ });
1044
+ const harness = createBotHarness();
1045
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1046
+ const found = harness.callbackHandler(`v1:renew:reject:${token}`);
1047
+ const ctx = createContext({
1048
+ fromId: 1,
1049
+ chatId: 1,
1050
+ chatType: "private",
1051
+ match: "",
1052
+ callbackData: `v1:renew:reject:${token}`,
1053
+ });
1054
+ ctx.match = found.match;
1055
+ await found.handler(ctx);
1056
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "closed");
1057
+ assert.equal(billing.getRenewalRequest(request.request.id)?.resolution, "rejected_unpaid");
1058
+ assert.equal(ctx.editedTexts[0]?.includes("resolution: rejected_unpaid"), true);
1059
+ assert.equal(ctx.editedMarkups[0], undefined);
1060
+ assert.equal(ctx.sentMessages[0]?.chatId, 42);
1061
+ assert.equal(ctx.sentMessages[0]?.text.includes("Your renewal request was not approved."), true);
1062
+ assert.equal(ctx.sentMessages[0]?.text.includes("reason: rejected_unpaid"), true);
1063
+ });
1064
+ });
1065
+ test("admin can switch between reject reasons and main renewal actions", async () => {
1066
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1067
+ identities.upsertUser({
1068
+ telegramUserId: "42",
1069
+ defaultRole: "customer",
1070
+ defaultStatus: "active",
1071
+ });
1072
+ const workspace = workspaces.ensureDefaultWorkspace({
1073
+ ownerTelegramUserId: "42",
1074
+ defaultClientRoute: "customers",
1075
+ status: "active",
1076
+ });
1077
+ const request = billing.createRenewalRequest({
1078
+ workspaceId: workspace.id,
1079
+ telegramUserId: "42",
1080
+ requestedPlanId: "basic",
1081
+ requestedDays: 15,
1082
+ now: new Date("2026-04-28T00:00:00.000Z"),
1083
+ });
1084
+ const rejectToken = sessions.issueCallbackToken({
1085
+ kind: "renewal_request_action",
1086
+ action: "show_reject_reasons",
1087
+ requestId: request.request.id,
1088
+ });
1089
+ const harness = createBotHarness();
1090
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1091
+ const rejectFound = harness.callbackHandler(`v1:renew:reject-reasons:${rejectToken}`);
1092
+ const rejectCtx = createContext({
1093
+ fromId: 1,
1094
+ chatId: 1,
1095
+ chatType: "private",
1096
+ match: "",
1097
+ callbackData: `v1:renew:reject-reasons:${rejectToken}`,
1098
+ });
1099
+ rejectCtx.match = rejectFound.match;
1100
+ await rejectFound.handler(rejectCtx);
1101
+ assert.equal(rejectCtx.editedTexts[0]?.includes("Choose a rejection reason."), true);
1102
+ const backToken = sessions.issueCallbackToken({
1103
+ kind: "renewal_request_action",
1104
+ action: "show_main_actions",
1105
+ requestId: request.request.id,
1106
+ });
1107
+ const backFound = harness.callbackHandler(`v1:renew:back:${backToken}`);
1108
+ const backCtx = createContext({
1109
+ fromId: 1,
1110
+ chatId: 1,
1111
+ chatType: "private",
1112
+ match: "",
1113
+ callbackData: `v1:renew:back:${backToken}`,
1114
+ });
1115
+ backCtx.match = backFound.match;
1116
+ await backFound.handler(backCtx);
1117
+ assert.equal(backCtx.editedTexts[0]?.includes("Renewal request") || backCtx.editedTexts[0]?.includes("New access request"), true);
1118
+ assert.equal(backCtx.editedTexts[0]?.includes(`• Request ID: ${request.request.id}`), true);
1119
+ });
1120
+ });
1121
+ test("admin can approve a renewal request with custom days input", async () => {
1122
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1123
+ identities.upsertUser({
1124
+ telegramUserId: "42",
1125
+ defaultRole: "customer",
1126
+ defaultStatus: "active",
1127
+ });
1128
+ const workspace = workspaces.ensureDefaultWorkspace({
1129
+ ownerTelegramUserId: "42",
1130
+ defaultClientRoute: "customers",
1131
+ status: "active",
1132
+ now: new Date("2026-04-27T00:00:00.000Z"),
1133
+ });
1134
+ customerKeys.createKey({
1135
+ workspaceId: workspace.id,
1136
+ telegramUserId: "42",
1137
+ clientRoute: "customers",
1138
+ now: new Date("2026-04-27T00:00:00.000Z"),
1139
+ });
1140
+ billing.grantSubscription({
1141
+ workspaceId: workspace.id,
1142
+ planId: "basic",
1143
+ days: 30,
1144
+ now: new Date("2026-04-27T00:00:00.000Z"),
1145
+ });
1146
+ const request = billing.createRenewalRequest({
1147
+ workspaceId: workspace.id,
1148
+ telegramUserId: "42",
1149
+ requestedPlanId: "basic",
1150
+ requestedDays: 15,
1151
+ now: new Date("2026-04-28T00:00:00.000Z"),
1152
+ });
1153
+ billing.confirmRenewalPayment({ id: request.request.id });
1154
+ const token = sessions.issueCallbackToken({
1155
+ kind: "renewal_request_action",
1156
+ action: "prompt_custom_days",
1157
+ requestId: request.request.id,
1158
+ });
1159
+ const harness = createBotHarness();
1160
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1161
+ const found = harness.callbackHandler(`v1:renew:approve-custom:${token}`);
1162
+ const callbackCtx = createContext({
1163
+ fromId: 1,
1164
+ chatId: 1,
1165
+ chatType: "private",
1166
+ match: "",
1167
+ callbackData: `v1:renew:approve-custom:${token}`,
1168
+ });
1169
+ callbackCtx.match = found.match;
1170
+ await found.handler(callbackCtx);
1171
+ assert.equal(callbackCtx.editedTexts[0]?.includes("Awaiting custom approval days."), true);
1172
+ const inputCtx = createContext({
1173
+ fromId: 1,
1174
+ chatId: 1,
1175
+ chatType: "private",
1176
+ match: "",
1177
+ });
1178
+ inputCtx.message.text = "45";
1179
+ await harness.runText(inputCtx);
1180
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
1181
+ assert.equal(inputCtx.editedTexts.at(-1)?.includes("• Days: 45"), true);
1182
+ });
1183
+ });
1184
+ test("admin can reject a renewal request with custom reason input", async () => {
1185
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1186
+ identities.upsertUser({
1187
+ telegramUserId: "42",
1188
+ defaultRole: "customer",
1189
+ defaultStatus: "active",
1190
+ });
1191
+ const workspace = workspaces.ensureDefaultWorkspace({
1192
+ ownerTelegramUserId: "42",
1193
+ defaultClientRoute: "customers",
1194
+ status: "active",
1195
+ });
1196
+ const request = billing.createRenewalRequest({
1197
+ workspaceId: workspace.id,
1198
+ telegramUserId: "42",
1199
+ requestedPlanId: "basic",
1200
+ requestedDays: 15,
1201
+ now: new Date("2026-04-28T00:00:00.000Z"),
1202
+ });
1203
+ billing.confirmRenewalPayment({ id: request.request.id });
1204
+ const token = sessions.issueCallbackToken({
1205
+ kind: "renewal_request_action",
1206
+ action: "prompt_custom_reason",
1207
+ requestId: request.request.id,
1208
+ });
1209
+ const harness = createBotHarness();
1210
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1211
+ const found = harness.callbackHandler(`v1:renew:reject-custom:${token}`);
1212
+ const callbackCtx = createContext({
1213
+ fromId: 1,
1214
+ chatId: 1,
1215
+ chatType: "private",
1216
+ match: "",
1217
+ callbackData: `v1:renew:reject-custom:${token}`,
1218
+ });
1219
+ callbackCtx.match = found.match;
1220
+ await found.handler(callbackCtx);
1221
+ assert.equal(callbackCtx.editedTexts[0]?.includes("Awaiting custom rejection reason."), true);
1222
+ const inputCtx = createContext({
1223
+ fromId: 1,
1224
+ chatId: 1,
1225
+ chatType: "private",
1226
+ match: "",
1227
+ });
1228
+ inputCtx.message.text = "customer asked to pay later";
1229
+ await harness.runText(inputCtx);
1230
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "closed");
1231
+ assert.equal(billing.getRenewalRequest(request.request.id)?.resolution, "customer asked to pay later");
1232
+ assert.equal(inputCtx.editedTexts.at(-1)?.includes("resolution: customer asked to pay later"), true);
1233
+ assert.equal(inputCtx.sentMessages[0]?.chatId, 42);
1234
+ assert.equal(inputCtx.sentMessages[0]?.text.includes("Your renewal request was not approved."), true);
1235
+ assert.equal(inputCtx.sentMessages[0]?.text.includes("reason: customer asked to pay later"), true);
1236
+ });
1237
+ });
1238
+ test("admin /renew list renders open requests in one admin screen", async () => {
1239
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1240
+ identities.upsertUser({
1241
+ telegramUserId: "42",
1242
+ defaultRole: "customer",
1243
+ defaultStatus: "active",
1244
+ });
1245
+ const workspace = workspaces.ensureDefaultWorkspace({
1246
+ ownerTelegramUserId: "42",
1247
+ defaultClientRoute: "customers",
1248
+ status: "active",
1249
+ });
1250
+ const request = billing.createRenewalRequest({
1251
+ workspaceId: workspace.id,
1252
+ telegramUserId: "42",
1253
+ requestedPlanId: "basic",
1254
+ requestedDays: 15,
1255
+ now: new Date("2026-04-28T00:00:00.000Z"),
1256
+ });
1257
+ const harness = createBotHarness();
1258
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1259
+ const ctx = createContext({
1260
+ fromId: 1,
1261
+ chatId: 1,
1262
+ chatType: "private",
1263
+ match: "list",
1264
+ });
1265
+ await harness.handler("renew")(ctx);
1266
+ assert.equal(ctx.replies[0]?.includes("Open renewal requests:"), true);
1267
+ assert.equal(ctx.replies[0]?.includes(request.request.id), true);
1268
+ assert.ok(ctx.replyMarkups[0]);
1269
+ });
1270
+ });
1271
+ test("admin can view customer details from callback button", async () => {
1272
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1273
+ identities.upsertUser({
1274
+ telegramUserId: "42",
1275
+ firstName: "Atger",
1276
+ username: "atger",
1277
+ defaultRole: "customer",
1278
+ defaultStatus: "active",
1279
+ });
1280
+ const workspace = workspaces.ensureDefaultWorkspace({
1281
+ ownerTelegramUserId: "42",
1282
+ defaultClientRoute: "customers",
1283
+ status: "active",
1284
+ now: new Date("2026-04-27T00:00:00.000Z"),
1285
+ });
1286
+ const created = customerKeys.createKey({
1287
+ workspaceId: workspace.id,
1288
+ telegramUserId: "42",
1289
+ clientRoute: "customers",
1290
+ now: new Date("2026-04-27T00:00:00.000Z"),
1291
+ });
1292
+ billing.grantSubscription({
1293
+ workspaceId: workspace.id,
1294
+ planId: "basic",
1295
+ days: 30,
1296
+ now: new Date("2026-04-27T00:00:00.000Z"),
1297
+ });
1298
+ const request = billing.createRenewalRequest({
1299
+ workspaceId: workspace.id,
1300
+ telegramUserId: "42",
1301
+ requestedPlanId: "basic",
1302
+ requestedDays: 15,
1303
+ now: new Date("2026-04-28T00:00:00.000Z"),
1304
+ });
1305
+ billing.confirmRenewalPayment({ id: request.request.id });
1306
+ const token = sessions.issueCallbackToken({
1307
+ kind: "renewal_request_action",
1308
+ action: "view_customer",
1309
+ requestId: request.request.id,
1310
+ });
1311
+ const harness = createBotHarness();
1312
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1313
+ const found = harness.callbackHandler(`v1:renew:view-customer:${token}`);
1314
+ const ctx = createContext({
1315
+ fromId: 1,
1316
+ chatId: 1,
1317
+ chatType: "private",
1318
+ match: "",
1319
+ callbackData: `v1:renew:view-customer:${token}`,
1320
+ });
1321
+ ctx.match = found.match;
1322
+ await found.handler(ctx);
1323
+ assert.equal(ctx.editedTexts[0]?.includes("Customer renewal review"), true);
1324
+ assert.equal(ctx.editedTexts[0]?.includes("customer: Atger | @atger | id=42"), true);
1325
+ assert.equal(ctx.editedTexts[0]?.includes(`api_key: ${created.apiKey}`), true);
1326
+ assert.equal(ctx.editedTexts[0]?.includes("request_status: payment_confirmed"), true);
1327
+ assert.equal(auditLog.listEvents({ event: "api_key.revealed", limit: 1 })[0]?.metadata.apiKey, "[redacted]");
1328
+ });
1329
+ });
1330
+ test("v1:renew:open callback refuses to run in a group chat", async () => {
1331
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1332
+ identities.upsertUser({
1333
+ telegramUserId: "42",
1334
+ defaultRole: "customer",
1335
+ defaultStatus: "active",
1336
+ });
1337
+ workspaces.ensureDefaultWorkspace({
1338
+ ownerTelegramUserId: "42",
1339
+ defaultClientRoute: "customers",
1340
+ status: "active",
1341
+ });
1342
+ const harness = createBotHarness();
1343
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1344
+ const found = harness.callbackHandler("v1:renew:open");
1345
+ const ctx = createContext({
1346
+ fromId: 42,
1347
+ chatId: 999,
1348
+ chatType: "group",
1349
+ match: "",
1350
+ callbackData: "v1:renew:open",
1351
+ });
1352
+ ctx.match = found.match;
1353
+ await found.handler(ctx);
1354
+ assert.equal(billing.listRenewalRequests("open").length, 0);
1355
+ assert.equal(ctx.replies.length, 0);
1356
+ assert.equal(ctx.editedTexts.length, 0);
1357
+ });
1358
+ });
1359
+ test("v1:topup:open callback refuses to run in a group chat", async () => {
1360
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1361
+ identities.upsertUser({
1362
+ telegramUserId: "42",
1363
+ defaultRole: "customer",
1364
+ defaultStatus: "active",
1365
+ });
1366
+ const workspace = workspaces.ensureDefaultWorkspace({
1367
+ ownerTelegramUserId: "42",
1368
+ defaultClientRoute: "customers",
1369
+ status: "active",
1370
+ });
1371
+ customerKeys.createKey({
1372
+ workspaceId: workspace.id,
1373
+ telegramUserId: "42",
1374
+ clientRoute: "customers",
1375
+ status: "active",
1376
+ });
1377
+ billing.grantSubscription({ workspaceId: workspace.id, planId: "basic", days: 1 });
1378
+ const harness = createBotHarness();
1379
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1380
+ const found = harness.callbackHandler("v1:topup:open");
1381
+ const ctx = createContext({
1382
+ fromId: 42,
1383
+ chatId: 999,
1384
+ chatType: "group",
1385
+ match: "",
1386
+ callbackData: "v1:topup:open",
1387
+ });
1388
+ ctx.match = found.match;
1389
+ await found.handler(ctx);
1390
+ assert.equal(billing.listRenewalRequests("open").length, 0);
1391
+ assert.equal(ctx.replies.length, 0);
1392
+ });
1393
+ });
1394
+ test("/renew shows payment amount that matches the plan price", async () => {
1395
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1396
+ billing.createPlan({
1397
+ id: "premium",
1398
+ name: "Premium",
1399
+ monthlyTokenLimit: 50_000_000,
1400
+ maxApiKeys: 1,
1401
+ priceCents: 20_000,
1402
+ currency: "VND",
1403
+ billingInterval: "month",
1404
+ });
1405
+ identities.upsertUser({
1406
+ telegramUserId: "42",
1407
+ defaultRole: "customer",
1408
+ defaultStatus: "active",
1409
+ });
1410
+ const workspace = workspaces.ensureDefaultWorkspace({
1411
+ ownerTelegramUserId: "42",
1412
+ defaultClientRoute: "customers",
1413
+ status: "active",
1414
+ });
1415
+ customerKeys.createKey({
1416
+ workspaceId: workspace.id,
1417
+ telegramUserId: "42",
1418
+ clientRoute: "customers",
1419
+ });
1420
+ // Seed an open request with explicit priceVnd matching premium plan.
1421
+ const created = billing.createRenewalRequest({
1422
+ workspaceId: workspace.id,
1423
+ telegramUserId: "42",
1424
+ requestedPlanId: "premium",
1425
+ requestedDays: 30,
1426
+ priceVnd: 20_000,
1427
+ });
1428
+ // Confirm via the admin command path which derives amount from the request.
1429
+ const harness = createBotHarness();
1430
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1431
+ const ctx = createContext({
1432
+ fromId: 1,
1433
+ chatId: 1,
1434
+ chatType: "private",
1435
+ match: `paid ${created.request.id}`,
1436
+ });
1437
+ await harness.handler("renew")(ctx);
1438
+ assert.equal(ctx.replies[0]?.includes("Payment"), true);
1439
+ assert.equal(ctx.replies[0]?.includes("• Amount: 20,000 VND"), true);
1440
+ const paidEvent = auditLog.listEvents({ event: "payment.confirmed_manual", limit: 1 })[0];
1441
+ assert.equal(paidEvent?.metadata.amountVnd, 20_000);
1442
+ });
1443
+ });
1444
+ test("second admin approval does not double-provision the request", async () => {
1445
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1446
+ identities.upsertUser({
1447
+ telegramUserId: "42",
1448
+ defaultRole: "customer",
1449
+ defaultStatus: "active",
1450
+ });
1451
+ const workspace = workspaces.ensureDefaultWorkspace({
1452
+ ownerTelegramUserId: "42",
1453
+ defaultClientRoute: "customers",
1454
+ status: "active",
1455
+ });
1456
+ const request = billing.createRenewalRequest({
1457
+ workspaceId: workspace.id,
1458
+ telegramUserId: "42",
1459
+ requestedPlanId: "basic",
1460
+ requestedDays: 15,
1461
+ });
1462
+ billing.confirmRenewalPayment({ id: request.request.id, expectedStatus: "open" });
1463
+ const firstToken = sessions.issueCallbackToken({
1464
+ kind: "renewal_request_action",
1465
+ action: "approve",
1466
+ requestId: request.request.id,
1467
+ });
1468
+ const secondToken = sessions.issueCallbackToken({
1469
+ kind: "renewal_request_action",
1470
+ action: "approve",
1471
+ requestId: request.request.id,
1472
+ });
1473
+ const harness = createBotHarness();
1474
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1475
+ const first = harness.callbackHandler(`v1:renew:approve:${firstToken}`);
1476
+ const firstCtx = createContext({
1477
+ fromId: 1,
1478
+ chatId: 1,
1479
+ chatType: "private",
1480
+ match: "",
1481
+ callbackData: `v1:renew:approve:${firstToken}`,
1482
+ });
1483
+ firstCtx.match = first.match;
1484
+ await first.handler(firstCtx);
1485
+ const second = harness.callbackHandler(`v1:renew:approve:${secondToken}`);
1486
+ const secondCtx = createContext({
1487
+ fromId: 1,
1488
+ chatId: 1,
1489
+ chatType: "private",
1490
+ match: "",
1491
+ callbackData: `v1:renew:approve:${secondToken}`,
1492
+ });
1493
+ secondCtx.match = second.match;
1494
+ await second.handler(secondCtx);
1495
+ assert.equal(billing.getRenewalRequest(request.request.id)?.status, "approved");
1496
+ assert.equal(auditLog.listEvents({ event: "renewal.approved", subjectId: request.request.id, limit: 5 }).length, 1);
1497
+ assert.equal(auditLog.listEvents({ event: "subscription.renewed", limit: 5 }).length, 1);
1498
+ });
1499
+ });
1500
+ test("concurrent approve-transition guard prevents a second DB side-effect", () => {
1501
+ const dir = mkdtempSync(path.join(os.tmpdir(), "approve-race-"));
1502
+ try {
1503
+ const dbFile = path.join(dir, "bot.sqlite");
1504
+ const billing = BillingRepository.create(dbFile);
1505
+ const workspaces = CustomerWorkspaceRepository.create(dbFile);
1506
+ const workspace = workspaces.ensureDefaultWorkspace({
1507
+ ownerTelegramUserId: "42",
1508
+ defaultClientRoute: "customers",
1509
+ status: "active",
1510
+ });
1511
+ const request = billing.createRenewalRequest({
1512
+ workspaceId: workspace.id,
1513
+ telegramUserId: "42",
1514
+ requestedPlanId: "basic",
1515
+ requestedDays: 1,
1516
+ });
1517
+ billing.confirmRenewalPayment({ id: request.request.id, expectedStatus: "open" });
1518
+ const firstApprove = billing.approveRenewalRequest({
1519
+ id: request.request.id,
1520
+ approvedPlanId: "basic",
1521
+ approvedDays: 1,
1522
+ expectedStatus: "payment_confirmed",
1523
+ });
1524
+ assert.ok(firstApprove);
1525
+ const secondApprove = billing.approveRenewalRequest({
1526
+ id: request.request.id,
1527
+ approvedPlanId: "basic",
1528
+ approvedDays: 1,
1529
+ expectedStatus: "payment_confirmed",
1530
+ });
1531
+ assert.equal(secondApprove, undefined);
1532
+ }
1533
+ finally {
1534
+ rmSync(dir, { recursive: true, force: true });
1535
+ }
1536
+ });
1537
+ test("token top-up rolls back the lot when approve transition fails", async () => {
1538
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1539
+ identities.upsertUser({
1540
+ telegramUserId: "42",
1541
+ defaultRole: "customer",
1542
+ defaultStatus: "active",
1543
+ });
1544
+ const workspace = workspaces.ensureDefaultWorkspace({
1545
+ ownerTelegramUserId: "42",
1546
+ defaultClientRoute: "customers",
1547
+ status: "active",
1548
+ });
1549
+ customerKeys.createKey({
1550
+ workspaceId: workspace.id,
1551
+ telegramUserId: "42",
1552
+ clientRoute: "customers",
1553
+ status: "active",
1554
+ });
1555
+ billing.grantSubscription({ workspaceId: workspace.id, planId: "basic", days: 1 });
1556
+ const request = billing.createRenewalRequest({
1557
+ workspaceId: workspace.id,
1558
+ telegramUserId: "42",
1559
+ kind: "token_topup",
1560
+ requestedTokenDelta: 1_000_000,
1561
+ requestedTokenLotDays: 7,
1562
+ priceVnd: 5_000,
1563
+ });
1564
+ // Mark as approved directly: second approve attempt must not create a lot.
1565
+ billing.confirmRenewalPayment({ id: request.request.id, expectedStatus: "open" });
1566
+ billing.approveTokenTopUpRequest({
1567
+ id: request.request.id,
1568
+ approvedTokenDelta: 1_000_000,
1569
+ approvedTokenLotDays: 7,
1570
+ });
1571
+ const lotsBefore = billing.getActiveEntitlementLotsForWorkspace(workspace.id);
1572
+ const token = sessions.issueCallbackToken({
1573
+ kind: "renewal_request_action",
1574
+ action: "approve",
1575
+ requestId: request.request.id,
1576
+ });
1577
+ const harness = createBotHarness();
1578
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1579
+ const found = harness.callbackHandler(`v1:renew:approve:${token}`);
1580
+ const ctx = createContext({
1581
+ fromId: 1,
1582
+ chatId: 1,
1583
+ chatType: "private",
1584
+ match: "",
1585
+ callbackData: `v1:renew:approve:${token}`,
1586
+ });
1587
+ ctx.match = found.match;
1588
+ await found.handler(ctx);
1589
+ const lotsAfter = billing.getActiveEntitlementLotsForWorkspace(workspace.id);
1590
+ assert.equal(lotsAfter.length, lotsBefore.length);
1591
+ });
1592
+ });
1593
+ test("approving renewal also records user.approved when user is pending_approval", async () => {
1594
+ await withRepos(async ({ identities, workspaces, customerKeys, billing, auditLog, sessions, deps }) => {
1595
+ identities.upsertUser({
1596
+ telegramUserId: "42",
1597
+ defaultRole: "customer",
1598
+ defaultStatus: "pending_approval",
1599
+ });
1600
+ workspaces.ensureDefaultWorkspace({
1601
+ ownerTelegramUserId: "42",
1602
+ defaultClientRoute: "customers",
1603
+ status: "pending_approval",
1604
+ });
1605
+ const request = billing.createRenewalRequest({
1606
+ workspaceId: workspaces.getDefaultWorkspace("42").id,
1607
+ telegramUserId: "42",
1608
+ requestedPlanId: "basic",
1609
+ requestedDays: 1,
1610
+ });
1611
+ billing.confirmRenewalPayment({ id: request.request.id, expectedStatus: "open" });
1612
+ const token = sessions.issueCallbackToken({
1613
+ kind: "renewal_request_action",
1614
+ action: "approve",
1615
+ requestId: request.request.id,
1616
+ });
1617
+ const harness = createBotHarness();
1618
+ registerRenewCommand(harness.bot, deps, sessions, identities, workspaces, customerKeys, billing, auditLog);
1619
+ const found = harness.callbackHandler(`v1:renew:approve:${token}`);
1620
+ const ctx = createContext({
1621
+ fromId: 1,
1622
+ chatId: 1,
1623
+ chatType: "private",
1624
+ match: "",
1625
+ callbackData: `v1:renew:approve:${token}`,
1626
+ });
1627
+ ctx.match = found.match;
1628
+ await found.handler(ctx);
1629
+ assert.equal(identities.getUser("42")?.status, "active");
1630
+ assert.equal(auditLog.listEvents({ event: "user.approved", limit: 5 }).length, 1);
1631
+ assert.equal(auditLog.listEvents({ event: "workspace.approved", limit: 5 }).length, 1);
1632
+ });
1633
+ });