@vonpay/checkout-cli 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/commands/checkout.d.ts.map +1 -1
- package/dist/commands/checkout.js +2 -0
- package/dist/commands/checkout.js.map +1 -1
- package/dist/commands/listen.d.ts +19 -0
- package/dist/commands/listen.d.ts.map +1 -0
- package/dist/commands/listen.js +236 -0
- package/dist/commands/listen.js.map +1 -0
- package/dist/commands/trigger.d.ts +20 -0
- package/dist/commands/trigger.d.ts.map +1 -1
- package/dist/commands/trigger.js +28 -11
- package/dist/commands/trigger.js.map +1 -1
- package/dist/utils/forward-to.d.ts +34 -0
- package/dist/utils/forward-to.d.ts.map +1 -0
- package/dist/utils/forward-to.js +88 -0
- package/dist/utils/forward-to.js.map +1 -0
- package/dist/utils/listen-forward.d.ts +49 -0
- package/dist/utils/listen-forward.d.ts.map +1 -0
- package/dist/utils/listen-forward.js +113 -0
- package/dist/utils/listen-forward.js.map +1 -0
- package/dist/utils/listen-output.d.ts +52 -0
- package/dist/utils/listen-output.d.ts.map +1 -0
- package/dist/utils/listen-output.js +85 -0
- package/dist/utils/listen-output.js.map +1 -0
- package/dist/utils/sse-consumer.d.ts +81 -0
- package/dist/utils/sse-consumer.d.ts.map +1 -0
- package/dist/utils/sse-consumer.js +172 -0
- package/dist/utils/sse-consumer.js.map +1 -0
- package/dist/utils/sse-parser.d.ts +63 -0
- package/dist/utils/sse-parser.d.ts.map +1 -0
- package/dist/utils/sse-parser.js +149 -0
- package/dist/utils/sse-parser.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,12 +36,15 @@ All commands are namespaced under `vonpay checkout`:
|
|
|
36
36
|
| `vonpay checkout refunds create` | Refund a captured intent (full or partial) |
|
|
37
37
|
| `vonpay checkout tokens create` | Create a payment-method token for saved-card / MIT flows |
|
|
38
38
|
| `vonpay checkout capabilities` | Show what this merchant's binder supports |
|
|
39
|
-
| `vonpay checkout trigger <event>` | Trigger a synthetic webhook event against your local endpoint (`session.succeeded`, `session.failed`, `
|
|
39
|
+
| `vonpay checkout trigger <event>` | Trigger a synthetic webhook event against your local endpoint (`session.succeeded`, `session.failed`, `refund.created`) |
|
|
40
|
+
| `vonpay checkout listen` | Stream live webhook delivery attempts; optionally re-forward each to a local URL with `--forward-to` |
|
|
40
41
|
| `vonpay checkout health` | Check API health and latency |
|
|
41
42
|
| `vonpay checkout doctor` | Diagnose runtime + env + live API probe |
|
|
42
43
|
|
|
43
44
|
Run `vonpay checkout <command> --help` for full flag reference.
|
|
44
45
|
|
|
46
|
+
> **`listen --forward-to` signs forwarded events with the v1 webhook scheme** — a single `x-vonpay-signature: t=<unix-seconds>,v1=<hex>` header, with the HMAC-SHA256 computed over `${t}.${rawBody}` and a fresh `t` stamped at forward time. There is no separate timestamp header. Verify them in your local handler with `vonpay.webhooks.constructEvent(rawBody, signatureHeader, secret)`. For this local forward loop the signing secret is your API key; production deliveries are keyed by your per-endpoint signing secret (`whsec_…`). This is a known limitation of local forwarding — the platform exposes no per-subscription signing secret on the listen stream, so the forwarder cannot reproduce the production secret.
|
|
47
|
+
|
|
45
48
|
### Common flags
|
|
46
49
|
|
|
47
50
|
- `--json` on every read/write command for machine-readable output
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../src/commands/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../src/commands/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgBvD"}
|
|
@@ -6,6 +6,7 @@ import { refundsCommand } from "./refunds.js";
|
|
|
6
6
|
import { tokensCommand } from "./tokens.js";
|
|
7
7
|
import { capabilitiesCommand } from "./capabilities.js";
|
|
8
8
|
import { triggerCommand } from "./trigger.js";
|
|
9
|
+
import { listenCommand } from "./listen.js";
|
|
9
10
|
import { healthCommand } from "./health.js";
|
|
10
11
|
import { doctorCommand } from "./doctor.js";
|
|
11
12
|
export function registerCheckout(program) {
|
|
@@ -20,6 +21,7 @@ export function registerCheckout(program) {
|
|
|
20
21
|
checkout.addCommand(tokensCommand());
|
|
21
22
|
checkout.addCommand(capabilitiesCommand());
|
|
22
23
|
checkout.addCommand(triggerCommand());
|
|
24
|
+
checkout.addCommand(listenCommand());
|
|
23
25
|
checkout.addCommand(healthCommand());
|
|
24
26
|
checkout.addCommand(doctorCommand());
|
|
25
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/commands/checkout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,MAAM,QAAQ,GAAG,OAAO;SACrB,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,4BAA4B,CAAC,CAAC;IAE7C,QAAQ,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;IACnC,QAAQ,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;IACvC,QAAQ,CAAC,UAAU,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACvC,CAAC"}
|
|
1
|
+
{"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/commands/checkout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,MAAM,QAAQ,GAAG,OAAO;SACrB,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,4BAA4B,CAAC,CAAC;IAE7C,QAAQ,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;IACnC,QAAQ,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;IACvC,QAAQ,CAAC,UAAU,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* `vonpay checkout listen` — long-lived SSE consumer for the merchant's
|
|
4
|
+
* webhook delivery attempts.
|
|
5
|
+
*
|
|
6
|
+
* Industry conventions followed: WHATWG SSE spec for parsing,
|
|
7
|
+
* Last-Event-ID for reconnect dedupe, exponential backoff, loopback-only
|
|
8
|
+
* forward-to (Stripe CLI posture), refuse-live gate (mirrors
|
|
9
|
+
* `vonpay checkout login --confirm-cli-exposure`), kubectl/gh-style
|
|
10
|
+
* default human output with `--json` opt-in for machine consumption.
|
|
11
|
+
*
|
|
12
|
+
* Output channels:
|
|
13
|
+
* stdout — attempt events (one per delivery), formatted per --json
|
|
14
|
+
* stderr — lifecycle (connecting, reconnecting, shutdown summary)
|
|
15
|
+
*
|
|
16
|
+
* Stdout stays parseable in JSON mode regardless of reconnects.
|
|
17
|
+
*/
|
|
18
|
+
export declare function listenCommand(): Command;
|
|
19
|
+
//# sourceMappingURL=listen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listen.d.ts","sourceRoot":"","sources":["../../src/commands/listen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAqOvC"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { VonPayCheckout } from "@vonpay/checkout-node";
|
|
3
|
+
import { requireApiKey } from "../config.js";
|
|
4
|
+
import { getBaseUrl } from "../client-factory.js";
|
|
5
|
+
import { validateForwardTo } from "../utils/forward-to.js";
|
|
6
|
+
import { consumeSse, SseAuthError, } from "../utils/sse-consumer.js";
|
|
7
|
+
import { formatAttemptHuman, formatAttemptJson, formatLifecycleHuman, formatLifecycleJson, } from "../utils/listen-output.js";
|
|
8
|
+
import { forwardPayload } from "../utils/listen-forward.js";
|
|
9
|
+
/**
|
|
10
|
+
* `vonpay checkout listen` — long-lived SSE consumer for the merchant's
|
|
11
|
+
* webhook delivery attempts.
|
|
12
|
+
*
|
|
13
|
+
* Industry conventions followed: WHATWG SSE spec for parsing,
|
|
14
|
+
* Last-Event-ID for reconnect dedupe, exponential backoff, loopback-only
|
|
15
|
+
* forward-to (Stripe CLI posture), refuse-live gate (mirrors
|
|
16
|
+
* `vonpay checkout login --confirm-cli-exposure`), kubectl/gh-style
|
|
17
|
+
* default human output with `--json` opt-in for machine consumption.
|
|
18
|
+
*
|
|
19
|
+
* Output channels:
|
|
20
|
+
* stdout — attempt events (one per delivery), formatted per --json
|
|
21
|
+
* stderr — lifecycle (connecting, reconnecting, shutdown summary)
|
|
22
|
+
*
|
|
23
|
+
* Stdout stays parseable in JSON mode regardless of reconnects.
|
|
24
|
+
*/
|
|
25
|
+
export function listenCommand() {
|
|
26
|
+
return new Command("listen")
|
|
27
|
+
.description("Watch webhook delivery attempts in real time. Optionally re-forward each event to a local URL for development.")
|
|
28
|
+
.option("--forward-to <url>", "Local URL to receive a shadow copy of each delivered event (loopback only by default)")
|
|
29
|
+
.option("--forward-to-tunnel", "Allow non-loopback --forward-to URLs (for ngrok / Cloudflare Tunnel workflows)")
|
|
30
|
+
.option("--events <type>", "Only stream attempts for the given event type. Repeat the flag for multiple types.", (value, prev) => (prev ? [...prev, value] : [value]))
|
|
31
|
+
.option("--json", "Emit one JSON object per line on stdout instead of the colored transcript")
|
|
32
|
+
.option("--api-key <key>", "Override the API key from env / config")
|
|
33
|
+
.option("--confirm-live", "Acknowledge that the listen stream may surface production webhook attempts (required for vp_sk_live_* keys)")
|
|
34
|
+
.action(async (opts) => {
|
|
35
|
+
const apiKey = opts.apiKey ?? requireApiKey();
|
|
36
|
+
if (apiKey.startsWith("vp_sk_live_") && !opts.confirmLive) {
|
|
37
|
+
process.stderr.write(formatLifecycleHuman("Refusing to start listen with a live key (vp_sk_live_*). Re-run with --confirm-live to acknowledge that production webhook attempts will stream to this terminal.", "error") + "\n");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
if (!apiKey.startsWith("vp_sk_test_") &&
|
|
41
|
+
!apiKey.startsWith("vp_sk_live_")) {
|
|
42
|
+
process.stderr.write(formatLifecycleHuman("Listen requires a secret key (vp_sk_*). Publishable keys are rejected by the server.", "error") + "\n");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
let forwardUrl;
|
|
46
|
+
let forwardWarning;
|
|
47
|
+
if (opts.forwardTo) {
|
|
48
|
+
const verdict = validateForwardTo(opts.forwardTo, {
|
|
49
|
+
allowTunnel: opts.forwardToTunnel === true,
|
|
50
|
+
});
|
|
51
|
+
if (!verdict.ok) {
|
|
52
|
+
process.stderr.write(formatLifecycleHuman(verdict.reason, "error") + "\n");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
forwardUrl = verdict.url;
|
|
56
|
+
forwardWarning = verdict.warning;
|
|
57
|
+
}
|
|
58
|
+
// Build the SSE URL from the SDK's resolved base URL — honors
|
|
59
|
+
// VONPAY_BASE_URL / config.json baseUrl overrides used by the rest
|
|
60
|
+
// of the CLI commands.
|
|
61
|
+
const baseUrl = (getBaseUrl() ?? "https://checkout.vonpay.com").replace(/\/+$/, "");
|
|
62
|
+
const sseUrl = new URL(`${baseUrl}/v1/webhook_listen`);
|
|
63
|
+
if (opts.events && opts.events.length > 0) {
|
|
64
|
+
sseUrl.searchParams.set("event_types", opts.events.join(","));
|
|
65
|
+
}
|
|
66
|
+
// SDK client for the per-attempt fetchEvent calls (only used when --forward-to is set)
|
|
67
|
+
const sdk = forwardUrl
|
|
68
|
+
? new VonPayCheckout({ apiKey, baseUrl })
|
|
69
|
+
: null;
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
let eventCount = 0;
|
|
72
|
+
const startedAt = Date.now();
|
|
73
|
+
const mode = opts.json === true ? "json" : "human";
|
|
74
|
+
const onSignal = (sig) => {
|
|
75
|
+
const elapsedSec = Math.round((Date.now() - startedAt) / 1000);
|
|
76
|
+
if (mode === "human") {
|
|
77
|
+
process.stderr.write("\n" +
|
|
78
|
+
formatLifecycleHuman(`Shutting down (${sig}). Received ${eventCount} event${eventCount === 1 ? "" : "s"} in ${elapsedSec}s.`, "info") +
|
|
79
|
+
"\n");
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
process.stderr.write(formatLifecycleJson("shutdown", {
|
|
83
|
+
signal: sig,
|
|
84
|
+
event_count: eventCount,
|
|
85
|
+
elapsed_seconds: elapsedSec,
|
|
86
|
+
}) + "\n");
|
|
87
|
+
}
|
|
88
|
+
controller.abort();
|
|
89
|
+
};
|
|
90
|
+
process.once("SIGINT", () => onSignal("SIGINT"));
|
|
91
|
+
process.once("SIGTERM", () => onSignal("SIGTERM"));
|
|
92
|
+
const onLifecycle = (event) => {
|
|
93
|
+
if (mode === "json") {
|
|
94
|
+
process.stderr.write(formatLifecycleJson(event.kind, lifecyclePayload(event)) + "\n");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
process.stderr.write(formatLifecycleHuman(lifecycleHumanMessage(event), lifecycleSeverity(event.kind)) + "\n");
|
|
98
|
+
};
|
|
99
|
+
// Pre-stream, emitted directly rather than through onLifecycle —
|
|
100
|
+
// "warning" is not a LifecycleEvent kind (it's a one-shot forward-target
|
|
101
|
+
// caveat, not a connection-lifecycle transition), so it intentionally
|
|
102
|
+
// bypasses the onLifecycle/lifecyclePayload path.
|
|
103
|
+
if (forwardWarning) {
|
|
104
|
+
process.stderr.write(mode === "human"
|
|
105
|
+
? formatLifecycleHuman(forwardWarning, "warn") + "\n"
|
|
106
|
+
: formatLifecycleJson("warning", { message: forwardWarning }) + "\n");
|
|
107
|
+
}
|
|
108
|
+
if (mode === "human") {
|
|
109
|
+
const filterNote = opts.events?.length
|
|
110
|
+
? ` (filtered to ${opts.events.join(", ")})`
|
|
111
|
+
: "";
|
|
112
|
+
const fwdNote = forwardUrl ? ` → forwarding to ${forwardUrl.toString()}` : "";
|
|
113
|
+
process.stderr.write(formatLifecycleHuman(`Ready! Listening for webhook delivery attempts${filterNote}${fwdNote}.`, "info") + "\n");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
process.stderr.write(formatLifecycleJson("ready", {
|
|
117
|
+
base_url: baseUrl,
|
|
118
|
+
filter: opts.events ?? null,
|
|
119
|
+
forward_to: forwardUrl?.toString() ?? null,
|
|
120
|
+
}) + "\n");
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
for await (const sseEvent of consumeSse({
|
|
124
|
+
url: sseUrl.toString(),
|
|
125
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
126
|
+
signal: controller.signal,
|
|
127
|
+
onLifecycle,
|
|
128
|
+
})) {
|
|
129
|
+
if (sseEvent.type === "timeout") {
|
|
130
|
+
// Server-ceiling. The consumer reconnects transparently —
|
|
131
|
+
// surface as a lifecycle note, not a stream event.
|
|
132
|
+
onLifecycle({
|
|
133
|
+
kind: "disconnected",
|
|
134
|
+
reason: "server-ceiling (1h cap; auto-reconnecting)",
|
|
135
|
+
willRetryInMs: 0,
|
|
136
|
+
});
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (sseEvent.type !== "webhook.attempt") {
|
|
140
|
+
// Future-compatible: skip unknown event types so an additive
|
|
141
|
+
// server-side surface doesn't break older CLI versions.
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
let payload;
|
|
145
|
+
try {
|
|
146
|
+
payload = JSON.parse(sseEvent.data);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
continue; // malformed frame; skip
|
|
150
|
+
}
|
|
151
|
+
eventCount += 1;
|
|
152
|
+
let forwardOutcome = null;
|
|
153
|
+
if (forwardUrl && sdk) {
|
|
154
|
+
try {
|
|
155
|
+
const eventRecord = await sdk.webhookEvents.retrieve(payload.event_id);
|
|
156
|
+
forwardOutcome = await forwardPayload(eventRecord.payload, {
|
|
157
|
+
url: forwardUrl,
|
|
158
|
+
signingSecret: apiKey,
|
|
159
|
+
signal: controller.signal,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
164
|
+
forwardOutcome = {
|
|
165
|
+
attempted: true,
|
|
166
|
+
status: null,
|
|
167
|
+
latencyMs: null,
|
|
168
|
+
error: `fetch-event failed: ${message.slice(0, 200)}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (mode === "human") {
|
|
173
|
+
process.stdout.write(formatAttemptHuman(payload, forwardOutcome) + "\n");
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
process.stdout.write(formatAttemptJson(payload, forwardOutcome) + "\n");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
if (err instanceof SseAuthError) {
|
|
182
|
+
if (mode === "human") {
|
|
183
|
+
process.stderr.write(formatLifecycleHuman(`Authentication failed (HTTP ${err.status}). Run \`vonpay checkout login\` with a valid secret key.`, "error") + "\n");
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
process.stderr.write(formatLifecycleJson("auth_failed", { status: err.status }) + "\n");
|
|
187
|
+
}
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function lifecyclePayload(event) {
|
|
195
|
+
switch (event.kind) {
|
|
196
|
+
case "connecting":
|
|
197
|
+
return { last_event_id: event.lastEventId ?? null };
|
|
198
|
+
case "connected":
|
|
199
|
+
return {};
|
|
200
|
+
case "disconnected":
|
|
201
|
+
return { reason: event.reason, will_retry_in_ms: event.willRetryInMs };
|
|
202
|
+
case "auth_failed":
|
|
203
|
+
return { status: event.status };
|
|
204
|
+
case "fatal":
|
|
205
|
+
return { error: event.error.message };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function lifecycleHumanMessage(event) {
|
|
209
|
+
switch (event.kind) {
|
|
210
|
+
case "connecting":
|
|
211
|
+
return event.lastEventId
|
|
212
|
+
? `Reconnecting (resume from ${event.lastEventId})`
|
|
213
|
+
: "Connecting...";
|
|
214
|
+
case "connected":
|
|
215
|
+
return "Connected.";
|
|
216
|
+
case "disconnected":
|
|
217
|
+
return `Disconnected: ${event.reason}. Retrying in ${event.willRetryInMs}ms.`;
|
|
218
|
+
case "auth_failed":
|
|
219
|
+
return `Authentication failed (HTTP ${event.status}).`;
|
|
220
|
+
case "fatal":
|
|
221
|
+
return `Fatal: ${event.error.message}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function lifecycleSeverity(kind) {
|
|
225
|
+
switch (kind) {
|
|
226
|
+
case "connecting":
|
|
227
|
+
case "connected":
|
|
228
|
+
return "info";
|
|
229
|
+
case "disconnected":
|
|
230
|
+
return "warn";
|
|
231
|
+
case "auth_failed":
|
|
232
|
+
case "fatal":
|
|
233
|
+
return "error";
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=listen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listen.js","sourceRoot":"","sources":["../../src/commands/listen.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;
|
|
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,CA+ExC"}
|
package/dist/commands/trigger.js
CHANGED
|
@@ -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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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() {
|
|
@@ -51,18 +63,23 @@ export function triggerCommand() {
|
|
|
51
63
|
const apiKey = requireApiKey();
|
|
52
64
|
const payload = buildPayload(event, opts);
|
|
53
65
|
const body = JSON.stringify(payload);
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
// v1 webhook signature contract: ONE `x-vonpay-signature` header of
|
|
67
|
+
// shape `t=<unix-seconds>,v1=<hex>`, HMAC-SHA256 over `${t}.${body}`.
|
|
68
|
+
// There is no separate timestamp header. For this local trigger the
|
|
69
|
+
// signing secret is your API key (verify with
|
|
70
|
+
// `constructEvent(rawBody, signatureHeader, apiKey)`).
|
|
71
|
+
const t = Math.floor(Date.now() / 1000);
|
|
72
|
+
const v1 = createHmac("sha256", apiKey)
|
|
73
|
+
.update(`${t}.${body}`)
|
|
56
74
|
.digest("hex");
|
|
57
|
-
const
|
|
75
|
+
const signatureHeader = `t=${t},v1=${v1}`;
|
|
58
76
|
const spinner = ora(`Sending ${event} to ${opts.url}...`).start();
|
|
59
77
|
try {
|
|
60
78
|
const response = await fetch(opts.url, {
|
|
61
79
|
method: "POST",
|
|
62
80
|
headers: {
|
|
63
81
|
"Content-Type": "application/json",
|
|
64
|
-
"
|
|
65
|
-
"X-VonPay-Timestamp": timestamp,
|
|
82
|
+
"x-vonpay-signature": signatureHeader,
|
|
66
83
|
"User-Agent": "VonPay-Webhook/1.0 (CLI trigger)",
|
|
67
84
|
},
|
|
68
85
|
body,
|
|
@@ -77,7 +94,7 @@ export function triggerCommand() {
|
|
|
77
94
|
}
|
|
78
95
|
console.log(chalk.dim(` Session: ${payload.sessionId}`));
|
|
79
96
|
console.log(chalk.dim(` Amount: ${payload.amount} ${payload.currency}`));
|
|
80
|
-
console.log(chalk.dim(`\n Verify with: vonpay.webhooks.constructEvent(rawBody,
|
|
97
|
+
console.log(chalk.dim(`\n Verify with: vonpay.webhooks.constructEvent(rawBody, signatureHeader, apiKey)`));
|
|
81
98
|
}
|
|
82
99
|
catch (err) {
|
|
83
100
|
spinner.stop();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trigger.js","sourceRoot":"","sources":["../../src/commands/trigger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,gBAAgB,GAAG;
|
|
1
|
+
{"version":3,"file":"trigger.js","sourceRoot":"","sources":["../../src/commands/trigger.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,49 @@
|
|
|
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. Signs it with the v1 webhook contract — a single
|
|
12
|
+
* `x-vonpay-signature: t=<unix-seconds>,v1=<hex>` header whose
|
|
13
|
+
* HMAC-SHA256 is computed over `${t}.${body}` (the same shape the
|
|
14
|
+
* SDK's `webhooks.constructEvent` verifies). For this local dev loop
|
|
15
|
+
* the signing secret is your API key, so verify with
|
|
16
|
+
* `webhooks.constructEvent(rawBody, signatureHeader, yourApiKey)`.
|
|
17
|
+
* 3. POSTs to the local URL with that single header, matching the live
|
|
18
|
+
* delivery shape. The `t` is stamped HERE, at forward time — see the
|
|
19
|
+
* `forwardPayload` body for the fresh-timestamp rationale.
|
|
20
|
+
*
|
|
21
|
+
* Bounded by a per-forward timeout (default 5s) and never retries —
|
|
22
|
+
* shadow-forward is best-effort; if the local URL is down, the
|
|
23
|
+
* transcript line shows the failure and the dev moves on.
|
|
24
|
+
*
|
|
25
|
+
* This re-delivers from the SSE delivery transcript, which differs from
|
|
26
|
+
* Stripe CLI's `stripe listen --forward-to` pre-empt model.
|
|
27
|
+
*/
|
|
28
|
+
interface ForwardOptions {
|
|
29
|
+
/** Local URL to POST the payload to. */
|
|
30
|
+
url: URL;
|
|
31
|
+
/** Secret used to key the v1 HMAC. The listen stream exposes no
|
|
32
|
+
* per-endpoint signing secret, so for this local forward loop the API
|
|
33
|
+
* key (vp_sk_*) is used as a stand-in — verify the forwarded delivery
|
|
34
|
+
* with `constructEvent(rawBody, signatureHeader, sameKey)`. Production
|
|
35
|
+
* deliveries are keyed by the per-endpoint signing secret (whsec_…). */
|
|
36
|
+
signingSecret: string;
|
|
37
|
+
/** Per-forward timeout. Default 5_000. */
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
/** AbortSignal for graceful shutdown. */
|
|
40
|
+
signal: AbortSignal;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Sign + POST a JSON payload to the local URL. Returns an outcome
|
|
44
|
+
* shape that the output formatters render — never throws (errors
|
|
45
|
+
* surface via `outcome.error`).
|
|
46
|
+
*/
|
|
47
|
+
export declare function forwardPayload(payload: Record<string, unknown>, opts: ForwardOptions): Promise<ForwardOutcome>;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=listen-forward.d.ts.map
|