@forjio/storlaunch-cli 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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/bin/storlaunch.js +2 -0
  3. package/dist/commands/auth.d.ts +4 -0
  4. package/dist/commands/auth.d.ts.map +1 -0
  5. package/dist/commands/auth.js +78 -0
  6. package/dist/commands/auth.js.map +1 -0
  7. package/dist/commands/config.d.ts +4 -0
  8. package/dist/commands/config.d.ts.map +1 -0
  9. package/dist/commands/config.js +86 -0
  10. package/dist/commands/config.js.map +1 -0
  11. package/dist/commands/payment.d.ts +4 -0
  12. package/dist/commands/payment.d.ts.map +1 -0
  13. package/dist/commands/payment.js +738 -0
  14. package/dist/commands/payment.js.map +1 -0
  15. package/dist/commands/storefront.d.ts +4 -0
  16. package/dist/commands/storefront.d.ts.map +1 -0
  17. package/dist/commands/storefront.js +433 -0
  18. package/dist/commands/storefront.js.map +1 -0
  19. package/dist/commands/webhook.d.ts +4 -0
  20. package/dist/commands/webhook.d.ts.map +1 -0
  21. package/dist/commands/webhook.js +249 -0
  22. package/dist/commands/webhook.js.map +1 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +25 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/api.d.ts +18 -0
  28. package/dist/lib/api.d.ts.map +1 -0
  29. package/dist/lib/api.js +62 -0
  30. package/dist/lib/api.js.map +1 -0
  31. package/dist/lib/config.d.ts +23 -0
  32. package/dist/lib/config.d.ts.map +1 -0
  33. package/dist/lib/config.js +58 -0
  34. package/dist/lib/config.js.map +1 -0
  35. package/dist/lib/output.d.ts +21 -0
  36. package/dist/lib/output.d.ts.map +1 -0
  37. package/dist/lib/output.js +66 -0
  38. package/dist/lib/output.js.map +1 -0
  39. package/package.json +34 -0
