@mercuryo-ai/magicpay-sdk 0.1.0-test.2

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.
@@ -0,0 +1,432 @@
1
+ # Examples
2
+
3
+ This document collects recipe-style examples for the current public MagicPay SDK
4
+ surface.
5
+
6
+ ## Choose An Integration Mode
7
+
8
+ The public SDK supports two explicit modes:
9
+
10
+ - Mode A: use `@mercuryo-ai/magicpay-sdk` when your runtime can create request
11
+ input and consume claimed secrets on its own.
12
+ - Mode B: use `@mercuryo-ai/magicpay-sdk/agentbrowse` only when your runtime
13
+ already depends on `@mercuryo-ai/agentbrowse` and needs the observed-form
14
+ bridge.
15
+
16
+ The examples below stay grouped the same way: root-package recipes first, then
17
+ the explicit AgentBrowse bridge.
18
+
19
+ ## Read Session State
20
+
21
+ ```ts
22
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
23
+
24
+ // Create a networked MagicPay client.
25
+ const client = createMagicPayClient({
26
+ gateway: {
27
+ apiKey: process.env.MAGICPAY_API_KEY!,
28
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
29
+ },
30
+ });
31
+
32
+ // Fetch the latest server-backed session envelope.
33
+ const session = await client.sessions.get('sess_123');
34
+
35
+ // Project the envelope into a simpler orchestration state.
36
+ const state = client.sessions.describe(session);
37
+
38
+ // Use the projected state for logs, UI, or control flow.
39
+ console.log({
40
+ closed: state.closed,
41
+ approvalPending: state.approvalPending,
42
+ activeRequestDomain: state.activeRequest?.domain ?? null,
43
+ });
44
+ ```
45
+
46
+ ## Request, Poll, And Claim
47
+
48
+ ```ts
49
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
50
+
51
+ // Create a networked MagicPay client.
52
+ const client = createMagicPayClient({
53
+ gateway: {
54
+ apiKey: process.env.MAGICPAY_API_KEY!,
55
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
56
+ },
57
+ });
58
+
59
+ const sessionId = 'sess_123';
60
+ const pageUrl = 'https://checkout.airline.example/payment';
61
+
62
+ // Load the stored-secret catalog for the current host.
63
+ const catalog = await client.secrets.fetchCatalog(sessionId, pageUrl);
64
+
65
+ // Create a request for one stored secret and the exact fields this step needs.
66
+ const created = await client.secrets.createRequest({
67
+ sessionId,
68
+ clientRequestId: 'payment-card-1',
69
+ fillRef: 'checkout_card',
70
+ purpose: 'payment_card',
71
+ merchantName: 'Airline Example',
72
+ page: {
73
+ url: pageUrl,
74
+ title: 'Payment',
75
+ },
76
+ secretHint: {
77
+ storedSecretRef: 'secret_card_primary',
78
+ kind: 'payment_card',
79
+ host: catalog.host,
80
+ scopeRef: 'payment_surface',
81
+ fields: ['pan', 'exp_month', 'exp_year', 'cvv'],
82
+ },
83
+ });
84
+
85
+ // Wait until the request is fulfilled or another terminal status appears.
86
+ const ready = await client.secrets.pollUntil(sessionId, created.requestId, {
87
+ stopWhen: 'fulfilled',
88
+ });
89
+
90
+ if (!ready.success) {
91
+ throw new Error(ready.reason);
92
+ }
93
+ if (ready.result.snapshot.status !== 'fulfilled') {
94
+ throw new Error(`Request ended as ${ready.result.snapshot.status}`);
95
+ }
96
+
97
+ // Claim the one-time payload after approval.
98
+ const claim = await client.secrets.claim(sessionId, created.requestId);
99
+ if (!claim.success) {
100
+ throw new Error(claim.reason);
101
+ }
102
+
103
+ // Use the claimed secret values in your runtime.
104
+ console.log(claim.result.secret.values);
105
+ ```
106
+
107
+ ## Poll Until A Terminal Status
108
+
109
+ ```ts
110
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
111
+
112
+ // Create a networked MagicPay client.
113
+ const client = createMagicPayClient({
114
+ gateway: {
115
+ apiKey: process.env.MAGICPAY_API_KEY!,
116
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
117
+ },
118
+ });
119
+
120
+ // Poll until the request reaches any terminal status.
121
+ const result = await client.secrets.pollUntil('sess_123', 'req_123', {
122
+ stopWhen: 'terminal',
123
+ timeoutMs: 180_000,
124
+ intervalMs: 10_000,
125
+ maxIntervalMs: 30_000,
126
+ backoffMultiplier: 1.2,
127
+ });
128
+
129
+ if (!result.success) {
130
+ // Transport failures and timeouts still need caller-level handling.
131
+ console.log(result.kind, result.reason);
132
+ } else {
133
+ // Branch on the final request status.
134
+ switch (result.result.snapshot.status) {
135
+ case 'fulfilled':
136
+ console.log('Claim and consume the one-time secret payload.');
137
+ break;
138
+ case 'denied':
139
+ console.log('A user denied the request.');
140
+ break;
141
+ case 'expired':
142
+ console.log('Create a new request.');
143
+ break;
144
+ case 'failed':
145
+ case 'canceled':
146
+ console.log('Inspect logs or surface recovery to the caller.');
147
+ break;
148
+ default:
149
+ throw new Error(`Unexpected status ${result.result.snapshot.status}`);
150
+ }
151
+ }
152
+ ```
153
+
154
+ ## AgentBrowse Observe -> Request -> Fill
155
+
156
+ This example is Mode B: explicit AgentBrowse bridge.
157
+
158
+ ```ts
159
+ import { act, observe } from '@mercuryo-ai/agentbrowse';
160
+ import {
161
+ fillProtectedForm,
162
+ type ProtectedFillForm,
163
+ } from '@mercuryo-ai/agentbrowse/protected-fill';
164
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
165
+ import {
166
+ buildRequestInputForObservedForm,
167
+ enrichObservedFormsForUrl,
168
+ prepareProtectedFillFromClaim,
169
+ } from '@mercuryo-ai/magicpay-sdk/agentbrowse';
170
+
171
+ // Create the MagicPay client that owns the approval flow.
172
+ const client = createMagicPayClient({
173
+ gateway: {
174
+ apiKey: process.env.MAGICPAY_API_KEY!,
175
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
176
+ },
177
+ });
178
+
179
+ const sessionId = 'sess_123';
180
+
181
+ // Observe the page and collect protected fill forms.
182
+ const observation = await observe(browserSession, 'Find the login form');
183
+ if (!observation.success || !observation.url || !Array.isArray(observation.fillableForms)) {
184
+ throw new Error('AgentBrowse did not return observed forms.');
185
+ }
186
+
187
+ const forms = observation.fillableForms as ProtectedFillForm[];
188
+
189
+ // Load the stored-secret catalog for the observed host.
190
+ const catalog = await client.secrets.fetchCatalog(sessionId, observation.url);
191
+
192
+ // Attach stored-secret candidates to the observed forms.
193
+ const enrichedForms = enrichObservedFormsForUrl(forms, { [catalog.host]: catalog }, observation.url);
194
+ const form = enrichedForms.find((candidate) => candidate.purpose === 'login') ?? enrichedForms[0] ?? null;
195
+ if (!form) {
196
+ throw new Error('No observed form is available.');
197
+ }
198
+
199
+ // Build a MagicPay request input from the observed login form.
200
+ const requestInput = buildRequestInputForObservedForm({
201
+ sessionId,
202
+ merchantName: 'Airline Example',
203
+ storedSecretRef: 'secret_login_primary',
204
+ urlOrHost: observation.url,
205
+ catalog,
206
+ fillableForm: form,
207
+ page: {
208
+ ref: form.pageRef,
209
+ url: observation.url,
210
+ ...(observation.title ? { title: observation.title } : {}),
211
+ },
212
+ });
213
+
214
+ if (!requestInput.success) {
215
+ throw new Error(requestInput.reason);
216
+ }
217
+
218
+ // Create the approval request and wait until it is fulfilled.
219
+ const created = await client.secrets.createRequest(requestInput.input);
220
+ const ready = await client.secrets.pollUntil(sessionId, created.requestId, {
221
+ stopWhen: 'fulfilled',
222
+ });
223
+
224
+ if (!ready.success) {
225
+ throw new Error(ready.reason);
226
+ }
227
+ if (ready.result.snapshot.status !== 'fulfilled') {
228
+ throw new Error(`Request ended as ${ready.result.snapshot.status}`);
229
+ }
230
+
231
+ // Claim the one-time payload after approval.
232
+ const claim = await client.secrets.claim(sessionId, created.requestId);
233
+ if (!claim.success) {
234
+ throw new Error(claim.reason);
235
+ }
236
+
237
+ // Convert the claim into AgentBrowse protected-fill input.
238
+ const prepared = prepareProtectedFillFromClaim({
239
+ fillableForm: form,
240
+ catalog,
241
+ claim: claim.result,
242
+ request: created.snapshot,
243
+ });
244
+
245
+ // Fill the protected fields in the browser.
246
+ const browserFill = await fillProtectedForm({
247
+ session: browserSession,
248
+ fillableForm: prepared.fillableForm,
249
+ protectedValues: prepared.protectedValues,
250
+ fieldPolicies: prepared.fieldPolicies,
251
+ });
252
+
253
+ if (!browserFill.success) {
254
+ throw new Error(browserFill.reason);
255
+ }
256
+
257
+ // Re-observe after the protected fill so the submit action comes from the live page.
258
+ const submitObservation = await observe(
259
+ browserSession,
260
+ 'Find the single actionable submit target that sends the filled login form.'
261
+ );
262
+ if (!submitObservation.success || submitObservation.targets.length === 0) {
263
+ throw new Error(submitObservation.reason ?? submitObservation.message);
264
+ }
265
+
266
+ // Goal-based observe already narrows the inventory to the submit step we need next.
267
+ const submitTarget = submitObservation.targets[0];
268
+ if (!submitTarget?.ref || submitTarget.capability !== 'actionable') {
269
+ throw new Error('AgentBrowse did not return an actionable submit target.');
270
+ }
271
+
272
+ const submitAction = await act(browserSession, submitTarget.ref, 'click');
273
+ if (!submitAction.success) {
274
+ throw new Error(submitAction.reason ?? submitAction.message);
275
+ }
276
+ ```
277
+
278
+ ## Claim And Call An External API
279
+
280
+ ```ts
281
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
282
+
283
+ // Create the MagicPay client for the claim step.
284
+ const client = createMagicPayClient({
285
+ gateway: {
286
+ apiKey: process.env.MAGICPAY_API_KEY!,
287
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
288
+ },
289
+ });
290
+
291
+ // Claim the approved secret payload.
292
+ const claim = await client.secrets.claim('sess_123', 'req_123');
293
+ if (!claim.success) {
294
+ throw new Error(claim.reason);
295
+ }
296
+
297
+ // Forward the claimed values to the external API call.
298
+ const response = await fetch('https://api.airline.example/login', {
299
+ method: 'POST',
300
+ headers: { 'content-type': 'application/json' },
301
+ body: JSON.stringify({
302
+ username: claim.result.secret.values.username,
303
+ password: claim.result.secret.values.password,
304
+ }),
305
+ });
306
+
307
+ if (!response.ok) {
308
+ throw new Error(`Provider login failed with ${response.status}`);
309
+ }
310
+ ```
311
+
312
+ ## Claim And Hand Off To An MCP Tool
313
+
314
+ ```ts
315
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
316
+
317
+ // Create the MagicPay client for the claim step.
318
+ const client = createMagicPayClient({
319
+ gateway: {
320
+ apiKey: process.env.MAGICPAY_API_KEY!,
321
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
322
+ },
323
+ });
324
+
325
+ // Claim the approved secret payload.
326
+ const claim = await client.secrets.claim('sess_123', 'req_123');
327
+ if (!claim.success) {
328
+ throw new Error(claim.reason);
329
+ }
330
+
331
+ // Repackage the claimed values for your MCP tool contract.
332
+ const mcpInput = {
333
+ type: 'provider_login',
334
+ payload: {
335
+ username: claim.result.secret.values.username,
336
+ password: claim.result.secret.values.password,
337
+ },
338
+ };
339
+
340
+ console.log(mcpInput);
341
+ ```
342
+
343
+ ## Testing With `fetchImpl`
344
+
345
+ ```ts
346
+ import { createMagicPayClient } from '@mercuryo-ai/magicpay-sdk';
347
+
348
+ // Predefine the fake transport responses your test expects.
349
+ const responses = new Map([
350
+ [
351
+ 'GET https://agents-api.mercuryo.io/functions/v1/api/sessions/sess_123/secrets/catalog?host=checkout.airline.example',
352
+ new Response(JSON.stringify([]), {
353
+ status: 200,
354
+ headers: { 'content-type': 'application/json' },
355
+ }),
356
+ ],
357
+ ]);
358
+
359
+ // Inject fetchImpl so tests never hit the real network.
360
+ const client = createMagicPayClient({
361
+ gateway: {
362
+ apiKey: 'test_api_key',
363
+ apiUrl: 'https://agents-api.mercuryo.io/functions/v1/api',
364
+ },
365
+ fetchImpl: async (url, init = {}) => {
366
+ const method = (init.method ?? 'GET').toUpperCase();
367
+ const key = `${method} ${String(url)}`;
368
+ const response = responses.get(key);
369
+ if (!response) {
370
+ throw new Error(`Unexpected ${key}`);
371
+ }
372
+ return response;
373
+ },
374
+ });
375
+
376
+ // Run the SDK call against the mocked transport.
377
+ await client.secrets.fetchCatalog('sess_123', 'https://checkout.airline.example/login');
378
+ ```
379
+
380
+ ## Pure Domain Helpers
381
+
382
+ `@mercuryo-ai/magicpay-sdk/core` keeps the pure helpers that do not need network
383
+ state from the client object.
384
+
385
+ ```ts
386
+ import {
387
+ describeSecretRequestStatus,
388
+ evaluateSecretRequestForFill,
389
+ resolveSecretCatalogContext,
390
+ } from '@mercuryo-ai/magicpay-sdk/core';
391
+
392
+ // Interpret what the current request status means for orchestration.
393
+ const status = describeSecretRequestStatus('pending', 'secret_read');
394
+ console.log(status.nextAction);
395
+
396
+ // Check whether the fulfilled request still matches the observed fill context.
397
+ const readiness = evaluateSecretRequestForFill(
398
+ {
399
+ requestId: 'req_123',
400
+ fillRef: 'checkout_card',
401
+ requestType: 'secret_read',
402
+ status: 'fulfilled',
403
+ host: 'checkout.airline.example',
404
+ scopeRef: 'payment_surface',
405
+ createdAt: '2026-03-31T12:00:00.000Z',
406
+ updatedAt: '2026-03-31T12:01:00.000Z',
407
+ },
408
+ {
409
+ fillRef: 'checkout_card',
410
+ observedHost: 'checkout.airline.example',
411
+ observedScopeRef: 'payment_surface',
412
+ }
413
+ );
414
+
415
+ // Consume the readiness result inside your own orchestration layer.
416
+ console.log(readiness.kind);
417
+
418
+ // Resolve the host-specific catalog entry without creating a client.
419
+ const context = resolveSecretCatalogContext(
420
+ {
421
+ 'checkout.airline.example': {
422
+ host: 'checkout.airline.example',
423
+ syncedAt: '2026-03-31T12:00:00.000Z',
424
+ storedSecrets: [],
425
+ },
426
+ },
427
+ 'https://checkout.airline.example/login'
428
+ );
429
+
430
+ // Use the resolved host and catalog in pure domain logic.
431
+ console.log(context.host, context.catalog);
432
+ ```
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@mercuryo-ai/magicpay-sdk",
3
+ "version": "0.1.0-test.2",
4
+ "description": "Official MagicPay SDK for TypeScript and Node — session state and secret request flows",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE.md",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./core": {
15
+ "types": "./dist/core.d.ts",
16
+ "import": "./dist/core.js"
17
+ },
18
+ "./agentbrowse": {
19
+ "types": "./dist/agentbrowse.d.ts",
20
+ "import": "./dist/agentbrowse.js"
21
+ },
22
+ "./gateway": {
23
+ "types": "./dist/gateway.d.ts",
24
+ "import": "./dist/gateway.js"
25
+ },
26
+ "./request-flow": {
27
+ "types": "./dist/request-flow.d.ts",
28
+ "import": "./dist/request-flow.js"
29
+ },
30
+ "./secret-flow": {
31
+ "types": "./dist/secret-flow.d.ts",
32
+ "import": "./dist/secret-flow.js"
33
+ },
34
+ "./session-client": {
35
+ "types": "./dist/session-client.d.ts",
36
+ "import": "./dist/session-client.js"
37
+ },
38
+ "./session-flow": {
39
+ "types": "./dist/session-flow.d.ts",
40
+ "import": "./dist/session-flow.js"
41
+ }
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/nuanu-ai/mercuryo-agent-pay.git",
46
+ "directory": "packages/magicpay-sdk"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/nuanu-ai/mercuryo-agent-pay/issues"
50
+ },
51
+ "homepage": "https://github.com/nuanu-ai/mercuryo-agent-pay/tree/main/packages/magicpay-sdk",
52
+ "keywords": [
53
+ "magicpay",
54
+ "sdk",
55
+ "payments",
56
+ "sessions",
57
+ "agent-payments"
58
+ ],
59
+ "files": [
60
+ "dist",
61
+ "README.md",
62
+ "LICENSE.md",
63
+ "docs"
64
+ ],
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "engines": {
69
+ "node": ">=18.0.0"
70
+ },
71
+ "peerDependencies": {
72
+ "@mercuryo-ai/agentbrowse": "^0.2.50"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "@mercuryo-ai/agentbrowse": {
76
+ "optional": true
77
+ }
78
+ },
79
+ "scripts": {
80
+ "build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.build.json",
81
+ "check-types": "tsc --noEmit",
82
+ "docs:verify": "node scripts/verify-public-docs.mjs",
83
+ "pack:verify": "node scripts/verify-pack-artifact.mjs",
84
+ "prepack": "npm run build",
85
+ "smoke:pack-install": "node scripts/verify-pack-install-smoke.mjs",
86
+ "test": "vitest run"
87
+ },
88
+ "devDependencies": {
89
+ "@mercuryo-ai/agentbrowse": "workspace:*",
90
+ "@types/node": "^22.0.0",
91
+ "typescript": "5.9.2",
92
+ "vitest": "^4.0.18"
93
+ }
94
+ }