BIP: 270 Title: Simplified Payment Protocol Author: Wallet Workshop Created: 2019-02-22
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:
- Customer adds items to an online shopping basket, and decides to pay using Bitcoin.
- Payment host generates a unique payment address, associates it with the customer's order, and asks the customer to pay.
- 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.
- Customer authorizes payment to the payment host's address and broadcasts the transaction through the Bitcoin p2p network.
- Payment host's server detects payment and after sufficient transaction confirmations considers the transaction final.
This BIP extends the naive protocol to simplify wallets, add new features, and make every future interaction on-chain possible:
- Human-readable, secure payment destinations-- customers will be asked to authorize payment to "example.com" instead of an inscrutable, 34-character bitcoin address.
- Secure proof of payment, which the customer can use in case of a dispute with the payment host.
- 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.
- Payment is sent directly to the payment host, removing the necessity of the customer engaging with the Bitcoin p2p network, and enabling quicker confirmation.
- Payment received messages, so the customer knows immediately that the payment host has received, and has processed (or is processing) their payment.
- 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.
- Payment can contain multiple outputs which can be arbitrary scripts and not just addresses, making new applications possible.
- It is no longer necessary for wallets to connect to the Bitcoin p2p network to broadcast transactions.
- It is no longer necessary for payment hosts to maintain an address index to monitor for in coming transactions.
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.
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:
- 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.
- Validate that customer's system unix time (UTC) is before PaymentRequest.expirationTimestamp. If it is not, then the payment request must be rejected.
- 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).
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. |
- Creates and signs a transaction that satisfy (pay in full) PaymentRequest.outputs. It may add additional outputs if needed.
- Validate that customer's system unix time (UTC) is still before PaymentDetails.expirationTimestamp. If it is not, the payment should be cancelled.
- A POST a Payment message to the paymentUrl URL. The Payment message is serialized and sent as the body of the POST request.
- 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.
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
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
None yet. There will be an open-source implementation in bsv.
Javascript Object Signing and Encryption working group : http://datatracker.ietf.org/wg/jose/
Wikipedia's page on Invoices: http://en.wikipedia.org/wiki/Invoice especially the list of Electronic Invoice standards
sipa's payment protocol proposal: https://gist.github.com/1237788
ThomasV's "Signed Aliases" proposal : http://ecdsa.org/bitcoin_URIs.html
Homomorphic Payment Addresses and the Pay-to-Contract Protocol : http://arxiv.org/abs/1212.3257