mirror of
https://github.com/seigler/dash-docs
synced 2025-07-27 09:46:12 +00:00
Merge remote-tracking branch 'harding/generatingtxes'
This commit is contained in:
commit
b428cbde26
18 changed files with 2135 additions and 755 deletions
|
@ -127,14 +127,11 @@ parent key:
|
||||||
parent private key:
|
parent private key:
|
||||||
parent private and public keys: parent key
|
parent private and public keys: parent key
|
||||||
parent public key:
|
parent public key:
|
||||||
Payment message: pp payment
|
|
||||||
payment protocol:
|
payment protocol:
|
||||||
"payment protocol's": payment protocol
|
"payment protocol's": payment protocol
|
||||||
PaymentACK:
|
|
||||||
PaymentDetails:
|
PaymentDetails:
|
||||||
PaymentRequest:
|
PaymentRequest:
|
||||||
PaymentRequests: paymentrequest
|
PaymentRequests: paymentrequest
|
||||||
'`payment_url`': pp payment url
|
|
||||||
peer:
|
peer:
|
||||||
peers: peer
|
peers: peer
|
||||||
peer-to-peer network: network
|
peer-to-peer network: network
|
||||||
|
@ -160,7 +157,6 @@ recurrent rebilling:
|
||||||
redeemScript:
|
redeemScript:
|
||||||
refund:
|
refund:
|
||||||
refunds: refund
|
refunds: refund
|
||||||
'`refund_to`': pp refund to
|
|
||||||
root certificate:
|
root certificate:
|
||||||
root seed:
|
root seed:
|
||||||
RPCs: rpc
|
RPCs: rpc
|
||||||
|
@ -222,6 +218,8 @@ BIP21:
|
||||||
BIP32:
|
BIP32:
|
||||||
BIP39:
|
BIP39:
|
||||||
BIP70:
|
BIP70:
|
||||||
|
BIP71:
|
||||||
|
BIP72:
|
||||||
|
|
||||||
## RPCs
|
## RPCs
|
||||||
'`addmultisigaddress`': rpc addmultisigaddress
|
'`addmultisigaddress`': rpc addmultisigaddress
|
||||||
|
|
455
_includes/example_payment_processing.md
Normal file
455
_includes/example_payment_processing.md
Normal file
|
@ -0,0 +1,455 @@
|
||||||
|
## Payment Processing
|
||||||
|
|
||||||
|
### 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=https://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-->.
|
||||||
|
|
||||||
|
**Resource:** Gavin Andresen's [Payment Request Generator][] generates
|
||||||
|
custom example URIs and payment requests for use with testnet.
|
||||||
|
|
||||||
|
{% 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 %}
|
1270
_includes/example_transactions.md
Normal file
1270
_includes/example_transactions.md
Normal file
File diff suppressed because it is too large
Load diff
7
_includes/fragment_reviews_needed.md
Normal file
7
_includes/fragment_reviews_needed.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<!--Temporary disclaimer BEGIN-->
|
||||||
|
<div id="develdocdisclaimer" class="develdocdisclaimer"><div>
|
||||||
|
<b>BETA</b>: This documentation has been written recently and still needs more reviews to ensure all content is covered correctly and accurately; if you find a mistake, please <a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">report an issue</a> on GitHub. <a href="#" onclick="disclaimerClose(event);">Click here</a> to close this disclaimer.
|
||||||
|
<a class="develdocdisclaimerclose" href="#" onclick="disclaimerClose(event);">X</a>
|
||||||
|
</div></div>
|
||||||
|
<script>disclaimerAutoClose();</script>
|
||||||
|
<!--Temporary disclaimer END-->
|
|
@ -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
|
||||||
|
@ -276,27 +221,14 @@ images, or in videos. Most mobile Bitcoin wallet apps, and some desktop
|
||||||
wallets, support scanning QR codes to pre-fill their payment screens.
|
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 four
|
||||||
below the image). The QR code can include the `label` and `message`
|
different error correction levels. 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,662 +264,143 @@ 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-->.
|
|
||||||
|
|
||||||
**Resource:** Gavin Andresen's [Payment Request Generator][] generates
|
|
||||||
custom example URIs and payment requests for use with testnet.
|
|
||||||
|
|
||||||
{% 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 PaymentRequest message.
|
||||||
The startup code above is quite simple, requiring nothing but the epoch
|
|
||||||
(Unix date) time function, the standard out file descriptor, a few
|
The PaymentRequest message returned 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 PaymentRequest
|
||||||
###### Configuration Code
|
URL contains the P2PKH address:
|
||||||
|
`https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN`
|
||||||
{% autocrossref %}
|
|
||||||
|
After receiving the HTTP GET to the URL above, the
|
||||||
Next, we'll set configuration settings which will typically only change
|
PaymentRequest-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 PaymentDetails 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 PaymentDetails 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 PaymentDetails message is put inside a PaymentRequest 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 PaymentRequest message, checks its signature, and
|
||||||
|
then displays the details from the PaymentDetails message 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 PaymentDetails 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 Bob has verified
|
||||||
optional in the case that the PaymentDetails doesn't provide
|
Charlie's payment---see the Verifying Payment subsection below---but it does mean
|
||||||
`merchant_data`. Receivers should be aware that malicious spenders can
|
that Charlie 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 cryptographically-proven
|
||||||
specified in the PaymentDetails.
|
[receipt][]{:#term-receipt}{:.term} out of the various signed or
|
||||||
|
otherwise-proven information.
|
||||||
|
|
||||||
<!-- BIP70 implies that refund_to is required (i.e. "one or more..."),
|
* The PaymentDetails 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 %}
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,7 @@ design these minimalist wallets:
|
||||||
* Pre-populate a database with a number of public keys or addresses, and
|
* Pre-populate a database with a number of public keys or addresses, and
|
||||||
then distribute on request an output script or address using one of
|
then distribute on request an output script or address using one of
|
||||||
the database entries. To [avoid key reuse][devguide avoiding key
|
the database entries. To [avoid key reuse][devguide avoiding key
|
||||||
resuse], webservers should keep track
|
reuse], webservers should keep track
|
||||||
of used keys and never run out of public keys. This can be made easier
|
of used keys and never run out of public keys. This can be made easier
|
||||||
by using parent public keys as suggested in the next method.
|
by using parent public keys as suggested in the next method.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
[broadcast]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner"
|
[broadcast]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner"
|
||||||
[broadcasts]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner"
|
[broadcasts]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner"
|
||||||
[broadcasting]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner)"
|
[broadcasting]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner)"
|
||||||
[certificate chain]: /en/developer-guide#term-certificate-chain "A chain of certificates connecting a individual's leaf certificate to the certificate authority's root certificate"
|
[certificate chain]: /en/developer-examples#term-certificate-chain "A chain of certificates connecting a individual's leaf certificate to the certificate authority's root certificate"
|
||||||
[chain code]: /en/developer-guide#term-chain-code "In HD wallets, 256 bits of entropy added to the master public and private keys to help them generate secure child keys; the chain code is usually derived from a seed along with the master private key"
|
[chain code]: /en/developer-guide#term-chain-code "In HD wallets, 256 bits of entropy added to the master public and private keys to help them generate secure child keys; the chain code is usually derived from a seed along with the master private key"
|
||||||
[change address]: /en/developer-guide#term-change-output "An output used by a spender to send back to himself some of the satoshis from the inputs"
|
[change address]: /en/developer-guide#term-change-output "An output used by a spender to send back to himself some of the satoshis from the inputs"
|
||||||
[change output]: /en/developer-guide#term-change-output "An output used by a spender to send back to himself some of the satoshis from the inputs"
|
[change output]: /en/developer-guide#term-change-output "An output used by a spender to send back to himself some of the satoshis from the inputs"
|
||||||
|
@ -45,11 +45,11 @@
|
||||||
[high-priority transactions]: /en/developer-guide#term-high-priority-transactions "Transactions which don't pay a transaction fee; only transactions spending long-idle outputs are eligible"
|
[high-priority transactions]: /en/developer-guide#term-high-priority-transactions "Transactions which don't pay a transaction fee; only transactions spending long-idle outputs are eligible"
|
||||||
[input]: /en/developer-guide#term-input "The input to a transaction linking to the output of a previous transaction which permits spending of satoshis"
|
[input]: /en/developer-guide#term-input "The input to a transaction linking to the output of a previous transaction which permits spending of satoshis"
|
||||||
[inputs]: /en/developer-guide#term-input "The input to a transaction linking to the output of a previous transaction which permits spending of satoshis"
|
[inputs]: /en/developer-guide#term-input "The input to a transaction linking to the output of a previous transaction which permits spending of satoshis"
|
||||||
[intermediate certificate]: /en/developer-guide#term-intermediate-certificate "A intermediate certificate authority certificate which helps connect a leaf (receiver) certificate to a root certificate authority"
|
[intermediate certificate]: /en/developer-examples#term-intermediate-certificate "A intermediate certificate authority certificate which helps connect a leaf (receiver) certificate to a root certificate authority"
|
||||||
[key index]: /en/developer-guide#term-key-index "An index number used in the HD wallet formula to generate child keys from a parent key"
|
[key index]: /en/developer-guide#term-key-index "An index number used in the HD wallet formula to generate child keys from a parent key"
|
||||||
[key pair]: /en/developer-guide#term-key-pair "A private key and its derived public key"
|
[key pair]: /en/developer-guide#term-key-pair "A private key and its derived public key"
|
||||||
[label]: /en/developer-guide#term-label "The label parameter of a bitcoin: URI which provides the spender with the receiver's name (unauthenticated)"
|
[label]: /en/developer-guide#term-label "The label parameter of a bitcoin: URI which provides the spender with the receiver's name (unauthenticated)"
|
||||||
[leaf certificate]: /en/developer-guide#term-leaf-certificate "The end-node in a certificate chain; in the payment protocol, it is the certificate belonging to the receiver of satoshis"
|
[leaf certificate]: /en/developer-examples#term-leaf-certificate "The end-node in a certificate chain; in the payment protocol, it is the certificate belonging to the receiver of satoshis"
|
||||||
[locktime]: /en/developer-guide#term-locktime "Part of a transaction which indicates the earliest time or earliest block when that transaction can be added to the block chain"
|
[locktime]: /en/developer-guide#term-locktime "Part of a transaction which indicates the earliest time or earliest block when that transaction can be added to the block chain"
|
||||||
[long-term fork]: /en/developer-guide#term-long-term-fork "When a series of blocks have corresponding block heights, indicating a possibly serious problem"
|
[long-term fork]: /en/developer-guide#term-long-term-fork "When a series of blocks have corresponding block heights, indicating a possibly serious problem"
|
||||||
[mainnet]: /en/developer-guide#term-mainnet "The Bitcoin main network used to transfer satoshis (compare to testnet, the test network)"
|
[mainnet]: /en/developer-guide#term-mainnet "The Bitcoin main network used to transfer satoshis (compare to testnet, the test network)"
|
||||||
|
@ -90,29 +90,24 @@
|
||||||
[parent private key]: /en/developer-guide#term-parent-private-key "A private key which has created child private keys"
|
[parent private key]: /en/developer-guide#term-parent-private-key "A private key which has created child private keys"
|
||||||
[parent public key]: /en/developer-guide#term-parent-public-key "A public key corresponding to a parent private key which has child private keys"
|
[parent public key]: /en/developer-guide#term-parent-public-key "A public key corresponding to a parent private key which has child private keys"
|
||||||
[payment protocol]: /en/developer-guide#term-payment-protocol "The protocol defined in BIP70 which lets spenders get signed payment details from receivers"
|
[payment protocol]: /en/developer-guide#term-payment-protocol "The protocol defined in BIP70 which lets spenders get signed payment details from receivers"
|
||||||
[PaymentACK]: /en/developer-guide#term-paymentack "The PaymentACK of the payment protocol which allows the receiver to indicate to the spender that the payment is being processed"
|
[PaymentDetails]: /en/developer-examples#term-paymentdetails "The PaymentDetails of the payment protocol which allows the receiver to specify the payment details to the spender"
|
||||||
[PaymentDetails]: /en/developer-guide#term-paymentdetails "The PaymentDetails of the payment protocol which allows the receiver to specify the payment details to the spender"
|
[PaymentRequest]: /en/developer-examples#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
||||||
[PaymentRequest]: /en/developer-guide#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
[PaymentRequests]: /en/developer-examples#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
||||||
[PaymentRequests]: /en/developer-guide#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
|
||||||
[peer]: /en/developer-guide#term-peer "Peer on the P2P network who receives and broadcasts transactions and blocks"
|
[peer]: /en/developer-guide#term-peer "Peer on the P2P network who receives and broadcasts transactions and blocks"
|
||||||
[peers]: /en/developer-guide#term-peer "Peers on the P2P network who receive and broadcast transactions and blocks"
|
[peers]: /en/developer-guide#term-peer "Peers on the P2P network who receive and broadcast transactions and blocks"
|
||||||
[PKI]: /en/developer-guide#term-pki "Public Key Infrastructure; usually meant to indicate the X.509 certificate system used for HTTP Secure (https)."
|
[PKI]: /en/developer-examples#term-pki "Public Key Infrastructure; usually meant to indicate the X.509 certificate system used for HTTP Secure (https)."
|
||||||
[point function]: /en/developer-guide#term-point-function "The ECDSA function used to create a public key from a private key"
|
[point function]: /en/developer-guide#term-point-function "The ECDSA function used to create a public key from a private key"
|
||||||
[private key]: /en/developer-guide#term-private-key "The private portion of a keypair which can create signatures which other people can verify using the public key"
|
[private key]: /en/developer-guide#term-private-key "The private portion of a keypair which can create signatures which other people can verify using the public key"
|
||||||
[private keys]: /en/developer-guide#term-private-key "The private portion of a keypair which can create signatures which other people can verify using the public key"
|
[private keys]: /en/developer-guide#term-private-key "The private portion of a keypair which can create signatures which other people can verify using the public key"
|
||||||
[pubkey hash]: /en/developer-guide#term-pubkey-hash "The hash of a public key which can be included in a P2PKH output"
|
[pubkey hash]: /en/developer-guide#term-pubkey-hash "The hash of a public key which can be included in a P2PKH output"
|
||||||
[public key]: /en/developer-guide#term-public-key "The public portion of a keypair which can be safely distributed to other people so they can verify a signature created with the corresponding private key"
|
[public key]: /en/developer-guide#term-public-key "The public portion of a keypair which can be safely distributed to other people so they can verify a signature created with the corresponding private key"
|
||||||
[pp amount]: /en/developer-guide#term-pp-amount "Part of the Output part of the PaymentDetails part of a payment protocol where receivers can specify the amount of satoshis they want paid to a particular output script"
|
[pp amount]: /en/developer-examples#term-pp-amount "Part of the Output part of the PaymentDetails part of a payment protocol where receivers can specify the amount of satoshis they want paid to a particular output script"
|
||||||
[pp expires]: /en/developer-guide#term-pp-expires "The expires field of a PaymentDetails where the receiver tells the spender when the PaymentDetails expires"
|
[pp expires]: /en/developer-examples#term-pp-expires "The expires field of a PaymentDetails where the receiver tells the spender when the PaymentDetails expires"
|
||||||
[pp memo]: /en/developer-guide#term-pp-memo "The memo fields of PaymentDetails, Payment, and PaymentACK which allow spenders and receivers to send each other memos"
|
[pp memo]: /en/developer-examples#term-pp-memo "The memo fields of PaymentDetails, Payment, and PaymentACK which allow spenders and receivers to send each other memos"
|
||||||
[pp merchant data]: /en/developer-guide#term-pp-merchant-data "The merchant_data part of PaymentDetails and Payment which allows the receiver to send arbitrary data to the spender in PaymentDetails and receive it back in Payments"
|
[pp merchant data]: /en/developer-examples#term-pp-merchant-data "The merchant_data part of PaymentDetails and Payment which allows the receiver to send arbitrary data to the spender in PaymentDetails and receive it back in Payments"
|
||||||
[pp Payment]: /en/developer-guide#term-pp-payment "The Payment message of the PaymentProtocol which allows the spender to send payment details to the receiver"
|
[pp PKI data]: /en/developer-examples#term-pp-pki-data "The pki_data field of a PaymentRequest which provides details such as certificates necessary to validate the request"
|
||||||
[pp PKI data]: /en/developer-guide#term-pp-pki-data "The pki_data field of a PaymentRequest which provides details such as certificates necessary to validate the request"
|
[pp pki type]: /en/developer-examples#term-pp-pki-type "The PKI field of a PaymentRequest which tells spenders how to validate this request as being from a specific recipient"
|
||||||
[pp pki type]: /en/developer-guide#term-pp-pki-type "The PKI field of a PaymentRequest which tells spenders how to validate this request as being from a specific recipient"
|
[pp script]: /en/developer-examples#term-pp-script "The script field of a PaymentDetails where the receiver tells the spender what output scripts to pay"
|
||||||
[pp refund to]: /en/developer-guide#term-pp-refund-to "The refund_to field of a Payment where the spender tells the receiver what outputs to send refunds to"
|
|
||||||
[pp script]: /en/developer-guide#term-pp-script "The script field of a PaymentDetails where the receiver tells the spender what output scripts to pay"
|
|
||||||
[pp transactions]: /en/developer-guide#term-pp-transactions "The transactions field of a Payment where the spender provides copies of signed transactions to the receiver"
|
|
||||||
[pp payment url]: /en/developer-guide#term-pp-payment-url "The payment_url of the PaymentDetails which allows the receiver to specify where the sender should post payment"
|
|
||||||
[proof of work]: /en/developer-guide#term-proof-of-work "Proof that computationally-difficult work was performed which helps secure blocks against modification, protecting transaction history"
|
[proof of work]: /en/developer-guide#term-proof-of-work "Proof that computationally-difficult work was performed which helps secure blocks against modification, protecting transaction history"
|
||||||
[Pubkey]: /en/developer-guide#term-pubkey "A standard output script which specifies the full public key to match a signature; used in coinbase transactions"
|
[Pubkey]: /en/developer-guide#term-pubkey "A standard output script which specifies the full public key to match a signature; used in coinbase transactions"
|
||||||
[r]: /en/developer-guide#term-r-parameter "The payment request parameter in a bitcoin: URI"
|
[r]: /en/developer-guide#term-r-parameter "The payment request parameter in a bitcoin: URI"
|
||||||
|
@ -121,7 +116,7 @@
|
||||||
[recurrent rebilling]: /en/developer-guide#rebilling-recurring-payments "Billing a spender on a regular schedule"
|
[recurrent rebilling]: /en/developer-guide#rebilling-recurring-payments "Billing a spender on a regular schedule"
|
||||||
[redeemScript]: /en/developer-guide#term-redeemscript "A script created by the recipient, hashed, and given to the spender for use in a P2SH output"
|
[redeemScript]: /en/developer-guide#term-redeemscript "A script created by the recipient, hashed, and given to the spender for use in a P2SH output"
|
||||||
[refund]: /en/developer-guide#issuing-refunds "A transaction which refunds some or all satoshis received in a previous transaction"
|
[refund]: /en/developer-guide#issuing-refunds "A transaction which refunds some or all satoshis received in a previous transaction"
|
||||||
[root certificate]: /en/developer-guide#term-root-certificate "A certificate belonging to a certificate authority (CA)"
|
[root certificate]: /en/developer-examples#term-root-certificate "A certificate belonging to a certificate authority (CA)"
|
||||||
[root seed]: /en/developer-guide#term-root-seed "A potentially-short value used as a seed to generate a master private key and master chain code for an HD wallet"
|
[root seed]: /en/developer-guide#term-root-seed "A potentially-short value used as a seed to generate a master private key and master chain code for an HD wallet"
|
||||||
[satoshi]: /en/developer-guide#term-satoshi "The smallest unit of Bitcoin value; 0.00000001 bitcoins. Also used generically for any value of bitcoins"
|
[satoshi]: /en/developer-guide#term-satoshi "The smallest unit of Bitcoin value; 0.00000001 bitcoins. Also used generically for any value of bitcoins"
|
||||||
[satoshis]: /en/developer-guide#term-satoshi "The smallest unit of Bitcoin value; 0.00000001 bitcoins. Also used generically for any value of bitcoins"
|
[satoshis]: /en/developer-guide#term-satoshi "The smallest unit of Bitcoin value; 0.00000001 bitcoins. Also used generically for any value of bitcoins"
|
||||||
|
@ -140,7 +135,7 @@
|
||||||
[signature]: /en/developer-guide#term-signature "The result of combining a private key and some data in an ECDSA signature operation which allows anyone with the corresponding public key to verify the signature"
|
[signature]: /en/developer-guide#term-signature "The result of combining a private key and some data in an ECDSA signature operation which allows anyone with the corresponding public key to verify the signature"
|
||||||
[signature hash]: /en/developer-guide#term-signature-hash "A byte appended onto signatures generated in Bitcoin which allows the signer to specify what data was signed, allowing modification of the unsigned data"
|
[signature hash]: /en/developer-guide#term-signature-hash "A byte appended onto signatures generated in Bitcoin which allows the signer to specify what data was signed, allowing modification of the unsigned data"
|
||||||
[spv]: /en/developer-guide#simplified-payment-verification-spv "A method for verifying particular transactions were included in blocks without downloading the entire contents of the block chain"
|
[spv]: /en/developer-guide#simplified-payment-verification-spv "A method for verifying particular transactions were included in blocks without downloading the entire contents of the block chain"
|
||||||
[ssl signature]: /en/developer-guide#term-ssl-signature "Signatures created and recognized by major SSL implementations such as OpenSSL"
|
[ssl signature]: /en/developer-examples#term-ssl-signature "Signatures created and recognized by major SSL implementations such as OpenSSL"
|
||||||
[stack]: /en/developer-guide#term-stack "An evaluation stack used in Bitcoin's script language"
|
[stack]: /en/developer-guide#term-stack "An evaluation stack used in Bitcoin's script language"
|
||||||
[standard script]: /en/developer-guide#standard-transactions "An output script which matches the isStandard() patterns specified in Bitcoin Core---or a transaction containing only standard outputs. Only standard transactions are mined or broadcast by peers running the default Bitcoin Core software"
|
[standard script]: /en/developer-guide#standard-transactions "An output script which matches the isStandard() patterns specified in Bitcoin Core---or a transaction containing only standard outputs. Only standard transactions are mined or broadcast by peers running the default Bitcoin Core software"
|
||||||
[target]: /en/developer-guide#term-target "The threshold below which a block header hash must be in order for the block to be added to the block chain"
|
[target]: /en/developer-guide#term-target "The threshold below which a block header hash must be in order for the block to be added to the block chain"
|
||||||
|
@ -163,13 +158,15 @@
|
||||||
[wallet]: /en/developer-guide#wallets "Software which stores private keys to allow users to spend and receive satoshis"
|
[wallet]: /en/developer-guide#wallets "Software which stores private keys to allow users to spend and receive satoshis"
|
||||||
[Wallet Import Format]: /en/developer-guide#term-wallet-import-format "A private key specially formatted to allow easy import into a wallet"
|
[Wallet Import Format]: /en/developer-guide#term-wallet-import-format "A private key specially formatted to allow easy import into a wallet"
|
||||||
[wallets]: /en/developer-guide#wallets "Software which stores private keys to allow users to spend and receive satoshis"
|
[wallets]: /en/developer-guide#wallets "Software which stores private keys to allow users to spend and receive satoshis"
|
||||||
[X509Certificates]: /en/developer-guide#term-x509certificates
|
[X509Certificates]: /en/developer-examples#term-x509certificates
|
||||||
|
|
||||||
[BFGMiner]: https://github.com/luke-jr/bfgminer
|
[BFGMiner]: https://github.com/luke-jr/bfgminer
|
||||||
[BIP21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
|
[BIP21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
|
||||||
[BIP32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
[BIP32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||||
[BIP39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
[BIP39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||||
[BIP70]: https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
|
[BIP70]: https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
|
||||||
|
[BIP71]: https://github.com/bitcoin/bips/blob/master/bip-0071.mediawiki
|
||||||
|
[BIP72]: https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki
|
||||||
[bitcoin-documentation mailing list]: https://groups.google.com/forum/#!forum/bitcoin-documentation
|
[bitcoin-documentation mailing list]: https://groups.google.com/forum/#!forum/bitcoin-documentation
|
||||||
[bitcoinpdf]: https://bitcoin.org/bitcoin.pdf
|
[bitcoinpdf]: https://bitcoin.org/bitcoin.pdf
|
||||||
[block170]: http://blockexplorer.com/block/00000000d1145790a8694403d4063f323d499e655c83426834d4ce2f8dd4a2ee
|
[block170]: http://blockexplorer.com/block/00000000d1145790a8694403d4063f323d499e655c83426834d4ce2f8dd4a2ee
|
||||||
|
@ -180,6 +177,8 @@
|
||||||
[core paymentrequest.proto]: https://github.com/bitcoin/bitcoin/blob/master/src/qt/paymentrequest.proto
|
[core paymentrequest.proto]: https://github.com/bitcoin/bitcoin/blob/master/src/qt/paymentrequest.proto
|
||||||
[core script.h]: https://github.com/bitcoin/bitcoin/blob/master/src/script.h
|
[core script.h]: https://github.com/bitcoin/bitcoin/blob/master/src/script.h
|
||||||
[DER]: https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
[DER]: https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
||||||
|
[devex payment protocol]: /en/developer-examples#payment-protocol
|
||||||
|
[devguide]: /en/developer-guide
|
||||||
[devguide avoiding key reuse]: /en/developer-guide#avoiding-key-reuse
|
[devguide avoiding key reuse]: /en/developer-guide#avoiding-key-reuse
|
||||||
[devguide payment processing]: /en/developer-guide#payment-processing
|
[devguide payment processing]: /en/developer-guide#payment-processing
|
||||||
[devguide wallets]: /en/developer-guide#wallets
|
[devguide wallets]: /en/developer-guide#wallets
|
||||||
|
|
|
@ -198,7 +198,6 @@ body{
|
||||||
.docreference a{
|
.docreference a{
|
||||||
zoom:1;
|
zoom:1;
|
||||||
display:inline;
|
display:inline;
|
||||||
width:200px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.downloadbox{
|
.downloadbox{
|
||||||
|
|
|
@ -642,8 +642,9 @@ table td,table th{
|
||||||
}
|
}
|
||||||
.docreference a{
|
.docreference a{
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
margin:0 15px 40px 15px;
|
margin:0 0 40px 0;
|
||||||
font-size:125%;
|
font-size:125%;
|
||||||
|
width:210px;
|
||||||
}
|
}
|
||||||
.docreference img{
|
.docreference img{
|
||||||
display:block;
|
display:block;
|
||||||
|
@ -782,6 +783,21 @@ table td,table th{
|
||||||
.toccontent a.term:visited code{
|
.toccontent a.term:visited code{
|
||||||
color:#646464;
|
color:#646464;
|
||||||
}
|
}
|
||||||
|
.toccontent .multicode{
|
||||||
|
background-color:#f5f5f5;
|
||||||
|
overflow-y:auto;
|
||||||
|
padding:17px;
|
||||||
|
border:1px solid #ccc;
|
||||||
|
-webkit-border-radius:3px;
|
||||||
|
-moz-border-radius:3px;
|
||||||
|
border-radius:3px;
|
||||||
|
}
|
||||||
|
.toccontent .multicode pre{
|
||||||
|
border:0px none;
|
||||||
|
padding:0;
|
||||||
|
margin:0;
|
||||||
|
overflow-y:visible;
|
||||||
|
}
|
||||||
|
|
||||||
.anchorAf{
|
.anchorAf{
|
||||||
position:relative;
|
position:relative;
|
||||||
|
|
|
@ -63,6 +63,8 @@ require 'yaml'
|
||||||
(?![^\[]*\]) ## No subst if key inside [brackets]
|
(?![^\[]*\]) ## No subst if key inside [brackets]
|
||||||
(?![^\{]*\}) ## No subst if key inside {braces}
|
(?![^\{]*\}) ## No subst if key inside {braces}
|
||||||
(?![^\s]*<!--noref-->) ## No subst if <!--noref--> after key
|
(?![^\s]*<!--noref-->) ## No subst if <!--noref--> after key
|
||||||
|
(?![\S ]*<\/span>) ## No subst on a line with a close span. This
|
||||||
|
## prevents matching in highlight blocks
|
||||||
(?![^\(]*(\.svg|\.png)) ## No subst if key inside an image name. This
|
(?![^\(]*(\.svg|\.png)) ## No subst if key inside an image name. This
|
||||||
## simple regex has the side effect that we can't
|
## simple regex has the side effect that we can't
|
||||||
## use .svg or .png in non-image base text; if that
|
## use .svg or .png in non-image base text; if that
|
||||||
|
@ -71,7 +73,7 @@ require 'yaml'
|
||||||
(?!\w) ## Don't match inside words
|
(?!\w) ## Don't match inside words
|
||||||
/xmi, "[\\&][#{term[1]}]{:.auto-link}")
|
/xmi, "[\\&][#{term[1]}]{:.auto-link}")
|
||||||
}
|
}
|
||||||
output.gsub!('<!--noref-->','') ## Remove all <!--noref--> comments
|
output.gsub!(/<!--.*?-->/m,'') ## Remove all HTML comments
|
||||||
|
|
||||||
output
|
output
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,8 +10,9 @@ title: "Developer Documentation - Bitcoin"
|
||||||
<p class="summary">Find useful resources, guides and reference material for developers.</p>
|
<p class="summary">Find useful resources, guides and reference material for developers.</p>
|
||||||
|
|
||||||
<div class="docreference">
|
<div class="docreference">
|
||||||
<a href="/en/developer-guide"><img src="/img/main_ico_guide.svg" alt="icon"><span>Developer Guide</span><span>(How Bitcoin works)</span></a>
|
<a href="/en/developer-guide"><img src="/img/main_ico_compass.svg" alt="icon"><span>Developer Guide</span><span>(How Bitcoin works)</span></a>
|
||||||
<a href="/en/developer-reference"><img src="/img/main_ico_guide.svg" alt="icon"><span>Developer Reference</span><span>(Specifications and APIs)</span></a>
|
<a href="/en/developer-reference"><img src="/img/main_ico_guide.svg" alt="icon"><span>Developer Reference</span><span>(Specifications and APIs)</span></a>
|
||||||
|
<a href="/en/developer-examples"><img src="/img/main_ico_hash.svg" alt="icon"><span>Developer Examples</span><span>(Examples you can use)</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="resources">
|
<div class="resources">
|
||||||
|
@ -23,6 +24,7 @@ title: "Developer Documentation - Bitcoin"
|
||||||
<h2><img src="/img/ico_micro.svg" class="titleicon" alt="Icon">Transactions</h2>
|
<h2><img src="/img/ico_micro.svg" class="titleicon" alt="Icon">Transactions</h2>
|
||||||
<p><a href="/en/developer-guide#transactions">Transactions Guide</a></p>
|
<p><a href="/en/developer-guide#transactions">Transactions Guide</a></p>
|
||||||
<p><a href="/en/developer-reference#transactions">Transactions Reference</a></p>
|
<p><a href="/en/developer-reference#transactions">Transactions Reference</a></p>
|
||||||
|
<p><a href="/en/developer-examples#transactions">Transaction Examples</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -47,6 +49,7 @@ title: "Developer Documentation - Bitcoin"
|
||||||
<div>
|
<div>
|
||||||
<h2><img src="/img/ico_bill.svg" class="titleicon" alt="Icon">Payment Processing</h2>
|
<h2><img src="/img/ico_bill.svg" class="titleicon" alt="Icon">Payment Processing</h2>
|
||||||
<p><a href="/en/developer-guide#payment-processing">Payment Processing Guide</a></p>
|
<p><a href="/en/developer-guide#payment-processing">Payment Processing Guide</a></p>
|
||||||
|
<p><a href="/en/developer-examples#payment-processing">Payment Processing Examples</a></p>
|
||||||
<div class="resourcesext">
|
<div class="resourcesext">
|
||||||
<p><a href="https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki">Payment Protocol</a> - BIP70</p>
|
<p><a href="https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki">Payment Protocol</a> - BIP70</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
34
en/developer-examples.md
Normal file
34
en/developer-examples.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
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 class="goback"><li><a href="/en/developer-documentation">Return To Overview</a></li></ul>
|
||||||
|
<ul class="reportissue"><li><a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">Report An Issue</a></li></ul>
|
||||||
|
|
||||||
|
</div></div>
|
||||||
|
<div markdown="1" class="toccontent">
|
||||||
|
|
||||||
|
{% include fragment_reviews_needed.md %}
|
||||||
|
|
||||||
|
{% include example_transactions.md %}
|
||||||
|
|
||||||
|
{% include example_payment_processing.md %}
|
||||||
|
|
||||||
|
{% include references.md %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>updateToc();</script>
|
||||||
|
<script>addAnchorLinks();</script>
|
|
@ -21,18 +21,12 @@ title: "Developer Guide - Bitcoin"
|
||||||
|
|
||||||
<div markdown="1" class="toccontent">
|
<div markdown="1" class="toccontent">
|
||||||
|
|
||||||
<!--Temporary disclaimer BEGIN-->
|
|
||||||
<div id="develdocdisclaimer" class="develdocdisclaimer"><div>
|
|
||||||
<b>BETA</b>: This documentation has been written recently and still needs more reviews to ensure all content is covered correctly and accurately; if you find a mistake, please <a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">report an issue</a> on GitHub. <a href="#" onclick="disclaimerClose(event);">Click here</a> to close this disclaimer.
|
|
||||||
<a class="develdocdisclaimerclose" href="#" onclick="disclaimerClose(event);">X</a>
|
|
||||||
</div></div>
|
|
||||||
<script>disclaimerAutoClose();</script>
|
|
||||||
<!--Temporary disclaimer END-->
|
|
||||||
|
|
||||||
<!-- includes should be separated by an empty line to prevent a
|
<!-- includes should be separated by an empty line to prevent a
|
||||||
paragraph at the end of one file from breaking the heading at the start
|
paragraph at the end of one file from breaking the heading at the start
|
||||||
of the following file. -->
|
of the following file. -->
|
||||||
|
|
||||||
|
{% include fragment_reviews_needed.md %}
|
||||||
|
|
||||||
{% include guide_intro.md %}
|
{% include guide_intro.md %}
|
||||||
|
|
||||||
{% include guide_block_chain.md %}
|
{% include guide_block_chain.md %}
|
||||||
|
|
|
@ -20,16 +20,12 @@ title: "Developer Reference - Bitcoin"
|
||||||
</div></div>
|
</div></div>
|
||||||
<div markdown="1" class="toccontent">
|
<div markdown="1" class="toccontent">
|
||||||
|
|
||||||
<!--Temporary disclaimer BEGIN-->
|
{% include fragment_reviews_needed.md %}
|
||||||
<div id="develdocdisclaimer" class="develdocdisclaimer"><div>
|
|
||||||
<b>BETA</b>: This documentation has been written recently and still needs more reviews to ensure all content is covered correctly and accurately; if you find a mistake, please <a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">report an issue</a> on GitHub. <a href="#" onclick="disclaimerClose(event);">Click here</a> to close this disclaimer.
|
|
||||||
<a class="develdocdisclaimerclose" href="#" onclick="disclaimerClose(event);">X</a>
|
|
||||||
</div></div>
|
|
||||||
<script>disclaimerAutoClose();</script>
|
|
||||||
<!--Temporary disclaimer END-->
|
|
||||||
|
|
||||||
{% include ref_block_chain.md %}
|
{% include ref_block_chain.md %}
|
||||||
|
|
||||||
{% include ref_transactions.md %}
|
{% include ref_transactions.md %}
|
||||||
|
|
||||||
{% include ref_wallets.md %}
|
{% include ref_wallets.md %}
|
||||||
|
|
||||||
## Bitcoin Core APIs
|
## Bitcoin Core APIs
|
||||||
|
|
BIN
img/main_ico_compass.png
Normal file
BIN
img/main_ico_compass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
127
img/main_ico_compass.svg
Normal file
127
img/main_ico_compass.svg
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xml:space="preserve"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
style="fill-rule:evenodd"
|
||||||
|
viewBox="0 0 13.546666 13.546666"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.3.1 r9886"
|
||||||
|
sodipodi:docname="Compass_rose_en_04p.svg"><metadata
|
||||||
|
id="metadata34"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1280"
|
||||||
|
inkscape:window-height="800"
|
||||||
|
id="namedview32"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="40.338701"
|
||||||
|
inkscape:cy="32.568949"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<style
|
||||||
|
type="text/css"
|
||||||
|
id="style6">
|
||||||
|
|
||||||
|
.str1 {stroke:#999999;stroke-width:0.0762}
|
||||||
|
.str0 {stroke:#B2B2B2;stroke-width:0.0762}
|
||||||
|
.fil0 {fill:none}
|
||||||
|
.fil1 {fill:#9999FF}
|
||||||
|
.fil2 {fill:#E5E5E5}
|
||||||
|
.fil3 {fill:black;fill-rule:nonzero}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<circle
|
||||||
|
d="M 76.5,45 C 76.5,62.39697 62.39697,76.5 45,76.5 27.60303,76.5 13.5,62.39697 13.5,45 13.5,27.60303 27.60303,13.5 45,13.5 62.39697,13.5 76.5,27.60303 76.5,45 z"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.28222221;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
sodipodi:ry="31.5"
|
||||||
|
sodipodi:rx="31.5"
|
||||||
|
sodipodi:cy="45"
|
||||||
|
sodipodi:cx="45"
|
||||||
|
id="circle10"
|
||||||
|
r="31.5"
|
||||||
|
cy="45"
|
||||||
|
cx="45"
|
||||||
|
class="fil0 str0"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125488368"
|
||||||
|
class="fil1 str1"
|
||||||
|
points="45.5813,45.003 53.596,37.003 45.5813,10.003 "
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125485752"
|
||||||
|
class="fil2 str1"
|
||||||
|
points="45.5813,45.003 37.5666,37.003 45.5813,10.003 "
|
||||||
|
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125486160"
|
||||||
|
class="fil1 str1"
|
||||||
|
points="45.5813,45.003 53.5813,53.0177 80.5813,45.003 "
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125485464"
|
||||||
|
class="fil2 str1"
|
||||||
|
points="45.5813,45.003 53.5813,36.9883 80.5813,45.003 "
|
||||||
|
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125486136"
|
||||||
|
class="fil1 str1"
|
||||||
|
points="45.5813,45.003 37.5666,53.003 45.5813,80.003 "
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125485440"
|
||||||
|
class="fil2 str1"
|
||||||
|
points="45.5813,45.003 53.596,53.003 45.5813,80.003 "
|
||||||
|
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125488056"
|
||||||
|
class="fil1 str1"
|
||||||
|
points="45.5813,45.003 37.5813,36.9883 10.5813,45.003 "
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||||
|
id="_125486208"
|
||||||
|
class="fil2 str1"
|
||||||
|
points="45.5813,45.003 37.5813,53.0177 10.5813,45.003 "
|
||||||
|
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.15696824px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 8.9329554,4.7290382 11.932577,1.7294216"
|
||||||
|
id="path3804"
|
||||||
|
inkscape:connector-curvature="0" /><path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.16438642px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 8.8605813,8.8551168 12.004951,11.993504"
|
||||||
|
id="path3804-4"
|
||||||
|
inkscape:connector-curvature="0" /><path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.16195713px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 1.659623,1.6832236 4.757519,4.7752362"
|
||||||
|
id="path3804-4-5"
|
||||||
|
inkscape:connector-curvature="0" /><path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.15696824px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 1.7087612,11.924122 4.7083808,8.9244992"
|
||||||
|
id="path3804-5"
|
||||||
|
inkscape:connector-curvature="0" /></svg>
|
After Width: | Height: | Size: 5.5 KiB |
BIN
img/main_ico_hash.png
Normal file
BIN
img/main_ico_hash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 915 B |
67
img/main_ico_hash.svg
Normal file
67
img/main_ico_hash.svg
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
id="svg3896"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.3.1 r9886"
|
||||||
|
sodipodi:docname="hashbang.svg">
|
||||||
|
<defs
|
||||||
|
id="defs3898" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="45.392857"
|
||||||
|
inkscape:cy="37.178572"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1278"
|
||||||
|
inkscape:window-height="778"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3901">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1004.3622)">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:67.09772491px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
|
||||||
|
x="2.3275659"
|
||||||
|
y="1052.3116"
|
||||||
|
id="text3904"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3906"
|
||||||
|
x="2.3275659"
|
||||||
|
y="1052.3116">#</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue