this repo has no description attested.network/
at main 294 lines 18 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>App Developers — attested.network</title> 7 <meta name="description" content="Guide for integrating attested payment verification into ATProtocol applications."> 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">Brokers</a></li> 49 <li><a href="app-developers.html" class="active">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> / App Developers</div> 61 <h1>App <span>Developers</span></h1> 62 <p class="subtitle">Integrate payment verification into your ATProtocol application.</p> 63 </div> 64</div> 65 66<main class="container"> 67 68 <!-- Why Verify Payments? --> 69 <section id="why-verify"> 70 <h2>Why Verify Payments?</h2> 71 <p class="section-desc">Payment verification unlocks a range of features for ATProtocol applications, from supporter recognition to premium content delivery.</p> 72 73 <div class="card-grid"> 74 <div class="card"> 75 <h4>Gate Content</h4> 76 <p>Restrict access to posts, media, or feeds based on whether a viewer has an active payment relationship with the creator.</p> 77 </div> 78 <div class="card"> 79 <h4>Supporter Badges</h4> 80 <p>Display visual indicators next to users who financially support a creator, building community recognition and social proof.</p> 81 </div> 82 <div class="card"> 83 <h4>Premium Features</h4> 84 <p>Unlock advanced functionality for paying supporters, such as extended upload limits, priority replies, or custom themes.</p> 85 </div> 86 <div class="card"> 87 <h4>Payment History</h4> 88 <p>Show a verifiable timeline of payments between accounts, useful for transparency dashboards and financial reporting.</p> 89 </div> 90 <div class="card"> 91 <h4>Sponsorship Proof</h4> 92 <p>Prove sponsorship relationships between accounts on-protocol, enabling verified sponsor listings and partnership displays.</p> 93 </div> 94 <div class="card"> 95 <h4>Access Control</h4> 96 <p>Combine payment status with other signals to build nuanced access policies for communities, groups, or collaborative spaces.</p> 97 </div> 98 </div> 99 </section> 100 101 <!-- Checking Payment Status --> 102 <section id="checking-status"> 103 <h2>Checking Payment Status</h2> 104 <p class="section-desc">The quickest way to check whether a payment exists between two accounts is to call <code>network.attested.payment.lookup</code> on a broker's XRPC endpoint.</p> 105 106 <p>Supply the <code>payer</code> and <code>recipient</code> DIDs as query parameters. You can optionally filter by <code>paymentType</code> using the full NSID of the payment collection (e.g. <code>network.attested.payment.recurring</code>). You can also pass one or more <code>brokers</code> DIDs to ensure results have at least one validating signature from a broker you trust, or pass <code>entitlements</code> AT-URIs to filter for payments that grant specific entitlements.</p> 107 108 <div class="code-block"> 109 <div class="code-label">Request</div> 110 <pre><span class="hl-comment">// GET request to broker's XRPC endpoint</span> 111GET /xrpc/network.attested.payment.lookup 112 <span class="hl-punc">?</span><span class="hl-key">payer</span><span class="hl-punc">=</span><span class="hl-str">did:plc:abc123supporter</span> 113 <span class="hl-punc">&amp;</span><span class="hl-key">recipient</span><span class="hl-punc">=</span><span class="hl-str">did:plc:xyz789creator</span> 114 <span class="hl-punc">&amp;</span><span class="hl-key">paymentType</span><span class="hl-punc">=</span><span class="hl-str">network.attested.payment.recurring</span> 115 <span class="hl-punc">&amp;</span><span class="hl-key">brokers</span><span class="hl-punc">=</span><span class="hl-str">did:plc:broker456</span></pre> 116 </div> 117 118 <div class="code-block"> 119 <div class="code-label">Response</div> 120 <pre><span class="hl-punc">{</span> 121 <span class="hl-key">"payments"</span><span class="hl-punc">:</span> <span class="hl-punc">[</span> 122 <span class="hl-punc">{</span> 123 <span class="hl-key">"$type"</span><span class="hl-punc">:</span> <span class="hl-str">"network.attested.payment.recurring"</span><span class="hl-punc">,</span> 124 <span class="hl-key">"uri"</span><span class="hl-punc">:</span> <span class="hl-str">"at://did:plc:abc123supporter/network.attested.payment.recurring/3abc"</span><span class="hl-punc">,</span> 125 <span class="hl-key">"cid"</span><span class="hl-punc">:</span> <span class="hl-str">"bafyreie..."</span><span class="hl-punc">,</span> 126 <span class="hl-key">"recipient"</span><span class="hl-punc">:</span> <span class="hl-str">"did:plc:xyz789creator"</span><span class="hl-punc">,</span> 127 <span class="hl-key">"amount"</span><span class="hl-punc">:</span> <span class="hl-num">500</span><span class="hl-punc">,</span> 128 <span class="hl-key">"currency"</span><span class="hl-punc">:</span> <span class="hl-str">"USD"</span><span class="hl-punc">,</span> 129 <span class="hl-key">"interval"</span><span class="hl-punc">:</span> <span class="hl-str">"monthly"</span><span class="hl-punc">,</span> 130 <span class="hl-key">"createdAt"</span><span class="hl-punc">:</span> <span class="hl-str">"2026-03-15T10:30:00Z"</span><span class="hl-punc">,</span> 131 <span class="hl-key">"entitlements"</span><span class="hl-punc">:</span> <span class="hl-punc">[</span> 132 <span class="hl-punc">{</span> <span class="hl-key">"uri"</span><span class="hl-punc">:</span> <span class="hl-str">"at://did:plc:xyz789creator/com.example.product/3jkl"</span><span class="hl-punc">,</span> <span class="hl-key">"cid"</span><span class="hl-punc">:</span> <span class="hl-str">"bafyreik..."</span> <span class="hl-punc">}</span> 133 <span class="hl-punc">]</span><span class="hl-punc">,</span> 134 <span class="hl-key">"signatures"</span><span class="hl-punc">:</span> <span class="hl-punc">[</span> 135 <span class="hl-punc">{</span> <span class="hl-key">"uri"</span><span class="hl-punc">:</span> <span class="hl-str">"at://did:plc:xyz789creator/network.attested.payment.proof/3def"</span><span class="hl-punc">,</span> <span class="hl-key">"cid"</span><span class="hl-punc">:</span> <span class="hl-str">"bafyreig..."</span> <span class="hl-punc">}</span><span class="hl-punc">,</span> 136 <span class="hl-punc">{</span> <span class="hl-key">"uri"</span><span class="hl-punc">:</span> <span class="hl-str">"at://did:plc:broker456/network.attested.payment.proof/3ghi"</span><span class="hl-punc">,</span> <span class="hl-key">"cid"</span><span class="hl-punc">:</span> <span class="hl-str">"bafyreih..."</span> <span class="hl-punc">}</span> 137 <span class="hl-punc">]</span> 138 <span class="hl-punc">}</span> 139 <span class="hl-punc">]</span> 140<span class="hl-punc">}</span></pre> 141 </div> 142 143 <div class="callout"> 144 <p><strong>Broker discovery.</strong> It is up to the recipient to contextually hint or broadcast which payment servicers (brokers) they use. The recipient&rsquo;s list may include more than one broker DID&mdash;each with a <code>#AttestedNetwork</code> service endpoint. Your application should resolve these DIDs and help the payer select the best option at that time, whether by preference order, availability, supported payment methods, or other criteria.</p> 145 </div> 146 </section> 147 148 <!-- Verification Walkthrough --> 149 <section id="verification"> 150 <h2>Verification Walkthrough</h2> 151 <p class="section-desc">When you need to cryptographically verify a payment record rather than trusting a broker's lookup response, follow these five steps.</p> 152 153 <ol class="steps"> 154 <li> 155 <strong>Fetch the payment record</strong> from the supporter's repository using <code>com.atproto.repo.getRecord</code>. The record lives under the <code>network.attested.payment</code> collection in the payer's repo. 156 </li> 157 <li> 158 <strong>Strip the <code>signatures</code> array</strong> from the record. The signatures are not part of the content-addressed payload and must be removed before hashing. 159 </li> 160 <li> 161 <strong>Prepare attestation metadata.</strong> Take each entry from the signatures array, add the repository DID as the signer identity, and strip the <code>cid</code> and <code>signature</code> fields. Insert the resulting object as the <code>$sig</code> field on the record. 162 </li> 163 <li> 164 <strong>Serialize and hash.</strong> Encode the prepared record as DAG-CBOR, hash the bytes with SHA-256, and wrap the digest as a CIDv1 with the DAG-CBOR codec (<code>0x71</code>). 165 </li> 166 <li> 167 <strong>Fetch and compare proof records.</strong> Using the <code>strongRef</code> URIs from the signatures array, retrieve the corresponding proof records from the creator's and broker's repositories. Confirm that the CID you computed matches the CID referenced in each proof record. 168 </li> 169 </ol> 170 171 <div class="callout"> 172 <p><strong>Why verify locally?</strong> Broker lookup is convenient but requires trusting the broker's response. Local verification lets your app independently confirm that the payment record is authentic and has been attested by the expected parties, without relying on any single intermediary.</p> 173 </div> 174 </section> 175 176 <!-- Trust Models --> 177 <section id="trust-models"> 178 <h2>Trust Models</h2> 179 <p class="section-desc">Your app can adopt different trust strategies depending on security requirements and the nature of the payment-gated feature.</p> 180 181 <ul class="trust-list"> 182 <li> 183 <strong>Strict</strong> 184 <span>Require attestation proofs from both the creator (recipient) and a specific trusted broker. This is the most secure model and is recommended for high-value access control, financial dashboards, or any context where payment fraud would have significant consequences.</span> 185 </li> 186 <li> 187 <strong>Creator-Trusted</strong> 188 <span>Accept any payment record that the creator has attested, regardless of which broker facilitated it. Suitable for social features like supporter badges or shout-outs, where the creator's endorsement is the primary signal.</span> 189 </li> 190 <li> 191 <strong>Federated</strong> 192 <span>Maintain a set of trusted broker DIDs and accept attestations from any of them. This balances security with flexibility, allowing your app to work with multiple brokers while still filtering out unknown or untrusted attestors. Good for platforms that aggregate content from many creators.</span> 193 </li> 194 </ul> 195 </section> 196 197 <!-- Initiating Payments --> 198 <section id="initiating-payments"> 199 <h2>Initiating Payments</h2> 200 <p class="section-desc">Your app can trigger a payment flow on behalf of the user, guiding them through the process without leaving your interface until checkout.</p> 201 202 <ol class="steps"> 203 <li> 204 <strong>Resolve the recipient's DID document</strong> to discover their available payment servicers. Use the identity resolution layer of ATProtocol to fetch the DID doc. 205 </li> 206 <li> 207 <strong>Find <code>#AttestedNetwork</code> service endpoints</strong> in the DID document. Each entry represents a broker or servicer that handles payments for this recipient. 208 </li> 209 <li> 210 <strong>Present available servicers to the user.</strong> If multiple endpoints exist, let the user choose which payment provider they prefer. 211 </li> 212 <li> 213 <strong>Call <code>network.attested.payment.initiate</code></strong> on the chosen servicer's XRPC endpoint, passing the product identifier and the payer's DID. 214 </li> 215 <li> 216 <strong>Redirect the payer to the returned URL.</strong> The servicer responds with a checkout URL and a polling token. Open the URL in the user's browser to complete the payment. 217 </li> 218 <li> 219 <strong>Poll <code>network.attested.payment.status</code></strong> with the token to track the payment outcome. On success, the response includes a <code>strongRef</code> pointing to the newly created payment record. 220 </li> 221 </ol> 222 223 <div class="mermaid-wrapper"> 224 <pre class="mermaid"> 225sequenceDiagram 226 participant App 227 participant DIDDoc as Recipient DID Doc 228 participant Servicer as Payment Servicer 229 participant Browser as Payer Browser 230 231 App->>DIDDoc: Resolve recipient DID 232 DIDDoc-->>App: Service endpoints (#AttestedNetwork) 233 App->>Servicer: network.attested.payment.initiate 234 Servicer-->>App: checkoutUrl + pollingToken 235 App->>Browser: Redirect to checkoutUrl 236 Browser->>Servicer: Complete payment 237 Servicer-->>Browser: Payment confirmation 238 loop Poll for result 239 App->>Servicer: network.attested.payment.status(token) 240 end 241 Servicer-->>App: strongRef (success) or error (failed) 242 </pre> 243 </div> 244 245 <div class="code-block"> 246 <div class="code-label">Initiate Request</div> 247 <pre><span class="hl-punc">{</span> 248 <span class="hl-key">"recipient"</span><span class="hl-punc">:</span> <span class="hl-str">"did:plc:xyz789creator"</span><span class="hl-punc">,</span> 249 <span class="hl-key">"payer"</span><span class="hl-punc">:</span> <span class="hl-str">"did:plc:abc123supporter"</span><span class="hl-punc">,</span> 250 <span class="hl-key">"product"</span><span class="hl-punc">:</span> <span class="hl-str">"monthly-subscription"</span> 251<span class="hl-punc">}</span></pre> 252 </div> 253 254 <div class="code-block"> 255 <div class="code-label">Initiate Response</div> 256 <pre><span class="hl-punc">{</span> 257 <span class="hl-key">"checkoutUrl"</span><span class="hl-punc">:</span> <span class="hl-str">"https://broker.example.com/pay/sess_abc123"</span><span class="hl-punc">,</span> 258 <span class="hl-key">"token"</span><span class="hl-punc">:</span> <span class="hl-str">"poll_tok_abc123"</span> 259<span class="hl-punc">}</span></pre> 260 </div> 261 262 <div class="code-block"> 263 <div class="code-label">Status Response (Success)</div> 264 <pre><span class="hl-punc">{</span> 265 <span class="hl-key">"status"</span><span class="hl-punc">:</span> <span class="hl-str">"completed"</span><span class="hl-punc">,</span> 266 <span class="hl-key">"paymentRef"</span><span class="hl-punc">:</span> <span class="hl-punc">{</span> 267 <span class="hl-key">"uri"</span><span class="hl-punc">:</span> <span class="hl-str">"at://did:plc:abc123supporter/network.attested.payment/3abc"</span><span class="hl-punc">,</span> 268 <span class="hl-key">"cid"</span><span class="hl-punc">:</span> <span class="hl-str">"bafyreie..."</span> 269 <span class="hl-punc">}</span> 270<span class="hl-punc">}</span></pre> 271 </div> 272 </section> 273 274 <!-- Private Payments --> 275 <section id="private-payments"> 276 <h2>Private Payments</h2> 277 <p class="section-desc">Payment records stored in Permissioned Data Spaces follow the same verification mechanics described above.</p> 278 279 <p>The only difference is access: records in a permissioned space are not publicly readable. Your application must hold valid space credentials to fetch the payment record and its associated proof records. Once retrieved, the verification steps (stripping signatures, computing the CID, and comparing against proof records) are identical to public payments.</p> 280 281 <div class="callout"> 282 <p><strong>Credential requirements.</strong> Contact the space operator to obtain read credentials. Your app will need to present these credentials when calling <code>com.atproto.repo.getRecord</code> for records stored in the permissioned space. The broker lookup endpoint may also require credentials if the broker enforces access control on private payment data.</p> 283 </div> 284 </section> 285 286</main> 287 288<footer> 289 <div class="container"> 290 <p>attested.network &middot; Built on <a href="https://badge.blue">badge.blue</a> attestations &middot; <a href="https://atproto.com">ATProtocol</a></p> 291 </div> 292</footer> 293</body> 294</html>