@vonpay/checkout-cli 0.3.2 → 0.4.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 (78) hide show
  1. package/README.md +55 -10
  2. package/dist/client-factory.d.ts +10 -0
  3. package/dist/client-factory.d.ts.map +1 -0
  4. package/dist/client-factory.js +26 -0
  5. package/dist/client-factory.js.map +1 -0
  6. package/dist/commands/capabilities.d.ts +11 -0
  7. package/dist/commands/capabilities.d.ts.map +1 -0
  8. package/dist/commands/capabilities.js +66 -0
  9. package/dist/commands/capabilities.js.map +1 -0
  10. package/dist/commands/checkout.d.ts.map +1 -1
  11. package/dist/commands/checkout.js +10 -0
  12. package/dist/commands/checkout.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +56 -0
  14. package/dist/commands/doctor.d.ts.map +1 -1
  15. package/dist/commands/doctor.js +15 -5
  16. package/dist/commands/doctor.js.map +1 -1
  17. package/dist/commands/health.js +3 -3
  18. package/dist/commands/health.js.map +1 -1
  19. package/dist/commands/listen.d.ts +19 -0
  20. package/dist/commands/listen.d.ts.map +1 -0
  21. package/dist/commands/listen.js +236 -0
  22. package/dist/commands/listen.js.map +1 -0
  23. package/dist/commands/payment-intents.d.ts +21 -0
  24. package/dist/commands/payment-intents.d.ts.map +1 -0
  25. package/dist/commands/payment-intents.js +195 -0
  26. package/dist/commands/payment-intents.js.map +1 -0
  27. package/dist/commands/refunds.d.ts +3 -0
  28. package/dist/commands/refunds.d.ts.map +1 -0
  29. package/dist/commands/refunds.js +78 -0
  30. package/dist/commands/refunds.js.map +1 -0
  31. package/dist/commands/sessions.d.ts.map +1 -1
  32. package/dist/commands/sessions.js +3 -18
  33. package/dist/commands/sessions.js.map +1 -1
  34. package/dist/commands/tokens.d.ts +3 -0
  35. package/dist/commands/tokens.d.ts.map +1 -0
  36. package/dist/commands/tokens.js +77 -0
  37. package/dist/commands/tokens.js.map +1 -0
  38. package/dist/commands/trigger.d.ts +20 -0
  39. package/dist/commands/trigger.d.ts.map +1 -1
  40. package/dist/commands/trigger.js +17 -5
  41. package/dist/commands/trigger.js.map +1 -1
  42. package/dist/utils/errors.d.ts +7 -0
  43. package/dist/utils/errors.d.ts.map +1 -0
  44. package/dist/utils/errors.js +24 -0
  45. package/dist/utils/errors.js.map +1 -0
  46. package/dist/utils/forward-to.d.ts +34 -0
  47. package/dist/utils/forward-to.d.ts.map +1 -0
  48. package/dist/utils/forward-to.js +88 -0
  49. package/dist/utils/forward-to.js.map +1 -0
  50. package/dist/utils/listen-forward.d.ts +43 -0
  51. package/dist/utils/listen-forward.d.ts.map +1 -0
  52. package/dist/utils/listen-forward.js +106 -0
  53. package/dist/utils/listen-forward.js.map +1 -0
  54. package/dist/utils/listen-output.d.ts +52 -0
  55. package/dist/utils/listen-output.d.ts.map +1 -0
  56. package/dist/utils/listen-output.js +85 -0
  57. package/dist/utils/listen-output.js.map +1 -0
  58. package/dist/utils/live-key.d.ts +16 -0
  59. package/dist/utils/live-key.d.ts.map +1 -0
  60. package/dist/utils/live-key.js +29 -0
  61. package/dist/utils/live-key.js.map +1 -0
  62. package/dist/utils/metadata.d.ts +2 -0
  63. package/dist/utils/metadata.d.ts.map +1 -0
  64. package/dist/utils/metadata.js +31 -0
  65. package/dist/utils/metadata.js.map +1 -0
  66. package/dist/utils/sanitize.d.ts +4 -0
  67. package/dist/utils/sanitize.d.ts.map +1 -0
  68. package/dist/utils/sanitize.js +18 -0
  69. package/dist/utils/sanitize.js.map +1 -0
  70. package/dist/utils/sse-consumer.d.ts +81 -0
  71. package/dist/utils/sse-consumer.d.ts.map +1 -0
  72. package/dist/utils/sse-consumer.js +172 -0
  73. package/dist/utils/sse-consumer.js.map +1 -0
  74. package/dist/utils/sse-parser.d.ts +63 -0
  75. package/dist/utils/sse-parser.d.ts.map +1 -0
  76. package/dist/utils/sse-parser.js +149 -0
  77. package/dist/utils/sse-parser.js.map +1 -0
  78. package/package.json +2 -2
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function tokensCommand(): Command;
3
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/commands/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,wBAAgB,aAAa,IAAI,OAAO,CAsFvC"}
@@ -0,0 +1,77 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import { requireApiKey } from "../config.js";
5
+ import { makeCheckoutClient } from "../client-factory.js";
6
+ import { parseMetadata } from "../utils/metadata.js";
7
+ import { handleError } from "../utils/errors.js";
8
+ import { isLiveKey, warnLiveSecretFlag } from "../utils/live-key.js";
9
+ function printToken(token) {
10
+ const statusColor = token.status === "active"
11
+ ? chalk.green
12
+ : token.status === "expired"
13
+ ? chalk.yellow
14
+ : chalk.red;
15
+ console.log(` ID: ${token.id}`);
16
+ console.log(` Status: ${statusColor(token.status)}`);
17
+ console.log(` Card: ${token.card.brand} •••• ${token.card.last4}`);
18
+ console.log(` Expires: ${String(token.card.expMonth).padStart(2, "0")}/${token.card.expYear}`);
19
+ }
20
+ export function tokensCommand() {
21
+ const tokens = new Command("tokens").description("Create payment-method tokens for saved-card / MIT flows");
22
+ tokens
23
+ .command("create")
24
+ .description("Create a payment-method token")
25
+ .option("--buyer-id <id>", "Buyer ID to attach the token to")
26
+ .option("--provider-reference <ref>", "Provider tokenization handle (required for live keys with iframe-vault providers)")
27
+ .option("--metadata <pair...>", "Metadata key=value (repeat flag for multiple pairs)")
28
+ .option("--idempotency-key <key>", "Idempotency key for safe retries")
29
+ .option("--json", "Output raw JSON")
30
+ .action(async (opts) => {
31
+ const apiKey = requireApiKey();
32
+ // Sandbox keys auto-mint a mock card when no provider reference is
33
+ // supplied. Live keys with iframe-vault providers require a
34
+ // browser-minted vault token; the server returns 422 otherwise.
35
+ if (!opts.buyerId && !opts.providerReference) {
36
+ if (isLiveKey(apiKey)) {
37
+ console.warn(chalk.yellow("Warning: no --buyer-id or --provider-reference supplied. Live keys with iframe-vault providers will fail with a 422 validation_error."));
38
+ }
39
+ else {
40
+ console.log(chalk.dim("No --provider-reference supplied; sandbox will auto-mint a mock card token."));
41
+ }
42
+ }
43
+ if (opts.providerReference !== undefined) {
44
+ warnLiveSecretFlag(apiKey, "--provider-reference");
45
+ }
46
+ const client = makeCheckoutClient();
47
+ const spinner = ora("Creating token...").start();
48
+ try {
49
+ const metadata = parseMetadata(opts.metadata);
50
+ const params = {};
51
+ if (opts.buyerId !== undefined)
52
+ params.buyerId = opts.buyerId;
53
+ if (opts.providerReference !== undefined) {
54
+ params.providerReference = opts.providerReference;
55
+ }
56
+ if (metadata !== undefined)
57
+ params.metadata = metadata;
58
+ const token = await client.tokens.create(params, opts.idempotencyKey
59
+ ? { idempotencyKey: opts.idempotencyKey }
60
+ : undefined);
61
+ spinner.stop();
62
+ if (opts.json) {
63
+ console.log(JSON.stringify(token, null, 2));
64
+ }
65
+ else {
66
+ console.log(chalk.green("✓ Token created"));
67
+ printToken(token);
68
+ }
69
+ }
70
+ catch (err) {
71
+ spinner.stop();
72
+ handleError(err);
73
+ }
74
+ });
75
+ return tokens;
76
+ }
77
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/commands/tokens.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,23 @@
1
1
  import { Command } from "commander";
