@envarai/envarpay 0.2.0-alpha.19 → 0.2.0-alpha.20
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 +18 -63
- package/dist/commerce/alerts.d.ts +5 -15
- package/dist/commerce/alerts.js +21 -77
- package/dist/commerce/card.js +1 -1
- package/dist/commerce/cli.js +17 -145
- package/dist/commerce/config.d.ts +1 -4
- package/dist/commerce/config.js +4 -82
- package/dist/commerce/configure.d.ts +0 -1
- package/dist/commerce/configure.js +0 -80
- package/dist/commerce/discovery.d.ts +1 -5
- package/dist/commerce/discovery.js +1 -4
- package/dist/commerce/envar-balance.js +1 -1
- package/dist/commerce/envar.d.ts +1 -32
- package/dist/commerce/envar.js +3 -82
- package/dist/commerce/operations.d.ts +1 -2
- package/dist/commerce/operations.js +17 -20
- package/dist/commerce/pricing.d.ts +0 -1
- package/dist/commerce/pricing.js +1 -3
- package/dist/commerce/runtime.d.ts +0 -2
- package/dist/commerce/runtime.js +0 -2
- package/dist/commerce/server.d.ts +0 -1
- package/dist/commerce/server.js +2 -3
- package/dist/commerce/setup.js +4 -3
- package/dist/commerce/types.d.ts +0 -59
- package/examples/envar-integration-policy.json +1 -9
- package/examples/seller.json +143 -26
- package/package.json +3 -16
- package/schemas/envarpay.config.schema.json +1 -80
- package/dist/commerce/buyer-store.d.ts +0 -101
- package/dist/commerce/buyer-store.js +0 -168
- package/dist/commerce/buyer.d.ts +0 -138
- package/dist/commerce/buyer.js +0 -775
- package/dist/commerce/chain.d.ts +0 -24
- package/dist/commerce/chain.js +0 -196
- package/dist/commerce/client-runtime.d.ts +0 -8
- package/dist/commerce/client-runtime.js +0 -7
- package/dist/commerce/management.d.ts +0 -14
- package/dist/commerce/management.js +0 -140
- package/dist/commerce/mpp-client.d.ts +0 -70
- package/dist/commerce/mpp-client.js +0 -29
- package/dist/commerce/mpp-runtime.d.ts +0 -4
- package/dist/commerce/mpp-runtime.js +0 -4
- package/dist/commerce/mpp.d.ts +0 -53
- package/dist/commerce/mpp.js +0 -163
- package/dist/commerce/peer-proxy.d.ts +0 -44
- package/dist/commerce/peer-proxy.js +0 -673
- package/dist/commerce/stripe-issuer.d.ts +0 -41
- package/dist/commerce/stripe-issuer.js +0 -351
- package/dist/commerce/stripe-provider.d.ts +0 -11
- package/dist/commerce/stripe-provider.js +0 -132
- package/dist/commerce/x402.d.ts +0 -38
- package/dist/commerce/x402.js +0 -181
- package/examples/buyer-policy.json +0 -18
- package/schemas/buyer-policy.schema.json +0 -253
package/dist/commerce/x402.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { findDefaultAsset } from '@x402/evm';
|
|
2
|
-
import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
|
|
3
|
-
import { decodePaymentSignatureHeader, encodePaymentRequiredHeader, encodePaymentResponseHeader } from '@x402/core/http';
|
|
4
|
-
import { ExactEvmScheme } from '@x402/evm/exact/server';
|
|
5
|
-
import { PAYMENT_IDENTIFIER, declarePaymentIdentifierExtension, extractPaymentIdentifier, validatePaymentIdentifier, paymentIdentifierResourceServerExtension, } from '@x402/extensions/payment-identifier';
|
|
6
|
-
import { CommerceError } from './types.js';
|
|
7
|
-
import { digest } from './config.js';
|
|
8
|
-
/** Official x402 v2 exact settlement, with independent chain evidence before work. */
|
|
9
|
-
export class X402Gate {
|
|
10
|
-
options;
|
|
11
|
-
servers = new Map();
|
|
12
|
-
constructor(options) {
|
|
13
|
-
this.options = options;
|
|
14
|
-
}
|
|
15
|
-
async check(profiles) {
|
|
16
|
-
for (const profile of profiles) {
|
|
17
|
-
const { facilitator } = await this.resource(profile);
|
|
18
|
-
const supported = await facilitator.getSupported();
|
|
19
|
-
if (!supported.kinds.some(k => k.x402Version === 2 && k.scheme === 'exact' && k.network === profile.network))
|
|
20
|
-
throw new CommerceError('facilitator_network', 'Configured facilitator does not support exact payments on this network');
|
|
21
|
-
if (this.options.checkpoint)
|
|
22
|
-
await this.options.checkpoint({ scheme: 'exact', network: profile.network, asset: profile.asset, payTo: profile.payTo, amount: '1', maxTimeoutSeconds: 300, extra: {} });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async recover(orderId, caller) {
|
|
26
|
-
const order = this.options.store.getOrder(orderId, caller);
|
|
27
|
-
if (!order)
|
|
28
|
-
throw new CommerceError('order_not_found', 'Order not found for this buyer');
|
|
29
|
-
const attempt = this.options.store.paymentForOrder(order.id);
|
|
30
|
-
if (!attempt)
|
|
31
|
-
throw new CommerceError('attempt_not_found', 'No original payment to recover');
|
|
32
|
-
if (attempt.state === 'confirmed')
|
|
33
|
-
return { state: 'confirmed', taskId: order.taskId };
|
|
34
|
-
if (attempt.state === 'rejected')
|
|
35
|
-
return { state: 'rejected', taskId: order.taskId };
|
|
36
|
-
const saved = this.options.vault.get(attempt.credentialRef);
|
|
37
|
-
const required = this.options.store.paymentChallenge(order.id);
|
|
38
|
-
if (saved.quoteId !== order.quote.quoteId || !required?.accepts.some(r => digest(r) === digest(saved.requirements))) {
|
|
39
|
-
throw new CommerceError('recovery_conflict', 'Stored authorization differs from the frozen challenge');
|
|
40
|
-
}
|
|
41
|
-
let receipt = attempt.receipt;
|
|
42
|
-
if (!receipt?.transaction && saved.fromBlock && this.options.findOriginalReceipt) {
|
|
43
|
-
receipt = await this.options.findOriginalReceipt(saved.payload, saved.requirements, saved.fromBlock) ?? null;
|
|
44
|
-
}
|
|
45
|
-
if (!receipt?.transaction) {
|
|
46
|
-
if (await this.options.proveExpiredUnused?.(saved.payload, saved.requirements)) {
|
|
47
|
-
this.options.store.recordSettlement(attempt.id, 'rejected', { errorReason: 'authorization_expired_unused' });
|
|
48
|
-
return { state: 'rejected', taskId: order.taskId };
|
|
49
|
-
}
|
|
50
|
-
return { state: 'unknown', taskId: order.taskId };
|
|
51
|
-
}
|
|
52
|
-
let proven = false;
|
|
53
|
-
try {
|
|
54
|
-
proven = await this.options.verifyReceipt(receipt, saved.payload, saved.requirements);
|
|
55
|
-
}
|
|
56
|
-
catch { }
|
|
57
|
-
// Persist the discovered transaction even if confirmations are still pending.
|
|
58
|
-
this.options.store.recordSettlement(attempt.id, proven ? 'confirmed' : 'unknown', receipt);
|
|
59
|
-
return { state: proven ? 'confirmed' : 'unknown', taskId: order.taskId };
|
|
60
|
-
}
|
|
61
|
-
async resource(profile) {
|
|
62
|
-
const key = digest(profile);
|
|
63
|
-
let promise = this.servers.get(key);
|
|
64
|
-
if (!promise) {
|
|
65
|
-
promise = (async () => {
|
|
66
|
-
const facilitator = this.options.facilitator?.(profile) ?? new HTTPFacilitatorClient({ url: profile.facilitatorUrl });
|
|
67
|
-
const server = new x402ResourceServer(facilitator)
|
|
68
|
-
.register(profile.network, new ExactEvmScheme())
|
|
69
|
-
.registerExtension(paymentIdentifierResourceServerExtension);
|
|
70
|
-
await server.initialize();
|
|
71
|
-
return { server, facilitator };
|
|
72
|
-
})();
|
|
73
|
-
this.servers.set(key, promise);
|
|
74
|
-
void promise.catch(() => this.servers.delete(key));
|
|
75
|
-
}
|
|
76
|
-
return promise;
|
|
77
|
-
}
|
|
78
|
-
async handle(request, order) {
|
|
79
|
-
const profile = order.quote.paymentProfile;
|
|
80
|
-
if (profile?.adapter !== 'x402')
|
|
81
|
-
throw new CommerceError('payment_method_unavailable', 'This offer requires another installed payment adapter');
|
|
82
|
-
const previous = this.options.store.paymentForOrder(order.id);
|
|
83
|
-
if (order.paymentState === 'confirmed' && previous?.receipt) {
|
|
84
|
-
return { headers: { 'PAYMENT-RESPONSE': encodePaymentResponseHeader(previous.receipt) } };
|
|
85
|
-
}
|
|
86
|
-
if (['settling', 'unknown'].includes(order.paymentState)) {
|
|
87
|
-
const recovered = await this.recover(order.id, order.caller);
|
|
88
|
-
if (recovered.state === 'confirmed') {
|
|
89
|
-
const receipt = this.options.store.paymentForOrder(order.id).receipt;
|
|
90
|
-
return { headers: { 'PAYMENT-RESPONSE': encodePaymentResponseHeader(receipt) } };
|
|
91
|
-
}
|
|
92
|
-
if (recovered.state === 'rejected')
|
|
93
|
-
return { response: Response.json({ error: 'authorization_expired_unused', taskId: order.taskId }, { status: 409, headers: { 'Cache-Control': 'no-store' } }) };
|
|
94
|
-
return { response: Response.json({ error: 'payment_reconciliation_required', taskId: order.taskId }, { status: 503, headers: { 'Cache-Control': 'no-store' } }) };
|
|
95
|
-
}
|
|
96
|
-
if (Date.now() >= Date.parse(order.quote.expiresAt))
|
|
97
|
-
return { response: Response.json({ error: 'quote_expired' }, { status: 409 }) };
|
|
98
|
-
const { server, facilitator } = await this.resource(profile);
|
|
99
|
-
let required = this.options.store.paymentChallenge(order.id);
|
|
100
|
-
if (!required) {
|
|
101
|
-
const token = findDefaultAsset(profile.asset, profile.network);
|
|
102
|
-
if (!token)
|
|
103
|
-
throw new CommerceError('unsupported_asset', 'Official token signing domain is unavailable');
|
|
104
|
-
const requirements = await server.buildPaymentRequirements({
|
|
105
|
-
scheme: 'exact', network: profile.network, payTo: profile.payTo,
|
|
106
|
-
price: { asset: profile.asset, amount: order.quote.amount, extra: { name: token.name, version: token.version } },
|
|
107
|
-
maxTimeoutSeconds: 300, extra: { paymentFlow: 'upfront', assetTransferMethod: 'eip3009' },
|
|
108
|
-
});
|
|
109
|
-
const resource = new URL(request.url);
|
|
110
|
-
resource.search = '';
|
|
111
|
-
resource.searchParams.set('quote', order.quote.quoteId);
|
|
112
|
-
required = this.options.store.freezePaymentChallenge(order.id, await server.createPaymentRequiredResponse(requirements, { url: resource.href, description: order.quote.serviceId, mimeType: 'application/json' }, undefined, { [PAYMENT_IDENTIFIER]: declarePaymentIdentifierExtension(false), 'urn:envarpay:quote:1': { info: { quoteId: order.quote.quoteId, messageId: order.messageId, serviceId: order.quote.serviceId, serviceRevision: order.quote.serviceRevision, offerId: order.offerId, inputDigest: order.inputDigest, termsDigest: order.quote.termsDigest, expiresAt: order.quote.expiresAt, amount: order.quote.amount, currency: order.quote.currency, recipient: order.quote.recipient } } }));
|
|
113
|
-
}
|
|
114
|
-
// Identifier is optional for unextended standard clients; nonce dedup is mandatory.
|
|
115
|
-
const challenge = () => ({ response: new Response('{}', {
|
|
116
|
-
status: 402, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'PAYMENT-REQUIRED': encodePaymentRequiredHeader(required) },
|
|
117
|
-
}) });
|
|
118
|
-
const header = request.headers.get('PAYMENT-SIGNATURE');
|
|
119
|
-
if (!header)
|
|
120
|
-
return challenge();
|
|
121
|
-
if (header.length > 32768)
|
|
122
|
-
throw new CommerceError('payment_payload_size', 'Payment credential exceeds limit');
|
|
123
|
-
let payload;
|
|
124
|
-
try {
|
|
125
|
-
payload = decodePaymentSignatureHeader(header);
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
throw new CommerceError('invalid_payment', 'Malformed payment credential');
|
|
129
|
-
}
|
|
130
|
-
const matched = server.findMatchingRequirements(required.accepts, payload);
|
|
131
|
-
if (!matched || payload.x402Version !== 2 || payload.resource?.url !== required.resource.url || digest(payload.accepted) !== digest(matched)) {
|
|
132
|
-
throw new CommerceError('quote_mismatch', 'Payment does not match the frozen quote');
|
|
133
|
-
}
|
|
134
|
-
const extension = payload.extensions?.[PAYMENT_IDENTIFIER];
|
|
135
|
-
// Validate against our schema; never compile a schema supplied by the buyer.
|
|
136
|
-
if (extension !== undefined && !validatePaymentIdentifier({ info: extension?.info, schema: declarePaymentIdentifierExtension(false).schema }).valid) {
|
|
137
|
-
throw new CommerceError('invalid_payment_identifier', 'Invalid standard payment identifier');
|
|
138
|
-
}
|
|
139
|
-
const identifier = extractPaymentIdentifier(payload, false);
|
|
140
|
-
const native = payload.payload;
|
|
141
|
-
const auth = native.authorization;
|
|
142
|
-
const payer = this.options.payerFor(order.caller);
|
|
143
|
-
if (!payer || !auth?.from || auth.from.toLowerCase() !== payer.toLowerCase() || auth.to?.toLowerCase() !== profile.payTo.toLowerCase() || auth.value !== order.quote.amount || !/^0x[0-9a-fA-F]{64}$/.test(auth.nonce ?? '') || !native.signature) {
|
|
144
|
-
throw new CommerceError('payment_identity', 'Signed payer, recipient, amount and nonce must match authorized buyer and quote');
|
|
145
|
-
}
|
|
146
|
-
// Quote UI metadata is locally checked above. It is not a facilitator
|
|
147
|
-
// settlement extension; do not require third-party facilitators to implement it.
|
|
148
|
-
const settlementPayload = { ...payload, extensions: Object.fromEntries(Object.entries(payload.extensions ?? {}).filter(([key]) => key !== 'urn:envarpay:quote:1')) };
|
|
149
|
-
// Upfront SDK verification may skip /verify; explicitly perform it before reserve.
|
|
150
|
-
const verified = await facilitator.verify(settlementPayload, matched);
|
|
151
|
-
if (!verified.isValid)
|
|
152
|
-
return challenge();
|
|
153
|
-
if (verified.payer && verified.payer.toLowerCase() !== payer.toLowerCase())
|
|
154
|
-
throw new CommerceError('payment_identity', 'Verified payer differs from authorized buyer');
|
|
155
|
-
const economicKey = digest({ network: profile.network, asset: profile.asset.toLowerCase(), payer: payer.toLowerCase(), nonce: auth.nonce.toLowerCase() });
|
|
156
|
-
const fromBlock = await this.options.checkpoint?.(matched);
|
|
157
|
-
const saved = { payload, requirements: matched, quoteId: order.quote.quoteId, ...(fromBlock !== undefined ? { fromBlock } : {}) };
|
|
158
|
-
const credentialRef = this.options.vault.put(saved);
|
|
159
|
-
const attempt = this.options.store.reservePayment(order.id, order.caller, economicKey, credentialRef, identifier ?? undefined);
|
|
160
|
-
let result;
|
|
161
|
-
try {
|
|
162
|
-
result = await server.settlePayment(settlementPayload, matched, undefined, undefined, undefined, 'before-handler');
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
this.options.store.recordSettlement(attempt, 'unknown', {});
|
|
166
|
-
return { response: Response.json({ error: 'payment_outcome_unknown' }, { status: 503 }) };
|
|
167
|
-
}
|
|
168
|
-
// A failure can follow broadcast. Never create a second economic authorization.
|
|
169
|
-
let proven = false;
|
|
170
|
-
if (result.success) {
|
|
171
|
-
try {
|
|
172
|
-
proven = await this.options.verifyReceipt(result, payload, matched);
|
|
173
|
-
}
|
|
174
|
-
catch { }
|
|
175
|
-
}
|
|
176
|
-
this.options.store.recordSettlement(attempt, proven ? 'confirmed' : 'unknown', result);
|
|
177
|
-
if (!proven)
|
|
178
|
-
return { response: Response.json({ error: result.success ? 'receipt_verification_pending' : 'payment_outcome_unknown' }, { status: 503 }) };
|
|
179
|
-
return { headers: { 'PAYMENT-RESPONSE': encodePaymentResponseHeader(result) } };
|
|
180
|
-
}
|
|
181
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"policyVersion":1,
|
|
3
|
-
"paymentsEnabled":false,
|
|
4
|
-
"approval":"per_purchase",
|
|
5
|
-
"peers":[{
|
|
6
|
-
"id":"research-seller",
|
|
7
|
-
"cardUrl":"https://seller.example/services/research/v1/offers/usdc-once/agent-card.json",
|
|
8
|
-
"protocol":"x402",
|
|
9
|
-
"currency":"eip155:84532/erc20:0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
10
|
-
"recipient":"0x2222222222222222222222222222222222222222",
|
|
11
|
-
"maxPerPurchase":"3000000"
|
|
12
|
-
}],
|
|
13
|
-
"budgets":[{
|
|
14
|
-
"currency":"eip155:84532/erc20:0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
15
|
-
"maxTotal":"10000000",
|
|
16
|
-
"period":"cumulative"
|
|
17
|
-
}]
|
|
18
|
-
}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:envarpay:buyer-policy:1",
|
|
4
|
-
"title": "Local buyer permission policy; not supplied by the seller",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"additionalProperties": false,
|
|
7
|
-
"required": [
|
|
8
|
-
"policyVersion",
|
|
9
|
-
"paymentsEnabled",
|
|
10
|
-
"approval",
|
|
11
|
-
"peers",
|
|
12
|
-
"budgets"
|
|
13
|
-
],
|
|
14
|
-
"properties": {
|
|
15
|
-
"policyVersion": {
|
|
16
|
-
"const": 1
|
|
17
|
-
},
|
|
18
|
-
"paymentsEnabled": {
|
|
19
|
-
"type": "boolean"
|
|
20
|
-
},
|
|
21
|
-
"approval": {
|
|
22
|
-
"enum": [
|
|
23
|
-
"per_purchase",
|
|
24
|
-
"within_preapproved_limits"
|
|
25
|
-
]
|
|
26
|
-
},
|
|
27
|
-
"peers": {
|
|
28
|
-
"type": "array",
|
|
29
|
-
"minItems": 1,
|
|
30
|
-
"items": {
|
|
31
|
-
"type": "object",
|
|
32
|
-
"additionalProperties": false,
|
|
33
|
-
"required": [
|
|
34
|
-
"id",
|
|
35
|
-
"cardUrl",
|
|
36
|
-
"protocol",
|
|
37
|
-
"currency",
|
|
38
|
-
"recipient",
|
|
39
|
-
"maxPerPurchase"
|
|
40
|
-
],
|
|
41
|
-
"properties": {
|
|
42
|
-
"id": {
|
|
43
|
-
"type": "string",
|
|
44
|
-
"pattern": "^[a-z][a-z0-9-]{0,63}$"
|
|
45
|
-
},
|
|
46
|
-
"cardUrl": {
|
|
47
|
-
"type": "string",
|
|
48
|
-
"format": "uri",
|
|
49
|
-
"pattern": "^https://"
|
|
50
|
-
},
|
|
51
|
-
"protocol": {
|
|
52
|
-
"enum": [
|
|
53
|
-
"x402",
|
|
54
|
-
"mpp",
|
|
55
|
-
"free"
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
"currency": {
|
|
59
|
-
"type": [
|
|
60
|
-
"string",
|
|
61
|
-
"null"
|
|
62
|
-
]
|
|
63
|
-
},
|
|
64
|
-
"recipient": {
|
|
65
|
-
"type": [
|
|
66
|
-
"string",
|
|
67
|
-
"null"
|
|
68
|
-
]
|
|
69
|
-
},
|
|
70
|
-
"maxPerPurchase": {
|
|
71
|
-
"type": "string",
|
|
72
|
-
"pattern": "^(0|[1-9][0-9]{0,77})$"
|
|
73
|
-
},
|
|
74
|
-
"mode": {
|
|
75
|
-
"enum": [
|
|
76
|
-
"envarpay",
|
|
77
|
-
"standard-a2a"
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
|
-
"endpoint": {
|
|
81
|
-
"type": "string",
|
|
82
|
-
"format": "uri",
|
|
83
|
-
"pattern": "^https://"
|
|
84
|
-
},
|
|
85
|
-
"authentication": {
|
|
86
|
-
"enum": [
|
|
87
|
-
"none",
|
|
88
|
-
"bearer"
|
|
89
|
-
]
|
|
90
|
-
},
|
|
91
|
-
"localContract": {
|
|
92
|
-
"type": "object",
|
|
93
|
-
"additionalProperties": false,
|
|
94
|
-
"required": [
|
|
95
|
-
"revision",
|
|
96
|
-
"offerId",
|
|
97
|
-
"inputSchema",
|
|
98
|
-
"amount",
|
|
99
|
-
"paidOnly"
|
|
100
|
-
],
|
|
101
|
-
"properties": {
|
|
102
|
-
"revision": {
|
|
103
|
-
"type": "integer",
|
|
104
|
-
"minimum": 1,
|
|
105
|
-
"maximum": 9007199254740991
|
|
106
|
-
},
|
|
107
|
-
"offerId": {
|
|
108
|
-
"type": "string",
|
|
109
|
-
"pattern": "^[a-z][a-z0-9-]{0,63}$"
|
|
110
|
-
},
|
|
111
|
-
"inputSchema": {
|
|
112
|
-
"type": "object"
|
|
113
|
-
},
|
|
114
|
-
"amount": {
|
|
115
|
-
"$ref": "#/$defs/amount"
|
|
116
|
-
},
|
|
117
|
-
"paidOnly": {
|
|
118
|
-
"const": true
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
"allOf": [
|
|
124
|
-
{
|
|
125
|
-
"if": {
|
|
126
|
-
"properties": {
|
|
127
|
-
"protocol": {
|
|
128
|
-
"const": "free"
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
"then": {
|
|
133
|
-
"properties": {
|
|
134
|
-
"currency": {
|
|
135
|
-
"const": null
|
|
136
|
-
},
|
|
137
|
-
"recipient": {
|
|
138
|
-
"const": null
|
|
139
|
-
},
|
|
140
|
-
"maxPerPurchase": {
|
|
141
|
-
"const": "0"
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
"else": {
|
|
146
|
-
"properties": {
|
|
147
|
-
"currency": {
|
|
148
|
-
"type": "string",
|
|
149
|
-
"minLength": 1
|
|
150
|
-
},
|
|
151
|
-
"recipient": {
|
|
152
|
-
"type": "string",
|
|
153
|
-
"minLength": 1
|
|
154
|
-
},
|
|
155
|
-
"maxPerPurchase": {
|
|
156
|
-
"$ref": "#/$defs/amount"
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
"if": {
|
|
163
|
-
"required": [
|
|
164
|
-
"mode"
|
|
165
|
-
],
|
|
166
|
-
"properties": {
|
|
167
|
-
"mode": {
|
|
168
|
-
"const": "standard-a2a"
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
"then": {
|
|
173
|
-
"required": [
|
|
174
|
-
"endpoint",
|
|
175
|
-
"authentication",
|
|
176
|
-
"localContract"
|
|
177
|
-
],
|
|
178
|
-
"properties": {
|
|
179
|
-
"protocol": {
|
|
180
|
-
"const": "x402"
|
|
181
|
-
},
|
|
182
|
-
"endpoint": {},
|
|
183
|
-
"authentication": {},
|
|
184
|
-
"localContract": {}
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
"else": {
|
|
188
|
-
"not": {
|
|
189
|
-
"anyOf": [
|
|
190
|
-
{
|
|
191
|
-
"required": [
|
|
192
|
-
"endpoint"
|
|
193
|
-
],
|
|
194
|
-
"properties": {
|
|
195
|
-
"endpoint": {}
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
"required": [
|
|
200
|
-
"authentication"
|
|
201
|
-
],
|
|
202
|
-
"properties": {
|
|
203
|
-
"authentication": {}
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
"required": [
|
|
208
|
-
"localContract"
|
|
209
|
-
],
|
|
210
|
-
"properties": {
|
|
211
|
-
"localContract": {}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
]
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
]
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
"budgets": {
|
|
222
|
-
"type": "array",
|
|
223
|
-
"minItems": 0,
|
|
224
|
-
"items": {
|
|
225
|
-
"type": "object",
|
|
226
|
-
"additionalProperties": false,
|
|
227
|
-
"required": [
|
|
228
|
-
"currency",
|
|
229
|
-
"maxTotal",
|
|
230
|
-
"period"
|
|
231
|
-
],
|
|
232
|
-
"properties": {
|
|
233
|
-
"currency": {
|
|
234
|
-
"type": "string",
|
|
235
|
-
"minLength": 1
|
|
236
|
-
},
|
|
237
|
-
"maxTotal": {
|
|
238
|
-
"$ref": "#/$defs/amount"
|
|
239
|
-
},
|
|
240
|
-
"period": {
|
|
241
|
-
"const": "cumulative"
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
"$defs": {
|
|
248
|
-
"amount": {
|
|
249
|
-
"type": "string",
|
|
250
|
-
"pattern": "^[1-9][0-9]{0,77}$"
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|