dash-docs/_includes/example_payment_processing.md

473 lines
18 KiB
Markdown

{% comment %}
This file is licensed under the MIT License (MIT) available on
http://opensource.org/licenses/MIT.
{% endcomment %}
{% assign filename="_includes/example_payment_processing.md" %}
## Payment Processing
{% include helpers/subhead-links.md %}
### Payment Protocol
{% include helpers/subhead-links.md %}
{% autocrossref %}
To request payment using the payment protocol, you use an extended (but
backwards-compatible) `bitcoin:` URI. For example:
{% endautocrossref %}
~~~
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
?amount=0.10\
&label=Example+Merchant\
&message=Order+of+flowers+%26+chocolates\
&r=https://example.com/pay.php/invoice%3Dda39a3ee
~~~
{% autocrossref %}
The browser, QR code reader, or other program processing the URI opens
the spender's Bitcoin wallet program on the URI. If the wallet program is
aware of the payment protocol, it accesses the URL specified in the `r`
parameter, which should provide it with a serialized PaymentRequest
served with the [MIME][] type `application/bitcoin-paymentrequest`<!--noref-->.
**Resource:** Gavin Andresen's [Payment Request Generator][] generates
custom example URIs and payment requests for use with testnet.
{% endautocrossref %}
#### PaymentRequest & PaymentDetails
{% include helpers/subhead-links.md %}
{% autocrossref %}
The [PaymentRequest][]{:#term-paymentrequest}{:.term} is created with data structures built using
Google's Protocol Buffers. BIP70 describes these data
structures in the non-sequential way they're defined in the payment
request protocol buffer code, but the text below will describe them in
a more linear order using a simple (but functional) Python CGI
program. (For brevity and clarity, many normal CGI best practices are
not used in this program.)
The full sequence of events is illustrated below, starting with the
spender clicking a `bitcoin:` URI or scanning a `bitcoin:` QR code.
![BIP70 Payment Protocol](/img/dev/en-payment-protocol.svg)
For the script to use the protocol buffer, you will need a copy of
Google's Protocol Buffer compiler (`protoc`), which is available in most
modern Linux package managers and [directly from Google.][protobuf] Non-Google
protocol buffer compilers are available for a variety of
programming languages. You will also need a copy of the PaymentRequest
[Protocol Buffer description][core paymentrequest.proto] from the Bitcoin Core source code.
{% endautocrossref %}
##### Initialization Code
{% include helpers/subhead-links.md %}
{% autocrossref %}
With the Python code generated by `protoc`, we can start our simple
CGI program.
{% highlight python %}
#!/usr/bin/env python
## This is the code generated by protoc --python_out=./ paymentrequest.proto
from paymentrequest_pb2 import *
## Load some functions
from time import time
from sys import stdout
from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign
## Copy three of the classes created by protoc into objects we can use
details = PaymentDetails()
request = PaymentRequest()
x509 = X509Certificates()
{% endhighlight %}
The startup code above is quite simple, requiring nothing but the epoch
(Unix date) time function, the standard out file descriptor, a few
functions from the OpenSSL library, and the data structures and
functions created by `protoc`.
{% endautocrossref %}
##### Configuration Code
{% include helpers/subhead-links.md %}
{% autocrossref %}
Next, we'll set configuration settings which will typically only change
when the receiver wants to do something differently. The code pushes a
few settings into the `request` (PaymentRequest) and `details`
(PaymentDetails) objects. When we serialize them,
[PaymentDetails][]{:#term-paymentdetails}{:.term} will be contained
within the PaymentRequest.
{% highlight python %}
## SSL Signature method
request.pki_type = "x509+sha256" ## Default: none
## Mainnet or testnet?
details.network = "test" ## Default: main
## Postback URL
details.payment_url = "https://example.com/pay.py"
## PaymentDetails version number
request.payment_details_version = 1 ## Default: 1
## Certificate chain
x509.certificate.append(file("/etc/apache2/example.com-cert.der", "r").read())
#x509.certificate.append(file("/some/intermediate/cert.der", "r").read())
## Load private SSL key into memory for signing later
priv_key = "/etc/apache2/example.com-key.pem"
pw = "test" ## Key password
private_key = load_privatekey(FILETYPE_PEM, file(priv_key, "r").read(), pw)
{% endhighlight %}
Each line is described below.
{% highlight python %}
request.pki_type = "x509+sha256" ## Default: none
{% endhighlight %}
`pki_type`: (optional) tell the receiving wallet program what [Public-Key
Infrastructure][PKI]{:#term-pki}{:.term} (PKI) type you're using to
cryptographically sign your PaymentRequest so that it can't be modified
by a man-in-the-middle attack.
If you don't want to sign the PaymentRequest, you can choose a
[`pki_type`][pp pki type]{:#term-pp-pki-type}{:.term} of `none`
(the default).
If you do choose the sign the PaymentRequest, you currently have two
options defined by BIP70: `x509+sha1` and `x509+sha256`. Both options
use the X.509 certificate system, the same system used for HTTP Secure
(HTTPS). To use either option, you will need a certificate signed by a
certificate authority or one of their intermediaries. (A self-signed
certificate will not work.)
Each wallet program may choose which certificate authorities to trust,
but it's likely that they'll trust whatever certificate authorities their
operating system trusts. If the wallet program doesn't have a full
operating system, as might be the case for small hardware wallets, BIP70
suggests they use the [Mozilla Root Certificate Store][mozrootstore]. In
general, if a certificate works in your web browser when you connect to
your webserver, it will work for your PaymentRequests.
{% highlight python %}
details.network = "test" ## Default: main
{% endhighlight %}
`network`:<!--noref--> (optional) tell the spender's wallet program what Bitcoin network you're
using; BIP70 defines "main" for mainnet (actual payments) and "test" for
testnet (like mainnet, but fake satoshis are used). If the wallet
program doesn't run on the network you indicate, it will reject the
PaymentRequest.
{% highlight python %}
details.payment_url = "https://example.com/pay.py"
{% endhighlight %}
`payment_url`: (required) tell the spender's wallet program where to send the Payment
message (described later). This can be a static URL, as in this example,
or a variable URL such as `https://example.com/pay.py?invoice=123.`
It should usually be an HTTPS address to prevent man-in-the-middle
attacks from modifying the message.
{% highlight python %}
request.payment_details_version = 1 ## Default: 1
{% endhighlight %}
`payment_details_version`: (optional) tell the spender's wallet program what version of the
PaymentDetails you're using. As of this writing, the only version is
version 1.
{% highlight python %}
## This is the pubkey/certificate corresponding to the private SSL key
## that we'll use to sign:
x509.certificate.append(file("/etc/apache2/example.com-cert.der", "r").read())
{% endhighlight %}
`x509certificates`:<!--noref--> (required for signed PaymentRequests) you must
provide the public SSL key/certificate corresponding to the private SSL
key you'll use to sign the PaymentRequest. The certificate must be in
ASN.1/DER format.
{% highlight python %}
## If the pubkey/cert above didn't have the signature of a root
## certificate authority, we'd then append the intermediate certificate
## which signed it:
#x509.certificate.append(file("/some/intermediate/cert.der", "r").read())
{% endhighlight %}
You must also provide any intermediate certificates necessary to link
your certificate to the root certificate of a certificate authority
trusted by the spender's software, such as a certificate from the
Mozilla root store.
The certificates must be provided in a specific order---the same order
used by Apache's `SSLCertificateFile` directive and other server
software. The figure below shows the [certificate chain][]{:#term-certificate-chain}{:.term} of the
www.bitcoin.org X.509 certificate and how each certificate (except the
root certificate) would be loaded into the [X509Certificates][]{:#term-x509certificates}{:.term} protocol
buffer message.
![X509Certificates Loading Order](/img/dev/en-cert-order.svg)
To be specific, the first certificate provided must be the
X.509 certificate corresponding to the private SSL key which will make the
signature<!--noref-->, called the [leaf certificate][]{:#term-leaf-certificate}{:.term}. Any [intermediate
certificates][intermediate certificate]{:#term-intermediate-certificate}{:.term} necessary to link that signed public SSL
key to the [root
certificate][]{:#term-root-certificate}{:.term} (the certificate authority) are attached separately, with each
certificate in DER format bearing the signature<!--noref--> of the certificate that
follows it all the way to (but not including) the root certificate.
{% highlight python %}
priv_key = "/etc/apache2/example.com-key.pem"
pw = "test" ## Key password
private_key = load_privatekey(FILETYPE_PEM, file(priv_key, "r").read(), pw)
{% endhighlight %}
(Required for signed PaymentRequests) you will need a private SSL key in
a format your SSL library supports (DER format is not required). In this
program, we'll load it from a PEM file. (Embedding your passphrase in
your CGI code, as done here, is obviously a bad idea in real life.)
The private SSL key will not be transmitted with your request. We're
only loading it into memory here so we can use it to sign the request
later.
{% endautocrossref %}
##### Code Variables
{% include helpers/subhead-links.md %}
{% autocrossref %}
Now let's look at the variables your CGI program will likely set for
each payment.
{% highlight python %}
## Amount of the request
amount = 10000000 ## In satoshis
## P2PKH pubkey hash
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
## P2PKH pubkey script entered as hex and converted to binary
# OP_DUP OP_HASH160 <push 20 bytes> <pubKey hash> OP_EQUALVERIFY OP_CHECKSIG
# 76 a9 14 <pubKey hash> 88 ac
hex_script = "76" + "a9" + "14" + pubkey_hash + "88" + "ac"
serialized_script = hex_script.decode("hex")
## Load amount and pubkey script into PaymentDetails
details.outputs.add(amount = amount, script = serialized_script)
## Memo to display to the spender
details.memo = "Flowers & chocolates"
## Data which should be returned to you with the payment
details.merchant_data = "Invoice #123"
{% endhighlight python %}
Each line is described below.
{% highlight python %}
amount = 10000000 ## In satoshis (=100 mBTC)
{% endhighlight %}
`amount`: (optional) the [amount][pp amount]{:#term-pp-amount}{:.term} you want the spender to pay. You'll probably get
this value from your shopping cart application or fiat-to-BTC exchange
rate conversion tool. If you leave the amount blank, the wallet
program will prompt the spender how much to pay (which can be useful
for donations).
{% highlight python %}
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
# OP_DUP OP_HASH160 <push 20 bytes> <pubKey hash> OP_EQUALVERIFY OP_CHECKSIG
# 76 a9 14 <pubKey hash> 88 ac
hex_script = "76" + "a9" + "14" + pubkey_hash + "88" + "ac"
serialized_script = hex_script.decode("hex")
{% endhighlight %}
`script`: (required) You must specify the pubkey script you want the spender to
pay---any valid pubkey script is acceptable. In this example, we'll request
payment to a P2PKH pubkey script.
First we get a pubkey hash. The hash above is the hash form of the
address used in the URI examples throughout this section,
mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN.
Next, we plug that hash into the standard P2PKH pubkey script using hex,
as illustrated by the code comments.
Finally, we convert the pubkey script from hex into its serialized form.
{% highlight python %}
details.outputs.add(amount = amount, script = serialized_script)
{% endhighlight %}
`outputs`:<!--noref--> (required) add the pubkey script and (optional) amount to the
PaymentDetails outputs<!--noref--> array.
It's possible to specify multiple [`scripts`][pp
script]{:#term-pp-script}{:.term} and `amounts` as part of a merge
avoidance strategy, described later in the [Merge Avoidance
subsection][]. However, effective merge avoidance is not possible under
the base BIP70 rules in which the spender pays each `script` the exact
amount specified by its paired `amount`. If the amounts are omitted from
all `amount`/`script` pairs, the spender will be prompted to choose an
amount to pay.
{% highlight python %}
details.memo = "Flowers & chocolates"
{% endhighlight %}
`memo`: (optional) add a memo which will be displayed to the spender as
plain UTF-8 text. Embedded HTML or other markup will not be processed.
{% highlight python %}
details.merchant_data = "Invoice #123"
{% endhighlight %}
`merchant_data`: (optional) add arbitrary data which should be sent back to the
receiver when the invoice is paid. You can use this to track your
invoices, although you can more reliably track payments by generating a
unique address for each payment and then tracking when it gets paid.
The [`memo`][pp memo]{:#term-pp-memo}{:.term} field and the [`merchant_data`][pp merchant data]{:#term-pp-merchant-data}{:.term} field can be arbitrarily long,
but if you make them too long, you'll run into the 50,000 byte limit on
the entire PaymentRequest, which includes the often several kilobytes
given over to storing the certificate chain. As will be described in a
later subsection, the `memo` field can be used by the spender after
payment as part of a cryptographically-proven receipt.
{% endautocrossref %}
##### Derivable Data
{% include helpers/subhead-links.md %}
{% autocrossref %}
Next, let's look at some information your CGI program can
automatically derive.
{% highlight python %}
## Request creation time
details.time = int(time()) ## Current epoch (Unix) time
## Request expiration time
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
## PaymentDetails is complete; serialize it and store it in PaymentRequest
request.serialized_payment_details = details.SerializeToString()
## Serialized certificate chain
request.pki_data = x509.SerializeToString()
## Initialize signature field so we can sign the full PaymentRequest
request.signature = ""
## Sign PaymentRequest
request.signature = sign(private_key, request.SerializeToString(), "sha256")
{% endhighlight %}
Each line is described below.
{% highlight python %}
details.time = int(time()) ## Current epoch (Unix) time
{% endhighlight %}
`time`: (required) PaymentRequests must indicate when they were created
in number of seconds elapsed since 1970-01-01T00:00 UTC (Unix
epoch time format).
{% highlight python %}
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
{% endhighlight %}
`expires`: (optional) the PaymentRequest may also set an [`expires`][pp
expires]{:#term-pp-expires}{:.term} time after
which they're no longer valid. You probably want to give receivers
the ability to configure the expiration time delta; here we used the
reasonable choice of 10 minutes. If this request is tied to an order
total based on a fiat-to-satoshis exchange rate, you probably want to
base this on a delta from the time you got the exchange rate.
{% highlight python %}
request.serialized_payment_details = details.SerializeToString()
{% endhighlight %}
`serialized_payment_details`: (required) we've now set everything we need to create the
PaymentDetails, so we'll use the SerializeToString function from the
protocol buffer code to store the PaymentDetails in the appropriate
field of the PaymentRequest.
{% highlight python %}
request.pki_data = x509.SerializeToString()
{% endhighlight %}
`pki_data`: (required for signed PaymentRequests) serialize the certificate chain
[PKI data][pp PKI data]{:#term-pp-pki-data}{:.term} and store it in the
PaymentRequest
{% highlight python %}
request.signature = ""
{% endhighlight %}
We've filled out everything in the PaymentRequest except the signature,
but before we sign it, we have to initialize the signature field by
setting it to a zero-byte placeholder.
{% highlight python %}
request.signature = sign(private_key, request.SerializeToString(), "sha256")
{% endhighlight %}
`signature`:<!--noref--> (required for signed PaymentRequests) now we
make the [signature][ssl signature]{:#term-ssl-signature}{:.term} by
signing the completed and serialized PaymentRequest. We'll use the
private key we stored in memory in the configuration section and the
same hashing formula we specified in `pki_type` (sha256 in this case)
{% endautocrossref %}
##### Output Code
{% include helpers/subhead-links.md %}
{% autocrossref %}
Now that we have PaymentRequest all filled out, we can serialize it and
send it along with the HTTP headers, as shown in the code below.
{% highlight python %}
print "Content-Type: application/bitcoin-paymentrequest"
print "Content-Transfer-Encoding: binary"
print ""
{% endhighlight %}
(Required) BIP71 defines the content types for PaymentRequests,
Payments, and PaymentACKs.
{% highlight python %}
file.write(stdout, request.SerializeToString())
{% endhighlight %}
`request`: (required) now, to finish, we just dump out the serialized
PaymentRequest (which contains the serialized PaymentDetails). The
serialized data is in binary, so we can't use Python's print()
because it would add an extraneous newline.
The following screenshot shows how the authenticated PaymentDetails
created by the program above appears in the GUI from Bitcoin Core 0.9.
![Bitcoin Core Showing Validated Payment Request](/img/dev/en-btcc-payment-request.png)
{% endautocrossref %}