Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time

  BIP: 270
  Title: Simplified Payment Protocol
  Author: Wallet Workshop
  Created: 2019-02-22

Table of Contents


This BIP describes a protocol for communication between a payment host (usually either a merchant, a payment processor, or simply the recipient's wallet) and their customer (the sender of the funds), enabling both a better customer experience, simpler wallet infrastructure, more advanced features such as multiple outputs, and better security against man-in-the-middle attacks on the payment process.

This BIP is a simplified update to BIP 70 with a few important differences. First, we have switched from protobufs (protocol buffers) to JSON. Second, we have removed the redundant signatures from the messages and define authentication to happen at the communication later (usually HTTPS). Thirdly, we add a notion of optional output values and output descriptions. There are a few other minor differences such as removing the redundant PaymentDetails object and changing all variable names to camelCase, and changing some names and terms.

TODO: Create a spec for NFC application ID for this. Ask Rafa.


The naive Bitcoin payment protocol operates as follows:

  1. Customer adds items to an online shopping basket, and decides to pay using Bitcoin.
  2. Payment host generates a unique payment address, associates it with the customer's order, and asks the customer to pay.
  3. Customer copies the Bitcoin address from the payment host's web page and pastes it into whatever wallet they are using OR follows a bitcoin: link and their wallet is launched with the amount to be paid.
  4. Customer authorizes payment to the payment host's address and broadcasts the transaction through the Bitcoin p2p network.
  5. Payment host's server detects payment and after sufficient transaction confirmations considers the transaction final.
There are several major problems with the naive approach. First, it assumes the customer broadcasts the transaction, which requires that the customer connect to the Bitcoin p2p network and for the payment host to wait for the transaction to propagate before accepting the payment. Second, it has no ability to allow for multiple outputs, which is necessary for many interesting use-cases of Bitcoin such as OP_RETURN data. Thirdly, there is no reliable way for a payment host to refund a payment in case of an issue.

This BIP extends the naive protocol to simplify wallets, add new features, and make every future interaction on-chain possible:

  1. Human-readable, secure payment destinations-- customers will be asked to authorize payment to "" instead of an inscrutable, 34-character bitcoin address.
  2. Secure proof of payment, which the customer can use in case of a dispute with the payment host.
  3. Resistance from man-in-the-middle attacks that replace a payment host's bitcoin address with an attacker's address before a transaction is authorized with a hardware wallet.
  4. Payment is sent directly to the payment host, removing the necessity of the customer engaging with the Bitcoin p2p network, and enabling quicker confirmation.
  5. Payment received messages, so the customer knows immediately that the payment host has received, and has processed (or is processing) their payment.
  6. Refund addresses, automatically given to the payment host by the customer's wallet software, so payment hosts do not have to contact customers before refunding overpayments or orders that cannot be fulfilled for some reason.
  7. Payment can contain multiple outputs which can be arbitrary scripts and not just addresses, making new applications possible.
  8. It is no longer necessary for wallets to connect to the Bitcoin p2p network to broadcast transactions.
  9. It is no longer necessary for payment hosts to maintain an address index to monitor for in coming transactions.
Please note that the "payment host" in this document may not actually be the recipient of the funds. Like BitPay, the payment host may actually be a host or payment processor who is providing this protocol as a service for another party who is the actual recipient of the funds. We have chosen the name "payment" host to be an abstraction over either a merchant, a payment processor, a wallet, or any other agent that receives funds or acts as a proxy to whoever is the true recipient of the funds.


This BIP describes payment protocol messages encoded using JSON, authenticated and communicated using HTTPS or other authentication systems and communication systems (such as the blockchain itself). Future BIPs might extend this payment protocol to other encodings, PKI systems, or transport protocols.

The payment protocol consists of three messages; PaymentRequest, Payment, and PaymentACK, and begins with the customer somehow indicating that they are ready to pay and the payment host's server responding with a PaymentRequest message:


There are several JSON objects used in the protocol. We start with a summary of the JSON objects, and then proceed to the details.

  • Output: A way of specifying a Bitcoin transaction output, including the value and script.
  • PaymentRequest: A way of specifying the details of a required payment, including a list of outputs, expiration data, and other meta data.
  • Payment: Includes a signed Bitcoin transaction.
  • PaymentAck: Acknowledgement of receipt of payment.
Note that in all cases we try to stay as close as possible to the original BIP 70 spec, but with some important differences due to the fact that we are using JSON instead of protocol buffers. The two biggest differences are that all values are represented as numbers and all byte strings are represented as hex encoded strings. Do NOT prefix the hex encoded strings with "0x".


Outputs are used in PaymentRequest messages to specify where a payment (or part of a payment) should be sent. They are also used in Payment messages to specify where a refund should be sent.

Output {
  amount // number. required.
  script // string. required. hexadecimal script.
  description // string. optional. must not have JSON string length of greater than 100.

amount Number of satoshis (0.00000001 BTC) to be paid.
script A "TxOut" script where payment should be sent, formatted as a hexadecimal string.
description An optional description such as "tip" or "sales tax". Maximum length is 50 characters.


A payment request contains the informtion the consumer needs to make the payment to the payment host.

PaymentRequest {
    network // string. required. always set to "bitcoin".
    outputs // an array of outputs. required, but can have zero elements.
    creationTimestamp // number. required.
    expirationTimestamp // number. optional.
    memo // string. optional.
    paymentUrl // string. required.
    merchantData // string. optional.
network This field is required and always set to "bitcoin". If set to any other value besides "bitcoin", no wallet should process the payments. For test purposes, one can set this field to "test" which will ensure that no wallet will accidentally send a payment to what might be invalid or test addresses.
outputs One or more outputs where Bitcoins are to be sent.
creationTimestamp Unix timestamp (seconds since 1-Jan-1970 UTC) when the PaymentRequest was created.
expirationTimestamp Unix timestamp (UTC) after which the PaymentRequest should be considered invalid.
memo Note that should be displayed to the customer, explaining what this PaymentRequest is for. Maximum length is 50 characters.
paymentUrl Secure HTTPS location where a Payment message (see below) will be sent to obtain a PaymentACK. Maximum length is 4000 characters.
merchantData Arbitrary data that may be used by the payment host to identify the PaymentRequest. May be omitted if the payment host does not need to associate Payments with PaymentRequest or if they associate each PaymentRequest with a separate payment address. Maximum length is 10000 characters.

The paymentUrl specified in the PaymentRequest should remain valid at least until the PaymentRequest expirationTimestamp (or as long as possible if the PaymentRequest does not expire). Note that this is irrespective of any state change in the underlying payment request; for example cancellation of an order should not invalidate the paymentUrl, as it is important that the payment host's server can record mis-payments in order to refund the payment.

When a Bitcoin wallet application receives a PaymentRequest, it must authorize payment by doing the following:

  1. Validate the payment host's identity and signature using the PKI system. The PKI system is assumed to occur in a wrapper layer (such as HTTPS) and if no PKI system is available then the wallet can choose whether or not to display the payment request. The PKI system is usually HTTPS, and as such if the payment request is retrieved over HTTPS then the domain name should be displayed as the payment host.
  2. Validate that customer's system unix time (UTC) is before PaymentRequest.expirationTimestamp. If it is not, then the payment request must be rejected.
  3. Display the payment host's identity and ask the customer if they would like to submit payment (e.g. display the "Common Name" in the first X.509 certificate).
PaymentRequest messages larger than 10 MB should be rejected by the wallet application, to mitigate denial-of-service attacks.

Forward compatibility

Similarly to BIP-21, additional fields can be added to the payment request, but if these fields change the nature of the request itself, a required field should be used. Variables which are prefixed with a "req-" are considered required. If a client does not implement a specific field which is prefixed with "req-", it MUST consider the entire payment request invalid. Any other variables which are not implemented, but which are not prefixed with a "req-", can be safely ignored.


Payment messages are sent after the customer has authorized payment:

Payment {
  merchantData // string. optional.
  transaction // a hex-formatted (and fully-signed and valid) transaction. required.
  refundTo // string. paymail to send a refund to. optional.
  memo // string. optional.

merchantData copied from PaymentDetails.merchantData. Payment hosts may use invoice numbers or any other data they require to match Payments to PaymentRequests. Note that malicious clients may modify the merchantData, so should be authenticated in some way (for example, signed with a payment host-only key). Maximum length is 10000 characters.
transaction A valid, signed Bitcoin transaction that fully pays the PaymentRequest. The transaction is hex-encoded and must NOT be prefixed with "0x".
refundTo A paymail to send a refund to should a refund be necessary. Maximum length is 100 characters.
memo A plain-text note from the customer to the payment host.
If the customer authorizes payment, then the Bitcoin client:

  1. Creates and signs a transaction that satisfy (pay in full) PaymentRequest.outputs. It may add additional outputs if needed.
  2. Validate that customer's system unix time (UTC) is still before PaymentDetails.expirationTimestamp. If it is not, the payment should be cancelled.
  3. A POST a Payment message to the paymentUrl URL. The Payment message is serialized and sent as the body of the POST request.
  4. The client can optionally broadcast the transaction themselves to the Bitcoin network, but they are not required to do so as the payment host is expected to do so. If the payment host never broadcasts the transaction, then after two months the client can recover their funds.
Errors communicating with the paymentUrl server should be communicated to the user. In the scenario where the payment host's server receives multiple identical Payment messages for an individual PaymentRequest, it must acknowledge each. The second and further PaymentACK messages sent from the payment host's server may vary by memo field to indicate current state of the Payment (for example number of confirmations seen on the network). This is required in order to ensure that in case of a transport level failure during transmission, recovery is possible by the Bitcoin client re-sending the Payment message.

PaymentDetails.paymentUrl should be secure against man-in-the-middle attacks that might alter Payment.refundTo because the wallet and payment host are using HTTPS or some other secure transit layer.

Wallet software sending Payment messages via HTTPS must set appropriate Content-Type and Accept headers, as specified in BIP 271:

Content-Type: application/bitcoinsv-payment
Accept: application/bitcoinsv-paymentack

When the payment host's server receives the Payment message, it must determine whether or not the transaction satisfies conditions of payment. If and only if they do, it should broadcast the transaction to the the Bitcoin p2p network.

Payment messages larger than 10 MB should be rejected by the payment host's server, to mitigate denial-of-service attacks.


PaymentACK is the final message in the payment protocol; it is sent from the payment host's server to the bitcoin wallet in response to a Payment message:

PaymentACK {
    payment // Payment. required.
    memo // string. optional.
    error // number. optional.
payment Copy of the Payment message that triggered this PaymentACK. Clients may ignore this if they implement another way of associating Payments with PaymentACKs.
memo Note that should be displayed to the customer giving the status of the transaction (e.g. "Payment of 1 BTC for eleven tribbles accepted for processing.")
error A number indicating why the transaction was not accepted. 0 or undefined indicates no error. A 1 or any other positive integer indicates an error. The errors are left undefined for now; it is recommended only to use "1" and to fill the memo with a textual explanation about why the transaction was not accepted until further numbers are defined and standardized.

PaymentACK messages larger than 11 MB bytes should be rejected by the wallet application, to mitigate denial-of-service attacks. This is larger than the limits on Payment and PaymentRequest messages as PaymentACK contains a full Payment message within it.


Payment hosts that support multiple languages should generate language-specific PaymentRequests, and either associate the language with the request or embed a language tag in the request's merchantData. They should also generate a language-specific PaymentACK based on the original request.

For example: A greek-speaking customer browsing the Greek version of a payment host's website clicks on a "Αγορά τώρα" link, which generates a PaymentRequest with merchantData set to "lang=el&basketId=11252". The customer pays, their bitcoin client sends a Payment message, and the payment host's website responds with PaymentACK.message "σας ευχαριστούμε".


The original BIP 70 which this spec is based on included signatures from X.509 certificates (the same certificates use for SSL / TLS / HTTPS). We assume the authentication happens at a separate layer and as such for simplicity we have removed authentication from BIP 270. Wallets should use other mechanisms to determine the name and identify of the recipient in a secure manner. Under the usual case on the web, BIP 270 messages will be sent and received over HTTPS, which uses X.509 certificates, and thus the wallet should simply show the domain name under most cases. HTTP (and insecure form of HTTPS) should not be supported.


We use JSON and JSON is naturally extensible. Any property of the objects not specified in this spec is a candidate to convey information for extensions.


Where message length limits are described in MB, or other units, they should be taken to be the SI form. Therefore 10MB means 10,000,000 bytes, and not 10MiB which would be 10,485,760 bytes.

A previous draft of BIP270 required scripts be formatted as a human-readable "isomorphic" ASM-formatted string, to simplify debugging. However ASM is a UI concept; it is best left to user-facing software to decide how display scripts readably rather than enshrining it in a network protocol.

ASM also has some disadvantages, including:

  • it is a more verbose serialization giving larger message sizes
  • correct serialization and deserialization is a tricky, arcane process which would raise the chance of implementations differing in corner cases
  • not all scripts have a representation as "isomorphic" ASM
  • it is ill-defined when BSV moves CScriptNum from 4-byte integers to bignums, because data pushes that can be represented as a CScriptNum are serialized in decimal, and all other data pushes are serialized as hexadecimal. Digit strings that are valid decimal and hexadecimal are therefore resolved merely on the basis of whether their interpretation as decimal can fit into a CScriptNum
  • ASM is ill-defined during any future enablement or transition of OpCodes
By contrast hexadecimal script is easy to implement and unambiguous.


BIP 0271 : Simplified Payment Protocol mime types

BIP 0272 : Simplified Payment Protocol URIs

BIP 0273 : Use "Accept" header for response type negotiation with Simplified Payment Request URLs

BIP 0274 : Simplified Payment Protocol Fee Rate Information

BIP 0070 : The original Payment Protocol specification

BIP 0071 : The original Payment Protocol mime types

BIP 0072 : The original Payment Protocol bitcoin: URI extensions

Reference implementation

None yet. There will be an open-source implementation in bsv.

See Also

Javascript Object Signing and Encryption working group :

Wikipedia's page on Invoices: especially the list of Electronic Invoice standards

sipa's payment protocol proposal:

ThomasV's "Signed Aliases" proposal :

Homomorphic Payment Addresses and the Pay-to-Contract Protocol :