@vonpay/checkout-node 0.15.1 → 1.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.
package/README.md CHANGED
@@ -95,7 +95,7 @@ Write endpoints (create / update / delete / rotate-signing-secret / send-test-ev
95
95
 
96
96
  - **Typed session / webhook / error objects** — full `CheckoutSession`, `SessionStatus`, `WebhookEvent`, `WebhookSubscription`, `WebhookEventRecord`, `VonPayError`, discriminated-union `ErrorCode`.
97
97
  - **Webhook verification** — `webhooks.constructEvent(rawBody, signatureHeader, signingSecret)` parses the `x-vonpay-signature: t=<unix>,v1=<hex>` header, verifies HMAC-SHA256 over `${t}.${rawBody}` keyed by your per-endpoint signing secret (`whsec_…`), and enforces the freshness window (≤5 min old / ≤30 sec future). Accepts multiple `v1=` entries for zero-downtime secret rotation.
98
- - **Signed return URL verification (v1 + v2)** `VonPayCheckout.verifyReturnSignature()` supports both legacy v1 signatures and v2 signatures that bind `successUrl`, `keyMode`, and `iat` freshness.
98
+ - **Return confirmation** `client.sessions.confirmReturn(params, sessionSecret)` verifies the signature **and** confirms server-side that the payment succeeded, returning `{ paid, signatureValid, status, reason }`. **Use this, and branch on `paid`.** ⚠️ `verifyReturnSignature()` is the low-level primitive: it proves the message is AUTHENTIC, and a **declined payment is signed just as validly**, so a `true` from it is not proof of payment. ⚠️ `reason === "still_pending"` means the charge is in flight (the ordinary 3-D Secure case) — show a neutral "confirming your payment", never a failure. ⚠️ `paid: true` still does not mean safe to fulfil: record which session IDs you have already fulfilled, and prefer fulfilling from the `charge.succeeded` webhook. ⚠️ **Not `session.succeeded`** — that event is emitted internally but is **not subscribable**: it is absent from the merchant subscription catalog, which accepts an unknown event key, stores nothing and returns success. An endpoint subscribed to it receives nothing, forever, with no error raised at any layer. `charge.succeeded` is the fulfilment event.
99
99
  - **Auto-retry** — exponential backoff on 429 / 5xx with `Retry-After` header support.
100
100
  - **Request ID tracing** — every response includes `X-Request-Id` for support tickets.
101
101
  - **Rate-limit info** — parsed from response headers into `VonPayError.rateLimit`.
package/dist/client.d.ts CHANGED
@@ -1,5 +1,8 @@
1
- import type { VonPayCheckoutConfig, CreateSessionParams, CheckoutSession, SessionStatus, DryRunResult, WebhookEvent, HealthStatus, RequestOptions, ReturnParams, PaymentIntent, CreatePaymentIntentParams, CapturePaymentIntentParams, Capabilities, Refund, CreateRefundParams, Token, CreateTokenParams, WebhookSubscription, ListWebhookSubscriptionsParams, WebhookSubscriptionsList, WebhookEventRecord } from "./types.js";
2
- /** Test-only: reset the one-time v1 replay warning. Underscore-prefixed; not public API. */
1
+ import type { VonPayCheckoutConfig, CreateSessionParams, CheckoutSession, SessionStatus, DryRunResult, WebhookEvent, HealthStatus, RequestOptions, ReturnOutcome, ReturnParams, PaymentIntent, CreatePaymentIntentParams, CapturePaymentIntentParams, Capabilities, Refund, CreateRefundParams, Token, CreateTokenParams, WebhookSubscription, ListWebhookSubscriptionsParams, WebhookSubscriptionsList, WebhookEventRecord } from "./types.js";
2
+ export declare const KNOWN_RETURN_SCHEMES: readonly ["v1", "v2"];
3
+ export type ReturnScheme = (typeof KNOWN_RETURN_SCHEMES)[number];
4
+ export declare const DEFAULT_RETURN_SCHEMES: readonly ReturnScheme[];
5
+ /** Test-only: reset the one-time return-signature advisories. Not public API. */
3
6
  export declare function __resetReturnSignatureWarning(): void;
4
7
  /** Test-only: reset the warn-once latch so each case can assert on it. */
5
8
  export declare function __resetDeprecationWarningsForTests(): void;
@@ -26,7 +29,7 @@ export declare class VonPayCheckout {
26
29
  * fail at the same instant (e.g. a dependency blip hitting a whole fleet). The
27
30
  * prior ±10% one-sided window (`base` … `1.1×base`) was narrow enough that a
28
31
  * fleet retried in near-lockstep and re-amplified the storm against a service
29
- * that was already struggling (kaiju #428). The mean delay is unchanged — the
32
+ * that was already struggling. The mean delay is unchanged — the
30
33
  * jitter is zero-centred — so backoff timing is preserved; only the spread
31
34
  * widens.
32
35
  */
@@ -79,6 +82,10 @@ export declare class VonPayCheckout {
79
82
  * iframe-vault providers require a `providerReference` minted
80
83
  * browser-side; the server returns 422 validation_error otherwise.
81
84
  * Sandbox keys auto-mint a mock card token if no card data is supplied.
85
+ *
86
+ * ⚠️ If you want the saved card to appear in the buyer's saved-card picker
87
+ * later, pass `allowRedisplay: "always"` HERE. That permission can only be
88
+ * recorded at save time and can never be added afterwards.
82
89
  */
83
90
  create: (params: CreateTokenParams, options?: RequestOptions) => Promise<Token>;
84
91
  };
@@ -88,6 +95,63 @@ export declare class VonPayCheckout {
88
95
  sessions: {
89
96
  create: (params: CreateSessionParams, options?: RequestOptions) => Promise<CheckoutSession>;
90
97
  /** Retrieve the current state of a checkout session. Requires a secret key (vp_sk_*). Publishable keys are rejected with 403. */
98
+ /**
99
+ * Confirm, in one call, that a returning buyer actually paid.
100
+ *
101
+ * **Use this instead of `verifyReturnSignature` unless you have a specific
102
+ * reason not to.** That function answers a narrower question than it
103
+ * appears to: it returns `true` for an *authentic* message, and a DECLINED
104
+ * payment is signed just as authentically as an approved one. Reading its
105
+ * boolean as "they paid" is the expensive mistake on this path.
106
+ *
107
+ * Reads the session status from the SERVER — not from the redirect URL. The
108
+ * URL is a hint carried by the buyer's browser; the server is the authority,
109
+ * it is fresher, and the read is already authenticated by your API key.
110
+ *
111
+ * ⚠️ **Do not pass a `secret` unless you actually hold the one the redirect
112
+ * was signed with.** Returns are signed with a platform-wide secret, so a
113
+ * per-merchant `ss_*` from the dashboard can never verify. In 1.0.0 a failed
114
+ * signature short-circuited to `paid: false` before the server was asked, so
115
+ * that combination reported every genuinely successful payment as unpaid.
116
+ * The signature is now optional hardening and cannot mask the truth;
117
+ * `signatureValid` is `null` when no check was performed.
118
+ *
119
+ * ⚠️ **This does not make fulfilment safe on its own, and cannot.**
120
+ * `paid: true` means this buyer paid; it does not mean you have not already
121
+ * shipped their order. The status keeps reading `succeeded` on every replay
122
+ * of the same URL, so record which session IDs you have fulfilled and
123
+ * refuse to fulfil one twice. That needs your database.
124
+ *
125
+ * ⚠️ **The redirect is not a guarantee of anything.** Buyers close laptops
126
+ * and never load your success page. Webhooks are the reliable fulfilment
127
+ * trigger; this is for what you show the buyer who did arrive.
128
+ * See https://docs.vonpay.com/integration/handle-return
129
+ *
130
+ * @throws on a failed session lookup — `VonPayError` for API errors, and the
131
+ * underlying transport error (e.g. a `TypeError` from `fetch`) for network
132
+ * failures, which is NOT wrapped. Catch broadly rather than narrowing to
133
+ * `VonPayError`, or a network blip will escape and 500 a buyer who just paid.
134
+ *
135
+ * A failed lookup is deliberately NOT reported as `paid: false` — that would
136
+ * turn our outage into the merchant's silent under-fulfilment, and the two
137
+ * need opposite handling.
138
+ *
139
+ * Requires a secret key (`vp_sk_*`): this reads `GET /v1/sessions/:id`,
140
+ * which rejects publishable keys with 403.
141
+ */
142
+ confirmReturn: (params: ReturnParams | Record<string, string>, secret?: string, options?: {
143
+ expectedSuccessUrl?: string;
144
+ expectedKeyMode?: "test" | "live";
145
+ maxAgeSeconds?: number;
146
+ acceptedSchemes?: readonly ReturnScheme[];
147
+ /**
148
+ * @deprecated Use `acceptedSchemes: ["v2"]`. Accepted here so a merchant
149
+ * who already hardened against v1 can move to this helper without
150
+ * dropping that protection — without it, the documented migration is
151
+ * blocked for exactly the most security-conscious integrators.
152
+ */
153
+ rejectV1?: boolean;
154
+ }) => Promise<ReturnOutcome>;
91
155
  get: (sessionId: string) => Promise<SessionStatus>;
92
156
  validate: (params: CreateSessionParams) => Promise<DryRunResult>;
93
157
  };
@@ -216,26 +280,37 @@ export declare class VonPayCheckout {
216
280
  * whether you have already fulfilled this order, and it keeps returning
217
281
  * `succeeded` on a replay. Record which session IDs you have fulfilled
218
282
  * (e.g. a UNIQUE column on the order row) and refuse to fulfil one twice.
219
- * Prefer v2 (pass `expectedSuccessUrl` + `expectedKeyMode`), which is
220
- * freshness- and URL-bound, or pass `{ rejectV1: true }` to refuse v1 outright
221
- * once your checkout server emits v2 returns. Note: `rejectV1` only refuses
222
- * v1 it does not by itself require a valid v2 signature, so still supply the
223
- * v2 options. A successful v1 verification logs a warning to this effect once
224
- * per process.
283
+ * `acceptedSchemes` is the allowlist THIS VERIFIER will honour. It exists
284
+ * because the scheme is otherwise chosen by the incoming signature i.e. by
285
+ * the sender and a verifier should declare what it accepts rather than let
286
+ * untrusted input select its own algorithm.
287
+ *
288
+ * ⚠️ `{ acceptedSchemes: ["v2"] }` refuses the v1 SCHEME. It does not by
289
+ * itself require a valid v2 signature, so keep supplying the v2 options. And
290
+ * confirm your account already issues v2 returns before setting it — that is
291
+ * a server-side setting, so if your account still issues v1 this refuses
292
+ * EVERY return you receive.
225
293
  *
226
294
  * @param params - URL search params from the redirect (session, status, amount, currency, transaction_id, sig)
227
295
  * @param secret - Your session signing secret, NOT your API key
228
- * @param options - expectedSuccessUrl (required for v2), expectedKeyMode (required for v2), maxAgeSeconds (v2 freshness, default 600), rejectV1 (refuse legacy v1 signatures)
296
+ * @param options - expectedSuccessUrl (required for v2), expectedKeyMode (required for v2), maxAgeSeconds (v2 freshness, default 600), acceptedSchemes (which signature schemes to honour)
297
+ * @throws TypeError if `acceptedSchemes` is empty or names an unknown scheme — a typo there would silently refuse every return.
229
298
  */
230
299
  static verifyReturnSignature(params: ReturnParams | Record<string, string>, secret: string, options?: {
231
300
  expectedSuccessUrl?: string;
232
301
  expectedKeyMode?: "test" | "live";
233
302
  maxAgeSeconds?: number;
234
303
  /**
235
- * When true, reject legacy v1 signatures outright (return `false`) and
236
- * only accept replay-safe v2 signatures. v1 binds no timestamp and no
237
- * success-URL/key-mode, so a captured v1 return URL replays indefinitely
238
- * (kaiju #425). Default `false` for backward compatibility.
304
+ * Signature schemes this verifier accepts. Defaults to `["v1", "v2"]`,
305
+ * preserving existing behaviour. Pass `["v2"]` to refuse the replayable
306
+ * legacy scheme. Mirrors Python's `accepted_schemes`.
307
+ */
308
+ acceptedSchemes?: readonly ReturnScheme[];
309
+ /**
310
+ * @deprecated Since 2026-08-17. Use `acceptedSchemes: ["v2"]` instead.
311
+ * Still honoured (it published in 0.12.0 on 2026-07-01, so upgrades must
312
+ * not break) and removed in the next major. An explicit `acceptedSchemes`
313
+ * takes precedence over this.
239
314
  */
240
315
  rejectV1?: boolean;
241
316
  }): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}