Beersy
BRC-166

P2PKH Payments for HTTP 402

Software that wants to pay ten cents for an API call, like a script, a CI job, or an automated agent, usually doesn't have and doesn't want a full crypto wallet setup with and special serialization formats. Before this, a BSV payment for a paywalled web request required adopting that whole wallet ecosystem just to send a small amount of money, so the payment often simply didn't happen. This makes it possible for anything that can sign a basic transaction to pay for an HTTP request and have that payment checked by anyone, not just the seller.

Andy Rowechanged 28 Aug 202621 min read

Reference for an AI

Everything an assistant needs to answer questions about BRC-166 accurately, including what it depends on.

Summary

Why
Requiring a full wallet ecosystem (identity keys, key derivation, special transaction formats) just to pay a few for an API call is too heavy a lift for scripts and automated clients, so the payment often never happens.
What
BRC-166 defines a way to pay for an HTTP 402 Payment Required response using a plain Bitcoin SV transaction sent to a one-time invoice address, expressed inside the x402 Foundation payment protocol's wire format.
How
A server returns a 402 response with a fresh receiving address and price in a PAYMENT-REQUIRED header, and the client resends the same request with a PAYMENT-SIGNATURE header carrying a fully signed transaction paying that address, which the server itself verifies and broadcasts before serving the resource.

What this lets you do

  • Pay a 402-gated resource with just a signing key, no wallet stack
  • Issue a fresh one-time address per invoice to prevent replay across invoices
  • Verify a settlement independently from public chain data alone
  • Retry a paid request idempotently without being charged twice
  • Decline the scheme cleanly if a client can't produce a raw signed transaction

Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.

payeroriginPAYMENT-REQUIR

The specification

1. Abstract

This document specifies an ecosystem-independent, publicly-verifiable profile for HTTP 402 Payment Required settlement on BSV, using plain payments to a per-invoice address. Two properties distinguish it from the other BSV 402 specifications:

  1. No client wallet ecosystem is required. A payer needs only the ability to construct, sign and hex-encode a BSV transaction paying a given address - not a BRC-100 wallet, BRC-42 key derivation, an , or serialization. Any holder of BSV can pay, which is the entire reason this profile exists (Section 3).
  2. Settlement is verifiable by any third party from public chain data alone, without trusting the 's database, because a payment is keyed off the on-chain locking script rather than a private invoice record (Section 9.1). This is what lets a facilitator other than the payee confirm a payment - precisely the case the key-derived modes cannot serve without the payee's own wallet.

Concretely, the profile realizes the exact payment scheme of the x402 Foundation HTTP payment protocol (hereafter x402-F) as its wire format: a server advertises one or more PaymentRequirements objects, each naming a scheme, and a client selects one it can satisfy. x402-F's upstream schemes are built on delegated-pull primitives (EIP-3009-style signed authorizations executed later by a facilitator); Bitcoin has no such primitive, so this profile carries a complete, signed transaction in the payload and the origin server acts as its own facilitator - verifying the transaction locally and broadcasting it. The x402-F envelope is the realization, not the identity: the ecosystem-independent, self-verifying settlement model is the contribution.

2. Status Note - and an important name collision

There are two unrelated protocols named "x402" in circulation, and this document is about the one that is not BRC-120. Implementers who miss this will build the wrong thing, so it is stated before anything else:

BRC-120This document
Protocolx402: Stateless Settlement-Gated HTTP Protocol v1.0 (Merkle Works, frozen)x402 Foundation x402, protocol version 2
OriginBSV-native, specified in merkleworks-x402-specCross-chain, originally EVM; x402-foundation/x402
Challenge headerX402-ChallengePAYMENT-REQUIRED
Proof headerX402-ProofPAYMENT-SIGNATURE
Replay control + RFC 8785 canonical JSON bindingper-invoice address + server-side invoice state
Extension pointnone (frozen; conformance is all-or-nothing)scheme - which is what this document defines

