this repo has no description
attested.network/
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Payment Brokers — attested.network</title>
7 <meta name="description" content="Guide for implementing a payment broker (servicer) in the attested.network specification.">
8 <link rel="preconnect" href="https://fonts.googleapis.com">
9 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
11 <link rel="icon" type="image/svg+xml" href="logo.svg">
12 <link rel="stylesheet" href="styles.css">
13 <script type="module">
14 import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
15 mermaid.initialize({
16 startOnLoad: true,
17 theme: 'base',
18 themeVariables: {
19 primaryColor: '#f5f3ff',
20 primaryTextColor: '#111827',
21 primaryBorderColor: '#ddd6fe',
22 secondaryColor: '#f8f9fb',
23 tertiaryColor: '#ffffff',
24 lineColor: '#8b5cf6',
25 textColor: '#111827',
26 mainBkg: '#f5f3ff',
27 nodeBorder: '#ddd6fe',
28 clusterBkg: '#faf9fc',
29 clusterBorder: '#e5e7eb',
30 fontFamily: 'Inter, -apple-system, system-ui, sans-serif',
31 fontSize: '13px'
32 }
33 });
34 </script>
35</head>
36<body>
37
38<div class="draft-banner">
39 <p><strong>Draft — Not yet published.</strong> This specification is in active development and is not ready for review or critique. Stay tuned for formal announcements.</p>
40</div>
41
42<div class="topnav">
43 <div class="topnav-inner">
44 <a class="topnav-logo" href="/"><img src="logo.svg" alt="" class="topnav-logo-img"><span><span>attested</span>.network</span></a>
45 <nav>
46 <ul class="topnav-links">
47 <li><a href="index.html">Spec</a></li>
48 <li><a href="brokers.html" class="active">Brokers</a></li>
49 <li><a href="app-developers.html">App Developers</a></li>
50 <li><a href="recipients.html">Recipients</a></li>
51 <li><a href="payers.html">Payers</a></li>
52 <li><a href="scenarios.html">Scenarios</a></li>
53 </ul>
54 </nav>
55 </div>
56</div>
57
58<div class="hero-sm">
59 <div class="hero-inner">
60 <div class="breadcrumb"><a href="index.html">Home</a> / Payment Brokers</div>
61 <h1>Payment <span>Brokers</span></h1>
62 <p class="subtitle">How to implement a payment servicer that processes transactions and writes attestation proofs.</p>
63 </div>
64</div>
65
66<main class="container">
67
68 <!-- What is a Broker? -->
69 <section>
70 <h2>What is a Broker?</h2>
71 <p class="section-desc">A broker (or payment servicer) facilitates payment between payers and recipients on ATProtocol and acts as a witness to the exchange.</p>
72
73 <p>A broker is whatever facilitates the exchange of value. One broker might process Stripe transactions. Another might handle peer-to-peer cash. Another might simply witness a virtual high-five. The role is to <strong>facilitate and witness</strong> the exchange between payer and recipient, then write <code>network.attested.payment.proof</code> records and expose XRPC endpoints for payment initiation and verification.</p>
74
75 <p>Recipients publish an ordered list of broker DIDs with <code>#AttestedNetwork</code> service endpoints in their DID document. When a payer wants to pay a recipient, the app resolves the recipient's DID document, finds the broker endpoints, and initiates the payment flow through one of them.</p>
76
77 <div class="callout">
78 <p><strong>Key principle:</strong> Brokers are untrusted intermediaries. The attestation proof they write is independently verifiable by anyone — the CID-based proof chain means you can confirm a payment without trusting the broker's word.</p>
79 </div>
80
81 <h3>Responsibilities</h3>
82 <ul class="steps">
83 <li>Accept payment initiation requests via XRPC from apps acting on behalf of payers</li>
84 <li>Facilitate the actual exchange (Stripe transaction, peer-to-peer cash, or any other mechanism)</li>
85 <li>Write payment records to the payer's repository via inter-service auth</li>
86 <li>Compute attestation CIDs and write proof records to the broker's own repository</li>
87 <li>Expose status and lookup endpoints so apps can verify payment state</li>
88 </ul>
89 </section>
90
91 <!-- DID Document Setup -->
92 <section>
93 <h2>DID Document Setup</h2>
94 <p class="section-desc">Brokers must configure an <code>#AttestedNetwork</code> service endpoint in their DID document so recipients can discover and reference them.</p>
95
96 <p>When a recipient adds your broker to their supported servicers list, they reference your DID. Apps then resolve your DID document, find the <code>#AttestedNetwork</code> service entry, and use its <code>serviceEndpoint</code> as the base URL for all XRPC calls.</p>
97
98 <p>Add the following service entry to your DID document:</p>
99
100 <div class="code-block">
101 <div class="code-label">DID Document Service Entry</div>
102<pre><span class="hl-punc">{</span>
103 <span class="hl-key">"id"</span><span class="hl-punc">:</span> <span class="hl-str">"#AttestedNetwork"</span><span class="hl-punc">,</span>
104 <span class="hl-key">"type"</span><span class="hl-punc">:</span> <span class="hl-str">"AttestedNetworkServicer"</span><span class="hl-punc">,</span>
105 <span class="hl-key">"serviceEndpoint"</span><span class="hl-punc">:</span> <span class="hl-str">"https://pay.example.com"</span>
106<span class="hl-punc">}</span></pre>
107 </div>
108
109 <div class="callout">
110 <p><strong>Note:</strong> The <code>serviceEndpoint</code> URL must be HTTPS and should point to the root of your XRPC service. All three payment methods (<code>initiate</code>, <code>status</code>, <code>lookup</code>) will be called against this base URL.</p>
111 </div>
112 </section>
113
114 <!-- XRPC Methods to Implement -->
115 <section>
116 <h2>XRPC Methods to Implement</h2>
117 <p class="section-desc">Brokers must implement three XRPC methods that apps use to initiate payments, check status, and look up verified payment records.</p>
118
119 <!-- initiate -->
120 <div class="lexicon-block">
121 <div class="lexicon-header">
122 <div class="lexicon-tag">Procedure · POST</div>
123 <div class="lexicon-name">network.attested.payment.initiate</div>
124 <p class="lexicon-desc">Initiate a new payment flow. Called by an app on behalf of a payer using inter-service auth. Returns an opaque token and a URL where the payer completes the payment in a browser.</p>
125 </div>
126 <div class="lexicon-body">
127 <p class="sub-heading">Input</p>
128 <table class="schema-table">
129 <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
130 <tbody>
131 <tr>
132 <td><span class="field-name">product</span></td>
133 <td><span class="field-type">string</span></td>
134 <td><span class="field-desc">Identifier for the product or subscription being purchased. Defined by the recipient.</span></td>
135 </tr>
136 </tbody>
137 </table>
138 <p class="sub-heading">Output</p>
139 <table class="schema-table">
140 <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
141 <tbody>
142 <tr>
143 <td><span class="field-name">token</span></td>
144 <td><span class="field-type">string</span></td>
145 <td><span class="field-desc">Opaque token used to track this payment session. Pass to <code>status</code> to poll for completion.</span></td>
146 </tr>
147 <tr>
148 <td><span class="field-name">url</span></td>
149 <td><span class="field-type">string</span></td>
150 <td><span class="field-desc">URL where the payer completes the payment flow in a browser (e.g. a Stripe Checkout page).</span></td>
151 </tr>
152 </tbody>
153 </table>
154 </div>
155 </div>
156
157 <!-- status -->
158 <div class="lexicon-block">
159 <div class="lexicon-header">
160 <div class="lexicon-tag">Query · GET</div>
161 <div class="lexicon-name">network.attested.payment.status</div>
162 <p class="lexicon-desc">Check the status of a payment initiated with a token from <code>initiate</code>. When completed, includes a strong reference to the proof record.</p>
163 </div>
164 <div class="lexicon-body">
165 <p class="sub-heading">Parameters</p>
166 <table class="schema-table">
167 <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
168 <tbody>
169 <tr>
170 <td><span class="field-name">token</span></td>
171 <td><span class="field-type">string</span></td>
172 <td><span class="field-desc">The opaque token returned from <code>initiate</code>.</span></td>
173 </tr>
174 </tbody>
175 </table>
176 <p class="sub-heading">Output</p>
177 <table class="schema-table">
178 <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
179 <tbody>
180 <tr>
181 <td><span class="field-name">status</span></td>
182 <td><span class="field-type">string</span></td>
183 <td><span class="field-desc">One of <code>"pending"</code>, <code>"completed"</code>, or <code>"failed"</code>.</span></td>
184 </tr>
185 <tr>
186 <td><span class="field-name">ref</span></td>
187 <td><span class="field-type">com.atproto.repo.strongRef</span> <span class="constraint">optional</span></td>
188 <td><span class="field-desc">When status is <code>"completed"</code>, a strong reference (URI + CID) to the <code>network.attested.payment.proof</code> record in the broker's repository.</span></td>
189 </tr>
190 </tbody>
191 </table>
192 </div>
193 </div>
194
195 <!-- lookup -->
196 <div class="lexicon-block">
197 <div class="lexicon-header">
198 <div class="lexicon-tag">Query · GET</div>
199 <div class="lexicon-name">network.attested.payment.lookup</div>
200 <p class="lexicon-desc">Look up verified payment records between a payer and recipient. Used by apps to check whether a payer has an active payment for a given recipient.</p>
201 </div>
202 <div class="lexicon-body">
203 <p class="sub-heading">Parameters</p>
204 <table class="schema-table">
205 <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
206 <tbody>
207 <tr>
208 <td><span class="field-name">payer</span></td>
209 <td><span class="field-type">did</span></td>
210 <td><span class="field-desc">DID of the payer.</span></td>
211 </tr>
212 <tr>
213 <td><span class="field-name">recipient</span></td>
214 <td><span class="field-type">did</span></td>
215 <td><span class="field-desc">DID of the recipient.</span></td>
216 </tr>
217 <tr>
218 <td><span class="field-name">paymentType</span></td>
219 <td><span class="field-type">string</span> <span class="constraint">optional</span></td>
220 <td><span class="field-desc">Filter by payment collection. Must be a full NSID: <code>network.attested.payment.oneTime</code>, <code>network.attested.payment.recurring</code>, or <code>network.attested.payment.scheduled</code>.</span></td>
221 </tr>
222 <tr>
223 <td><span class="field-name">brokers</span></td>
224 <td><span class="field-type">array(did)</span> <span class="constraint">optional, repeating</span></td>
225 <td><span class="field-desc">List of broker DIDs. When provided, only returns payments that have at least one validating signature from an identity in this list.</span></td>
226 </tr>
227 <tr>
228 <td><span class="field-name">entitlements</span></td>
229 <td><span class="field-type">array(at-uri)</span> <span class="constraint">optional, repeating</span></td>
230 <td><span class="field-desc">List of entitlement AT-URIs. When provided, only returns payments whose <code>entitlements</code> array contains one or more of the given references.</span></td>
231 </tr>
232 </tbody>
233 </table>
234 <p class="sub-heading">Output</p>
235 <table class="schema-table">
236 <thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
237 <tbody>
238 <tr>
239 <td><span class="field-name">payments</span></td>
240 <td><span class="field-type">array(union)</span></td>
241 <td><span class="field-desc">Array of verified payment records matching the query. Each element is a union of <code>network.attested.payment.oneTime</code>, <code>network.attested.payment.recurring</code>, or <code>network.attested.payment.scheduled</code>.</span></td>
242 </tr>
243 </tbody>
244 </table>
245 </div>
246 </div>
247
248 <div class="callout">
249 <p><strong>Auth model:</strong> The <code>initiate</code> method uses ATProtocol inter-service auth — the calling app presents a signed service token on behalf of the payer. The <code>status</code> and <code>lookup</code> methods can be called with standard auth or unauthenticated, depending on your privacy requirements.</p>
250 </div>
251 </section>
252
253 <!-- Writing Attestation Proofs -->
254 <section>
255 <h2>Writing Attestation Proofs</h2>
256 <p class="section-desc">When a payment completes, the broker writes the payment record and a corresponding attestation proof. This is the core of the system.</p>
257
258 <h3>Steps</h3>
259 <ol class="steps">
260 <li>Write the payment record (<code>oneTime</code>, <code>recurring</code>, or <code>scheduled</code>) to the <strong>payer's repository</strong> using inter-service auth.</li>
261 <li>Compute the attestation CID per the badge.blue spec: strip signatures from the record, add a <code>$sig</code> field containing the payer's repository DID, DAG-CBOR serialize, SHA-256 hash, produce a CIDv1.</li>
262 <li>Write a <code>network.attested.payment.proof</code> record to the <strong>broker's own repository</strong> containing the CID and an optional <code>status</code>.</li>
263 <li>Update the payment record's <code>signatures</code> array in the payer's repo with a <code>com.atproto.repo.strongRef</code> pointing to the proof record.</li>
264 </ol>
265
266 <div class="code-block">
267 <div class="code-label">Proof Record Example</div>
268<pre><span class="hl-punc">{</span>
269 <span class="hl-key">"$type"</span><span class="hl-punc">:</span> <span class="hl-str">"network.attested.payment.proof"</span><span class="hl-punc">,</span>
270 <span class="hl-key">"cid"</span><span class="hl-punc">:</span> <span class="hl-str">"bafyreig5..."</span>
271<span class="hl-punc">}</span></pre>
272 </div>
273
274 <h3>Payment Flow</h3>
275 <p>The following diagram shows the complete flow from payment completion through proof writing:</p>
276
277 <div class="mermaid-wrapper">
278 <pre class="mermaid">
279sequenceDiagram
280 participant PP as Payment Processor
281 participant B as Broker
282 participant PR as Payer's Repo
283 participant BR as Broker's Repo
284
285 PP->>B: Payment webhook (success)
286 B->>PR: Write payment record
287 PR-->>B: Record AT-URI + CID
288 B->>B: Compute attestation CID
289 B->>BR: Write proof record
290 BR-->>B: Proof AT-URI + CID
291 B->>PR: Update signatures array with strongRef
292 PR-->>B: Confirmed
293 </pre>
294 </div>
295 </section>
296
297 <!-- Private Payments -->
298 <section>
299 <h2>Private Payments</h2>
300 <p class="section-desc">For payments that should not be publicly visible, brokers manage Permissioned Data Spaces on behalf of the payer.</p>
301
302 <p>When a payment is marked as private, the broker additionally creates a permissioned space in the payer's repository and ensures that only the relevant parties — the payer, the recipient, and the broker itself — have read access to the payment record.</p>
303
304 <p>The attestation mechanics are identical to public payments. The proof record is still written to the broker's repository and the CID chain is still independently verifiable. The only difference is that the underlying payment record lives inside a permissioned space rather than being publicly readable.</p>
305
306 <div class="callout">
307 <p><strong>Implementation note:</strong> The broker must have write access to the payer's permissioned data space collection. This is granted via the same inter-service auth mechanism used for writing payment records. Ensure your access control logic correctly scopes permissions to only the records your broker manages.</p>
308 </div>
309 </section>
310
311 <!-- Invalidation -->
312 <section>
313 <h2>Invalidation</h2>
314 <p class="section-desc">When support ends — whether through cancellation, failed retries, or refund — the broker should invalidate the attestation proof.</p>
315
316 <div class="callout">
317 <p><strong>Guidance, not requirements.</strong> The following invalidation and retry processes are suggested patterns, not enforced rules. Brokers are free to implement whatever process they believe to be appropriate for their use case and their users.</p>
318 </div>
319
320 <p>To invalidate a payment, the broker deletes or updates its <code>network.attested.payment.proof</code> record and notifies the recipient. Apps that verify payments will see the proof is missing or marked invalid and treat the payment as no longer active.</p>
321
322 <h3>Recurring Payment Retries</h3>
323 <p>For recurring payments, a failed charge does not immediately invalidate the attestation. The broker should follow this retry schedule:</p>
324
325 <ul class="steps">
326 <li><strong>Day 1:</strong> First retry attempt after the initial charge failure.</li>
327 <li><strong>Day 3:</strong> Second retry attempt.</li>
328 <li><strong>Day 7:</strong> Third and final retry attempt.</li>
329 <li><strong>Day 8 (grace period expires):</strong> If all retries have failed, the broker deletes the proof record and notifies the recipient that the payment is no longer active.</li>
330 </ul>
331
332 <div class="callout">
333 <p><strong>Grace period:</strong> During the 8-day retry window, the existing proof record remains valid. The payer's access should not be interrupted while retries are in progress. Only after the grace period expires and all retries have failed should the broker invalidate the proof.</p>
334 </div>
335 </section>
336
337</main>
338
339<footer>
340 <div class="container">
341 <p>attested.network · Built on <a href="https://badge.blue">badge.blue</a> attestations · <a href="https://atproto.com">ATProtocol</a></p>
342 </div>
343</footer>
344</body>
345</html>