@stableyard/mppx-stableyard 0.1.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 +301 -0
- package/demo/.env.example +11 -0
- package/demo/agent.ts +75 -0
- package/demo/server.ts +158 -0
- package/package.json +48 -0
- package/specs/draft-stableyard-charge-00.md +607 -0
- package/src/client.ts +143 -0
- package/src/index.ts +3 -0
- package/src/method.ts +37 -0
- package/src/server.ts +87 -0
- package/src/stableyard-api.ts +275 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Stableyard Charge Intent for HTTP Payment Authentication
|
|
3
|
+
abbrev: Stableyard Charge Intent
|
|
4
|
+
docname: draft-stableyard-charge-00
|
|
5
|
+
version: 00
|
|
6
|
+
category: info
|
|
7
|
+
ipr: noModificationTrust200902
|
|
8
|
+
submissiontype: independent
|
|
9
|
+
consensus: false
|
|
10
|
+
|
|
11
|
+
author:
|
|
12
|
+
- name: Mitesh Metha
|
|
13
|
+
ins: M. Metha
|
|
14
|
+
email: mitesh@stableyard.fi
|
|
15
|
+
org: Stableyard
|
|
16
|
+
|
|
17
|
+
normative:
|
|
18
|
+
RFC2119:
|
|
19
|
+
RFC3339:
|
|
20
|
+
RFC4648:
|
|
21
|
+
RFC8174:
|
|
22
|
+
RFC8259:
|
|
23
|
+
RFC8785:
|
|
24
|
+
RFC9457:
|
|
25
|
+
I-D.payment-intent-charge:
|
|
26
|
+
title: "'charge' Intent for HTTP Payment Authentication"
|
|
27
|
+
target: https://datatracker.ietf.org/doc/draft-payment-intent-charge/
|
|
28
|
+
author:
|
|
29
|
+
- name: Jake Moxey
|
|
30
|
+
- name: Brendan Ryan
|
|
31
|
+
- name: Tom Meagher
|
|
32
|
+
date: 2026
|
|
33
|
+
I-D.httpauth-payment:
|
|
34
|
+
title: "The 'Payment' HTTP Authentication Scheme"
|
|
35
|
+
target: https://datatracker.ietf.org/doc/draft-ietf-httpauth-payment/
|
|
36
|
+
author:
|
|
37
|
+
- name: Jake Moxey
|
|
38
|
+
date: 2026-01
|
|
39
|
+
|
|
40
|
+
informative:
|
|
41
|
+
EIP-712:
|
|
42
|
+
title: "Typed structured data hashing and signing"
|
|
43
|
+
target: https://eips.ethereum.org/EIPS/eip-712
|
|
44
|
+
author:
|
|
45
|
+
- org: Ethereum Foundation
|
|
46
|
+
date: 2017
|
|
47
|
+
W3C-DID:
|
|
48
|
+
title: "Decentralized Identifiers (DIDs) v1.0"
|
|
49
|
+
target: https://www.w3.org/TR/did-core/
|
|
50
|
+
author:
|
|
51
|
+
- org: W3C
|
|
52
|
+
date: 2022
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
--- abstract
|
|
56
|
+
|
|
57
|
+
This document defines the "charge" intent for the "stableyard"
|
|
58
|
+
payment method within the Payment HTTP Authentication Scheme
|
|
59
|
+
{{I-D.httpauth-payment}}. The server issues a challenge specifying
|
|
60
|
+
amount and destination; the client fulfills it by creating a
|
|
61
|
+
Stableyard payment session from any supported blockchain, then
|
|
62
|
+
presents the session identifier as a credential.
|
|
63
|
+
|
|
64
|
+
Stableyard settles cross-chain: the client pays from whichever chain
|
|
65
|
+
it holds funds on (Base, Arbitrum, Polygon, Ethereum, Solana, or
|
|
66
|
+
Movement), and the merchant receives on their preferred chain, token,
|
|
67
|
+
or fiat currency. The server verifies payment via Stableyard's
|
|
68
|
+
settlement API without needing chain-specific logic.
|
|
69
|
+
|
|
70
|
+
--- middle
|
|
71
|
+
|
|
72
|
+
# Introduction
|
|
73
|
+
|
|
74
|
+
HTTP Payment Authentication {{I-D.httpauth-payment}} defines a
|
|
75
|
+
challenge-response mechanism that gates access to resources behind
|
|
76
|
+
payments. This document registers the "charge" intent for the
|
|
77
|
+
"stableyard" payment method.
|
|
78
|
+
|
|
79
|
+
Existing payment methods in MPP are chain-specific: "tempo" requires
|
|
80
|
+
funds on Tempo chain, "lightning" requires a Lightning wallet. The
|
|
81
|
+
"stableyard" method removes this constraint — an agent pays from
|
|
82
|
+
any supported chain and the merchant receives on any other chain.
|
|
83
|
+
|
|
84
|
+
The flow proceeds as follows:
|
|
85
|
+
|
|
86
|
+
~~~
|
|
87
|
+
Client Server Stableyard
|
|
88
|
+
| | |
|
|
89
|
+
| (1) GET /resource | |
|
|
90
|
+
|----------------------> | |
|
|
91
|
+
| | |
|
|
92
|
+
| (2) 402 Payment | |
|
|
93
|
+
| Required | |
|
|
94
|
+
| (amount, dest) | |
|
|
95
|
+
|<---------------------- | |
|
|
96
|
+
| | |
|
|
97
|
+
| (3) Create session |
|
|
98
|
+
| {amount, dest, sourceChain} |
|
|
99
|
+
|----------------------------------------------> |
|
|
100
|
+
| (4) Session + deposit address |
|
|
101
|
+
|<---------------------------------------------- |
|
|
102
|
+
| | |
|
|
103
|
+
| (5) Send USDC to |
|
|
104
|
+
| deposit address |
|
|
105
|
+
|----------------------------------------------> |
|
|
106
|
+
| (6) Submit tx hash |
|
|
107
|
+
|----------------------------------------------> |
|
|
108
|
+
| | |
|
|
109
|
+
| | (7) Stableyard routes |
|
|
110
|
+
| | cross-chain |
|
|
111
|
+
| | |
|
|
112
|
+
| (8) GET /resource | |
|
|
113
|
+
| credential: | |
|
|
114
|
+
| {sessionId} | |
|
|
115
|
+
|----------------------> | |
|
|
116
|
+
| | (9) Verify session |
|
|
117
|
+
| |----------------------> |
|
|
118
|
+
| | (10) {verified: true} |
|
|
119
|
+
| |<---------------------- |
|
|
120
|
+
| | |
|
|
121
|
+
| (11) 200 OK | |
|
|
122
|
+
| (resource) | |
|
|
123
|
+
|<---------------------- | |
|
|
124
|
+
| | |
|
|
125
|
+
~~~
|
|
126
|
+
|
|
127
|
+
## Relationship to the Charge Intent
|
|
128
|
+
|
|
129
|
+
This document inherits the shared request semantics of the "charge"
|
|
130
|
+
intent from {{I-D.payment-intent-charge}}. It defines only the
|
|
131
|
+
stableyard-specific `methodDetails`, credential payload, and
|
|
132
|
+
verification procedure.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# Requirements Language
|
|
136
|
+
|
|
137
|
+
{::boilerplate bcp14-tagged}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Terminology
|
|
141
|
+
|
|
142
|
+
Session:
|
|
143
|
+
: A Stableyard payment session representing a single payment intent.
|
|
144
|
+
Sessions track status from creation through settlement.
|
|
145
|
+
|
|
146
|
+
Deposit Address:
|
|
147
|
+
: A blockchain address generated by Stableyard's Intent Engine where
|
|
148
|
+
the client sends funds. May be a direct wallet address (same-chain)
|
|
149
|
+
or a gateway contract address (cross-chain).
|
|
150
|
+
|
|
151
|
+
Settlement:
|
|
152
|
+
: The process by which Stableyard routes funds from the source chain
|
|
153
|
+
to the merchant's preferred destination chain, token, or fiat
|
|
154
|
+
currency.
|
|
155
|
+
|
|
156
|
+
Intent Engine:
|
|
157
|
+
: Stableyard's cross-chain routing system that generates deposit
|
|
158
|
+
addresses, monitors payments, and executes settlement via a
|
|
159
|
+
solver network.
|
|
160
|
+
|
|
161
|
+
Payment Address:
|
|
162
|
+
: A human-readable identifier in the format `name@stableyard` that
|
|
163
|
+
resolves to an account with configured settlement preferences.
|
|
164
|
+
|
|
165
|
+
Vault:
|
|
166
|
+
: An optional Gnosis Safe smart contract wallet enabling gasless
|
|
167
|
+
payments via EIP-712 signed messages.
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Intent Identifier
|
|
171
|
+
|
|
172
|
+
The intent identifier for this specification is "charge".
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# Intent: "charge"
|
|
176
|
+
|
|
177
|
+
The "charge" intent represents a one-time payment for resource access.
|
|
178
|
+
The client creates a Stableyard session, funds it from any supported
|
|
179
|
+
chain, and presents the settled session ID as proof of payment.
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Encoding Conventions {#encoding}
|
|
183
|
+
|
|
184
|
+
All JSON values in challenges and credentials MUST be serialized
|
|
185
|
+
using JSON Canonicalization Scheme (JCS) {{RFC8785}} and encoded
|
|
186
|
+
with base64url {{RFC4648}} without padding, consistent with the
|
|
187
|
+
base specification {{I-D.httpauth-payment}}.
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Request Schema
|
|
191
|
+
|
|
192
|
+
The challenge `request` parameter is a base64url-encoded JSON object
|
|
193
|
+
containing the following fields:
|
|
194
|
+
|
|
195
|
+
## Shared Fields
|
|
196
|
+
|
|
197
|
+
These fields follow the shared charge intent schema from
|
|
198
|
+
{{I-D.payment-intent-charge}}:
|
|
199
|
+
|
|
200
|
+
amount:
|
|
201
|
+
: REQUIRED. String. Amount in base units (smallest denomination).
|
|
202
|
+
For USDC with 6 decimals, "100000" represents $0.10.
|
|
203
|
+
|
|
204
|
+
currency:
|
|
205
|
+
: REQUIRED. String. Currency identifier. MUST be "USDC" or "USDT".
|
|
206
|
+
|
|
207
|
+
decimals:
|
|
208
|
+
: REQUIRED. Number. Decimal places for the currency. MUST be 6 for
|
|
209
|
+
USDC and USDT.
|
|
210
|
+
|
|
211
|
+
destination:
|
|
212
|
+
: REQUIRED. String. Stableyard payment address. Either a
|
|
213
|
+
human-readable address (e.g., "merchant@stableyard") or a wallet
|
|
214
|
+
address (e.g., "0x742d...fA99").
|
|
215
|
+
|
|
216
|
+
description:
|
|
217
|
+
: OPTIONAL. String. Human-readable description of the payment
|
|
218
|
+
purpose. Maximum 500 characters.
|
|
219
|
+
|
|
220
|
+
externalId:
|
|
221
|
+
: OPTIONAL. String. Merchant-defined reference for reconciliation.
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
## Method Details
|
|
225
|
+
|
|
226
|
+
No additional `methodDetails` fields are required for the stableyard
|
|
227
|
+
charge intent. The method is chain-agnostic — the client selects its
|
|
228
|
+
source chain when creating the Stableyard session.
|
|
229
|
+
|
|
230
|
+
## Example Request (Decoded)
|
|
231
|
+
|
|
232
|
+
~~~json
|
|
233
|
+
{
|
|
234
|
+
"amount": "100000",
|
|
235
|
+
"currency": "USDC",
|
|
236
|
+
"decimals": 6,
|
|
237
|
+
"destination": "merchant@stableyard"
|
|
238
|
+
}
|
|
239
|
+
~~~
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# Credential Schema
|
|
243
|
+
|
|
244
|
+
The credential `payload` field is a JSON object containing:
|
|
245
|
+
|
|
246
|
+
sessionId:
|
|
247
|
+
: REQUIRED. String. The Stableyard session identifier (e.g.,
|
|
248
|
+
"ses_b6afc57b153e2ae1f8fb1025"). This serves as the proof of
|
|
249
|
+
payment — the server verifies it via Stableyard's API.
|
|
250
|
+
|
|
251
|
+
txHash:
|
|
252
|
+
: OPTIONAL. String. The on-chain transaction hash with "0x" prefix.
|
|
253
|
+
Included for transparency and auditability. Not required for
|
|
254
|
+
verification — the session ID is sufficient.
|
|
255
|
+
|
|
256
|
+
## Example Credential Payload
|
|
257
|
+
|
|
258
|
+
~~~json
|
|
259
|
+
{
|
|
260
|
+
"sessionId": "ses_b6afc57b153e2ae1f8fb1025",
|
|
261
|
+
"txHash": "0xbeaf32d41f8f7573e02653a79e02bf56a73ae667dca203acb9e5c181116914a9"
|
|
262
|
+
}
|
|
263
|
+
~~~
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# Verification Procedure {#verification}
|
|
267
|
+
|
|
268
|
+
Upon receiving a credential with method "stableyard" and intent
|
|
269
|
+
"charge", the server MUST execute the following steps:
|
|
270
|
+
|
|
271
|
+
1. Extract `sessionId` from `credential.payload`.
|
|
272
|
+
|
|
273
|
+
2. Call `POST /v2/sessions/{sessionId}/verify` on the Stableyard
|
|
274
|
+
API with the server's API key.
|
|
275
|
+
|
|
276
|
+
3. The Stableyard API returns `{verified: true}` if and only if:
|
|
277
|
+
- The session exists and is in "settled" status
|
|
278
|
+
- The session amount matches the challenge amount
|
|
279
|
+
- The session destination matches the challenge destination
|
|
280
|
+
- The session has a `resource` field matching the challenge realm
|
|
281
|
+
|
|
282
|
+
4. If verification succeeds, the server MUST invalidate the
|
|
283
|
+
challenge and return HTTP 200 with the resource.
|
|
284
|
+
|
|
285
|
+
5. If verification fails (session not settled, amount mismatch,
|
|
286
|
+
or API error), the server MUST return HTTP 402 with a fresh
|
|
287
|
+
challenge and a Problem Details {{RFC9457}} body.
|
|
288
|
+
|
|
289
|
+
## Challenge Binding
|
|
290
|
+
|
|
291
|
+
The server SHOULD set the `resource` field when the client creates
|
|
292
|
+
the Stableyard session, binding the session to the specific API
|
|
293
|
+
endpoint being paid for. The Stableyard verify endpoint checks this
|
|
294
|
+
field to prevent session reuse across different resources.
|
|
295
|
+
|
|
296
|
+
## Delegated Verification
|
|
297
|
+
|
|
298
|
+
Unlike methods where the server independently verifies a
|
|
299
|
+
cryptographic proof (e.g., preimage in Lightning, signed transaction
|
|
300
|
+
in Tempo), the stableyard method delegates verification to
|
|
301
|
+
Stableyard's API. This is analogous to how the "stripe" method
|
|
302
|
+
delegates verification to Stripe's API.
|
|
303
|
+
|
|
304
|
+
This design choice enables:
|
|
305
|
+
- Chain-agnostic verification (server needs no blockchain RPC)
|
|
306
|
+
- Cross-chain settlement (Stableyard handles routing)
|
|
307
|
+
- Unified merchant experience (one API for all chains)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# Settlement Procedure
|
|
311
|
+
|
|
312
|
+
Settlement is handled entirely by Stableyard's Intent Engine:
|
|
313
|
+
|
|
314
|
+
1. Client creates a session with `sourceChain` parameter.
|
|
315
|
+
2. Stableyard returns a deposit address on the specified chain.
|
|
316
|
+
3. Client sends the requested amount to the deposit address.
|
|
317
|
+
4. Client calls `POST /v2/sessions/{id}/submit-tx` with the
|
|
318
|
+
transaction hash for faster detection.
|
|
319
|
+
5. Stableyard detects the payment and routes it to the merchant's
|
|
320
|
+
configured settlement destination.
|
|
321
|
+
|
|
322
|
+
Settlement timing depends on the route:
|
|
323
|
+
|
|
324
|
+
| Route | Estimated Time |
|
|
325
|
+
|-------|---------------|
|
|
326
|
+
| Same-chain (e.g., Base to Base) | ~1 second |
|
|
327
|
+
| Cross-chain (e.g., Polygon to Base) | ~15 seconds |
|
|
328
|
+
|
|
329
|
+
## Deposit Types
|
|
330
|
+
|
|
331
|
+
Stableyard returns one of two deposit types based on the route:
|
|
332
|
+
|
|
333
|
+
direct_transfer:
|
|
334
|
+
: Same-chain payments. Client sends an ERC20 `transfer()` to the
|
|
335
|
+
deposit address.
|
|
336
|
+
|
|
337
|
+
gasyard:
|
|
338
|
+
: Cross-chain payments via Stableyard's gateway contract. Client
|
|
339
|
+
executes `approve()` + `deposit()` transactions. Gateway calldata
|
|
340
|
+
is provided in the session response.
|
|
341
|
+
|
|
342
|
+
## Gasless Payments
|
|
343
|
+
|
|
344
|
+
If the client has an active Stableyard Vault (Gnosis Safe), it MAY
|
|
345
|
+
pay via `POST /v2/sessions/{id}/pay` with an EIP-712 {{EIP-712}}
|
|
346
|
+
signed message. This enables zero-gas payments — Stableyard executes
|
|
347
|
+
the transaction on behalf of the client.
|
|
348
|
+
|
|
349
|
+
## Receipt Generation
|
|
350
|
+
|
|
351
|
+
Upon successful verification, the server MUST return a
|
|
352
|
+
`Payment-Receipt` header containing:
|
|
353
|
+
|
|
354
|
+
~~~json
|
|
355
|
+
{
|
|
356
|
+
"method": "stableyard",
|
|
357
|
+
"status": "success",
|
|
358
|
+
"reference": "ses_b6afc57b153e2ae1f8fb1025",
|
|
359
|
+
"timestamp": "2026-03-19T16:38:34Z"
|
|
360
|
+
}
|
|
361
|
+
~~~
|
|
362
|
+
|
|
363
|
+
The `reference` field contains the Stableyard session ID, which can
|
|
364
|
+
be used for reconciliation via the Stableyard merchant dashboard.
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# Error Responses
|
|
368
|
+
|
|
369
|
+
Failed payment attempts MUST return HTTP 402 with a fresh challenge
|
|
370
|
+
and a Problem Details {{RFC9457}} body. Error types:
|
|
371
|
+
|
|
372
|
+
verification-failed:
|
|
373
|
+
: Session is not in "settled" status, or amount/destination mismatch.
|
|
374
|
+
|
|
375
|
+
invalid-session:
|
|
376
|
+
: Session ID is unknown or expired.
|
|
377
|
+
|
|
378
|
+
malformed-credential:
|
|
379
|
+
: Credential payload is missing `sessionId` or is not valid JSON.
|
|
380
|
+
|
|
381
|
+
settlement-failed:
|
|
382
|
+
: Stableyard detected the payment but settlement routing failed.
|
|
383
|
+
This triggers an automatic refund via Stableyard.
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# Security Considerations
|
|
387
|
+
|
|
388
|
+
## Transport Security
|
|
389
|
+
|
|
390
|
+
All interactions between client, server, and Stableyard API MUST
|
|
391
|
+
use TLS 1.2 or later. Stableyard API endpoints enforce HTTPS.
|
|
392
|
+
|
|
393
|
+
## Session Uniqueness
|
|
394
|
+
|
|
395
|
+
Each Stableyard session is single-use. Once verified, the session
|
|
396
|
+
transitions to a consumed state and cannot be reused for another
|
|
397
|
+
challenge. The Stableyard API enforces this server-side.
|
|
398
|
+
|
|
399
|
+
## Amount Verification
|
|
400
|
+
|
|
401
|
+
Clients SHOULD verify that the requested amount, currency, and
|
|
402
|
+
destination are reasonable before creating a session and sending
|
|
403
|
+
funds. The Stableyard API validates that the payment amount matches
|
|
404
|
+
the session amount during settlement.
|
|
405
|
+
|
|
406
|
+
## API Key Security
|
|
407
|
+
|
|
408
|
+
Server-side API keys (`sy_secret_*`) MUST be treated as secrets.
|
|
409
|
+
They MUST NOT be exposed in client-side code, logs, or error
|
|
410
|
+
responses. Client-side operations SHOULD use public keys
|
|
411
|
+
(`sy_pub_*`) which have restricted permissions.
|
|
412
|
+
|
|
413
|
+
## Replay Protection
|
|
414
|
+
|
|
415
|
+
Replay attacks are prevented at two levels:
|
|
416
|
+
1. MPP challenge IDs are HMAC-bound and single-use per the base
|
|
417
|
+
specification {{I-D.httpauth-payment}}.
|
|
418
|
+
2. Stableyard sessions are single-use — once verified, the session
|
|
419
|
+
cannot be presented again.
|
|
420
|
+
|
|
421
|
+
## Destination Verification
|
|
422
|
+
|
|
423
|
+
Servers MUST verify that the `destination` in the credential's
|
|
424
|
+
session matches the `destination` in the original challenge. The
|
|
425
|
+
Stableyard verify endpoint performs this check automatically.
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
# IANA Considerations
|
|
429
|
+
|
|
430
|
+
## Payment Method Registration
|
|
431
|
+
|
|
432
|
+
This document requests registration of the following payment method
|
|
433
|
+
in the "HTTP Payment Methods" registry:
|
|
434
|
+
|
|
435
|
+
Method: stableyard
|
|
436
|
+
|
|
437
|
+
Description: Cross-chain stablecoin payments via Stableyard
|
|
438
|
+
settlement network. Supports USDC and USDT across multiple EVM
|
|
439
|
+
chains, Solana, and Movement, with fiat settlement available.
|
|
440
|
+
|
|
441
|
+
Reference: This document
|
|
442
|
+
|
|
443
|
+
Contact: Mitesh Metha (mitesh@stableyard.fi)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
## Payment Intent Registration
|
|
447
|
+
|
|
448
|
+
This document requests registration of the following payment intent
|
|
449
|
+
for the "stableyard" method:
|
|
450
|
+
|
|
451
|
+
Intent: charge
|
|
452
|
+
|
|
453
|
+
Method: stableyard
|
|
454
|
+
|
|
455
|
+
Description: One-time cross-chain payment for resource access.
|
|
456
|
+
Client creates a Stableyard session, funds it from any supported
|
|
457
|
+
chain, and presents the session ID as proof.
|
|
458
|
+
|
|
459
|
+
Reference: This document
|
|
460
|
+
|
|
461
|
+
Contact: Mitesh Metha (mitesh@stableyard.fi)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# Examples
|
|
465
|
+
|
|
466
|
+
## Initial Request and 402 Challenge
|
|
467
|
+
|
|
468
|
+
Client requests a protected resource:
|
|
469
|
+
|
|
470
|
+
~~~
|
|
471
|
+
GET /api/market-data HTTP/1.1
|
|
472
|
+
Host: api.example.com
|
|
473
|
+
~~~
|
|
474
|
+
|
|
475
|
+
Server responds with a payment challenge:
|
|
476
|
+
|
|
477
|
+
~~~
|
|
478
|
+
HTTP/1.1 402 Payment Required
|
|
479
|
+
WWW-Authenticate: Payment id="POudmDHUpiZKtSqrMfFHAdEtLTsc",
|
|
480
|
+
realm="api.example.com",
|
|
481
|
+
method="stableyard",
|
|
482
|
+
intent="charge",
|
|
483
|
+
request="eyJhbW91bnQiOiIxMDAwMDAiLCJjdXJyZW5jeSI6IlVTRE
|
|
484
|
+
MiLCJkZWNpbWFscyI6NiwiZGVzdGluYXRpb24iOiJtZXJjaGFu
|
|
485
|
+
dEBzdGFibGV5YXJkIn0",
|
|
486
|
+
description="Market data feed",
|
|
487
|
+
expires="2026-03-19T16:49:52Z"
|
|
488
|
+
Cache-Control: no-store
|
|
489
|
+
Content-Type: application/problem+json
|
|
490
|
+
|
|
491
|
+
{
|
|
492
|
+
"type": "https://paymentauth.org/problems/payment-required",
|
|
493
|
+
"title": "Payment Required",
|
|
494
|
+
"status": 402,
|
|
495
|
+
"detail": "Payment is required (Market data feed)."
|
|
496
|
+
}
|
|
497
|
+
~~~
|
|
498
|
+
|
|
499
|
+
The decoded `request` parameter contains:
|
|
500
|
+
|
|
501
|
+
~~~json
|
|
502
|
+
{
|
|
503
|
+
"amount": "100000",
|
|
504
|
+
"currency": "USDC",
|
|
505
|
+
"decimals": 6,
|
|
506
|
+
"destination": "merchant@stableyard"
|
|
507
|
+
}
|
|
508
|
+
~~~
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
## Client Payment Flow
|
|
512
|
+
|
|
513
|
+
The client creates a Stableyard session:
|
|
514
|
+
|
|
515
|
+
~~~
|
|
516
|
+
POST /v2/sessions HTTP/1.1
|
|
517
|
+
Host: api.stableyard.fi
|
|
518
|
+
Authorization: Bearer sy_secret_...
|
|
519
|
+
Content-Type: application/json
|
|
520
|
+
|
|
521
|
+
{
|
|
522
|
+
"amount": 100000,
|
|
523
|
+
"destination": "merchant@stableyard",
|
|
524
|
+
"sourceChain": "base",
|
|
525
|
+
"resource": "api.example.com/charge"
|
|
526
|
+
}
|
|
527
|
+
~~~
|
|
528
|
+
|
|
529
|
+
Stableyard responds with deposit info:
|
|
530
|
+
|
|
531
|
+
~~~json
|
|
532
|
+
{
|
|
533
|
+
"id": "ses_b6afc57b153e2ae1f8fb1025",
|
|
534
|
+
"status": "open",
|
|
535
|
+
"deposit": {
|
|
536
|
+
"type": "direct_transfer",
|
|
537
|
+
"address": "0xdfd4ab80e163d6864e26f37540563cbf2e52a582",
|
|
538
|
+
"chainId": 8453,
|
|
539
|
+
"token": {
|
|
540
|
+
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
541
|
+
"symbol": "USDC",
|
|
542
|
+
"decimals": 6
|
|
543
|
+
},
|
|
544
|
+
"amount": {
|
|
545
|
+
"raw": "100000",
|
|
546
|
+
"formatted": "0.10"
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
~~~
|
|
551
|
+
|
|
552
|
+
The client sends 0.10 USDC to the deposit address on Base, then
|
|
553
|
+
submits the transaction hash. After settlement (~1 second for
|
|
554
|
+
same-chain), the client retries with the credential.
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
## Retry with Credential
|
|
558
|
+
|
|
559
|
+
~~~
|
|
560
|
+
GET /api/market-data HTTP/1.1
|
|
561
|
+
Host: api.example.com
|
|
562
|
+
Authorization: Payment eyJjaGFsbGVuZ2UiOnsiY2hhbGxlbmdlSWQi
|
|
563
|
+
OiJQT3VkbURIVXBpWkt0U3FyTWZGSEFkRXRMVHNjIiwibWV0aG9kIj
|
|
564
|
+
oic3RhYmxleWFyZCIsImludGVudCI6ImNoYXJnZSJ9LCJwYXlsb2FkI
|
|
565
|
+
jp7InNlc3Npb25JZCI6InNlc19iNmFmYzU3YjE1M2UyYWUxZjhmYjEw
|
|
566
|
+
MjUifX0
|
|
567
|
+
~~~
|
|
568
|
+
|
|
569
|
+
Server verifies via Stableyard API:
|
|
570
|
+
|
|
571
|
+
~~~
|
|
572
|
+
POST /v2/sessions/ses_b6afc57b153e2ae1f8fb1025/verify HTTP/1.1
|
|
573
|
+
Host: api.stableyard.fi
|
|
574
|
+
Authorization: Bearer sy_secret_...
|
|
575
|
+
~~~
|
|
576
|
+
|
|
577
|
+
~~~json
|
|
578
|
+
{
|
|
579
|
+
"verified": true,
|
|
580
|
+
"sessionId": "ses_b6afc57b153e2ae1f8fb1025",
|
|
581
|
+
"resource": "api.example.com/charge"
|
|
582
|
+
}
|
|
583
|
+
~~~
|
|
584
|
+
|
|
585
|
+
Server returns the resource with a receipt:
|
|
586
|
+
|
|
587
|
+
~~~
|
|
588
|
+
HTTP/1.1 200 OK
|
|
589
|
+
Payment-Receipt: eyJtZXRob2QiOiJzdGFibGV5YXJkIiwic3RhdHVzIj
|
|
590
|
+
oic3VjY2VzcyIsInJlZmVyZW5jZSI6InNlc19iNmFmYzU3YjE1M2Uy
|
|
591
|
+
YWUxZjhmYjEwMjUiLCJ0aW1lc3RhbXAiOiIyMDI2LTAzLTE5VDE2Oj
|
|
592
|
+
Mzjoy...
|
|
593
|
+
Content-Type: application/json
|
|
594
|
+
|
|
595
|
+
{
|
|
596
|
+
"data": {
|
|
597
|
+
"btc": {"price": 98450.23, "change24h": 2.1},
|
|
598
|
+
"eth": {"price": 3842.67, "change24h": -0.5}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
~~~
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
# Acknowledgements
|
|
605
|
+
|
|
606
|
+
The Machine Payments Protocol was created by Jake Moxey, Brendan
|
|
607
|
+
Ryan, Tom Meagher, and the teams at Tempo and Stripe.
|