The two share a number and an idea, not a wire format. They are not interoperable and neither is a profile of the other. This document does not modify, extend, or claim conformance with BRC-120.

Relationship to the other BSV 402 specifications:

  • BRC-105 (HTTP Service Monetization Framework) monetizes an already-authenticated BRC-103/104 session. It is the right choice when the client and server have a mutual-auth relationship and exchange. This binding has no session and no identity layer.
  • BRC-121 (Simple 402 Payments) is the nearest neighbour in spirit - deliberately minimal, single round-trip pair. It differs in requiring a BRC-42-compatible wallet on the client, BRC-29 derivation, BEEF with ancestry, and an identity key per payer.
  • BRC-27 (Direct Payment Protocol) is not HTTP-status-driven.

The distinguishing property of this binding is that the paying client is not required to run a BSV wallet-ecosystem stack - no BRC-100 wallet, BRC-29/BRC-42 derivation, identity key, or BEEF. It needs only a key that controls some BSV and the ability to sign a raw transaction. That is the entire reason it exists; see Section 3. Everything it gives up relative to BRC-121 follows from that one choice, and those costs are stated in Section 9 rather than buried.

3. Motivation

The intended payer is an autonomous agent, a CI job, or a script - software whose HTTP stack is being taught one 402 grammar by its SDK vendor, and that grammar is increasingly x402-F. Such a client typically has:

  • an HTTP client with middleware hooks,
  • the ability to sign a transaction (or an API that will), and
  • no BSV wallet stack, no identity key, and no reason to acquire one.

Requiring BRC-42 derivation and BEEF from that client is not a small ask; it is a request to adopt an ecosystem in order to spend ten . The observed consequence is that the payment does not happen.

To be precise about the floor: this profile does assume the payer can spend BSV - it must hold a key with enough funds to cover the invoice and be able to build and sign a P2PKH-spending transaction (a few lines with any general Bitcoin library). That is a wallet only in the minimal sense of key custody plus signing. What it is not is the BSV wallet ecosystem - no wallet application, identity key, BRC-29/BRC-42 derivation, or BEEF

  • and shedding that ecosystem requirement is exactly what this profile buys.

Conversely, x402-F's existing schemes cannot be honestly implemented on Bitcoin. A UTXO payment is a complete signed transaction; there is nothing left for a third-party facilitator to "execute" afterwards. Advertising scheme: "exact" while shipping a raw transaction would be a false statement in the one field a client reads to decide how to pay. Naming the scheme for what it actually is preserves the protocol's own failure mode: an unrecognised scheme is one a client can cleanly decline.

Design goals:

  1. No client-side wallet standard. Sign a transaction to an address; that is all.
  2. Unambiguous attribution. A payment settles exactly one invoice, by construction.
  3. Single round trip on the paying request. Payment travels with the retry.
  4. Honest field semantics. No field claims more than the implementation delivers.

4. Terminology

  • Origin - the HTTP server exposing the paid resource. Under this scheme the origin is also the facilitator: it verifies and broadcasts.
  • Payer - the client. It holds no account with the origin and is not identified.
  • Invoice - server-side record of one challenge: a resource, an amount, a unique receiving address, and an expiry. This scheme is pay-per-call and mints no reusable credential; a settled invoice is retained only for idempotent retries (Section 5.6).

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and OPTIONAL are to be interpreted as described in RFC 2119 and RFC 8174.

5. Specification

5.1 Scheme identifier

scheme = "exact"

An origin implementing this document MUST advertise exactly this string in the scheme field of any PaymentRequirements object it offers for BSV settlement, and MUST reject a PAYMENT-SIGNATURE payload whose accepted.scheme is present and names a different scheme, rather than attempting to interpret it.

