mirror of
https://github.com/seigler/dash-docs
synced 2025-07-27 09:46:12 +00:00
Move BIP70 Example To Dev Examples
* Move the extened BIP70 example from the guide to the developer examples page. * Add new text introducing BIP70 to the guide
This commit is contained in:
parent
d1b4b08729
commit
7e8e52b966
3 changed files with 721 additions and 705 deletions
572
_includes/example_payment_processing.md
Normal file
572
_includes/example_payment_processing.md
Normal file
|
@ -0,0 +1,572 @@
|
||||||
|
## Payment Processing
|
||||||
|
|
||||||
|
### QR Code Error Correction
|
||||||
|
|
||||||
|
{% autocrossref %}
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
QR encoders offer four possible levels of error correction:
|
||||||
|
|
||||||
|
1. Low: corrects up to 7% damage
|
||||||
|
|
||||||
|
2. Medium: corrects up to 15% damage but results in approximately 8%
|
||||||
|
larger images over low-level damage correction.
|
||||||
|
|
||||||
|
3. Quartile: corrects corrects up to 25% damage but results in
|
||||||
|
approximately 20% larger images over low-level damage correction.
|
||||||
|
|
||||||
|
4. High: corrects up to 30% damage but results in approximately 26%
|
||||||
|
larger images over low-level damage correction.
|
||||||
|
|
||||||
|
{% endautocrossref %}
|
||||||
|
|
||||||
|
#### Payment Protocol
|
||||||
|
|
||||||
|
{% autocrossref %}
|
||||||
|
|
||||||
|
To request payment using the payment protocol, you use an extended (but
|
||||||
|
backwards-compatible) `bitcoin:` URI. For example:
|
||||||
|
|
||||||
|
~~~
|
||||||
|
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||||
|
?amount=0.10\
|
||||||
|
&label=Example+Merchant\
|
||||||
|
&message=Order+of+flowers+%26+chocolates\
|
||||||
|
&r=http://example.com/pay.php/invoice%3Dda39a3ee
|
||||||
|
~~~
|
||||||
|
|
||||||
|
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-->.
|
||||||
|
|
||||||
|
{% endautocrossref %}
|
||||||
|
|
||||||
|
##### PaymentRequest & PaymentDetails
|
||||||
|
|
||||||
|
{% 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
{% 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
|
||||||
|
|
||||||
|
{% 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
{% 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 output 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 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 output script you want the spender to
|
||||||
|
pay---any valid script is acceptable. In this example, we'll request
|
||||||
|
payment to a P2PKH output 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 output script using hex,
|
||||||
|
as illustrated by the code comments.
|
||||||
|
|
||||||
|
Finally, we convert the output script from hex into its serialized form.
|
||||||
|
|
||||||
|
{% highlight python %}
|
||||||
|
details.outputs.add(amount = amount, script = serialized_script)
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
`outputs`:<!--noref--> (required) add the output 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
|
||||||
|
|
||||||
|
{% 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
|
||||||
|
|
||||||
|
{% 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
{% endautocrossref %}
|
||||||
|
|
||||||
|
##### Payment
|
||||||
|
|
||||||
|
{% autocrossref %}
|
||||||
|
|
||||||
|
If the spender declines to pay, the wallet program will not send any
|
||||||
|
further messages to the receiver's server unless the spender clicks
|
||||||
|
another URI pointing to that server. If the spender does decide to pay,
|
||||||
|
the wallet program will create at least one transaction paying each of
|
||||||
|
the outputs in the PaymentDetails section. The wallet may broadcast
|
||||||
|
the transaction or transactions, as Bitcoin Core 0.9 does, but it
|
||||||
|
doesn't need to.
|
||||||
|
|
||||||
|
Whether or not it broadcasts the transaction or transactions, the wallet
|
||||||
|
program composes a reply to the PaymentRequest; the reply is called the
|
||||||
|
Payment. [Payment][pp payment]{:#term-pp-payment}{:.term} contains four fields:
|
||||||
|
|
||||||
|
* `merchant_data`: (optional) an exact copy of the
|
||||||
|
`merchant_data` from the PaymentDetails. This is
|
||||||
|
optional in the case that the PaymentDetails doesn't provide
|
||||||
|
`merchant_data`. Receivers should be aware that malicious spenders can
|
||||||
|
modify the merchant data before sending it back, so receivers may wish to
|
||||||
|
cryptographically sign it before giving it to the spender and then
|
||||||
|
validate it before relying on it.
|
||||||
|
|
||||||
|
* [`transactions`][pp transactions]{:#term-pp-transactions}{:.term}: (required) one or more signed transactions which pay the outputs
|
||||||
|
specified in the PaymentDetails.
|
||||||
|
|
||||||
|
<!-- BIP70 implies that refund_to is required (i.e. "one or more..."),
|
||||||
|
but Mike Hearn implied on bitcoin-devel that it's optional (i.e. "wallets have
|
||||||
|
to either never submit refund data, or always submit it").
|
||||||
|
I'll use the BIP70 version here until I hear differently. -harding -->
|
||||||
|
|
||||||
|
* [`refund_to`][pp refund to]{:#term-pp-refund-to}{:.term}: (required) one or more output scripts to which the
|
||||||
|
receiver can send a partial or complete refund. As of this writing, a
|
||||||
|
proposal is gaining traction to expire refund output scripts after a
|
||||||
|
certain amount of time (not defined yet) so spenders don't need to
|
||||||
|
worry about receiving refunds to addresses they no longer monitor.
|
||||||
|
|
||||||
|
* `memo`: (optional) a plain UTF-8 text memo sent to the receiver. It
|
||||||
|
should not contain HTML or any other markup. Spenders should not depend
|
||||||
|
on receivers reading their memos.
|
||||||
|
|
||||||
|
The Payment is sent to the [`payment_url`][pp payment
|
||||||
|
url]{:#term-pp-payment-url}{:.term} provided in the PaymentDetails.
|
||||||
|
The URL should be a HTTPS address to prevent a man-in-the-middle attack
|
||||||
|
from modifying the spender's `refund_to` output scripts. When sending the
|
||||||
|
Payment, the wallet program must set the following HTTP client headers:
|
||||||
|
|
||||||
|
{% endautocrossref %}
|
||||||
|
|
||||||
|
~~~
|
||||||
|
Content-Type: application/bitcoin-payment
|
||||||
|
Accept: application/bitcoin-paymentack
|
||||||
|
~~~
|
||||||
|
|
||||||
|
##### PaymentACK
|
||||||
|
|
||||||
|
{% autocrossref %}
|
||||||
|
|
||||||
|
The receiver's CGI program at the `payment_url` receives the Payment message and
|
||||||
|
decodes it using its Protocol Buffers code. The `transactions` are
|
||||||
|
checked to see if they pay the output scripts the receiver requested in
|
||||||
|
PaymentDetails and are then broadcast to the network (unless the network
|
||||||
|
already has them).
|
||||||
|
|
||||||
|
The CGI program checks the `merchant_data` parameter if necessary and issues
|
||||||
|
a [PaymentACK][]{:#term-paymentack}{:.term} (acknowledgment) with the following HTTP headers:
|
||||||
|
|
||||||
|
{% endautocrossref %}
|
||||||
|
|
||||||
|
~~~
|
||||||
|
Content-Type: application/bitcoin-paymentack
|
||||||
|
Content-Transfer-Encoding: binary
|
||||||
|
~~~
|
||||||
|
|
||||||
|
{% autocrossref %}
|
||||||
|
|
||||||
|
Then it sends another Protocol-Buffers-encoded message with one or two
|
||||||
|
fields:
|
||||||
|
|
||||||
|
* `payment`: (required) A copy of the the entire Payment message (in
|
||||||
|
serialized form) which is being acknowledged.
|
||||||
|
|
||||||
|
* `memo`: (optional) A plain UTF-8 text memo displayed to the spender
|
||||||
|
informing them about the status of their payment. It should not
|
||||||
|
contain HTML or any other markup. Receivers should not depend on
|
||||||
|
spenders reading their memos.
|
||||||
|
|
||||||
|
The PaymentACK does not mean that the payment is final; it just means
|
||||||
|
that everything seems to be correct. The payment is final once the
|
||||||
|
payment transactions are block-chain confirmed to the receiver's
|
||||||
|
satisfaction.
|
||||||
|
|
||||||
|
However, the spender's wallet program should indicate to the spender that
|
||||||
|
the payment was accepted for processing so the spender can direct his or
|
||||||
|
her attention elsewhere.
|
||||||
|
|
||||||
|
{% endautocrossref %}
|
|
@ -150,8 +150,6 @@ or [millibits][]{:#term-millibits}{:.term} (mBTC). Choosing between BTC and mBTC
|
||||||
but other software also lets its users select denomination amounts from
|
but other software also lets its users select denomination amounts from
|
||||||
some or all of the following options:
|
some or all of the following options:
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
| Bitcoins | Unit (Abbreviation) |
|
| Bitcoins | Unit (Abbreviation) |
|
||||||
|-------------|---------------------|
|
|-------------|---------------------|
|
||||||
| 1.0 | bitcoin (BTC) |
|
| 1.0 | bitcoin (BTC) |
|
||||||
|
@ -160,20 +158,8 @@ some or all of the following options:
|
||||||
| 0.000001 | microbit (uBTC) |
|
| 0.000001 | microbit (uBTC) |
|
||||||
| 0.00000001 | [satoshi][]{:#term-satoshi}{:.term} |
|
| 0.00000001 | [satoshi][]{:#term-satoshi}{:.term} |
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Because of the widespread popularity of BTC and mBTC, it may be more
|
|
||||||
useful to specify the amount in both denominations when the text is
|
|
||||||
meant to be copied and pasted. For example:
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
{% endautocrossref %}
|
||||||
|
|
||||||
~~~
|
|
||||||
Pay: mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN
|
|
||||||
Amount: 100 BTC (100000 mBTC)
|
|
||||||
You must pay by: 2014-04-01 at 23:00 UTC
|
|
||||||
~~~
|
|
||||||
|
|
||||||
#### bitcoin: URI
|
#### bitcoin: URI
|
||||||
|
|
||||||
{% autocrossref %}
|
{% autocrossref %}
|
||||||
|
@ -191,38 +177,23 @@ bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=100
|
||||||
|
|
||||||
{% autocrossref %}
|
{% autocrossref %}
|
||||||
|
|
||||||
Only the address is required, and if it is the only thing specified,
|
Only the address is required, and if it is the only thing
|
||||||
wallets will pre-fill a payment request with it and let the spender enter
|
specified, wallets will pre-fill a payment request with it and let
|
||||||
an amount.
|
the spender enter an amount. The amount specified is always in
|
||||||
|
decimal bitcoins (BTC).
|
||||||
|
|
||||||
The amount specified is always in decimal bitcoins (BTC), although requests
|
Two other parameters are widely supported. The
|
||||||
only for whole bitcoins (as in the example above), may omit the decimal
|
[`label`][label]{:#term-label}{:.term} parameter is generally used to
|
||||||
point. The amount field must not contain any commas. Fractional bitcoins
|
provide wallet software with the recipient's name. The
|
||||||
may be specified with or without a leading zero; for example, either of
|
[`message`][message]{:#term-message}{:.term} parameter is generally used
|
||||||
the URIs below requesting one millibit are valid:
|
to describe the payment request to the spender. Both the label and the
|
||||||
|
message are commonly stored by the spender's wallet software---but they
|
||||||
{% endautocrossref %}
|
are never added to the actual transaction, so other Bitcoin users cannot
|
||||||
|
see them. Both the label and the message must be [URI encoded][].
|
||||||
~~~
|
|
||||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=.001
|
|
||||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=0.001
|
|
||||||
~~~
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Two other parameters are widely supported. The [`label`][label]{:#term-label}{:.term} parameter is
|
|
||||||
generally used to provide wallet software with the recipient's name. The
|
|
||||||
[`message`][message]{:#term-message}{:.term} parameter is generally used to describe the payment request to
|
|
||||||
the spender. Both the label and the message are commonly stored by the
|
|
||||||
spender's wallet software---but they are never added to the actual
|
|
||||||
transaction, so other Bitcoin users cannot see them. Both the label and
|
|
||||||
the message must be [URI encoded][].
|
|
||||||
|
|
||||||
All four parameters used together, with appropriate URI encoding, can be
|
All four parameters used together, with appropriate URI encoding, can be
|
||||||
seen in the line-wrapped example below.
|
seen in the line-wrapped example below.
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||||
?amount=0.10\
|
?amount=0.10\
|
||||||
|
@ -230,32 +201,6 @@ bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||||
&message=Order+of+flowers+%26+chocolates
|
&message=Order+of+flowers+%26+chocolates
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
The URI above could be encoded in HTML as follows, providing compatibility
|
|
||||||
with wallet software which can't accept URI links and allowing you to
|
|
||||||
specify an expiration date to the spender.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
~~~
|
|
||||||
<a href="bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
|
||||||
?amount=0.10\
|
|
||||||
&label=Example+Merchant\
|
|
||||||
&message=Order+of+flowers+%26+chocolates"
|
|
||||||
>Order flowers & chocolate using Bitcoin</a>
|
|
||||||
(Pay 0.10 BTC [100 mBTC] to mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN by 2014-04-01 at 23:00 UTC)
|
|
||||||
~~~
|
|
||||||
|
|
||||||
Which produces:
|
|
||||||
|
|
||||||
> <a href="bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=0.10&label=Example+Merchant&message=Order+of+flowers+%26+chocolates">Order flowers & chocolates using Bitcoin</a> (Pay 0.10 BTC [100 mBTC] to mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN by 2014-04-01 at 23:00 UTC)
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Some payment processors use Javascript to display countdown timers
|
|
||||||
indicating the number of minutes and seconds until the offer expires.
|
|
||||||
|
|
||||||
The URI scheme can be extended, as will be seen in the payment protocol
|
The URI scheme can be extended, as will be seen in the payment protocol
|
||||||
section below, with both new optional and required parameters. As of this
|
section below, with both new optional and required parameters. As of this
|
||||||
writing, the only widely-used parameter besides the four described above
|
writing, the only widely-used parameter besides the four described above
|
||||||
|
@ -277,26 +222,13 @@ wallets, support scanning QR codes to pre-fill their payment screens.
|
||||||
|
|
||||||
The figure below shows the same `bitcoin:` URI code encoded as four
|
The figure below shows the same `bitcoin:` URI code encoded as four
|
||||||
different [Bitcoin QR codes][URI QR code]{:#term-uri-qr-code}{:.term} at different error correction levels (described
|
different [Bitcoin QR codes][URI QR code]{:#term-uri-qr-code}{:.term} at different error correction levels (described
|
||||||
below the image). The QR code can include the `label` and `message`
|
in the Developer Examples [QR Codes subsection][devex qr codes]). The QR code can include the `label` and `message`
|
||||||
parameters---and any other optional parameters---but they were
|
parameters---and any other optional parameters---but they were
|
||||||
omitted here to keep the QR code small and easy to scan with unsteady
|
omitted here to keep the QR code small and easy to scan with unsteady
|
||||||
or low-resolution mobile cameras.
|
or low-resolution mobile cameras.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
QR encoders offer four possible levels of error correction:
|
|
||||||
|
|
||||||
1. Low: corrects up to 7% damage
|
|
||||||
|
|
||||||
2. Medium: corrects up to 15% damage but results in approximately 8%
|
|
||||||
larger images over low-level damage correction.
|
|
||||||
|
|
||||||
3. Quartile: corrects corrects up to 25% damage but results in
|
|
||||||
approximately 20% larger images over low-level damage correction.
|
|
||||||
|
|
||||||
4. High: corrects up to 30% damage but results in approximately 26%
|
|
||||||
larger images over low-level damage correction.
|
|
||||||
|
|
||||||
The error correction is combined with a checksum to ensure the Bitcoin QR code
|
The error correction is combined with a checksum to ensure the Bitcoin QR code
|
||||||
cannot be successfully decoded with data missing or accidentally altered,
|
cannot be successfully decoded with data missing or accidentally altered,
|
||||||
so your applications should choose the appropriate level of error
|
so your applications should choose the appropriate level of error
|
||||||
|
@ -332,659 +264,142 @@ as "www.bitcoin.org".
|
||||||
To request payment using the payment protocol, you use an extended (but
|
To request payment using the payment protocol, you use an extended (but
|
||||||
backwards-compatible) `bitcoin:` URI. For example:
|
backwards-compatible) `bitcoin:` URI. For example:
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||||
?amount=0.10\
|
?amount=0.10\
|
||||||
&label=Example+Merchant\
|
&label=Example+Merchant\
|
||||||
&message=Order+of+flowers+%26+chocolates\
|
&message=Order+of+flowers+%26+chocolates\
|
||||||
&r=http://example.com/pay.php/invoice%3Dda39a3ee
|
&r=https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
None of the parameters provided above, except `r`, are required for the
|
None of the parameters provided above, except `r`, are required for the
|
||||||
payment protocol---but your applications may include them for backwards
|
payment protocol---but your applications may include them for backwards
|
||||||
compatibility with wallet programs which don't yet handle the payment
|
compatibility with wallet programs which don't yet handle the payment
|
||||||
protocol.
|
protocol.
|
||||||
|
|
||||||
The [`r`][r]{:#term-r-parameter}{:.term} parameter tells payment-protocol-aware wallet programs to ignore
|
The [`r`][r]{:#term-r-parameter}{:.term} parameter tells payment-protocol-aware wallet programs to ignore
|
||||||
the other parameters and fetch a PaymentRequest from the URL provided. If the
|
the other parameters and fetch a PaymentRequest from the URL provided.
|
||||||
request will be signed, which is recommended but not required, it can be
|
|
||||||
fetched from an HTTP server---although fetching it from an HTTPS server
|
|
||||||
would still be preferable.
|
|
||||||
|
|
||||||
The browser, QR code reader, or other program processing the URI opens
|
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
|
the spender's Bitcoin wallet program on the URI.
|
||||||
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-->.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
##### PaymentRequest & PaymentDetails
|
|
||||||
|
|
||||||
{% 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.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
For the script to use the protocol buffer, you will need a copy of
|
The Payment Protocol is described in depth in BIP70, BIP71, and BIP72.
|
||||||
Google's Protocol Buffer compiler (`protoc`), which is available in most
|
An example CGI program and description of all the parameters which can
|
||||||
modern Linux package managers and [directly from Google.][protobuf] Non-Google
|
be used in the Payment Protocol is provided in the Developer Examples
|
||||||
protocol buffer compilers are available for a variety of
|
[Payment Protocol][devex payment protocol] subsection. In this
|
||||||
programming languages. You will also need a copy of the PaymentRequest
|
subsection, we will briefly describe in story format how the Payment
|
||||||
[Protocol Buffer description][core paymentrequest.proto] from the Bitcoin Core source code.
|
Protocol is typically used.
|
||||||
|
|
||||||
###### Initialization Code
|
Charlie, the client, is shopping on a website run by Bob, the
|
||||||
|
businessman. Charlie adds a few items to his shopping cart and clicks
|
||||||
With the Python code generated by `protoc`, we can start our simple
|
the Checkout With Bitcoin button.
|
||||||
CGI program.
|
|
||||||
|
Bob's server automatically adds the following information to its
|
||||||
{% endautocrossref %}
|
invoice database:
|
||||||
|
|
||||||
{% highlight python %}
|
* The details of Charlie's order, including items ordered and
|
||||||
#!/usr/bin/env python
|
shipping address.
|
||||||
|
|
||||||
## This is the code generated by protoc --python_out=./ paymentrequest.proto
|
* An order total in satoshis, perhaps created by converting prices in
|
||||||
from paymentrequest_pb2 import *
|
fiat to prices in satoshis.
|
||||||
|
|
||||||
## Load some functions
|
* An expiration time when that total will no longer be acceptable.
|
||||||
from time import time
|
|
||||||
from sys import stdout
|
* An output script to which Charlie should send payment. Typically this
|
||||||
from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign
|
will be a P2PKH or P2SH output script containing a unique (never
|
||||||
|
before used) public key.
|
||||||
## Copy three of the classes created by protoc into objects we can use
|
|
||||||
details = PaymentDetails()
|
After adding all that information to the database, Bob's server displays
|
||||||
request = PaymentRequest()
|
a `bitcoin:` URI for Charlie to click to pay.
|
||||||
x509 = X509Certificates()
|
|
||||||
{% endhighlight %}
|
Charlie clicks on the `bitcoin:` URI in his browser. His browser's URI
|
||||||
|
handler sends the URI to his wallet program. The wallet is aware of the
|
||||||
{% autocrossref %}
|
Payment Protocol, so it parses the `r` parameter and sends an HTTP GET
|
||||||
|
to that URL looking for a Payment Request.
|
||||||
The startup code above is quite simple, requiring nothing but the epoch
|
|
||||||
(Unix date) time function, the standard out file descriptor, a few
|
The Payment Request may include private information, such as Charlie's
|
||||||
functions from the OpenSSL library, and the data structures and
|
mailing address, but the wallet must be able to access it without using prior
|
||||||
functions created by `protoc`.
|
authentication, such as HTTP cookies, so a publicly-accessible HTTPS URL
|
||||||
|
with a guess-resistant part is typically used. The
|
||||||
{% endautocrossref %}
|
unique public key created for the Payment Request can be used to create
|
||||||
|
a unique identifier. This is why, in the example URI above, the Payment
|
||||||
###### Configuration Code
|
Request URL contains the P2PKH address:
|
||||||
|
`https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN`
|
||||||
{% autocrossref %}
|
|
||||||
|
Upon receipt of the HTTP GET to the URL above, the Payment
|
||||||
Next, we'll set configuration settings which will typically only change
|
Request-generating CGI program on Bob's webserver takes the
|
||||||
when the receiver wants to do something differently. The code pushes a
|
unique identifier from the URL and looks up the corresponding details in
|
||||||
few settings into the `request` (PaymentRequest) and `details`
|
the database. It then creates a Payment Details message with the
|
||||||
(PaymentDetails) objects. When we serialize them,
|
following information:
|
||||||
[PaymentDetails][]{:#term-paymentdetails}{:.term} will be contained
|
|
||||||
within the PaymentRequest.
|
* The amount of the order in satoshis and the output script to be paid.
|
||||||
|
|
||||||
{% endautocrossref %}
|
* A memo containing the list of items ordered, so Charlie knows what
|
||||||
|
he's paying for. It may also include Charlie's mailing address so he can
|
||||||
{% highlight python %}
|
double-check it.
|
||||||
## SSL Signature method
|
|
||||||
request.pki_type = "x509+sha256" ## Default: none
|
* The time the Payment Details message was created plus the time
|
||||||
|
it expires.
|
||||||
## Mainnet or Testnet?
|
|
||||||
details.network = "test" ## Default: main
|
* A URL to which Charlie's wallet should send its completed transaction.
|
||||||
|
|
||||||
## Postback URL
|
That Payment Details message is put inside a Payment Request message.
|
||||||
details.payment_url = "https://example.com/pay.py"
|
The Payment Request lets Bob's server sign the entire Request with the
|
||||||
|
server's X.509 SSL certificate. (The Payment Protocol has been designed
|
||||||
## PaymentDetails version number
|
to allow other signing methods in the future.) Bob's server sends the
|
||||||
request.payment_details_version = 1 ## Default: 1
|
Payment Request to Charlie's wallet in the reply to the HTTP GET.
|
||||||
|
|
||||||
## 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
details.network = "test" ## Default: main
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
details.payment_url = "https://example.com/pay.py"
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
request.payment_details_version = 1 ## Default: 1
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
(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
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Now let's look at the variables your CGI program will likely set for
|
|
||||||
each payment.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
## Amount of the request
|
|
||||||
amount = 10000000 ## In satoshis
|
|
||||||
|
|
||||||
## P2PKH pubkey hash
|
|
||||||
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
|
|
||||||
## P2PKH output 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 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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).
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`script`: (required) You must specify the output script you want the spender to
|
|
||||||
pay---any valid script is acceptable. In this example, we'll request
|
|
||||||
payment to a P2PKH output 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 output script using hex,
|
|
||||||
as illustrated by the code comments.
|
|
||||||
|
|
||||||
Finally, we convert the output script from hex into its serialized form.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
details.outputs.add(amount = amount, script = serialized_script)
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`outputs`:<!--noref--> (required) add the output 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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
details.memo = "Flowers & chocolates"
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
details.merchant_data = "Invoice #123"
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Next, let's look at some information your CGI program can
|
|
||||||
automatically derive.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% 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 %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`time`: (required) PaymentRequests must indicate when they were created
|
|
||||||
in number of seconds elapsed since 1970-01-01T00:00 UTC (Unix
|
|
||||||
epoch time format).
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
request.serialized_payment_details = details.SerializeToString()
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
request.pki_data = x509.SerializeToString()
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
request.signature = ""
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
request.signature = sign(private_key, request.SerializeToString(), "sha256")
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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
|
|
||||||
|
|
||||||
{% 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.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
print "Content-Type: application/bitcoin-paymentrequest"
|
|
||||||
print "Content-Transfer-Encoding: binary"
|
|
||||||
print ""
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
(Required) BIP71 defines the content types for PaymentRequests,
|
|
||||||
Payments, and PaymentACKs.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
{% highlight python %}
|
|
||||||
file.write(stdout, request.SerializeToString())
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
{% endautocrossref %}
|
Charlie's wallet receives the Payment Request, checks its signature, and
|
||||||
|
then displays the details from the Payment Details to Charlie. Charlie
|
||||||
|
agrees to pay, so the wallet constructs a payment to the output script
|
||||||
|
Bob's server provided. Unlike a traditional Bitcoin payment, Charlie's
|
||||||
|
wallet doesn't necessarily automatically broadcast this payment to the
|
||||||
|
network. Instead, the wallet constructs a Payment message and sends it to
|
||||||
|
the URL provided in the Payment Details message as an HTTP POST. Among
|
||||||
|
other things, the Payment message contains:
|
||||||
|
|
||||||
##### Payment
|
* The signed transaction in which Charlie pays Bob.
|
||||||
|
|
||||||
{% autocrossref %}
|
* An optional memo Charlie can send to Bob. (There's no guarantee that
|
||||||
|
Bob will read it.)
|
||||||
|
|
||||||
If the spender declines to pay, the wallet program will not send any
|
* A refund address (output script) which Bob can pay if he needs to
|
||||||
further messages to the receiver's server unless the spender clicks
|
return some or all of Charlie's satoshis.
|
||||||
another URI pointing to that server. If the spender does decide to pay,
|
|
||||||
the wallet program will create at least one transaction paying each of
|
|
||||||
the outputs in the PaymentDetails section. The wallet may broadcast
|
|
||||||
the transaction or transactions, as Bitcoin Core 0.9 does, but it
|
|
||||||
doesn't need to.
|
|
||||||
|
|
||||||
Whether or not it broadcasts the transaction or transactions, the wallet
|
Bob's server receives the Payment message, verifies the transaction pays
|
||||||
program composes a reply to the PaymentRequest; the reply is called the
|
the requested amount to the address provided, and then broadcasts the
|
||||||
Payment. [Payment][pp payment]{:#term-pp-payment}{:.term} contains four fields:
|
transaction to the network. It also replies to the HTTP POSTed Payment
|
||||||
|
message with a PaymentACK message, which includes an optional memo
|
||||||
|
from Bob's server thanking Charlie for his patronage and providing other
|
||||||
|
information about the order, such as the expected arrival date.
|
||||||
|
|
||||||
* `merchant_data`: (optional) an exact copy of the
|
Charlie's wallet sees the PaymentACK and tells Charlie that the payment
|
||||||
`merchant_data` from the PaymentDetails. This is
|
has been sent. The PaymentACK doesn't mean that Charlie has actually
|
||||||
optional in the case that the PaymentDetails doesn't provide
|
paid Bob---see the Verifying Payment subsection below---but it does mean
|
||||||
`merchant_data`. Receivers should be aware that malicious spenders can
|
that he can go do something else while the transaction gets confirmed.
|
||||||
modify the merchant data before sending it back, so receivers may wish to
|
After Bob's server verifies from the block chain that Charlie's
|
||||||
cryptographically sign it before giving it to the spender and then
|
transaction has been suitably confirmed, it authorizes shipping
|
||||||
validate it before relying on it.
|
Charlie's order.
|
||||||
|
|
||||||
* [`transactions`][pp transactions]{:#term-pp-transactions}{:.term}: (required) one or more signed transactions which pay the outputs
|
In the case of a dispute, Charlie can generate a cryptgraphically-proven
|
||||||
specified in the PaymentDetails.
|
receipt out of the various signed or otherwise-proven information.
|
||||||
|
|
||||||
<!-- BIP70 implies that refund_to is required (i.e. "one or more..."),
|
* The Payment Details message signed by Bob's webserver proves Charlie
|
||||||
but Mike Hearn implied on bitcoin-devel that it's optional (i.e. "wallets have
|
received an invoice to pay a specified output script for a specified
|
||||||
to either never submit refund data, or always submit it").
|
number of satoshis for goods specified in the memo field.
|
||||||
I'll use the BIP70 version here until I hear differently. -harding -->
|
|
||||||
|
|
||||||
* [`refund_to`][pp refund to]{:#term-pp-refund-to}{:.term}: (required) one or more output scripts to which the
|
* The Bitcoin block chain can prove that the output script specified by
|
||||||
receiver can send a partial or complete refund. As of this writing, a
|
Bob was paid the specified number of satoshis.
|
||||||
proposal is gaining traction to expire refund output scripts after a
|
|
||||||
certain amount of time (not defined yet) so spenders don't need to
|
|
||||||
worry about receiving refunds to addresses they no longer monitor.
|
|
||||||
|
|
||||||
* `memo`: (optional) a plain UTF-8 text memo sent to the receiver. It
|
If a refund needs to be issued, Bob's server can safely pay the
|
||||||
should not contain HTML or any other markup. Spenders should not depend
|
refund-to output script provided by Charlie. (Note: a proposal has been
|
||||||
on receivers reading their memos.
|
discussed to give refund-to addresses an implicit expiration date so
|
||||||
|
users and software don't need to worry about payments being sent to
|
||||||
The Payment is sent to the [`payment_url`][pp payment
|
addresses which are no longer monitored.) See the Refunds section below
|
||||||
url]{:#term-pp-payment-url}{:.term} provided in the PaymentDetails.
|
for more details.
|
||||||
The URL should be a HTTPS address to prevent a man-in-the-middle attack
|
|
||||||
from modifying the spender's `refund_to` output scripts. When sending the
|
|
||||||
Payment, the wallet program must set the following HTTP client headers:
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
~~~
|
|
||||||
Content-Type: application/bitcoin-payment
|
|
||||||
Accept: application/bitcoin-paymentack
|
|
||||||
~~~
|
|
||||||
|
|
||||||
##### PaymentACK
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
The receiver's CGI program at the `payment_url` receives the Payment message and
|
|
||||||
decodes it using its Protocol Buffers code. The `transactions` are
|
|
||||||
checked to see if they pay the output scripts the receiver requested in
|
|
||||||
PaymentDetails and are then broadcast to the network (unless the network
|
|
||||||
already has them).
|
|
||||||
|
|
||||||
The CGI program checks the `merchant_data` parameter if necessary and issues
|
|
||||||
a [PaymentACK][]{:#term-paymentack}{:.term} (acknowledgment) with the following HTTP headers:
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
~~~
|
|
||||||
Content-Type: application/bitcoin-paymentack
|
|
||||||
Content-Transfer-Encoding: binary
|
|
||||||
~~~
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Then it sends another Protocol-Buffers-encoded message with one or two
|
|
||||||
fields:
|
|
||||||
|
|
||||||
* `payment`: (required) A copy of the the entire Payment message (in
|
|
||||||
serialized form) which is being acknowledged.
|
|
||||||
|
|
||||||
* `memo`: (optional) A plain UTF-8 text memo displayed to the spender
|
|
||||||
informing them about the status of their payment. It should not
|
|
||||||
contain HTML or any other markup. Receivers should not depend on
|
|
||||||
spenders reading their memos.
|
|
||||||
|
|
||||||
The PaymentACK does not mean that the payment is final; it just means
|
|
||||||
that everything seems to be correct. The payment is final once the
|
|
||||||
payment transactions are block-chain confirmed to the receiver's
|
|
||||||
satisfaction.
|
|
||||||
|
|
||||||
However, the spender's wallet program should indicate to the spender that
|
|
||||||
the payment was accepted for processing so the spender can direct his or
|
|
||||||
her attention elsewhere.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
|
||||||
|
|
||||||
##### Receipts
|
|
||||||
|
|
||||||
{% autocrossref %}
|
|
||||||
|
|
||||||
Unlike PaymentRequest, PaymentDetails, [Payment][pp payment]{:.auto-link}, and PaymentACK, there is
|
|
||||||
no specific [receipt][]{:#term-receipt}{:.term} object. However, a cryptographically-verifiable
|
|
||||||
receipt can be derived from a signed PaymentDetails and one or more confirmed
|
|
||||||
transactions.
|
|
||||||
|
|
||||||
A signed PaymentDetails indicates what output scripts should be paid
|
|
||||||
(`script`), how much they should be paid (`amount`), and by when
|
|
||||||
(`expires`). The Bitcoin block chain indicates whether those outputs
|
|
||||||
were paid the requested amount and can provide a rough idea of when the
|
|
||||||
transactions were generated. Together, this information provides
|
|
||||||
verifiable proof that the spender paid somebody with the
|
|
||||||
receiver's private SSL key.
|
|
||||||
|
|
||||||
{% endautocrossref %}
|
{% endautocrossref %}
|
||||||
|
|
||||||
|
|
29
en/developer-examples.md
Normal file
29
en/developer-examples.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
layout: base
|
||||||
|
lang: en
|
||||||
|
id: developer-examples
|
||||||
|
title: "Developer Examples - Bitcoin"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bitcoin Developer Examples
|
||||||
|
|
||||||
|
<p class="summary">Find examples of how to build programs using Bitcoin.</p>
|
||||||
|
|
||||||
|
<div markdown="1" id="toc" class="toc"><div markdown="1">
|
||||||
|
|
||||||
|
* Table of contents
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
<ul><li><a href="/en/developer-documentation">Return To Overview</a></li></ul>
|
||||||
|
|
||||||
|
</div></div>
|
||||||
|
<div markdown="1" class="toccontent">
|
||||||
|
|
||||||
|
{% include example_transactions.md %}
|
||||||
|
{% include example_payment_processing.md %}
|
||||||
|
|
||||||
|
{% include references.md %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>updateToc();</script>
|
Loading…
Add table
Add a link
Reference in a new issue