@402flow/sdk 0.1.0-alpha.22 → 0.1.0-alpha.24
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 +184 -3
- package/dist/contracts.d.ts +1317 -156
- package/dist/contracts.js +94 -1
- package/dist/contracts.js.map +1 -1
- package/dist/executors.d.ts +27 -0
- package/dist/executors.js +2 -0
- package/dist/executors.js.map +1 -0
- package/dist/harness-instructions.d.ts +8 -0
- package/dist/harness-instructions.js.map +1 -1
- package/dist/http-utils.d.ts +1 -0
- package/dist/http-utils.js +12 -0
- package/dist/http-utils.js.map +1 -0
- package/dist/index.d.ts +14 -3
- package/dist/index.js +119 -28
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -132,6 +132,46 @@ If the merchant does not require payment for that exact request, the SDK returns
|
|
|
132
132
|
|
|
133
133
|
`result.response` is always the merchant HTTP response. SDK-owned payment metadata such as `paidRequestId`, `paymentAttemptId`, `receiptId`, and `receipt` stays on the SDK result instead of being injected into the merchant JSON body.
|
|
134
134
|
|
|
135
|
+
### Optional Attribution
|
|
136
|
+
|
|
137
|
+
Most SDK users do not need to send attribution at all.
|
|
138
|
+
|
|
139
|
+
The optional `attribution` field is for callers that already know where this paid endpoint came from and want that provenance to survive into control-plane audit, reporting, and later policy analysis.
|
|
140
|
+
|
|
141
|
+
Common cases:
|
|
142
|
+
|
|
143
|
+
1. your app found the endpoint through your own registry or catalog
|
|
144
|
+
2. your app imported the endpoint from a saved workspace config
|
|
145
|
+
3. your agent is calling a merchant directly and you want to mark that path as `direct`
|
|
146
|
+
4. your host selected the endpoint from a discovery surface such as Bazaar, Dexter, pay.sh, x402scan, or another catalog
|
|
147
|
+
|
|
148
|
+
This field does not make payment execution work. It improves lifecycle explainability.
|
|
149
|
+
|
|
150
|
+
If you do not already know the endpoint provenance, omit it.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const result = await client.fetchPaid(
|
|
154
|
+
'https://merchant.example.com/reports/daily',
|
|
155
|
+
{
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: {
|
|
158
|
+
'content-type': 'application/json',
|
|
159
|
+
},
|
|
160
|
+
body: createJsonRequestBody({
|
|
161
|
+
date: '2026-03-25',
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
description: 'sync daily paid report',
|
|
166
|
+
attribution: {
|
|
167
|
+
discoverySource: 'direct',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
For the first attribution slice, `discoverySource` is the main field callers should set when they have a clear answer. The control plane derives an early endpoint-level `resourceIdentity` from the request method and URL, so callers do not need to compute that themselves.
|
|
174
|
+
|
|
135
175
|
### Interpreting Merchant Responses
|
|
136
176
|
|
|
137
177
|
The SDK gives you a stable place for payment metadata, but it does not invent a universal fulfilled-response schema for merchant content.
|
|
@@ -228,6 +268,13 @@ const prepared = await client.preparePaidRequest(
|
|
|
228
268
|
|
|
229
269
|
`externalMetadata` is optional caller context. It improves preparation when the caller already has structured endpoint knowledge, but it is not required for normal SDK use.
|
|
230
270
|
|
|
271
|
+
`attribution` is separate from `externalMetadata`.
|
|
272
|
+
|
|
273
|
+
1. `externalMetadata` helps the SDK understand request shape before execution
|
|
274
|
+
2. `attribution` helps the control plane explain where the paid endpoint came from after execution
|
|
275
|
+
|
|
276
|
+
Use `externalMetadata` for request hints. Use `attribution` for provenance. Either may be omitted.
|
|
277
|
+
|
|
231
278
|
### What `ready` Means
|
|
232
279
|
|
|
233
280
|
`ready` means this exact request can proceed through governed paid execution as-is; it does not mean the SDK has inferred the best task parameters for you.
|
|
@@ -256,6 +303,13 @@ const prepared = await client.preparePaidRequest(
|
|
|
256
303
|
prompt: 'foggy coastline',
|
|
257
304
|
}),
|
|
258
305
|
},
|
|
306
|
+
|
|
307
|
+
const result = await client.executePreparedRequest(prepared, {
|
|
308
|
+
description: 'generate paid image',
|
|
309
|
+
attribution: {
|
|
310
|
+
discoverySource: 'manual',
|
|
311
|
+
},
|
|
312
|
+
});
|
|
259
313
|
);
|
|
260
314
|
|
|
261
315
|
if (prepared.kind === 'ready') {
|
|
@@ -270,6 +324,131 @@ if (prepared.kind === 'ready') {
|
|
|
270
324
|
|
|
271
325
|
If preparation does not return `kind === 'ready'`, that is not necessarily an error. It means this exact request did not currently resolve to a payable executable path. The caller can accept that result, run a normal non-paid path, or revise and prepare again.
|
|
272
326
|
|
|
327
|
+
### Delegated Execution With Custom Executors
|
|
328
|
+
|
|
329
|
+
`executePreparedRequest()` now supports governed delegated execution through a caller-supplied executor interface.
|
|
330
|
+
|
|
331
|
+
This lets the SDK keep authorization, policy, receipts, and final outcome normalization in the 402flow control plane while handing the final paid merchant call to a provider-specific executor owned by the host app or a separate integration package.
|
|
332
|
+
|
|
333
|
+
The core SDK stays provider-neutral. That means third-party payment clients such as Dexter or pay.sh should live in the host app's own dependency graph, not inside `@402flow/sdk` itself.
|
|
334
|
+
|
|
335
|
+
The flow is:
|
|
336
|
+
|
|
337
|
+
1. prepare the exact request locally with `preparePaidRequest()`
|
|
338
|
+
2. ask the control plane to authorize delegated execution
|
|
339
|
+
3. if authorized, let the supplied executor perform the actual paid merchant request
|
|
340
|
+
4. finalize the normalized executor result back through the control plane
|
|
341
|
+
5. return the same stable SDK result or `FetchPaidError` contract the caller already uses elsewhere
|
|
342
|
+
|
|
343
|
+
If the control plane denies authorization, the delegated executor is never invoked.
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import {
|
|
347
|
+
createJsonRequestBody,
|
|
348
|
+
type PreparedRequestExecutorInput,
|
|
349
|
+
type PreparedRequestExecutor,
|
|
350
|
+
} from '@402flow/sdk';
|
|
351
|
+
|
|
352
|
+
async function callDexter(
|
|
353
|
+
prepared: PreparedRequestExecutorInput['prepared'],
|
|
354
|
+
) {
|
|
355
|
+
// Replace this with your real Dexter SDK call.
|
|
356
|
+
return {
|
|
357
|
+
status: 200,
|
|
358
|
+
body: { ok: true },
|
|
359
|
+
settlementReference: 'settlement-ref-1',
|
|
360
|
+
paymentReference: 'payment-ref-1',
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function mapDexterResult(
|
|
365
|
+
prepared: PreparedRequestExecutorInput['prepared'],
|
|
366
|
+
result: Awaited<ReturnType<typeof callDexter>>,
|
|
367
|
+
) {
|
|
368
|
+
return {
|
|
369
|
+
protocol: prepared.protocol,
|
|
370
|
+
executionStatus: 'succeeded' as const,
|
|
371
|
+
settlementEvidenceClass: 'settled' as const,
|
|
372
|
+
merchantOutcome: 'success_response' as const,
|
|
373
|
+
merchantResponse: {
|
|
374
|
+
status: result.status,
|
|
375
|
+
headers: {
|
|
376
|
+
'content-type': 'application/json',
|
|
377
|
+
},
|
|
378
|
+
body: JSON.stringify(result.body),
|
|
379
|
+
},
|
|
380
|
+
settlementReference: result.settlementReference,
|
|
381
|
+
paymentReference: result.paymentReference,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const dexterExecutor: PreparedRequestExecutor = {
|
|
386
|
+
provider: 'dexter',
|
|
387
|
+
async execute({ prepared }) {
|
|
388
|
+
const dexterResult = await callDexter(prepared);
|
|
389
|
+
return mapDexterResult(prepared, dexterResult);
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const prepared = await client.preparePaidRequest(
|
|
394
|
+
'https://merchant.example.com/images/generate',
|
|
395
|
+
{
|
|
396
|
+
method: 'POST',
|
|
397
|
+
headers: {
|
|
398
|
+
'content-type': 'application/json',
|
|
399
|
+
},
|
|
400
|
+
body: createJsonRequestBody({
|
|
401
|
+
prompt: 'foggy coastline',
|
|
402
|
+
}),
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
if (prepared.kind === 'ready') {
|
|
407
|
+
const result = await client.executePreparedRequest(prepared, {
|
|
408
|
+
description: 'generate image through delegated execution',
|
|
409
|
+
executionProvider: 'dexter',
|
|
410
|
+
executor: dexterExecutor,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
console.log('paid response status:', result.response.status);
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
This snippet shows the intended split directly: your host-owned code does the provider call and maps it into the delegated execution contract, while the SDK still owns authorize, finalize, and the outward result shape. For a real host-owned Dexter integration that performs the paid call, see `third-party-executors/examples/dexter-delegated-executor.mjs`.
|
|
418
|
+
|
|
419
|
+
Responsibility split:
|
|
420
|
+
|
|
421
|
+
1. the SDK asks the control plane for delegated authorization
|
|
422
|
+
2. if authorized, the SDK invokes your executor
|
|
423
|
+
3. your executor performs the provider-specific paid request and returns `SdkDelegatedExecutionResult`
|
|
424
|
+
4. the SDK finalizes that result with the control plane
|
|
425
|
+
5. the SDK returns the same outward `PaidResponse` or `FetchPaidError` contract as the direct path
|
|
426
|
+
|
|
427
|
+
If your host app wants to execute through Dexter, pay.sh, or another provider, that integration should install and own the third-party SDK directly. `@402flow/sdk` only owns the executor contract and the governed authorize/finalize flow.
|
|
428
|
+
|
|
429
|
+
The repo keeps third-party executor proofs in the separate `third-party-executors/` package so the main `@402flow/sdk` install path stays provider-neutral.
|
|
430
|
+
|
|
431
|
+
For a repo-wide verification pass from the SDK root, run:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
npm run install:all
|
|
435
|
+
npm run check:all
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`npm run install:all` installs both the main SDK package and the separate `third-party-executors` package from the SDK root. `npm run check` still validates only the main SDK package. `npm run check:all` runs the main SDK checks first, then runs the separate `third-party-executors` package checks from the top level.
|
|
439
|
+
|
|
440
|
+
For a repo-local host-owned Dexter example, run:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
cd third-party-executors
|
|
444
|
+
npm install
|
|
445
|
+
export DEXTER_EVM_PRIVATE_KEY="..."
|
|
446
|
+
|
|
447
|
+
npm run example:dexter-delegated-executor -- \
|
|
448
|
+
"https://merchant.example.com/paid-endpoint" \
|
|
449
|
+
'{"topic":"sdk integration rollout","audience":"platform engineers","format":"bullets"}'
|
|
450
|
+
```
|
|
451
|
+
|
|
273
452
|
## Prepared Result Semantics
|
|
274
453
|
|
|
275
454
|
`preparePaidRequest()` separates request checking from paid execution.
|
|
@@ -423,8 +602,10 @@ When unset, first-party fixtures default to the self-hosted demo merchant at `ht
|
|
|
423
602
|
## Publish
|
|
424
603
|
|
|
425
604
|
```bash
|
|
426
|
-
npm install
|
|
427
|
-
npm run check
|
|
605
|
+
npm run install:all
|
|
606
|
+
npm run check:all
|
|
428
607
|
npm run pack:check
|
|
429
608
|
npm publish --access public
|
|
430
|
-
```
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
`npm publish` now also runs `npm run check:all` through the root `prepublishOnly` hook, so the repo-wide test pass is enforced before publish even if you skip that step manually.
|