2
+ export declare const SUPPORTED_EVENTS: readonly ["session.succeeded", "session.failed", "payment_intent.succeeded", "payment_intent.failed", "payment_intent.cancelled", "refund.created"];
3
+ type SupportedEvent = (typeof SUPPORTED_EVENTS)[number];
4
+ interface TriggerPayload {
5
+ event: SupportedEvent;
6
+ sessionId: string;
7
+ merchantId: string;
8
+ amount: number;
9
+ currency: string;
10
+ status: string;
11
+ timestamp: string;
12
+ transactionId?: string;
13
+ refundId?: string;
14
+ paymentIntentId?: string;
15
+ }
16
+ export declare function buildPayload(event: SupportedEvent, opts: {
17
+ sessionId?: string;
18
+ amount?: number;
19
+ currency?: string;
20
+ }): TriggerPayload;
2
21
  export declare function triggerCommand(): Command;
22
+ export {};
3
23
  //# sourceMappingURL=trigger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"trigger.d.ts","sourceRoot":"","sources":["../../src/commands/trigger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0DpC,wBAAgB,cAAc,IAAI,OAAO,CA0ExC"}
1
+ {"version":3,"file":"trigger.d.ts","sourceRoot":"","sources":["../../src/commands/trigger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,gBAAgB,qJAOnB,CAAC;AACX,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAWxD,UAAU,cAAc;IACtB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,cAAc,CA4BhB;AAED,wBAAgB,cAAc,IAAI,OAAO,CA0ExC"}
@@ -3,21 +3,30 @@ import { createHmac, randomUUID } from "node:crypto";
3
3
  import chalk from "chalk";
4
4
  import ora from "ora";
5
5
  import { requireApiKey } from "../config.js";
6
- const SUPPORTED_EVENTS = [
6
+ export const SUPPORTED_EVENTS = [
7
7
  "session.succeeded",
8
8
  "session.failed",
9
- "session.expired",
9
+ "payment_intent.succeeded",
10
+ "payment_intent.failed",
11
+ "payment_intent.cancelled",
10
12
  "refund.created",
11
13
  ];
12
14
  const STATUS_BY_EVENT = {
13
15
  "session.succeeded": "succeeded",
14
16
  "session.failed": "failed",
15
- "session.expired": "expired",
17
+ "payment_intent.succeeded": "succeeded",
18
+ "payment_intent.failed": "failed",
19
+ "payment_intent.cancelled": "cancelled",
16
20
  "refund.created": "created",
17
21
  };
18
- function buildPayload(event, opts) {
19
- const hasTransaction = event === "session.succeeded" || event === "refund.created";
22
+ export function buildPayload(event, opts) {
23
+ const isPaymentIntent = event.startsWith("payment_intent.");
20
24
  const isRefund = event === "refund.created";
25
+ // A transaction row is written for any terminal success — a settled
26
+ // session, a captured payment intent, or a refund.
27
+ const hasTransaction = event === "session.succeeded" ||
28
+ event === "payment_intent.succeeded" ||
29
+ isRefund;
21
30
  const payload = {
22
31
  event,
23
32
  sessionId: opts.sessionId ?? `session_test_${randomUUID().slice(0, 8)}`,
@@ -33,6 +42,9 @@ function buildPayload(event, opts) {
33
42
  if (isRefund) {
34
43
  payload.refundId = `re_test_${randomUUID().slice(0, 8)}`;
35
44
  }
45
+ if (isPaymentIntent) {
46
+ payload.paymentIntentId = `vpi_test_${randomUUID().slice(0, 8)}`;
47
+ }
36
48
  return payload;
37
49
  }
38
50
  export function triggerCommand() {
@@ -1 +1 @@
1
- {"version":3,"file":"trigger.js","sourceRoot":"","sources":["../../src/commands/trigger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trigger.js","sourceRoot":"","sources":["../../src/commands/trigger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ declare function scrubSecrets(s: string): string;
2
+ export declare function handleError(err: unknown): never;
3
+ export declare const __testing__: {
4
+ scrubSecrets: typeof scrubSecrets;
5
+ };
6
+ export {};
7
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAKA,iBAAS,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CAa/C;AAED,eAAO,MAAM,WAAW;;CAEvB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import chalk from "chalk";
2
+ import { VonPayError } from "@vonpay/checkout-node";
3
+ const SECRET_KEY_PATTERN = /vp_sk_(test|live)_[A-Za-z0-9_-]+/g;
4
+ function scrubSecrets(s) {
5
+ return s.replace(SECRET_KEY_PATTERN, "vp_sk_***");
6
+ }
7
+ export function handleError(err) {
8
+ if (err instanceof VonPayError) {
9
+ console.error(chalk.red(`Error: ${err.message}`));
10
+ console.error(chalk.dim(` Code: ${err.code}`));
11
+ console.error(chalk.dim(` Fix: ${err.fix}`));
12
+ if (err.requestId) {
13
+ console.error(chalk.dim(` Request ID: ${err.requestId}`));
14
+ }
15
+ process.exit(1);
16
+ }
17
+ const message = err instanceof Error ? err.message : String(err);
18
+ console.error(chalk.red(`Error: ${scrubSecrets(message)}`));
19
+ process.exit(1);
20
+ }
21
+ export const __testing__ = {
22
+ scrubSecrets,
23
+ };
24
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAE/D,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC/C,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,YAAY;CACb,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * URL validation for `vonpay checkout listen --forward-to`.
3
+ *
4
+ * Default posture: loopback-only. Refuses any URL whose hostname is not
5
+ * one of the IPv4/IPv6 loopback aliases. This protects against the
6
+ * "I typed the prod URL by mistake" class of incident, where a dev
7
+ * starts forwarding production webhook attempts at their staging
8
+ * endpoint or a public colleague's URL.
9
+ *
10
+ * The opt-in --forward-to-tunnel flag allows non-loopback hosts for
11
+ * ngrok / Cloudflare Tunnel workflows. When set, only `https://` is
12
+ * additionally rejected for non-loopback — http on a public host is
13
+ * a stronger anti-pattern than tunneled https.
14
+ *
15
+ * Scheme: http and https accepted on loopback; non-loopback requires
16
+ * the tunnel flag.
17
+ */
18
+ export type ForwardToVerdict = {
19
+ ok: true;
20
+ url: URL;
21
+ isLoopback: boolean;
22
+ warning?: string;
23
+ } | {
24
+ ok: false;
25
+ reason: string;
26
+ };
27
+ /**
28
+ * Pure validator. No I/O. Caller decides what to do with the verdict
29
+ * (the CLI surfaces `reason` to stderr and exits non-zero).
30
+ */
31
+ export declare function validateForwardTo(raw: string, opts: {
32
+ allowTunnel: boolean;
33
+ }): ForwardToVerdict;
34
+ //# sourceMappingURL=forward-to.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forward-to.d.ts","sourceRoot":"","sources":["../../src/utils/forward-to.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAmBH,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,GAC7B,gBAAgB,CA6ClB"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * URL validation for `vonpay checkout listen --forward-to`.
3
+ *
4
+ * Default posture: loopback-only. Refuses any URL whose hostname is not
5
+ * one of the IPv4/IPv6 loopback aliases. This protects against the
6
+ * "I typed the prod URL by mistake" class of incident, where a dev
7
+ * starts forwarding production webhook attempts at their staging
8
+ * endpoint or a public colleague's URL.
9
+ *
10
+ * The opt-in --forward-to-tunnel flag allows non-loopback hosts for
11
+ * ngrok / Cloudflare Tunnel workflows. When set, only `https://` is
12
+ * additionally rejected for non-loopback — http on a public host is
13
+ * a stronger anti-pattern than tunneled https.
14
+ *
15
+ * Scheme: http and https accepted on loopback; non-loopback requires
16
+ * the tunnel flag.
17
+ */
18
+ const LOOPBACK_HOSTS = new Set([
19
+ "localhost",
20
+ "127.0.0.1",
21
+ "0.0.0.0", // kernel-wildcard often routes to loopback on Linux / macOS dev hosts
22
+ "::1",
23
+ // URL.hostname surrounds IPv6 with brackets, but the bracket-stripping
24
+ // happens before this set is consulted (see normalizeHost). Both forms
25
+ // listed for defensiveness.
26
+ "[::1]",
27
+ // IPv6-mapped IPv4 loopback. Node's URL parser normalizes
28
+ // `[::ffff:127.0.0.1]` to either form depending on version — both
29
+ // are functionally loopback so we list every variant we have
30
+ // observed in the wild.
31
+ "::ffff:127.0.0.1",
32
+ "::ffff:7f00:1",
33
+ ]);
34
+ /**
35
+ * Pure validator. No I/O. Caller decides what to do with the verdict
36
+ * (the CLI surfaces `reason` to stderr and exits non-zero).
37
+ */
38
+ export function validateForwardTo(raw, opts) {
39
+ if (typeof raw !== "string" || raw.length === 0) {
40
+ return { ok: false, reason: "--forward-to URL is required" };
41
+ }
42
+ let url;
43
+ try {
44
+ url = new URL(raw);
45
+ }
46
+ catch {
47
+ return {
48
+ ok: false,
49
+ reason: `--forward-to is not a parseable URL: ${truncate(raw)}`,
50
+ };
51
+ }
52
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
53
+ return {
54
+ ok: false,
55
+ reason: `--forward-to scheme must be http or https; got ${url.protocol}`,
56
+ };
57
+ }
58
+ const host = normalizeHost(url.hostname);
59
+ const isLoopback = LOOPBACK_HOSTS.has(host);
60
+ if (!isLoopback && !opts.allowTunnel) {
61
+ return {
62
+ ok: false,
63
+ reason: `--forward-to refuses non-loopback hosts by default (got ${url.hostname}). ` +
64
+ `Re-run with --forward-to-tunnel if forwarding through ngrok / Cloudflare Tunnel / etc.`,
65
+ };
66
+ }
67
+ // `0.0.0.0` is treated as loopback for dev convenience, but it's a
68
+ // bind-wildcard, not a guaranteed destination — in some container /
69
+ // network configs an outbound connection to 0.0.0.0 routes off-host.
70
+ // Accept it (non-breaking) but warn so a dev who meant "localhost"
71
+ // isn't silently forwarding off-loopback.
72
+ const warning = host === "0.0.0.0"
73
+ ? "forwarding to 0.0.0.0 — that's a bind-wildcard, not a guaranteed loopback destination (it can route off-host in some container/network setups). Prefer 127.0.0.1 or localhost if you meant your local machine."
74
+ : undefined;
75
+ return { ok: true, url, isLoopback, warning };
76
+ }
77
+ function normalizeHost(hostname) {
78
+ // URL.hostname returns IPv6 addresses unwrapped (no brackets), but
79
+ // dual-coverage doesn't hurt.
80
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
81
+ return hostname.slice(1, -1);
82
+ }
83
+ return hostname.toLowerCase();
84
+ }
85
+ function truncate(s) {
86
+ return s.length <= 120 ? s : `${s.slice(0, 117)}...`;
87
+ }
88
+ //# sourceMappingURL=forward-to.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forward-to.js","sourceRoot":"","sources":["../../src/utils/forward-to.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,WAAW;IACX,WAAW;IACX,SAAS,EAAE,sEAAsE;IACjF,KAAK;IACL,uEAAuE;IACvE,uEAAuE;IACvE,4BAA4B;IAC5B,OAAO;IACP,0DAA0D;IAC1D,kEAAkE;IAClE,6DAA6D;IAC7D,wBAAwB;IACxB,kBAAkB;IAClB,eAAe;CAChB,CAAC,CAAC;AAMH;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAAW,EACX,IAA8B;IAE9B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;IAC/D,CAAC;IAED,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,wCAAwC,QAAQ,CAAC,GAAG,CAAC,EAAE;SAChE,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,kDAAkD,GAAG,CAAC,QAAQ,EAAE;SACzE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE5C,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACrC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,2DAA2D,GAAG,CAAC,QAAQ,KAAK;gBAC5E,wFAAwF;SAC3F,CAAC;IACJ,CAAC;IAED,mEAAmE;IACnE,oEAAoE;IACpE,qEAAqE;IACrE,mEAAmE;IACnE,0CAA0C;IAC1C,MAAM,OAAO,GACX,IAAI,KAAK,SAAS;QAChB,CAAC,CAAC,gNAAgN;QAClN,CAAC,CAAC,SAAS,CAAC;IAEhB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AAChD,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,mEAAmE;IACnE,8BAA8B;IAC9B,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS;IACzB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC;AACvD,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { ForwardOutcome } from "./listen-output.js";
2
+ /**
3
+ * Shadow-forward a webhook event to a local URL.
4
+ *
5
+ * The CLI receives a delivery-attempt TRANSCRIPT over SSE — not the
6
+ * payload itself. To replay the same delivery against a local URL,
7
+ * the CLI:
8
+ *
9
+ * 1. Fetches the full event record via GET /v1/webhook_events/:id
10
+ * (the public webhook-event read endpoint)
11
+ * 2. Computes an HMAC-SHA256 signature over the body using the
12
+ * merchant's API key (same secret-equals-key convention the SDK's
13
+ * `webhooks.constructEvent` verifies)
14
+ * 3. POSTs to the local URL with `X-VonPay-Signature` +
15
+ * `X-VonPay-Timestamp` headers, matching the live delivery shape.
16
+ * The timestamp is stamped HERE, at forward time — see the
17
+ * `forwardPayload` body for the fresh-timestamp rationale.
18
+ *
19
+ * Bounded by a per-forward timeout (default 5s) and never retries —
20
+ * shadow-forward is best-effort; if the local URL is down, the
21
+ * transcript line shows the failure and the dev moves on.
22
+ *
23
+ * This re-delivers from the SSE delivery transcript, which differs from
24
+ * Stripe CLI's `stripe listen --forward-to` pre-empt model.
25
+ */
26
+ interface ForwardOptions {
27
+ /** Local URL to POST the payload to. */
28
+ url: URL;
29
+ /** Merchant API key (vp_sk_*) — used as HMAC secret. */
30
+ signingSecret: string;
31
+ /** Per-forward timeout. Default 5_000. */
32
+ timeoutMs?: number;
33
+ /** AbortSignal for graceful shutdown. */
34
+ signal: AbortSignal;
35
+ }
36
+ /**
37
+ * Sign + POST a JSON payload to the local URL. Returns an outcome
38
+ * shape that the output formatters render — never throws (errors
39
+ * surface via `outcome.error`).
40
+ */
41
+ export declare function forwardPayload(payload: Record<string, unknown>, opts: ForwardOptions): Promise<ForwardOutcome>;
42
+ export {};
43
+ //# sourceMappingURL=listen-forward.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-forward.d.ts","sourceRoot":"","sources":["../../src/utils/listen-forward.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,UAAU,cAAc;IACtB,wCAAwC;IACxC,GAAG,EAAE,GAAG,CAAC;IACT,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,cAAc,CAAC,CA8DzB"}
@@ -0,0 +1,106 @@
1
+ import { createHmac } from "node:crypto";
2
+ /**
3
+ * Sign + POST a JSON payload to the local URL. Returns an outcome
4
+ * shape that the output formatters render — never throws (errors
5
+ * surface via `outcome.error`).
6
+ */
7
+ export async function forwardPayload(payload, opts) {
8
+ const body = JSON.stringify(payload);
9
+ // Stamp the moment we forward — NOT the server's original
10
+ // delivery-attempt time. The CLI is the *re-sender* for this local
11
+ // delivery: the dev's handler calls `webhooks.constructEvent`, which
12
+ // rejects any `X-VonPay-Timestamp` outside ±5 min of now. The server's
13
+ // original timestamp (carried on the SSE `webhook.attempt` frame) is
14
+ // routinely minutes old — retries, processing lag, a transcript the
15
+ // dev scrolled back through — so replaying it trips the freshness
16
+ // window and breaks local verification. A fresh timestamp here matches
17
+ // Stripe CLI's re-sign posture. The v1 signature covers the body only
18
+ // (constructEvent verifies the timestamp independently), so the fresh
19
+ // timestamp does NOT need to be folded into the HMAC.
20
+ const timestamp = new Date().toISOString();
21
+ const signature = createHmac("sha256", opts.signingSecret)
22
+ .update(body)
23
+ .digest("hex");
24
+ const timeoutMs = opts.timeoutMs ?? 5_000;
25
+ const localController = new AbortController();
26
+ const timeoutId = setTimeout(() => localController.abort(), timeoutMs);
27
+ const combinedSignal = anySignal([opts.signal, localController.signal]);
28
+ const start = Date.now();
29
+ try {
30
+ const res = await fetch(opts.url.toString(), {
31
+ method: "POST",
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ "X-VonPay-Signature": signature,
35
+ "X-VonPay-Timestamp": timestamp,
36
+ "User-Agent": "vonpay-cli-listen",
37
+ },
38
+ body,
39
+ signal: combinedSignal,
40
+ });
41
+ // Drain response body so the connection can be reused — best practice
42
+ // for repeated POSTs to the same local endpoint.
43
+ try {
44
+ await res.arrayBuffer();
45
+ }
46
+ catch {
47
+ // ignore drain errors; not relevant to forward semantics
48
+ }
49
+ return {
50
+ attempted: true,
51
+ status: res.status,
52
+ latencyMs: Date.now() - start,
53
+ error: null,
54
+ };
55
+ }
56
+ catch (err) {
57
+ if (opts.signal.aborted) {
58
+ return { attempted: true, status: null, latencyMs: null, error: "aborted" };
59
+ }
60
+ if (localController.signal.aborted) {
61
+ return { attempted: true, status: null, latencyMs: null, error: `timeout after ${timeoutMs}ms` };
62
+ }
63
+ const message = err instanceof Error ? err.message : String(err);
64
+ return { attempted: true, status: null, latencyMs: null, error: message };
65
+ }
66
+ finally {
67
+ clearTimeout(timeoutId);
68
+ }
69
+ }
70
+ /**
71
+ * Combine multiple AbortSignals into one. The returned signal is
72
+ * aborted when any input signal aborts. Polyfill for `AbortSignal.any`
73
+ * which is Node 20.5+ only — guard for older 20.x deployments.
74
+ *
75
+ * The polyfill tracks every listener it registers and removes them all
76
+ * once any input fires — without this, the long-lived parent
77
+ * `opts.signal` accumulates one orphaned listener per forward call,
78
+ * which on a busy listen stream is a measurable leak.
79
+ */
80
+ function anySignal(signals) {
81
+ if (typeof AbortSignal.any === "function") {
82
+ return AbortSignal.any(signals);
83
+ }
84
+ const controller = new AbortController();
85
+ const tracked = [];
86
+ const onAbort = () => {
87
+ for (const sig of tracked) {
88
+ sig.removeEventListener("abort", onAbort);
89
+ }
90
+ controller.abort();
91
+ };
92
+ for (const signal of signals) {
93
+ if (signal.aborted) {
94
+ controller.abort();
95
+ // Remove already-added listeners — none yet, but defensive.
96
+ for (const sig of tracked) {
97
+ sig.removeEventListener("abort", onAbort);
98
+ }
99
+ return controller.signal;
100
+ }
101
+ signal.addEventListener("abort", onAbort);
102
+ tracked.push(signal);
103
+ }
104
+ return controller.signal;
105
+ }
106
+ //# sourceMappingURL=listen-forward.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-forward.js","sourceRoot":"","sources":["../../src/utils/listen-forward.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Output formatters for `vonpay checkout listen`.
3
+ *
4
+ * Two modes:
5
+ * - "human" (default) — colored transcript, one line per attempt.
6
+ * Optimized for the dev sitting at a terminal watching events
7
+ * flow in real time.
8
+ * - "json" — one JSON object per line on stdout. Optimized for
9
+ * piping to `jq`, `grep`, or log aggregators.
10
+ *
11
+ * Lifecycle messages (connecting, reconnecting, etc.) ALWAYS go to
12
+ * stderr in human mode so stdout stays parseable in `--json` mode
13
+ * even when interleaved with reconnects.
14
+ */
15
+ export type OutputMode = "human" | "json";
16
+ /**
17
+ * Wire shape of the SSE `webhook.attempt` payload. Mirrors
18
+ * vonpay-checkout's WebhookListenAttempt (snake_case on the wire).
19
+ *
20
+ * Duplicated here rather than importing from @vonpay/checkout-node
21
+ * because the SDK does not currently export an SSE-event type — the
22
+ * listen surface is CLI-only. If the SDK adds a streaming consumer in
23
+ * a future minor, this type should move to the SDK and be re-imported.
24
+ */
25
+ export interface WebhookAttemptPayload {
26
+ type: "webhook.attempt";
27
+ subscription_id: string;
28
+ event_id: string;
29
+ event_type: string;
30
+ attempt: number;
31
+ succeeded: boolean;
32
+ response_status: number | null;
33
+ latency_ms: number;
34
+ timestamp: string;
35
+ livemode: boolean;
36
+ }
37
+ export interface ForwardOutcome {
38
+ attempted: boolean;
39
+ /** Local-URL response status, or null when no attempt was made (no --forward-to). */
40
+ status: number | null;
41
+ /** Latency ms of the local-URL POST. */
42
+ latencyMs: number | null;
43
+ /** Error message if the forward attempt failed at the network layer. */
44
+ error: string | null;
45
+ }
46
+ export declare function formatAttemptHuman(payload: WebhookAttemptPayload, forward: ForwardOutcome | null): string;
47
+ export declare function formatAttemptJson(payload: WebhookAttemptPayload, forward: ForwardOutcome | null): string;
48
+ /** Lifecycle line for human mode (stderr). */
49
+ export declare function formatLifecycleHuman(message: string, kind: "info" | "warn" | "error"): string;
50
+ /** Lifecycle line for JSON mode (stderr). */
51
+ export declare function formatLifecycleJson(kind: "connecting" | "connected" | "disconnected" | "auth_failed" | "fatal" | "ready" | "shutdown" | "warning", extra?: Record<string, unknown>): string;
52
+ //# sourceMappingURL=listen-output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-output.d.ts","sourceRoot":"","sources":["../../src/utils/listen-output.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,qFAAqF;IACrF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wCAAwC;IACxC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,wEAAwE;IACxE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GAAG,IAAI,GAC7B,MAAM,CAwBR;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GAAG,IAAI,GAC7B,MAAM,CAqBR;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAQ7F;AAED,6CAA6C;AAC7C,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,aAAa,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,EAC9G,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,MAAM,CAER"}