@obbyx/sdk 0.1.0 → 0.2.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 +14 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/seller/consumed-proofs.d.ts +6 -0
- package/dist/seller/consumed-proofs.d.ts.map +1 -1
- package/dist/seller/consumed-proofs.js +20 -0
- package/dist/seller/consumed-proofs.js.map +1 -1
- package/dist/seller/cors.d.ts +33 -0
- package/dist/seller/cors.d.ts.map +1 -0
- package/dist/seller/cors.js +77 -0
- package/dist/seller/cors.js.map +1 -0
- package/dist/seller/defaults.d.ts +7 -0
- package/dist/seller/defaults.d.ts.map +1 -0
- package/dist/seller/defaults.js +7 -0
- package/dist/seller/defaults.js.map +1 -0
- package/dist/seller/paywall.d.ts +14 -0
- package/dist/seller/paywall.d.ts.map +1 -1
- package/dist/seller/paywall.js +42 -7
- package/dist/seller/paywall.js.map +1 -1
- package/dist/seller/x402-server.d.ts +50 -8
- package/dist/seller/x402-server.d.ts.map +1 -1
- package/dist/seller/x402-server.js +86 -24
- package/dist/seller/x402-server.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +10 -1
- package/src/seller/consumed-proofs.ts +22 -0
- package/src/seller/cors.ts +106 -0
- package/src/seller/defaults.ts +6 -0
- package/src/seller/paywall.ts +57 -8
- package/src/seller/x402-server.ts +130 -31
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { AssetAmount, Network, PaymentRequirements, Price, SchemeNetworkServer, SchemePaymentRequiredContext, SchemeServerHooks } from '@x402/core/types';
|
|
2
2
|
import { type Logger } from '@obbyx/obyte/logger';
|
|
3
|
+
import { BATCH_SCHEME, EXACT_SCHEME } from '@obbyx/x402-obyte';
|
|
3
4
|
import type { ConsumedProofsStore } from './consumed-proofs.js';
|
|
4
5
|
/**
|
|
5
|
-
* Obyte
|
|
6
|
-
* `@x402/express`, `@x402/hono`, …):
|
|
6
|
+
* Obyte schemes for the OFFICIAL x402 resource server (`@x402/core/server`,
|
|
7
|
+
* `@x402/express`, `@x402/hono`, …): SchemeNetworkServer implementations for
|
|
8
|
+
* `exact` ({@link ObyteExactServer}) and `batch-settlement`
|
|
9
|
+
* ({@link ObyteBatchServer}), sharing {@link ObyteSchemeServer}.
|
|
7
10
|
*
|
|
8
11
|
* - Binding (I1/I2): `enrichPaymentRequiredResponse` adds a per-request HMAC
|
|
9
12
|
* binding + quote to every offer. On a paid request it recomputes the HMAC
|
|
@@ -17,7 +20,9 @@ import type { ConsumedProofsStore } from './consumed-proofs.js';
|
|
|
17
20
|
* Unlike `@obbyx/sdk`'s ObytePaywall, a repeated request gets a 402 instead
|
|
18
21
|
* of the stored response (the official middleware cannot replay responses).
|
|
19
22
|
*/
|
|
20
|
-
export interface
|
|
23
|
+
export interface ObyteSchemeServerOptions {
|
|
24
|
+
/** How long a request may hold a payment in `serving` before a retry takes over (default 300 s). */
|
|
25
|
+
readonly servingLeaseSeconds?: number;
|
|
21
26
|
readonly sellerId: string;
|
|
22
27
|
/** HMAC key for bindings (≥ 32 bytes). */
|
|
23
28
|
readonly secret: string | Uint8Array;
|
|
@@ -26,9 +31,34 @@ export interface ObyteExactServerOptions {
|
|
|
26
31
|
readonly quoteTtlSeconds?: number;
|
|
27
32
|
readonly now?: () => number;
|
|
28
33
|
}
|
|
29
|
-
|
|
34
|
+
/** @deprecated Use {@link ObyteSchemeServerOptions}. */
|
|
35
|
+
export type ObyteExactServerOptions = ObyteSchemeServerOptions;
|
|
36
|
+
/** Options of {@link ObyteBatchServer}. */
|
|
37
|
+
export interface ObyteBatchServerOptions extends ObyteSchemeServerOptions {
|
|
38
|
+
/** Minimum channel dispute window the seller accepts (seconds); must match the facilitator's. */
|
|
39
|
+
readonly minChannelTimeoutSeconds: number;
|
|
40
|
+
}
|
|
41
|
+
/** Per-scheme parts of an Obyte resource-server scheme. */
|
|
42
|
+
export interface ObyteSchemeSpec {
|
|
43
|
+
readonly scheme: typeof EXACT_SCHEME | typeof BATCH_SCHEME;
|
|
44
|
+
/** `extra` fields the scheme adds to every offer besides binding and quote. */
|
|
45
|
+
readonly staticExtra: Record<string, unknown>;
|
|
46
|
+
/** Parses the quote fields of the accepted offer. */
|
|
47
|
+
readonly acceptedExtra: (extra: unknown) => {
|
|
48
|
+
readonly binding: string;
|
|
49
|
+
readonly quoteId: string;
|
|
50
|
+
readonly validUntil: number;
|
|
51
|
+
};
|
|
52
|
+
/** Proof id and key kind of a payment payload (undefined when malformed). */
|
|
53
|
+
readonly proofOf: (payload: Record<string, unknown>) => {
|
|
54
|
+
kind: string;
|
|
55
|
+
id: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
}
|
|
58
|
+
export declare abstract class ObyteSchemeServer implements SchemeNetworkServer {
|
|
30
59
|
private readonly options;
|
|
31
|
-
readonly
|
|
60
|
+
private readonly spec;
|
|
61
|
+
readonly scheme: string;
|
|
32
62
|
readonly defaultAssetTransferMethod = "default";
|
|
33
63
|
readonly paymentFlows: {
|
|
34
64
|
default: {
|
|
@@ -40,11 +70,11 @@ export declare class ObyteExactServer implements SchemeNetworkServer {
|
|
|
40
70
|
readonly schemeHooks: SchemeServerHooks;
|
|
41
71
|
private readonly log;
|
|
42
72
|
private readonly now;
|
|
43
|
-
constructor(options:
|
|
73
|
+
protected constructor(options: ObyteSchemeServerOptions, spec: ObyteSchemeSpec);
|
|
44
74
|
/** `{asset, amount}` in atomic units, or "20000 bytes" / "0.00002 GBYTE". */
|
|
45
75
|
parsePrice(price: Price, network: Network): Promise<AssetAmount>;
|
|
46
76
|
getAssetDecimals(asset: string): number | undefined;
|
|
47
|
-
/** Nothing to add from /supported
|
|
77
|
+
/** Nothing to add from /supported; the per-request fields come from the enrich hook. */
|
|
48
78
|
enhancePaymentRequirements(requirements: PaymentRequirements): Promise<PaymentRequirements>;
|
|
49
79
|
/** Adds (or, for a valid paid request, re-confirms) the per-request binding and quote. */
|
|
50
80
|
enrichPaymentRequiredResponse: (ctx: SchemePaymentRequiredContext) => Promise<PaymentRequirements[]>;
|
|
@@ -52,6 +82,18 @@ export declare class ObyteExactServer implements SchemeNetworkServer {
|
|
|
52
82
|
private bindingFor;
|
|
53
83
|
/** Proof key of the payment in a hook context (undefined when malformed). */
|
|
54
84
|
private keyOf;
|
|
55
|
-
|
|
85
|
+
}
|
|
86
|
+
/** `exact`: the proof is the unit (key `<network>:unit:<hash>`, as ObytePaywall). */
|
|
87
|
+
export declare class ObyteExactServer extends ObyteSchemeServer {
|
|
88
|
+
constructor(options: ObyteSchemeServerOptions);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* `batch-settlement` (AA payment channels): the proof is the voucher (key
|
|
92
|
+
* `<network>:voucher:<sha256 of the canonical signed package>`, as
|
|
93
|
+
* ObytePaywall). Offers carry the channel base AA and the minimum dispute
|
|
94
|
+
* window. Needs a facilitator with batch-settlement and the seller's watcher.
|
|
95
|
+
*/
|
|
96
|
+
export declare class ObyteBatchServer extends ObyteSchemeServer {
|
|
97
|
+
constructor(options: ObyteBatchServerOptions);
|
|
56
98
|
}
|
|
57
99
|
//# sourceMappingURL=x402-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x402-server.d.ts","sourceRoot":"","sources":["../../src/seller/x402-server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,mBAAmB,EACnB,KAAK,EACL,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"x402-server.d.ts","sourceRoot":"","sources":["../../src/seller/x402-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
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
|
+
import { DEFAULT_QUOTE_TTL_SECONDS, DEFAULT_SERVING_LEASE_SECONDS } from './defaults.js';
|
|
5
6
|
import { bodyHashOf, newQuote, offerBinding } from './quote.js';
|
|
6
7
|
function requestOf(transportContext) {
|
|
7
8
|
const request = transportContext?.request;
|
|
@@ -16,9 +17,10 @@ function requestOf(transportContext) {
|
|
|
16
17
|
paymentHeader: typeof request?.['paymentHeader'] === 'string' ? request['paymentHeader'] : undefined,
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
|
-
export class
|
|
20
|
+
export class ObyteSchemeServer {
|
|
20
21
|
options;
|
|
21
|
-
|
|
22
|
+
spec;
|
|
23
|
+
scheme;
|
|
22
24
|
defaultAssetTransferMethod = 'default';
|
|
23
25
|
paymentFlows = {
|
|
24
26
|
default: { supported: ['upfront'], default: 'upfront' },
|
|
@@ -27,22 +29,24 @@ export class ObyteExactServer {
|
|
|
27
29
|
schemeHooks;
|
|
28
30
|
log;
|
|
29
31
|
now;
|
|
30
|
-
constructor(options) {
|
|
32
|
+
constructor(options, spec) {
|
|
31
33
|
this.options = options;
|
|
32
|
-
this.
|
|
34
|
+
this.spec = spec;
|
|
35
|
+
this.scheme = spec.scheme;
|
|
36
|
+
this.log = moduleLogger(options.logger, 'sdk.x402-server', { scheme: spec.scheme });
|
|
33
37
|
this.now = options.now ?? (() => Math.floor(Date.now() / 1000));
|
|
34
38
|
this.schemeHooks = {
|
|
35
39
|
onBeforeVerify: async (ctx) => {
|
|
36
|
-
const
|
|
40
|
+
const key = this.keyOf(ctx);
|
|
37
41
|
const binding = ctx.requirements.extra.binding;
|
|
38
|
-
if (
|
|
42
|
+
if (key === undefined || typeof binding !== 'string') {
|
|
39
43
|
return { abort: true, reason: 'invalid_payload' };
|
|
40
44
|
}
|
|
41
|
-
const { record } = this.options.proofs.begin(
|
|
45
|
+
const { record } = this.options.proofs.begin(key, binding);
|
|
42
46
|
if (!bindingsEqual(record.binding, binding))
|
|
43
47
|
return { abort: true, reason: 'invalid_payment_reused' };
|
|
44
48
|
if (record.state === 'served') {
|
|
45
|
-
this.log.warn({ proofId:
|
|
49
|
+
this.log.warn({ proofId: key }, 'payment already served a request; refused');
|
|
46
50
|
return { abort: true, reason: 'payment_already_used' };
|
|
47
51
|
}
|
|
48
52
|
return undefined;
|
|
@@ -55,8 +59,14 @@ export class ObyteExactServer {
|
|
|
55
59
|
const key = this.keyOf(ctx);
|
|
56
60
|
if (key === undefined)
|
|
57
61
|
return { abort: true, reason: 'invalid_payload' };
|
|
58
|
-
|
|
62
|
+
const proofs = this.options.proofs;
|
|
63
|
+
// A claim left behind by a crashed process is taken over after the lease.
|
|
64
|
+
proofs.reclaimStale(key, this.options.servingLeaseSeconds ?? DEFAULT_SERVING_LEASE_SECONDS);
|
|
65
|
+
if (proofs.transition(key, 'settling', 'serving') ||
|
|
66
|
+
// settled earlier, but the handler failed: serve it now (settle is idempotent)
|
|
67
|
+
proofs.transition(key, 'paid_not_served', 'serving')) {
|
|
59
68
|
return undefined;
|
|
69
|
+
}
|
|
60
70
|
const state = this.options.proofs.get(key)?.state;
|
|
61
71
|
this.log.warn({ state }, 'payment already in use by another request; refused');
|
|
62
72
|
return {
|
|
@@ -75,6 +85,20 @@ export class ObyteExactServer {
|
|
|
75
85
|
else
|
|
76
86
|
this.options.proofs.transition(key, 'serving', 'settling');
|
|
77
87
|
},
|
|
88
|
+
// The handler failed or threw after the payment was settled (upfront):
|
|
89
|
+
// the payment stays usable for the SAME operation instead of "already used".
|
|
90
|
+
onVerifiedPaymentCanceled: async (ctx) => {
|
|
91
|
+
const key = this.keyOf(ctx);
|
|
92
|
+
if (key === undefined)
|
|
93
|
+
return;
|
|
94
|
+
if (ctx.settledPhases.includes('before-handler')) {
|
|
95
|
+
this.options.proofs.transition(key, 'served', 'paid_not_served');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.options.proofs.transition(key, 'serving', 'settling');
|
|
99
|
+
}
|
|
100
|
+
this.log.warn({ reason: ctx.reason }, 'paid request not served; the same payment can retry');
|
|
101
|
+
},
|
|
78
102
|
onSettleFailure: async (ctx) => {
|
|
79
103
|
const key = this.keyOf(ctx);
|
|
80
104
|
if (key !== undefined)
|
|
@@ -100,7 +124,7 @@ export class ObyteExactServer {
|
|
|
100
124
|
getAssetDecimals(asset) {
|
|
101
125
|
return asset === 'base' ? 9 : undefined;
|
|
102
126
|
}
|
|
103
|
-
/** Nothing to add from /supported
|
|
127
|
+
/** Nothing to add from /supported; the per-request fields come from the enrich hook. */
|
|
104
128
|
async enhancePaymentRequirements(requirements) {
|
|
105
129
|
return requirements;
|
|
106
130
|
}
|
|
@@ -108,21 +132,22 @@ export class ObyteExactServer {
|
|
|
108
132
|
enrichPaymentRequiredResponse = async (ctx) => {
|
|
109
133
|
const request = requestOf(ctx.transportContext);
|
|
110
134
|
if (request === undefined)
|
|
111
|
-
throw new ObbyxError('no_request',
|
|
135
|
+
throw new ObbyxError('no_request', `Obyte ${this.scheme} needs the HTTP request context`);
|
|
112
136
|
const accepted = this.acceptedQuote(request);
|
|
113
137
|
return ctx.requirements.map((req) => {
|
|
114
|
-
if (req.scheme !==
|
|
138
|
+
if (req.scheme !== this.spec.scheme)
|
|
115
139
|
return req;
|
|
116
140
|
const quote = accepted !== undefined &&
|
|
117
141
|
bindingsEqual(this.bindingFor(req, request, accepted), accepted.binding)
|
|
118
142
|
? accepted
|
|
119
143
|
: undefined;
|
|
144
|
+
const extra = { ...req.extra, ...this.spec.staticExtra };
|
|
120
145
|
if (quote !== undefined)
|
|
121
|
-
return { ...req, extra: { ...
|
|
122
|
-
const fresh = newQuote(this.now(), this.options.quoteTtlSeconds ??
|
|
146
|
+
return { ...req, extra: { ...extra, ...quote } };
|
|
147
|
+
const fresh = newQuote(this.now(), this.options.quoteTtlSeconds ?? DEFAULT_QUOTE_TTL_SECONDS);
|
|
123
148
|
return {
|
|
124
149
|
...req,
|
|
125
|
-
extra: { ...
|
|
150
|
+
extra: { ...extra, binding: this.bindingFor(req, request, fresh), ...fresh },
|
|
126
151
|
};
|
|
127
152
|
});
|
|
128
153
|
};
|
|
@@ -130,23 +155,60 @@ export class ObyteExactServer {
|
|
|
130
155
|
if (request.paymentHeader === undefined)
|
|
131
156
|
return undefined;
|
|
132
157
|
try {
|
|
133
|
-
const
|
|
134
|
-
|
|
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 };
|
|
135
163
|
}
|
|
136
164
|
catch {
|
|
137
165
|
return undefined;
|
|
138
166
|
}
|
|
139
167
|
}
|
|
140
168
|
bindingFor(req, request, quote) {
|
|
141
|
-
return offerBinding(this.options,
|
|
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 });
|
|
142
170
|
}
|
|
143
171
|
/** Proof key of the payment in a hook context (undefined when malformed). */
|
|
144
172
|
keyOf(ctx) {
|
|
145
|
-
const
|
|
146
|
-
return
|
|
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
|
+
});
|
|
147
191
|
}
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
});
|
|
150
212
|
}
|
|
151
213
|
}
|
|
152
214
|
//# sourceMappingURL=x402-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x402-server.js","sourceRoot":"","sources":["../../src/seller/x402-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAU/D,OAAO,EACL,UAAU,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,
|
|
1
|
+
{"version":3,"file":"x402-server.js","sourceRoot":"","sources":["../../src/seller/x402-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAU/D,OAAO,EACL,eAAe,EACf,UAAU,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EAAc,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAuC5E,SAAS,SAAS,CAAC,gBAAyB;IAC1C,MAAM,OAAO,GAAI,gBAAsE,EAAE,OAAO,CAAC;IACjG,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC,SAAS,CACyC,CAAC;IAC7E,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAC1D,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE;QAC3B,GAAG,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;QAC9B,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE;QACzB,aAAa,EACX,OAAO,OAAO,EAAE,CAAC,eAAe,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS;KACxF,CAAC;AACJ,CAAC;AA0BD,MAAM,OAAgB,iBAAiB;IAYlB;IACA;IAZV,MAAM,CAAS;IACf,0BAA0B,GAAG,SAAS,CAAC;IACvC,YAAY,GAAG;QACtB,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,SAAS,CAAU,EAAE,OAAO,EAAE,SAAkB,EAAE;KAC1E,CAAC;IACO,kBAAkB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAC1D,WAAW,CAAoB;IACvB,GAAG,CAAS;IACZ,GAAG,CAAe;IAEnC,YACmB,OAAiC,EACjC,IAAqB;QADrB,YAAO,GAAP,OAAO,CAA0B;QACjC,SAAI,GAAJ,IAAI,CAAiB;QAEtC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,WAAW,GAAG;YACjB,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,MAAM,OAAO,GAAI,GAAG,CAAC,YAAY,CAAC,KAA+B,CAAC,OAAO,CAAC;gBAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;gBACpD,CAAC;gBACD,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAC3D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;oBACzC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;gBAC3D,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,2CAA2C,CAAC,CAAC;oBAC7E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;gBACzD,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,0EAA0E;YAC1E,qEAAqE;YACrE,uEAAuE;YACvE,qEAAqE;YACrE,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,IAAI,GAAG,KAAK,SAAS;oBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;gBACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;gBACnC,0EAA0E;gBAC1E,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,mBAAmB,IAAI,6BAA6B,CAAC,CAAC;gBAC5F,IACE,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC;oBAC7C,+EAA+E;oBAC/E,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,iBAAiB,EAAE,SAAS,CAAC,EACpD,CAAC;oBACD,OAAO,SAAS,CAAC;gBACnB,CAAC;gBACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;gBAClD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,oDAAoD,CAAC,CAAC;gBAC/E,OAAO;oBACL,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB;iBAC5E,CAAC;YACJ,CAAC;YACD,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,IAAI,GAAG,KAAK,SAAS;oBAAE,OAAO;gBAC9B,0EAA0E;gBAC1E,wDAAwD;gBACxD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO;oBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;;oBAC5E,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;YAClE,CAAC;YACD,uEAAuE;YACvE,6EAA6E;YAC7E,yBAAyB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,IAAI,GAAG,KAAK,SAAS;oBAAE,OAAO;gBAC9B,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;oBACjD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;gBACnE,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;gBAC7D,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,IAAI,CACX,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EACtB,qDAAqD,CACtD,CAAC;YACJ,CAAC;YACD,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,IAAI,GAAG,KAAK,SAAS;oBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;YACpF,CAAC;SACF,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,KAAK,CAAC,UAAU,CAAC,KAAY,EAAE,OAAgB;QAC7C,UAAU,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;gBACzB,MAAM,IAAI,UAAU,CAAC,eAAe,EAAE,iBAAiB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACxE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC9E,CAAC;QACD,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GACnB,gDAAgD,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7E,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9C,MAAM,IAAI,UAAU,CAClB,eAAe,EACf,sFAAsF,CACvF,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC3F,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IACtD,CAAC;IAED,gBAAgB,CAAC,KAAa;QAC5B,OAAO,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1C,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,0BAA0B,CAC9B,YAAiC;QAEjC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,0FAA0F;IAC1F,6BAA6B,GAAG,KAAK,EACnC,GAAiC,EACD,EAAE;QAClC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAChD,IAAI,OAAO,KAAK,SAAS;YACvB,MAAM,IAAI,UAAU,CAAC,YAAY,EAAE,SAAS,IAAI,CAAC,MAAM,iCAAiC,CAAC,CAAC;QAC5F,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YAClC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO,GAAG,CAAC;YAChD,MAAM,KAAK,GACT,QAAQ,KAAK,SAAS;gBACtB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;gBACtE,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,SAAS,CAAC;YAChB,MAAM,KAAK,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACzD,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC;YAC1E,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC,CAAC;YAC9F,OAAO;gBACL,GAAG,GAAG;gBACN,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE;aAC7E,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEM,aAAa,CAAC,OAAoB;QACxC,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC1D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,4BAA4B,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC;YAC9E,IAAI,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACtD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1F,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,GAAwB,EAAE,OAAoB,EAAE,KAAY;QAC7E,OAAO,YAAY,CACjB,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,IAAI,CAAC,MAAM,EAChB,KAAK,EACL,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAChF,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CACjF,CAAC;IACJ,CAAC;IAED,6EAA6E;IACrE,KAAK,CAAC,GAGb;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,KAAK,KAAK,SAAS;YACxB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;CACF;AAED,qFAAqF;AACrF,MAAM,OAAO,gBAAiB,SAAQ,iBAAiB;IACrD,YAAY,OAAiC;QAC3C,KAAK,CAAC,OAAO,EAAE;YACb,MAAM,EAAE,YAAY;YACpB,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE;gBACnB,MAAM,IAAI,GAAI,OAAyC,CAAC,IAAI,EAAE,IAAI,CAAC;gBACnE,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3E,CAAC;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,gBAAiB,SAAQ,iBAAiB;IACrD,YAAY,OAAgC;QAC1C,KAAK,CAAC,OAAO,EAAE;YACb,MAAM,EAAE,YAAY;YACpB,WAAW,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,CAAC,wBAAwB,EAAE;YACtF,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;YACvD,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE;gBACnB,MAAM,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;gBACrC,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;oBAC5C,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE;oBACpD,CAAC,CAAC,SAAS,CAAC;YAChB,CAAC;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obbyx/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@obbyx/obyte": "^0.1.0",
|
|
47
|
-
"@obbyx/x402-obyte": "^0.1
|
|
47
|
+
"@obbyx/x402-obyte": "^0.2.1",
|
|
48
48
|
"@x402/core": "^2.29.0",
|
|
49
49
|
"obyte": "0.2.1",
|
|
50
50
|
"zod": "^4.6.7"
|
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 {
|
|
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';
|
|
@@ -174,6 +174,28 @@ export class ConsumedProofsStore {
|
|
|
174
174
|
return Number(res.changes) === 1;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Records a SETTLED payment as paid (`settling → paid_not_served`), also when
|
|
179
|
+
* a concurrent request removed or moved its row meanwhile: the payment did
|
|
180
|
+
* settle, so it must stay servable once. Atomic; returns the stored state.
|
|
181
|
+
*/
|
|
182
|
+
markSettled(key: string, binding: string, paymentResponse: string): ProofState {
|
|
183
|
+
const ts = this.now();
|
|
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);
|
|
194
|
+
const record = this.get(key);
|
|
195
|
+
if (record === undefined) throw new Error(`consumed proof ${key} vanished`);
|
|
196
|
+
return record.state;
|
|
197
|
+
}
|
|
198
|
+
|
|
177
199
|
/**
|
|
178
200
|
* Recovers a proof stuck in `serving` (the process died while the handler
|
|
179
201
|
* ran): after `leaseSeconds` it goes back to `paid_not_served` (already
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Seller-side defaults shared by ObytePaywall and ObyteExactServer (seconds). */
|
|
2
|
+
export const DEFAULT_QUOTE_TTL_SECONDS = 300;
|
|
3
|
+
/** Freshness window of the payment unit's timestamp offered in `maxTimeoutSeconds`. */
|
|
4
|
+
export const DEFAULT_MAX_TIMEOUT_SECONDS = 300;
|
|
5
|
+
/** How long a request may hold a payment in `serving` before a retry takes it over. */
|
|
6
|
+
export const DEFAULT_SERVING_LEASE_SECONDS = 300;
|