mirror of
https://github.com/seigler/dash-docs
synced 2025-07-27 09:46:12 +00:00
Group _includes files in subfolders
This commit is contained in:
parent
90dc6a02b6
commit
af1704002b
120 changed files with 230 additions and 230 deletions
473
_includes/devdoc/example_payment_processing.md
Normal file
473
_includes/devdoc/example_payment_processing.md
Normal file
|
@ -0,0 +1,473 @@
|
|||
{% comment %}
|
||||
This file is licensed under the MIT License (MIT) available on
|
||||
http://opensource.org/licenses/MIT.
|
||||
{% endcomment %}
|
||||
{% assign filename="_includes/devdoc/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.
|
||||
|
||||

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

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

|
||||
|
||||
{% endautocrossref %}
|
Loading…
Add table
Add a link
Reference in a new issue