Disambiguating two exact addressing modes on BSV. The sibling specification x402#2890 also advertises scheme: "exact" on bsv:mainnet, for a BRC-29/BRC-42 derived-address payload carried as BEEF. scheme and network therefore do not, by themselves, tell a payer which payload to construct - a client that guessed would build the wrong transaction. This profile is identified by an explicit payload-format discriminator in extra (Section 5.3): an origin offering this plain-P2PKH mode MUST set extra.payloadFormat.kind to "p2pkh-rawtx" and MUST include extra.lockingScriptHex, and MUST NOT carry the BRC-29/BRC-42 derivation fields that #2890 defines (e.g. derivationPrefix, derivationSuffix, a senderIdentityKey). A payer MUST select this profile only when it recognises extra.payloadFormat.kind as "p2pkh-rawtx" - or, for an origin predating this field, only when extra.lockingScriptHex is present and no BRC-29 derivation fields are - and MUST otherwise decline the offer exactly as it would an unknown scheme, rather than guessing the payload shape. An origin MUST NOT describe both addressing modes in a single PaymentRequirements object; to offer both it MUST emit two entries in the accepts array, one per mode, each self-identified by its discriminator.

Design (non-normative): why BSV carries two exact modes. In x402, exact names an amount semantic, and each network otherwise defines a single payload for (exact, network). BSV is a deliberate exception: it supports both a wallet-ecosystem addressing mode (BRC-29/BRC-42 derivation, #2890) and a permissionless plain-P2PKH mode (this document), and the second exists precisely to serve payers who cannot adopt the first. The two are complementary, not competing. The clean convention for their coexistence is that both modes carry an explicit extra.payloadFormat.kind, so a payer dispatches on (scheme, network, payloadFormat.kind) and never guesses from a silent default. This document self-identifies as "p2pkh-rawtx"; the derived-address mode is encouraged to self-identify symmetrically (e.g. "brc29-beef"). A single umbrella scheme document for BSV exact MAY enumerate both modes and their discriminators.

5.2 Network identifier

network = "bsv:mainnet"    (mainnet)
network = "bsv:testnet"    (testnet)

These are the registered CAIP-2 bsv: namespace references (ChainAgnostic/namespaces#190, merged 2026-08-18), which name the network directly. The older bip122:<genesis-hash> bitcoin-family form does not distinguish the chain - BSV shares its genesis block with BTC and BCH, so a fork is invisible to it - and MUST NOT be emitted. #2890 reached the same conclusion; this document matches it. Readers MAY still accept a legacy bip122: value for backward compatibility.

Origins MUST therefore also set asset to "BSV" and extra.chain to "bsv", and payers that care which chain they are spending on MUST read those fields rather than relying on network alone. Inventing a non-standard chain identifier was considered and rejected: no existing library would recognise it, and the ambiguity would still need documenting.

5.3 The 402 challenge

On an unpaid request to a protected resource, the origin MUST respond with status 402 Payment Required and a PAYMENT-REQUIRED header whose value is base64(JSON(PaymentRequired)), per x402-F v2.

The PaymentRequired object MUST contain x402Version: 2, a resource descriptor, and an accepts array containing at least one PaymentRequirements object of the following shape:

{
  "scheme": "exact",
  "network": "bsv:mainnet",
  "amount": "10",              // satoshis, decimal string (x402-F requires a string)
  "asset": "BSV",
  "payTo": "1Dy8XGHcuyjsfSs2QpyEiP1w4cVNQaXHYn",
  "maxTimeoutSeconds": 900,
  "extra": {
    "chain": "bsv",
    "unit": "satoshi",
    "invoiceId": "6f036062ef5f91f526faf7da",
    "lockingScriptHex": "76a9148e3f1506db9e226b5850554953829d536299312588ac",
    "expiresAt": "2026-08-21T12:00:00.000Z",
    "perCallSats": 10,
    "payloadFormat": { "kind": "p2pkh-rawtx", "rawtx": "...", "note": "..." },
    "submitUrl": "https://bsv.cx/pay/6f036062ef5f91f526faf7da/submit"
  }
}

Normative requirements on extra:

  • invoiceId (string) - REQUIRED. Opaque; identifies the invoice on settlement.
  • lockingScriptHex (string) - REQUIRED. The exact P2PKH the origin will match. Publishing the script, not merely the address, lets a payer construct the output that will be recognised byte-for-byte and removes address-encoding as a failure mode.
  • payloadFormat.kind (string) - REQUIRED. MUST be "p2pkh-rawtx". It identifies this plain-P2PKH raw-transaction addressing mode and is the discriminator that distinguishes this profile from the BRC-29/BRC-42 exact mode of #2890 (Section 5.1); a payer keys its choice of payload off this field. payloadFormat MAY carry additional advisory keys (e.g. a human-readable note), which a payer MUST ignore for mode selection.
  • expiresAt (RFC 3339 string) - REQUIRED. After this instant the origin MUST refuse settlement of this invoice.
  • chain, unit - REQUIRED, per Section 5.2.
  • perCallSats (number) - OPTIONAL, informational: the price of the one call this invoice buys. Under this pay-per-call scheme it equals amount; it is emitted so a reader need not divide.
  • submitUrl (string) - OPTIONAL. A URL to which a payer MAY POST the PaymentPayload (Section 5.5) as an alternative to retrying the original request with the PAYMENT-SIGNATURE header. Both settle the same invoice identically; a payer that ignores it loses nothing.

An origin MAY additionally carry the same offer in the response body and MAY emit a WWW-Authenticate challenge, for clients that do not read the header. Where both are present they MUST describe the same invoice.

5.4 payTo - per-invoice addresses

The origin MUST issue a fresh receiving address for every invoice, and MUST NOT reuse an address across invoices.

This is what makes attribution unambiguous without any identity layer: a transaction paying invoice X's address settles invoice X and nothing else. There is no "which invoice did this pay?" question, no cross-invoice replay, and no need for the payer to identify itself.

The derivation of that address is an origin-local matter and is deliberately not specified. An origin MAY derive it deterministically from a master secret and the invoice id (the reference implementation uses sha256(secret || ":" || invoiceId) as the private key scalar) so that funds remain sweepable without storing a key table, or MAY use any other method. The payer neither knows nor needs to know. Section 9.1 states what the payer consequently cannot verify.

5.5 The paying request

A payer that has obtained a challenge MUST retransmit the identical original request - same method, same URL, same body - with one header added:

PAYMENT-SIGNATURE: base64(JSON(PaymentPayload))

where PaymentPayload is:

{
  "x402Version": 2,
  "accepted": { /* the PaymentRequirements object the payer selected */ },
  "payload": {
    "lockingScriptHex": "76a9148e3f1506db9e226b5850554953829d536299312588ac",
    "rawtx": "0100000001..."     // hex-encoded, fully signed BSV transaction
  }
}

The transaction MUST contain one or more outputs whose locking script equals extra.lockingScriptHex, summing to at least amount satoshis. The payer SHOULD NOT broadcast the transaction itself (Section 5.6 explains why).

Origins MUST accept lockingScriptHex from either payload or accepted.extra, and MUST compare it case-insensitively. Rejecting an otherwise-valid payment over field placement or hex case costs the origin a sale and costs the payer a transaction fee that bought nothing; tolerance here is cheap and asymmetrically valuable. Tolerance MUST NOT extend to the scheme field (Section 5.1) - misreading what a payment means is how money goes missing.

5.6 Verification and settlement

On receiving a PAYMENT-SIGNATURE, the origin MUST, in order:

  1. Reject with 400 if the payload is not base64-encoded JSON, names an unsupported scheme, or omits the transaction or any payment identifier (lockingScriptHex preferred, invoiceId accepted).
  2. Resolve the invoice. Unknown -> 402 with errorReason: "invoice_not_found" (an unresolvable invoice is a failed payment, not a missing web resource; see Section 7).
  3. Verify that the invoice's resource matches the resource actually being requested. Without this check a payer could buy the cheapest invoice and present it at the most expensive endpoint; the payment would verify perfectly and buy the wrong thing (reject with 402, errorReason: "resource_mismatch"). This check MUST run before step 4: an already-settled invoice for resource A is not proof of payment for resource B, and the idempotent-replay shortcut in step 4 MUST NOT be allowed to bypass it.
  4. If the invoice is already settled, treat the request as paid and serve the resource again rather than charging a second time - regardless of the invoice's expiry, since expiry governs acceptance of new payment, not a resource already bought. Settlement MUST be idempotent on the invoice: agents retry by default, and a dropped response must not cost the payer twice. The scheme mints no credential, so there is nothing to re-issue on retry and nothing an on-chain observer could replay to obtain one.
  5. Expired -> 402 with errorReason: "invoice_expired".
  6. Parse the transaction. Sum the satoshis paid to lockingScriptHex. If less than amount, reject with 402 (errorReason: "insufficient_funds") and report both figures. Overpayment MUST be accepted. (A malformed or unparseable payload is a 400 per step 1, not a 402.)
  7. Broadcast the transaction. The origin, not the payer, broadcasts: this is how the origin actually collects, and it lets the network arbitrate double-spends. A broadcast rejection meaning "already known to the mempool" MUST be treated as success - it is the expected result of an idempotent retry. A transaction the network rejects (e.g. a double-spend or an already-spent input) is a failed payment: 402 with errorReason: "broadcast_rejected" and retryable: false. A failure to reach a broadcaster is an origin-side fault, not a bad payment: 500 with errorReason: "broadcast_unavailable" and retryable: true, and the payer SHOULD retry the same signed transaction rather than paying a second fee.
  8. Mark the invoice settled and serve the resource in the same response.

Concurrent settlement attempts for the same invoice MUST be serialized. The check "is this invoice already paid?" and the write "mark it paid" straddle a network round-trip; two concurrent submissions that both pass the check would both broadcast and both be served, giving away a second resource delivery for one payment.

Authorization is granted on mempool acceptance (0-conf), not on confirmation. See Section 9.2.

5.7 Settlement response (the scheme is credential-less)

This scheme is pay-per-call: one payment buys one resource access, and it issues no reusable credential. A second call is a second 402 and a second payment. This is deliberate - it keeps the money path stateless-per-payment and gives an observer nothing to steal.

On success the origin MUST respond with the resource, status 200, and:

PAYMENT-RESPONSE: base64({
  "success": true,
  "transaction": "<txid>",
  "network": "bsv:mainnet",
  "extensions": { "status": "mempool" }
})

The transaction id is the payer's receipt; there is no extra object and no token. The extensions.status field declares the confirmation policy in x402-F's extensions mechanism (the field the Cardano exact binding uses for it): this scheme authorizes on mempool acceptance (0-conf), stated in the response so a reader can judge the risk up front rather than discover it from a reorg (Section 9.2).

On a failed payment attempt the origin MUST emit PAYMENT-RESPONSE: base64({"success": false, "network": "...", "errorReason": "..."}) alongside the error status, so a client learns why in the protocol's own vocabulary (Section 7).

A note on layered credentials. Because a per-invoice address becomes public the moment the settlement transaction is broadcast, any reusable credential an origin might layer on top of this scheme MUST be bound to a secret held only by the payer - for example, the preimage of a hash committed at payment time - and MUST NOT be re-derivable from, or re-issuable in response to, public chain data; otherwise any observer who names a settled invoice by its on-chain locking script could obtain it. The base scheme sidesteps this concern entirely by carrying no credential: pay-per-call has nothing to steal.

5.8 Production precedes settlement (no charge on handler failure)

The origin broadcasts - the only point at which money moves - only after it has produced the resource. A handler that fails therefore means the transaction was never broadcast and the payer was never charged: there is no debit to refund and no reconciliation to get wrong. An origin that instead settles before producing MUST refund on every handler-failure path, but produce-then-broadcast is the safer construction and the one the reference implementation uses.

5.9 Discovery (non-normative)

x402-F catalogues resources through facilitator-side indexing of the bazaar extension, not through a well-known URL. An origin MAY publish a convenience document listing its resources, prices and input/output schemas; the reference implementation serves one at /.well-known/x402. This is not a standardised discovery endpoint and this document does not make it one. Discovery that costs something does not get discovered, so such a document SHOULD be free and unmetered.

6. Complete example

A complete, tokenless round trip against the reference origin: one payment buys one call, and the response carries the settlement txid as the receipt - no credential is minted.

$ curl -i https://bsv.cx/pay/hello

HTTP/2 402
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJwYXltZW50IHJlcXVpcmVkIiw...
www-authenticate: BSV-402 invoiceId="cbc69cd9d737b07ed1dd6430", amountSats="10"

Decoded, the payer learns it owes 10 satoshis to 1JtZVopxM5FAG9XdxjvPPG9YjuN5kPoVYi and that the invoice expires in 15 minutes. It signs a transaction paying that script and retries with the payment attached:

$ curl -i https://bsv.cx/pay/hello \
    -H "PAYMENT-SIGNATURE: $(printf '%s' "$PAYLOAD" | base64 -w0)"

HTTP/2 200
payment-response: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6...
x-pay-txid: 8beeaead43c578de...

{"resource":"hello","txid":"8beeaead43c578de...","data":{"ok":true,...}}

The PAYMENT-RESPONSE header decodes to {success, transaction, network, extensions:{status}}

  • no extra object, per Section 5.7 - and transaction is the payer's receipt. A second call is a new 402 and a new payment - there is no reusable credential.

Backed by a real payment. The example above reflects a captured mainnet settlement - transaction 102699c752889a60a0c2cf687ba361d98b89b3842a4cc2f7014a1c3302613a2a, whose output 0 pays 10 satoshis to the P2PKH script 76a914c43a7aaf410bceb07b7c45b1c44f96865f4b15dc88ac (1JtZVopxM5FAG9XdxjvPPG9YjuN5kPoVYi). Any BSV explorer will confirm it. This section asks you to verify it, not to trust it.

7. Error codes

This profile follows the x402-F v2 HTTP error model: the status line carries only a coarse outcome, and the specific reason travels in the errorReason field of the PAYMENT-RESPONSE header (echoed in the JSON body). A well-formed payment that fails verification or settlement is a 402, not a bespoke 4xx.

StatusMeaningExample errorReason
400Invalid payment: payload not base64 JSON, unsupported scheme, missing/unparseable transactionmalformed_payment
402Payment required (challenge, no payment presented) OR a well-formed payment that failed verification or settlementinsufficient_funds, invoice_not_found, invoice_expired, resource_mismatch, broadcast_rejected
500Origin-side fault while processing an otherwise-valid payment (e.g. a broadcaster could not be reached); retryable: truebroadcast_unavailable
503Payment rail unavailable or mispriced: the origin declines business before any payment, rather than selling below cost-
200Payment verified and settled; the resource is served in the same response-

The response body carries an explicit retryable boolean so a payer holding a signed transaction knows whether to try again or to stop: a 500 broadcast_unavailable is retryable: true (the origin could not reach a broadcaster; the transaction is valid and SHOULD be re-presented), while a 402 broadcast_rejected is retryable: false (the network rejected the payment, so re-sending only burns a second fee). Note that an unknown resource path - as opposed to a failed payment - is an ordinary 404, outside this payment-flow taxonomy.

8. Security considerations

  • Replay of a payment across invoices is structurally impossible (Section 5.4): the transaction pays an address that exists for exactly one invoice.
  • Replay of a settlement request is handled by idempotency (Section 5.6 step 4): a settled invoice re-serves the resource without charging again. Because the base scheme issues no credential, a replay yields at most a repeat of a resource already paid for - there is nothing an observer can obtain that they could not already fetch. No timestamp window is required, and consequently there is no clock-skew failure mode.
  • Cross-resource substitution is prevented by Section 5.6 step 3, which runs before the idempotency shortcut so a settled invoice can't be replayed against another resource.
  • No credential to steal. Being pay-per-call, the scheme mints no reusable credential, so the theft surface that a bearer token would create simply does not exist. An origin that chooses to layer its own credential on top MUST bind it to a payer-held secret and MUST NOT make it re-derivable from public chain data (Section 5.7).
  • Receiving keys are not spending keys. An origin SHOULD hold the payment-receiving secret separately from any hot wallet it spends from for other purposes. Commingling corrupts UTXO selection and the books at once.
  • Unmetered challenge creation is a cheap request that allocates server state. Origins SHOULD rate-limit challenge issuance, but MUST NOT rate-limit a request carrying payment ahead of settling it - refusing a paid request leaves the payer holding a broadcast transaction and nothing to show for it.

9. Limitations

These are the costs of the design goal in Section 3. They are listed at the same weight as the benefits because an implementer choosing between this and BRC-121 needs them.

9.1 The payer cannot verify the address is "theirs." Under BRC-29/BRC-42 derivation the payer participates in deriving the destination key and can confirm the output is correctly formed. Here the origin simply asserts an address. A payer's protection is that the address is unique to its invoice and that non-delivery is publicly attributable to a named service - reputational, not cryptographic. For payments large enough that this matters, use BRC-105 or BRC-121. This scheme is designed for amounts where the transaction fee is a meaningful fraction of the payment.

What is checkable by any third party, from public data alone, is the payment: given a challenge (which names payTo/lockingScriptHex, amount and invoiceId), anyone can confirm on-chain that a transaction pays that script for at least that amount, without the origin's cooperation. What is not checkable at base is the binding - that the address genuinely belongs to that invoice, and that the origin did not issue the same address to two payers. An origin MAY provide a stronger construction that makes the full invoice->address->transaction binding verifiable without trusting the origin (for example, a signed receipt) as an optional extension; the base scheme neither requires nor guarantees it.

9.2 Authorization is granted at 0-conf. The origin serves on mempool acceptance. A double-spend could in principle claw back the payment after the resource is delivered. For sub-cent amounts this is economically absurd - the attack costs more than it recovers - but it is a real property and it does not scale to large payments. Genuine settlement finality is a payment-channel upgrade, not a fix to this scheme.

9.3 No SPV ancestry. BRC-121 ships BEEF, so the origin can validate the payment's ancestry locally. Here the origin receives a bare transaction and must consult the network to broadcast it, which is a liveness dependency on a broadcaster and a per-payment latency cost.

9.4 No payer identity. There is no senderIdentityKey. The origin cannot correlate payments to a payer, offer refunds to a known party, or build reputation. For the target use case this is a feature; for a subscription business it is disqualifying.

9.5 The origin holds invoice state. Invoices are server-side records with expiry, unlike BRC-121's stateless verification, so an origin must persist and prune them. The footprint is small: the scheme is pay-per-call and holds no per-payer credential state - only short-lived invoice records that a settled payment can retire.

10. Implementations

Live on mainnet at https://bsv.cx/pay, serving several resources behind this scheme. Machine-readable catalogue: https://bsv.cx/.well-known/x402.

Source: the x402.ts and routes/pay.ts modules of the bsv.cx service. The wire shapes in Section 6 match that endpoint's live output field-for-field, and Section 6 is backed by a real captured mainnet settlement: transaction 102699c752889a60a0c2cf687ba361d98b89b3842a4cc2f7014a1c3302613a2a, whose output 0 pays 10 satoshis to the P2PKH script 76a914c43a7aaf410bceb07b7c45b1c44f96865f4b15dc88ac (1JtZVopxM5FAG9XdxjvPPG9YjuN5kPoVYi). Verify it on any BSV explorer rather than trust it.

11. References

Was this helpful?

Search Beersy

Search standards by number, title, author or topic