@@ -0,0 +1,738 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { writeFileSync, mkdirSync } from "node:fs";
4
+ import { dirname } from "node:path";
5
+ import { apiRequest, ApiClientError } from "../lib/api.js";
6
+ import { output } from "../lib/output.js";
7
+ function getExitCode(err) {
8
+ if (err instanceof ApiClientError) {
9
+ if (err.status === 401 || err.status === 403)
10
+ return 2;
11
+ if (err.status === 429)
12
+ return 3;
13
+ if (err.code === "QUOTA_EXCEEDED")
14
+ return 4;
15
+ }
16
+ return 1;
17
+ }
18
+ function handleError(err, json) {
19
+ if (json && err instanceof ApiClientError) {
20
+ console.error(JSON.stringify({ data: null, error: { code: err.code, message: err.message } }, null, 2));
21
+ }
22
+ else {
23
+ console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
24
+ }
25
+ process.exit(getExitCode(err));
26
+ }
27
+ function parseMetadata(raw) {
28
+ if (!raw)
29
+ return undefined;
30
+ try {
31
+ return JSON.parse(raw);
32
+ }
33
+ catch {
34
+ console.error(chalk.red("Error: --metadata must be a valid JSON string"));
35
+ process.exit(1);
36
+ }
37
+ }
38
+ function parseCommaSeparated(raw) {
39
+ if (!raw)
40
+ return undefined;
41
+ return raw.split(",").map((s) => s.trim());
42
+ }
43
+ // ─── Checkout ────────────────────────────────────────────────
44
+ const checkout = new Command("checkout").description("Manage checkout sessions");
45
+ checkout
46
+ .command("create")
47
+ .description("Create a checkout session")
48
+ .requiredOption("--amount <amount>", "Amount in smallest currency unit", parseInt)
49
+ .requiredOption("--currency <code>", "Currency code (IDR, USD)")
50
+ .option("--description <text>", "Shown on checkout page")
51
+ .option("--customer <id>", "Customer ID")
52
+ .option("--customer-email <email>", "Pre-fill email")
53
+ .option("--success-url <url>", "Redirect URL after payment")
54
+ .option("--cancel-url <url>", "Redirect URL on cancel")
55
+ .option("--expires-in <minutes>", "Session expiry in minutes", parseInt)
56
+ .option("--payment-methods <methods>", "Comma-separated: qris,ewallet,va,card,paypal")
57
+ .option("--metadata <json>", "JSON key-value pairs")
58
+ .action(async (opts, cmd) => {
59
+ const g = cmd.optsWithGlobals();
60
+ try {
61
+ const body = {
62
+ amount: opts.amount,
63
+ currency: opts.currency,
64
+ };
65
+ if (opts.description)
66
+ body["description"] = opts.description;
67
+ if (opts.customer)
68
+ body["customerId"] = opts.customer;
69
+ if (opts.customerEmail)
70
+ body["customerEmail"] = opts.customerEmail;
71
+ if (opts.successUrl)
72
+ body["successUrl"] = opts.successUrl;
73
+ if (opts.cancelUrl)
74
+ body["cancelUrl"] = opts.cancelUrl;
75
+ if (opts.expiresIn)
76
+ body["expiresInMinutes"] = opts.expiresIn;
77
+ if (opts.paymentMethods)
78
+ body["paymentMethods"] = parseCommaSeparated(opts.paymentMethods);
79
+ if (opts.metadata)
80
+ body["metadata"] = parseMetadata(opts.metadata);
81
+ const result = await apiRequest("/payment/checkout-sessions", {
82
+ method: "POST",
83
+ body,
84
+ sandbox: g.sandbox,
85
+ });
86
+ if (g.json) {
87
+ output(result, { json: true });
88
+ }
89
+ else {
90
+ console.log(`Checkout session created: ${chalk.bold(String(result["id"]))}`);
91
+ if (result["url"])
92
+ console.log(`URL: ${result["url"]}`);
93
+ console.log(`Status: ${result["status"]}`);
94
+ if (result["expiresAt"])
95
+ console.log(`Expires: ${result["expiresAt"]}`);
96
+ }
97
+ }
98
+ catch (err) {
99
+ handleError(err, g.json);
100
+ }
101
+ });
102
+ checkout
103
+ .command("list")
104
+ .description("List checkout sessions")
105
+ .option("--status <status>", "Filter: open, completed, expired")
106
+ .option("--customer <id>", "Filter by customer ID")
107
+ .option("--limit <n>", "Items per page", parseInt)
108
+ .option("--cursor <cursor>", "Pagination cursor")
109
+ .action(async (opts, cmd) => {
110
+ const g = cmd.optsWithGlobals();
111
+ try {
112
+ const result = await apiRequest("/payment/checkout-sessions", {
113
+ query: { status: opts.status, customer: opts.customer, limit: opts.limit, cursor: opts.cursor },
114
+ sandbox: g.sandbox,
115
+ });
116
+ if (g.json) {
117
+ output(result, { json: true });
118
+ }
119
+ else {
120
+ const cols = [
121
+ { key: "id", header: "ID", width: 24 },
122
+ { key: "amount", header: "Amount" },
123
+ { key: "currency", header: "Currency" },
124
+ { key: "status", header: "Status" },
125
+ { key: "createdAt", header: "Created" },
126
+ ];
127
+ output((result["data"] ?? result), { columns: cols });
128
+ }
129
+ }
130
+ catch (err) {
131
+ handleError(err, g.json);
132
+ }
133
+ });
134
+ checkout
135
+ .command("get <id>")
136
+ .description("Get a checkout session")
137
+ .action(async (id, _, cmd) => {
138
+ const g = cmd.optsWithGlobals();
139
+ try {
140
+ const result = await apiRequest(`/payment/checkout-sessions/${id}`, {
141
+ sandbox: g.sandbox,
142
+ });
143
+ output(result, { json: g.json });
144
+ }
145
+ catch (err) {
146
+ handleError(err, g.json);
147
+ }
148
+ });
149
+ // ─── Plans ───────────────────────────────────────────────────
150
+ const plans = new Command("plans").description("Manage subscription plans");
151
+ plans
152
+ .command("create")
153
+ .description("Create a subscription plan")
154
+ .requiredOption("--name <name>", "Plan display name")
155
+ .requiredOption("--amount <amount>", "Amount per interval", parseInt)
156
+ .requiredOption("--currency <code>", "Currency code")
157
+ .requiredOption("--interval <interval>", "weekly, monthly, or yearly")
158
+ .option("--description <text>", "Plan description")
159
+ .option("--trial-days <days>", "Free trial duration", parseInt)
160
+ .option("--features <list>", "Comma-separated feature list")
161
+ .option("--metadata <json>", "JSON string")
162
+ .action(async (opts, cmd) => {
163
+ const g = cmd.optsWithGlobals();
164
+ try {
165
+ const body = {
166
+ name: opts.name,
167
+ amount: opts.amount,
168
+ currency: opts.currency,
169
+ interval: opts.interval,
170
+ };
171
+ if (opts.description)
172
+ body["description"] = opts.description;
173
+ if (opts.trialDays !== undefined)
174
+ body["trialDays"] = opts.trialDays;
175
+ if (opts.features)
176
+ body["features"] = parseCommaSeparated(opts.features);
177
+ if (opts.metadata)
178
+ body["metadata"] = parseMetadata(opts.metadata);
179
+ const result = await apiRequest("/payment/plans", {
180
+ method: "POST",
181
+ body,
182
+ sandbox: g.sandbox,
183
+ });
184
+ if (g.json) {
185
+ output(result, { json: true });
186
+ }
187
+ else {
188
+ console.log(`Plan created: ${chalk.bold(String(result["id"]))}`);
189
+ console.log(`Name: ${result["name"]}`);
190
+ console.log(`Amount: ${result["amount"]} ${result["currency"]}`);
191
+ console.log(`Interval: ${result["interval"]}`);
192
+ }
193
+ }
194
+ catch (err) {
195
+ handleError(err, g.json);
196
+ }
197
+ });
198
+ plans
199
+ .command("list")
200
+ .description("List subscription plans")
201
+ .option("--active", "Filter by active status")
202
+ .option("--limit <n>", "Items per page", parseInt)
203
+ .option("--cursor <cursor>", "Pagination cursor")
204
+ .action(async (opts, cmd) => {
205
+ const g = cmd.optsWithGlobals();
206
+ try {
207
+ const result = await apiRequest("/payment/plans", {
208
+ query: { active: opts.active ? "true" : undefined, limit: opts.limit, cursor: opts.cursor },
209
+ sandbox: g.sandbox,
210
+ });
211
+ if (g.json) {
212
+ output(result, { json: true });
213
+ }
214
+ else {
215
+ const cols = [
216
+ { key: "id", header: "ID", width: 20 },
217
+ { key: "name", header: "Name" },
218
+ { key: "amount", header: "Amount" },
219
+ { key: "currency", header: "Currency" },
220
+ { key: "interval", header: "Interval" },
221
+ { key: "active", header: "Active" },
222
+ ];
223
+ output((result["data"] ?? result), { columns: cols });
224
+ }
225
+ }
226
+ catch (err) {
227
+ handleError(err, g.json);
228
+ }
229
+ });
230
+ plans
231
+ .command("get <id>")
232
+ .description("Get a plan")
233
+ .action(async (id, _, cmd) => {
234
+ const g = cmd.optsWithGlobals();
235
+ try {
236
+ const result = await apiRequest(`/payment/plans/${id}`, {
237
+ sandbox: g.sandbox,
238
+ });
239
+ output(result, { json: g.json });
240
+ }
241
+ catch (err) {
242
+ handleError(err, g.json);
243
+ }
244
+ });
245
+ plans
246
+ .command("update <id>")
247
+ .description("Update a plan")
248
+ .option("--name <name>", "New plan name")
249
+ .option("--amount <amount>", "New amount", parseInt)
250
+ .option("--description <text>", "New description")
251
+ .option("--trial-days <days>", "New trial duration", parseInt)
252
+ .option("--features <list>", "Comma-separated feature list")
253
+ .option("--active", "Enable the plan")
254
+ .option("--no-active", "Disable the plan")
255
+ .action(async (id, opts, cmd) => {
256
+ const g = cmd.optsWithGlobals();
257
+ try {
258
+ const body = {};
259
+ if (opts.name)
260
+ body["name"] = opts.name;
261
+ if (opts.amount !== undefined)
262
+ body["amount"] = opts.amount;
263
+ if (opts.description)
264
+ body["description"] = opts.description;
265
+ if (opts.trialDays !== undefined)
266
+ body["trialDays"] = opts.trialDays;
267
+ if (opts.features)
268
+ body["features"] = parseCommaSeparated(opts.features);
269
+ if (opts.active !== undefined)
270
+ body["active"] = opts.active;
271
+ const result = await apiRequest(`/payment/plans/${id}`, {
272
+ method: "PATCH",
273
+ body,
274
+ sandbox: g.sandbox,
275
+ });
276
+ if (g.json) {
277
+ output(result, { json: true });
278
+ }
279
+ else {
280
+ console.log(chalk.green(`Plan ${id} updated.`));
281
+ }
282
+ }
283
+ catch (err) {
284
+ handleError(err, g.json);
285
+ }
286
+ });
287
+ plans
288
+ .command("delete <id>")
289
+ .description("Archive a plan")
290
+ .action(async (id, _, cmd) => {
291
+ const g = cmd.optsWithGlobals();
292
+ try {
293
+ await apiRequest(`/payment/plans/${id}`, { method: "DELETE", sandbox: g.sandbox });
294
+ if (g.json) {
295
+ output({ deleted: true, id }, { json: true });
296
+ }
297
+ else {
298
+ console.log(chalk.green(`Plan ${id} archived.`));
299
+ }
300
+ }
301
+ catch (err) {
302
+ handleError(err, g.json);
303
+ }
304
+ });
305
+ // ─── Subscriptions ───────────────────────────────────────────
306
+ const subscriptions = new Command("subscriptions").description("Manage subscriptions");
307
+ subscriptions
308
+ .command("create")
309
+ .description("Create a subscription")
310
+ .requiredOption("--customer <id>", "Customer ID")
311
+ .requiredOption("--plan <id>", "Plan ID")
312
+ .option("--payment-method <method>", "Preferred payment method")
313
+ .option("--trial-end <date>", "Override trial period (ISO 8601)")
314
+ .option("--metadata <json>", "JSON string")
315
+ .action(async (opts, cmd) => {
316
+ const g = cmd.optsWithGlobals();
317
+ try {
318
+ const body = {
319
+ customerId: opts.customer,
320
+ planId: opts.plan,
321
+ };
322
+ if (opts.paymentMethod)
323
+ body["paymentMethod"] = opts.paymentMethod;
324
+ if (opts.trialEnd)
325
+ body["trialEnd"] = opts.trialEnd;
326
+ if (opts.metadata)
327
+ body["metadata"] = parseMetadata(opts.metadata);
328
+ const result = await apiRequest("/payment/subscriptions", {
329
+ method: "POST",
330
+ body,
331
+ sandbox: g.sandbox,
332
+ });
333
+ if (g.json) {
334
+ output(result, { json: true });
335
+ }
336
+ else {
337
+ console.log(`Subscription created: ${chalk.bold(String(result["id"]))}`);
338
+ console.log(`Status: ${result["status"]}`);
339
+ console.log(`Plan: ${result["planId"]}`);
340
+ }
341
+ }
342
+ catch (err) {
343
+ handleError(err, g.json);
344
+ }
345
+ });
346
+ subscriptions
347
+ .command("list")
348
+ .description("List subscriptions")
349
+ .option("--customer <id>", "Filter by customer ID")
350
+ .option("--plan <id>", "Filter by plan ID")
351
+ .option("--status <status>", "Filter: trialing, active, past_due, paused, canceled")
352
+ .option("--limit <n>", "Items per page", parseInt)
353
+ .option("--cursor <cursor>", "Pagination cursor")
354
+ .action(async (opts, cmd) => {
355
+ const g = cmd.optsWithGlobals();
356
+ try {
357
+ const result = await apiRequest("/payment/subscriptions", {
358
+ query: { customer: opts.customer, plan: opts.plan, status: opts.status, limit: opts.limit, cursor: opts.cursor },
359
+ sandbox: g.sandbox,
360
+ });
361
+ if (g.json) {
362
+ output(result, { json: true });
363
+ }
364
+ else {
365
+ const cols = [
366
+ { key: "id", header: "ID", width: 20 },
367
+ { key: "customerId", header: "Customer", width: 20 },
368
+ { key: "planId", header: "Plan", width: 20 },
369
+ { key: "status", header: "Status" },
370
+ { key: "currentPeriodEnd", header: "Period End" },
371
+ ];
372
+ output((result["data"] ?? result), { columns: cols });
373
+ }
374
+ }
375
+ catch (err) {
376
+ handleError(err, g.json);
377
+ }
378
+ });
379
+ subscriptions
380
+ .command("get <id>")
381
+ .description("Get a subscription")
382
+ .action(async (id, _, cmd) => {
383
+ const g = cmd.optsWithGlobals();
384
+ try {
385
+ const result = await apiRequest(`/payment/subscriptions/${id}`, {
386
+ sandbox: g.sandbox,
387
+ });
388
+ output(result, { json: g.json });
389
+ }
390
+ catch (err) {
391
+ handleError(err, g.json);
392
+ }
393
+ });
394
+ subscriptions
395
+ .command("update <id>")
396
+ .description("Update a subscription plan")
397
+ .option("--plan <id>", "New plan ID")
398
+ .option("--prorate", "Apply proration immediately")
399
+ .action(async (id, opts, cmd) => {
400
+ const g = cmd.optsWithGlobals();
401
+ try {
402
+ const body = {};
403
+ if (opts.plan)
404
+ body["planId"] = opts.plan;
405
+ if (opts.prorate)
406
+ body["prorate"] = true;
407
+ const result = await apiRequest(`/payment/subscriptions/${id}`, {
408
+ method: "PATCH",
409
+ body,
410
+ sandbox: g.sandbox,
411
+ });
412
+ if (g.json) {
413
+ output(result, { json: true });
414
+ }
415
+ else {
416
+ console.log(chalk.green(`Subscription ${id} updated.`));
417
+ }
418
+ }
419
+ catch (err) {
420
+ handleError(err, g.json);
421
+ }
422
+ });
423
+ subscriptions
424
+ .command("cancel <id>")
425
+ .description("Cancel a subscription")
426
+ .option("--immediate", "Cancel now instead of at period end")
427
+ .action(async (id, opts, cmd) => {
428
+ const g = cmd.optsWithGlobals();
429
+ try {
430
+ await apiRequest(`/payment/subscriptions/${id}`, {
431
+ method: "DELETE",
432
+ body: opts.immediate ? { immediate: true } : undefined,
433
+ sandbox: g.sandbox,
434
+ });
435
+ if (g.json) {
436
+ output({ canceled: true, id, immediate: !!opts.immediate }, { json: true });
437
+ }
438
+ else {
439
+ const mode = opts.immediate ? "immediately" : "at period end";
440
+ console.log(chalk.green(`Subscription ${id} canceled ${mode}.`));
441
+ }
442
+ }
443
+ catch (err) {
444
+ handleError(err, g.json);
445
+ }
446
+ });
447
+ subscriptions
448
+ .command("pause <id>")
449
+ .description("Pause a subscription")
450
+ .action(async (id, _, cmd) => {
451
+ const g = cmd.optsWithGlobals();
452
+ try {
453
+ const result = await apiRequest(`/payment/subscriptions/${id}`, {
454
+ method: "PATCH",
455
+ body: { status: "paused" },
456
+ sandbox: g.sandbox,
457
+ });
458
+ if (g.json) {
459
+ output(result, { json: true });
460
+ }
461
+ else {
462
+ console.log(chalk.green(`Subscription ${id} paused.`));
463
+ }
464
+ }
465
+ catch (err) {
466
+ handleError(err, g.json);
467
+ }
468
+ });
469
+ subscriptions
470
+ .command("resume <id>")
471
+ .description("Resume a paused subscription")
472
+ .action(async (id, _, cmd) => {
473
+ const g = cmd.optsWithGlobals();
474
+ try {
475
+ const result = await apiRequest(`/payment/subscriptions/${id}`, {
476
+ method: "PATCH",
477
+ body: { status: "active" },
478
+ sandbox: g.sandbox,
479
+ });
480
+ if (g.json) {
481
+ output(result, { json: true });
482
+ }
483
+ else {
484
+ console.log(chalk.green(`Subscription ${id} resumed.`));
485
+ }
486
+ }
487
+ catch (err) {
488
+ handleError(err, g.json);
489
+ }
490
+ });
491
+ // ─── Invoices ────────────────────────────────────────────────
492
+ const invoices = new Command("invoices").description("Manage invoices");
493
+ invoices
494
+ .command("list")
495
+ .description("List invoices")
496
+ .option("--customer <id>", "Filter by customer ID")
497
+ .option("--subscription <id>", "Filter by subscription ID")
498
+ .option("--status <status>", "Filter: draft, open, paid, overdue, void")
499
+ .option("--limit <n>", "Items per page", parseInt)
500
+ .option("--cursor <cursor>", "Pagination cursor")
501
+ .action(async (opts, cmd) => {
502
+ const g = cmd.optsWithGlobals();
503
+ try {
504
+ const result = await apiRequest("/payment/invoices", {
505
+ query: { customer: opts.customer, subscription: opts.subscription, status: opts.status, limit: opts.limit, cursor: opts.cursor },
506
+ sandbox: g.sandbox,
507
+ });
508
+ if (g.json) {
509
+ output(result, { json: true });
510
+ }
511
+ else {
512
+ const cols = [
513
+ { key: "id", header: "ID", width: 20 },
514
+ { key: "amount", header: "Amount" },
515
+ { key: "currency", header: "Currency" },
516
+ { key: "status", header: "Status" },
517
+ { key: "createdAt", header: "Created" },
518
+ ];
519
+ output((result["data"] ?? result), { columns: cols });
520
+ }
521
+ }
522
+ catch (err) {
523
+ handleError(err, g.json);
524
+ }
525
+ });
526
+ invoices
527
+ .command("get <id>")
528
+ .description("Get an invoice")
529
+ .action(async (id, _, cmd) => {
530
+ const g = cmd.optsWithGlobals();
531
+ try {
532
+ const result = await apiRequest(`/payment/invoices/${id}`, {
533
+ sandbox: g.sandbox,
534
+ });
535
+ output(result, { json: g.json });
536
+ }
537
+ catch (err) {
538
+ handleError(err, g.json);
539
+ }
540
+ });
541
+ invoices
542
+ .command("download <id>")
543
+ .description("Download invoice as PDF")
544
+ .option("--output <path>", "Output file path")
545
+ .action(async (id, opts, cmd) => {
546
+ const g = cmd.optsWithGlobals();
547
+ try {
548
+ // Get invoice details first to determine filename
549
+ const invoice = await apiRequest(`/payment/invoices/${id}`, {
550
+ sandbox: g.sandbox,
551
+ });
552
+ const outputPath = opts.output ?? `./${invoice["number"] ?? id}.pdf`;
553
+ // Download the PDF
554
+ const { resolveApiKey, resolveApiUrl } = await import("../lib/config.js");
555
+ const token = resolveApiKey({ sandbox: g.sandbox });
556
+ const baseUrl = resolveApiUrl();
557
+ const url = `${baseUrl.replace(/\/$/, "")}/payment/invoices/${id}/pdf`;
558
+ const response = await fetch(url, {
559
+ headers: { Authorization: `Bearer ${token}` },
560
+ });
561
+ if (!response.ok) {
562
+ throw new ApiClientError({
563
+ status: response.status,
564
+ message: `Failed to download invoice: ${response.statusText}`,
565
+ });
566
+ }
567
+ const buffer = Buffer.from(await response.arrayBuffer());
568
+ mkdirSync(dirname(outputPath), { recursive: true });
569
+ writeFileSync(outputPath, buffer);
570
+ if (g.json) {
571
+ output({ id, path: outputPath, size: buffer.length }, { json: true });
572
+ }
573
+ else {
574
+ console.log(chalk.green(`Invoice downloaded: ${outputPath} (${buffer.length} bytes)`));
575
+ }
576
+ }
577
+ catch (err) {
578
+ handleError(err, g.json);
579
+ }
580
+ });
581
+ // ─── Customers ───────────────────────────────────────────────
582
+ const customers = new Command("customers").description("Manage customers");
583
+ customers
584
+ .command("create")
585
+ .description("Create a customer")
586
+ .requiredOption("--email <email>", "Customer email")
587
+ .option("--name <name>", "Display name")
588
+ .option("--metadata <json>", "JSON string")
589
+ .action(async (opts, cmd) => {
590
+ const g = cmd.optsWithGlobals();
591
+ try {
592
+ const body = { email: opts.email };
593
+ if (opts.name)
594
+ body["name"] = opts.name;
595
+ if (opts.metadata)
596
+ body["metadata"] = parseMetadata(opts.metadata);
597
+ const result = await apiRequest("/payment/customers", {
598
+ method: "POST",
599
+ body,
600
+ sandbox: g.sandbox,
601
+ });
602
+ if (g.json) {
603
+ output(result, { json: true });
604
+ }
605
+ else {
606
+ console.log(`Customer created: ${chalk.bold(String(result["id"]))}`);
607
+ console.log(`Email: ${result["email"]}`);
608
+ if (result["name"])
609
+ console.log(`Name: ${result["name"]}`);
610
+ }
611
+ }
612
+ catch (err) {
613
+ handleError(err, g.json);
614
+ }
615
+ });
616
+ customers
617
+ .command("list")
618
+ .description("List customers")
619
+ .option("--email <email>", "Filter by exact email")
620
+ .option("--limit <n>", "Items per page", parseInt)
621
+ .option("--cursor <cursor>", "Pagination cursor")
622
+ .action(async (opts, cmd) => {
623
+ const g = cmd.optsWithGlobals();
624
+ try {
625
+ const result = await apiRequest("/payment/customers", {
626
+ query: { email: opts.email, limit: opts.limit, cursor: opts.cursor },
627
+ sandbox: g.sandbox,
628
+ });
629
+ if (g.json) {
630
+ output(result, { json: true });
631
+ }
632
+ else {
633
+ const cols = [
634
+ { key: "id", header: "ID", width: 20 },
635
+ { key: "email", header: "Email", width: 30 },
636
+ { key: "name", header: "Name" },
637
+ { key: "createdAt", header: "Created" },
638
+ ];
639
+ output((result["data"] ?? result), { columns: cols });
640
+ }
641
+ }
642
+ catch (err) {
643
+ handleError(err, g.json);
644
+ }
645
+ });
646
+ customers
647
+ .command("get <id>")
648
+ .description("Get a customer")
649
+ .action(async (id, _, cmd) => {
650
+ const g = cmd.optsWithGlobals();
651
+ try {
652
+ const result = await apiRequest(`/payment/customers/${id}`, {
653
+ sandbox: g.sandbox,
654
+ });
655
+ output(result, { json: g.json });
656
+ }
657
+ catch (err) {
658
+ handleError(err, g.json);
659
+ }
660
+ });
661
+ customers
662
+ .command("update <id>")
663
+ .description("Update a customer")
664
+ .option("--name <name>", "New display name")
665
+ .option("--email <email>", "New email")
666
+ .option("--metadata <json>", "JSON string")
667
+ .action(async (id, opts, cmd) => {
668
+ const g = cmd.optsWithGlobals();
669
+ try {
670
+ const body = {};
671
+ if (opts.name)
672
+ body["name"] = opts.name;
673
+ if (opts.email)
674
+ body["email"] = opts.email;
675
+ if (opts.metadata)
676
+ body["metadata"] = parseMetadata(opts.metadata);
677
+ const result = await apiRequest(`/payment/customers/${id}`, {
678
+ method: "PATCH",
679
+ body,
680
+ sandbox: g.sandbox,
681
+ });
682
+ if (g.json) {
683
+ output(result, { json: true });
684
+ }
685
+ else {
686
+ console.log(chalk.green(`Customer ${id} updated.`));
687
+ }
688
+ }
689
+ catch (err) {
690
+ handleError(err, g.json);
691
+ }
692
+ });
693
+ // ─── Portal ──────────────────────────────────────────────────
694
+ const portal = new Command("portal").description("Manage billing portal sessions");
695
+ portal
696
+ .command("create")
697
+ .description("Create a billing portal session")
698
+ .requiredOption("--customer <id>", "Customer ID")
699
+ .option("--return-url <url>", "Redirect when customer is done")
700
+ .option("--expires-in <minutes>", "Session duration in minutes", parseInt)
701
+ .action(async (opts, cmd) => {
702
+ const g = cmd.optsWithGlobals();
703
+ try {
704
+ const body = { customerId: opts.customer };
705
+ if (opts.returnUrl)
706
+ body["returnUrl"] = opts.returnUrl;
707
+ if (opts.expiresIn)
708
+ body["expiresInMinutes"] = opts.expiresIn;
709
+ const result = await apiRequest("/payment/portal-sessions", {
710
+ method: "POST",
711
+ body,
712
+ sandbox: g.sandbox,
713
+ });
714
+ if (g.json) {
715
+ output(result, { json: true });
716
+ }
717
+ else {
718
+ console.log(`Portal session created: ${chalk.bold(String(result["id"]))}`);
719
+ if (result["url"])
720
+ console.log(`URL: ${result["url"]}`);
721
+ if (result["expiresAt"])
722
+ console.log(`Expires: ${result["expiresAt"]}`);
723
+ }
724
+ }
725
+ catch (err) {
726
+ handleError(err, g.json);
727
+ }
728
+ });
729
+ // ─── Top-level payment command ───────────────────────────────
730
+ const payment = new Command("payment").description("Manage payments, plans, subscriptions, and customers");
731
+ payment.addCommand(checkout);
732
+ payment.addCommand(plans);
733
+ payment.addCommand(subscriptions);
734
+ payment.addCommand(invoices);
735
+ payment.addCommand(customers);
736
+ payment.addCommand(portal);
737
+ export { payment };
738
+ //# sourceMappingURL=payment.js.map