@obbyx/sdk 0.1.1 → 0.2.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.
@@ -1,7 +1,7 @@
1
1
  import { decodePaymentSignatureHeader } from '@x402/core/http';
2
- import { ObbyxError, fromDisplay, getNetwork, isAssetId, networkKey, parseAmount, } from '@obbyx/obyte';
2
+ import { CHANNEL_BASE_AA, ObbyxError, fromDisplay, getNetwork, isAssetId, networkKey, parseAmount, } from '@obbyx/obyte';
3
3
  import { moduleLogger } from '@obbyx/obyte/logger';
4
- import { EXACT_SCHEME, bindingsEqual, exactExtraSchema } from '@obbyx/x402-obyte';
4
+ import { BATCH_SCHEME, EXACT_SCHEME, batchExtraSchema, bindingsEqual, canonical, exactExtraSchema, sha256Hex, } from '@obbyx/x402-obyte';
5
5
  import { DEFAULT_QUOTE_TTL_SECONDS, DEFAULT_SERVING_LEASE_SECONDS } from './defaults.js';
6
6
  import { bodyHashOf, newQuote, offerBinding } from './quote.js';
7
7
  function requestOf(transportContext) {
@@ -17,9 +17,10 @@ function requestOf(transportContext) {
17
17
  paymentHeader: typeof request?.['paymentHeader'] === 'string' ? request['paymentHeader'] : undefined,
18
18
  };
19
19
  }
