this repo has no description attested.network/
at main 345 lines 19 kB view raw
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 &mdash; 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 &mdash; 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 &middot; 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 &middot; 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 &middot; 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 &mdash; 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 &mdash; the payer, the recipient, and the broker itself &mdash; 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 &mdash; whether through cancellation, failed retries, or refund &mdash; 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 &middot; Built on <a href="https://badge.blue">badge.blue</a> attestations &middot; <a href="https://atproto.com">ATProtocol</a></p> 342 </div> 343</footer> 344</body> 345</html>