20
- export class ObyteExactServer {
20
+ export class ObyteSchemeServer {
21
21
  options;
22
- scheme = EXACT_SCHEME;
22
+ spec;
23
+ scheme;
23
24
  defaultAssetTransferMethod = 'default';
24
25
  paymentFlows = {
25
26
  default: { supported: ['upfront'], default: 'upfront' },
@@ -28,22 +29,24 @@ export class ObyteExactServer {
28
29
  schemeHooks;
29
30
  log;
30
31
  now;
31
- constructor(options) {
32
+ constructor(options, spec) {
32
33
  this.options = options;
33
- this.log = moduleLogger(options.logger, 'sdk.x402-server', { scheme: EXACT_SCHEME });
34
+ this.spec = spec;
35
+ this.scheme = spec.scheme;
36
+ this.log = moduleLogger(options.logger, 'sdk.x402-server', { scheme: spec.scheme });
34
37
  this.now = options.now ?? (() => Math.floor(Date.now() / 1000));
35
38
  this.schemeHooks = {
36
39
  onBeforeVerify: async (ctx) => {
37
- const unit = ctx.paymentPayload.payload.unit?.unit;
40
+ const key = this.keyOf(ctx);
38
41
  const binding = ctx.requirements.extra.binding;
39
- if (typeof unit !== 'string' || typeof binding !== 'string') {
42
+ if (key === undefined || typeof binding !== 'string') {
40
43
  return { abort: true, reason: 'invalid_payload' };
41
44
  }
42
- const { record } = this.options.proofs.begin(this.key(ctx.requirements.network, unit), binding);
45
+ const { record } = this.options.proofs.begin(key, binding);
43
46
  if (!bindingsEqual(record.binding, binding))
44
47
  return { abort: true, reason: 'invalid_payment_reused' };
45
48
  if (record.state === 'served') {
46
- this.log.warn({ proofId: unit }, 'payment already served a request; refused');
49
+ this.log.warn({ proofId: key }, 'payment already served a request; refused');
47
50
  return { abort: true, reason: 'payment_already_used' };
48
51
  }
49
52
  return undefined;
@@ -121,7 +124,7 @@ export class ObyteExactServer {
121
124
  getAssetDecimals(asset) {
122
125
  return asset === 'base' ? 9 : undefined;
123
126
  }
124
- /** Nothing to add from /supported for exact; the per-request fields come from the enrich hook. */
127
+ /** Nothing to add from /supported; the per-request fields come from the enrich hook. */
125
128
  async enhancePaymentRequirements(requirements) {
126
129
  return requirements;
127
130
  }
@@ -129,21 +132,22 @@ export class ObyteExactServer {
129
132
  enrichPaymentRequiredResponse = async (ctx) => {
130
133
  const request = requestOf(ctx.transportContext);
131
134
  if (request === undefined)
132
- throw new ObbyxError('no_request', 'Obyte exact needs the HTTP request context');
135
+ throw new ObbyxError('no_request', `Obyte ${this.scheme} needs the HTTP request context`);
133
136
  const accepted = this.acceptedQuote(request);
134
137
  return ctx.requirements.map((req) => {
135
- if (req.scheme !== EXACT_SCHEME)
138
+ if (req.scheme !== this.spec.scheme)
136
139
  return req;
137
140
  const quote = accepted !== undefined &&
138
141
  bindingsEqual(this.bindingFor(req, request, accepted), accepted.binding)
139
142
  ? accepted
140
143
  : undefined;
144
+ const extra = { ...req.extra, ...this.spec.staticExtra };
141
145
  if (quote !== undefined)
142
- return { ...req, extra: { ...req.extra, ...quote } };
146
+ return { ...req, extra: { ...extra, ...quote } };
143
147
  const fresh = newQuote(this.now(), this.options.quoteTtlSeconds ?? DEFAULT_QUOTE_TTL_SECONDS);
144
148
  return {
145
149
  ...req,
146
- extra: { ...req.extra, binding: this.bindingFor(req, request, fresh), ...fresh },
150
+ extra: { ...extra, binding: this.bindingFor(req, request, fresh), ...fresh },
147
151
  };
148
152
  });
149
153
  };
@@ -151,23 +155,60 @@ export class ObyteExactServer {
151
155
  if (request.paymentHeader === undefined)
152
156
  return undefined;
153
157
  try {
154
- const extra = exactExtraSchema.safeParse(decodePaymentSignatureHeader(request.paymentHeader).accepted?.extra);
155
- return extra.success ? extra.data : undefined;
158
+ const accepted = decodePaymentSignatureHeader(request.paymentHeader).accepted;
159
+ if (accepted?.scheme !== this.spec.scheme)
160
+ return undefined;
161
+ const extra = this.spec.acceptedExtra(accepted.extra);
162
+ return { quoteId: extra.quoteId, validUntil: extra.validUntil, binding: extra.binding };
156
163
  }
157
164
  catch {
158
165
  return undefined;
159
166
  }
160
167
  }
161
168
  bindingFor(req, request, quote) {
162
- return offerBinding(this.options, EXACT_SCHEME, quote, { method: request.method, url: request.url, bodyHash: bodyHashOf(request.body) }, { network: req.network, asset: req.asset, amount: req.amount, payTo: req.payTo });
169
+ return offerBinding(this.options, this.spec.scheme, quote, { method: request.method, url: request.url, bodyHash: bodyHashOf(request.body) }, { network: req.network, asset: req.asset, amount: req.amount, payTo: req.payTo });
163
170
  }
164
171
  /** Proof key of the payment in a hook context (undefined when malformed). */
165
172
  keyOf(ctx) {
166
- const unit = ctx.paymentPayload.payload.unit?.unit;
167
- return typeof unit === 'string' ? this.key(ctx.requirements.network, unit) : undefined;
173
+ const proof = this.spec.proofOf(ctx.paymentPayload.payload);
174
+ return proof === undefined
175
+ ? undefined
176
+ : networkKey(getNetwork(ctx.requirements.network), proof.kind, proof.id);
177
+ }
178
+ }
179
+ /** `exact`: the proof is the unit (key `<network>:unit:<hash>`, as ObytePaywall). */
180
+ export class ObyteExactServer extends ObyteSchemeServer {
181
+ constructor(options) {
182
+ super(options, {
183
+ scheme: EXACT_SCHEME,
184
+ staticExtra: {},
185
+ acceptedExtra: (extra) => exactExtraSchema.parse(extra),
186
+ proofOf: (payload) => {
187
+ const unit = payload.unit?.unit;
188
+ return typeof unit === 'string' ? { kind: 'unit', id: unit } : undefined;
189
+ },
190
+ });
168
191
  }
169
- key(network, unit) {
170
- return networkKey(getNetwork(network), 'unit', unit);
192
+ }
193
+ /**
194
+ * `batch-settlement` (AA payment channels): the proof is the voucher (key
195
+ * `<network>:voucher:<sha256 of the canonical signed package>`, as
196
+ * ObytePaywall). Offers carry the channel base AA and the minimum dispute
197
+ * window. Needs a facilitator with batch-settlement and the seller's watcher.
198
+ */
199
+ export class ObyteBatchServer extends ObyteSchemeServer {
200
+ constructor(options) {
201
+ super(options, {
202
+ scheme: BATCH_SCHEME,
203
+ staticExtra: { baseAa: CHANNEL_BASE_AA, minTimeout: options.minChannelTimeoutSeconds },
204
+ acceptedExtra: (extra) => batchExtraSchema.parse(extra),
205
+ proofOf: (payload) => {
206
+ const pkg = payload['signedPackage'];
207
+ return pkg !== null && typeof pkg === 'object'
208
+ ? { kind: 'voucher', id: sha256Hex(canonical(pkg)) }
209
+ : undefined;
210
+ },
211
+ });
171
212
  }
172
213
  }
173
214
  //# sourceMappingURL=x402-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"x402-server.js","sourceRoot":"","sources":["../../src/seller/x402-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"x402-server.js","sourceRoot":"","sources":["../../src/seller/x402-server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obbyx/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Accept and make x402 payments on Obyte: Express paywall, adapter for the official @x402 servers, paying fetch with spending budgets",
5
5
  "keywords": [
6
6
  "x402",
@@ -43,8 +43,8 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "@obbyx/obyte": "^0.1.0",
47
- "@obbyx/x402-obyte": "^0.2.0",
46
+ "@obbyx/obyte": "^0.1.1",
47
+ "@obbyx/x402-obyte": "^0.2.2",
48
48
  "@x402/core": "^2.29.0",
49
49
  "obyte": "0.2.1",
50
50
  "zod": "^4.6.7"
@@ -5,7 +5,13 @@ import {
5
5
  decodePaymentSignatureHeader,
6
6
  encodePaymentSignatureHeader,
7
7
  } from '@x402/core/http';
8
- import type { PaymentPayload, PaymentRequirements, SettleResponse } from '@x402/core/types';
8
+ import type {
9
+ PaymentPayload,
10
+ PaymentPayloadResult,
11
+ PaymentRequired,
12
+ PaymentRequirements,
13
+ SettleResponse,
14
+ } from '@x402/core/types';
9
15
  import { ObbyxError } from '@obbyx/obyte';
10
16
  import { type Logger, moduleLogger } from '@obbyx/obyte/logger';
11
17
  import type { BudgetReservation, SpendingBudget } from './budget.js';
@@ -84,59 +90,13 @@ export async function payingFetch(
84
90
  const required = response.headers.get('PAYMENT-REQUIRED');
85
91
  if (required === null)
86
92
  throw new ObbyxError('invalid_402', '402 without PAYMENT-REQUIRED header');
87
- const offer = decodePaymentRequiredHeader(required);
88
- // Offers in preference order (channel first). A channel that turns out to
89
- // be unusable before anything is signed (not funded, closing, exhausted,
90
- // busy) falls through to the next offer, normally `exact`.
91
- let accepted: PaymentRequirements | undefined;
92
- let result: Awaited<ReturnType<ExactObyteClient['createPaymentPayload']>> | undefined;
93
- let lastChannelError: ObbyxError | undefined;
94
- for (const candidate of chooseOffers(offer.accepts, options)) {
95
- const mechanism =
96
- candidate.scheme === BATCH_SCHEME ? options.mechanisms.batch : options.mechanisms.exact;
97
- if (mechanism === undefined) continue;
98
- log.info(
99
- {
100
- url,
101
- scheme: candidate.scheme,
102
- payTo: candidate.payTo,
103
- asset: candidate.asset,
104
- amount: candidate.amount,
105
- },
106
- 'paying for request',
107
- );
108
- // Reserve the price BEFORE signing: concurrent calls cannot overshoot the budget.
109
- reservation = options.budget?.reserve(candidate.asset, BigInt(candidate.amount));
110
- try {
111
- result = await mechanism.createPaymentPayload(2, candidate);
112
- accepted = candidate;
113
- break;
114
- } catch (err) {
115
- if (reservation) options.budget?.release(reservation); // nothing was signed or sent
116
- reservation = undefined;
117
- if (err instanceof ObbyxError && CHANNEL_UNUSABLE.has(err.code)) {
118
- lastChannelError = err;
119
- log.info(
120
- { reason: err.code, detail: err.message },
121
- 'channel unusable; trying the next offer',
122
- );
123
- continue;
124
- }
125
- throw err;
126
- }
127
- }
128
- if (accepted === undefined || result === undefined) {
129
- // Nothing else to pay with: report why the channel could not (e.g. payment_pending).
130
- throw lastChannelError ?? new ObbyxError('no_acceptable_offer', 'No offer could be paid');
131
- }
132
- const payload: PaymentPayload = {
133
- x402Version: 2,
134
- resource: offer.resource,
135
- accepted,
136
- payload: result.payload,
137
- ...(offer.extensions ? { extensions: withPaymentId(offer.extensions, init.paymentId) } : {}),
138
- };
139
- header = encodePaymentSignatureHeader(payload);
93
+ ({ header, reservation } = await signPayment(
94
+ decodePaymentRequiredHeader(required),
95
+ init.paymentId,
96
+ url,
97
+ options,
98
+ log,
99
+ ));
140
100
  response = await request(header);
141
101
  }
142
102
 
@@ -144,16 +104,7 @@ export async function payingFetch(
144
104
  for (;;) {
145
105
  const settle = readSettle(response);
146
106
  if (response.status !== 402 || settle?.errorReason !== 'settlement_pending') {
147
- const accepted = acceptedOf(header);
148
- if (accepted?.scheme === BATCH_SCHEME) options.mechanisms.batch?.onSettled(accepted, settle);
149
- if (reservation !== undefined && options.budget !== undefined) {
150
- // I7: spend on success; release ONLY on a definite refusal (402: the
151
- // payment was not used). Anything else (5xx, 409, no settlement info)
152
- // is an unknown outcome: the reservation stays counted.
153
- if (settle?.success === true || (response.ok && settle === undefined))
154
- options.budget.spend(reservation);
155
- else if (response.status === 402) options.budget.release(reservation);
156
- }
107
+ recordOutcome(response, settle, header, reservation, options);
157
108
  return { response, ...(settle ? { payment: settle } : {}), paymentHeader: header };
158
109
  }
159
110
  if (Date.now() > deadline) throw new PaymentPendingError(header, settle.transaction);
@@ -169,6 +120,87 @@ export async function payingFetch(
169
120
  }
170
121
  }
171
122
 
123
+ /**
124
+ * Signs a payment for the first usable offer, in preference order (channel
125
+ * first). A channel that turns out to be unusable before anything is signed
126
+ * (not funded, closing, exhausted, busy) falls through to the next offer,
127
+ * normally `exact`. The price is reserved in the budget BEFORE signing, so
128
+ * concurrent calls cannot overshoot it.
129
+ */
130
+ async function signPayment(
131
+ offer: PaymentRequired,
132
+ paymentId: string | undefined,
133
+ url: string,
134
+ options: PayingFetchOptions,
135
+ log: Logger,
136
+ ): Promise<{ header: string; reservation: BudgetReservation | undefined }> {
137
+ let lastChannelError: ObbyxError | undefined;
138
+ for (const candidate of chooseOffers(offer.accepts, options)) {
139
+ const mechanism =
140
+ candidate.scheme === BATCH_SCHEME ? options.mechanisms.batch : options.mechanisms.exact;
141
+ if (mechanism === undefined) continue;
142
+ log.info(
143
+ {
144
+ url,
145
+ scheme: candidate.scheme,
146
+ payTo: candidate.payTo,
147
+ asset: candidate.asset,
148
+ amount: candidate.amount,
149
+ },
150
+ 'paying for request',
151
+ );
152
+ const reservation = options.budget?.reserve(candidate.asset, BigInt(candidate.amount));
153
+ let result: PaymentPayloadResult;
154
+ try {
155
+ result = await mechanism.createPaymentPayload(2, candidate);
156
+ } catch (err) {
157
+ if (reservation) options.budget?.release(reservation); // nothing was signed or sent
158
+ if (err instanceof ObbyxError && CHANNEL_UNUSABLE.has(err.code)) {
159
+ lastChannelError = err;
160
+ log.info(
161
+ { reason: err.code, detail: err.message },
162
+ 'channel unusable; trying the next offer',
163
+ );
164
+ continue;
165
+ }
166
+ throw err;
167
+ }
168
+ const payload: PaymentPayload = {
169
+ x402Version: 2,
170
+ resource: offer.resource,
171
+ accepted: candidate,
172
+ payload: result.payload,
173
+ ...(offer.extensions ? { extensions: withPaymentId(offer.extensions, paymentId) } : {}),
174
+ };
175
+ return { header: encodePaymentSignatureHeader(payload), reservation };
176
+ }
177
+ // Nothing else to pay with: report why the channel could not (e.g. payment_pending).
178
+ throw lastChannelError ?? new ObbyxError('no_acceptable_offer', 'No offer could be paid');
179
+ }
180
+
181
+ /**
182
+ * Applies the final answer: the channel voucher is confirmed or released, and
183
+ * the budget reservation is spent on success and released ONLY on a definite
184
+ * refusal (402: the payment was not used). Anything else (5xx, 409, no
185
+ * settlement info) is an unknown outcome: the reservation stays counted (I7).
186
+ */
187
+ function recordOutcome(
188
+ response: Response,
189
+ settle: SettleResponse | undefined,
190
+ header: string,
191
+ reservation: BudgetReservation | undefined,
192
+ options: PayingFetchOptions,
193
+ ): void {
194
+ const accepted = acceptedOf(header);
195
+ if (accepted?.scheme === BATCH_SCHEME) options.mechanisms.batch?.onSettled(accepted, settle);
196
+ if (reservation === undefined || options.budget === undefined) return;
197
+ if (settle?.success === true || (response.ok && settle === undefined)) {
198
+ options.budget.spend(reservation);
199
+ } else if (response.status === 402) {
200
+ options.budget.release(reservation);
201
+ }
202
+ }
203
+
172
204
  /** An offer from an untrusted seller: integer amount in smallest units, strings elsewhere. */
173
205
  const offerShape = z
174
206
  .object({
package/src/index.ts CHANGED
@@ -32,4 +32,13 @@ export {
32
32
  type BudgetReservation,
33
33
  SpendingBudget,
34
34
  } from './client/budget.js';
35
- export { ObyteExactServer, type ObyteExactServerOptions } from './seller/x402-server.js';
35
+ export {
36
+ ObyteBatchServer,
37
+ type ObyteBatchServerOptions,
38
+ ObyteExactServer,
39
+ type ObyteExactServerOptions,
40
+ ObyteSchemeServer,
41
+ type ObyteSchemeSpec,
42
+ type ObyteSchemeServerOptions,
43
+ } from './seller/x402-server.js';
44
+ export type { PaywallCorsOptions } from './seller/cors.js';
@@ -181,22 +181,16 @@ export class ConsumedProofsStore {
181
181
  */
182
182
  markSettled(key: string, binding: string, paymentResponse: string): ProofState {
183
183
  const ts = this.now();
184
- this.db.exec('BEGIN IMMEDIATE');
185
- try {
186
- this.db
187
- .prepare(
188
- `INSERT INTO consumed_proofs (key, binding, state, payment_response, created_at, updated_at)
189
- VALUES (?, ?, 'paid_not_served', ?, ?, ?)
190
- ON CONFLICT(key) DO UPDATE SET state = 'paid_not_served', payment_response = excluded.payment_response,
191
- updated_at = excluded.updated_at
192
- WHERE consumed_proofs.state = 'settling'`,
193
- )
194
- .run(key, binding, paymentResponse, ts, ts);
195
- this.db.exec('COMMIT');
196
- } catch (err) {
197
- this.db.exec('ROLLBACK');
198
- throw err;
199
- }
184
+ // One UPSERT statement: atomic on its own.
185
+ this.db
186
+ .prepare(
187
+ `INSERT INTO consumed_proofs (key, binding, state, payment_response, created_at, updated_at)
188
+ VALUES (?, ?, 'paid_not_served', ?, ?, ?)
189
+ ON CONFLICT(key) DO UPDATE SET state = 'paid_not_served', payment_response = excluded.payment_response,
190
+ updated_at = excluded.updated_at
191
+ WHERE consumed_proofs.state = 'settling'`,
192
+ )
193
+ .run(key, binding, paymentResponse, ts, ts);
200
194
  const record = this.get(key);
201
195
  if (record === undefined) throw new Error(`consumed proof ${key} vanished`);
202
196
  return record.state;
@@ -0,0 +1,106 @@
1
+ import type { Request, RequestHandler, Response } from 'express';
2
+
3
+ /**
4
+ * CORS for browser clients of a paid route (x402 `transports-v2/http.md`,
5
+ * "Browser clients"). None of the x402 headers is CORS-safelisted: without
6
+ * these headers a cross-origin page cannot read the price (402) or send the
7
+ * payment. CORS decides who may READ a response; it is not a payment control.
8
+ */
9
+ export interface PaywallCorsOptions {
10
+ /** Allowed origins (exact `scheme://host[:port]`), or `'*'` for any. */
11
+ readonly origins: readonly string[] | '*';
12
+ /** Whether requests may carry credentials (cookies); origins are then reflected, never `*`. */
13
+ readonly credentials?: boolean;
14
+ /** Preflight cache lifetime (default 600 s). */
15
+ readonly maxAgeSeconds?: number;
16
+ }
17
+
18
+ /** Response headers a browser client must read: the price and the settlement. */
19
+ export const EXPOSED_HEADERS = ['PAYMENT-REQUIRED', 'PAYMENT-RESPONSE'] as const;
20
+ /** Request headers a browser client sends: the payment, and Content-Type for a JSON body. */
21
+ export const ALLOWED_HEADERS = ['PAYMENT-SIGNATURE', 'Content-Type'] as const;
22
+ const DEFAULT_MAX_AGE_SECONDS = 600;
23
+
24
+ /** Merges header names into a comma-separated list, keeping existing ones (case-insensitive). */
25
+ export function mergeHeaderList(
26
+ existing: string | number | readonly string[] | undefined,
27
+ add: readonly string[],
28
+ ): string {
29
+ const current =
30
+ existing === undefined
31
+ ? []
32
+ : (Array.isArray(existing) ? existing.join(',') : String(existing))
33
+ .split(',')
34
+ .map((h) => h.trim())
35
+ .filter((h) => h.length > 0);
36
+ const seen = new Set(current.map((h) => h.toLowerCase()));
37
+ for (const h of add) {
38
+ if (!seen.has(h.toLowerCase())) {
39
+ current.push(h);
40
+ seen.add(h.toLowerCase());
41
+ }
42
+ }
43
+ return current.join(', ');
44
+ }
45
+
46
+ /** Always: lets a cross-origin page read the x402 headers (merged with any value already set). */
47
+ export function exposePaymentHeaders(res: Response): void {
48
+ res.setHeader(
49
+ 'Access-Control-Expose-Headers',
50
+ mergeHeaderList(res.getHeader('Access-Control-Expose-Headers'), EXPOSED_HEADERS),
51
+ );
52
+ }
53
+
54
+ /** Sets `Access-Control-Allow-Origin` for an allowed calling origin; false when not allowed. */
55
+ export function allowOrigin(req: Request, res: Response, cors: PaywallCorsOptions): boolean {
56
+ const origin = req.get('origin');
57
+ if (origin === undefined) return false;
58
+ const anyOrigin = cors.origins === '*';
59
+ if (!anyOrigin && !cors.origins.includes(origin)) return false;
60
+ if (anyOrigin && cors.credentials !== true) {
61
+ res.setHeader('Access-Control-Allow-Origin', '*');
62
+ } else {
63
+ // A reflected origin varies per request: shared caches must key on it.
64
+ res.setHeader('Access-Control-Allow-Origin', origin);
65
+ res.setHeader('Vary', mergeHeaderList(res.getHeader('Vary'), ['Origin']));
66
+ }
67
+ if (cors.credentials === true) res.setHeader('Access-Control-Allow-Credentials', 'true');
68
+ return true;
69
+ }
70
+
71
+ /**
72
+ * Answers the CORS preflight (`OPTIONS`) of paid routes without requiring
73
+ * payment. Allows the x402 request headers plus whatever else the browser
74
+ * announces: x402 client SDKs have added request headers of their own, and an
75
+ * allow list naming exactly the protocol headers would refuse their retry.
76
+ */
77
+ export function preflightHandler(cors: PaywallCorsOptions): RequestHandler {
78
+ return (req, res, next) => {
79
+ if (req.method !== 'OPTIONS' || req.get('access-control-request-method') === undefined) {
80
+ next();
81
+ return;
82
+ }
83
+ if (!allowOrigin(req, res, cors)) {
84
+ res.status(204).end(); // no CORS headers: the browser refuses the call
85
+ return;
86
+ }
87
+ const requested = (req.get('access-control-request-headers') ?? '')
88
+ .split(',')
89
+ .map((h) => h.trim())
90
+ .filter((h) => h.length > 0);
91
+ res.setHeader(
92
+ 'Access-Control-Allow-Headers',
93
+ mergeHeaderList(undefined, [...ALLOWED_HEADERS, ...requested]),
94
+ );
95
+ res.setHeader(
96
+ 'Access-Control-Allow-Methods',
97
+ mergeHeaderList(undefined, [
98
+ req.get('access-control-request-method') ?? 'POST',
99
+ 'GET',
100
+ 'POST',
101
+ ]),
102
+ );
103
+ res.setHeader('Access-Control-Max-Age', String(cors.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS));
104
+ res.status(204).end();
105
+ };
106
+ }