PHP developer intro: Difference between revisions
Line 99: | Line 99: | ||
== Accounts == | == Accounts == | ||
In Bitcoin, money is sent to addresses. Your balance is the total of all the money in all the address in your wallet. | |||
Bitcoin goes another step. You can have accounts. Each account holds multiple addresses and adds like a mini-Bitcoin. | |||
<source lang="bash"> | |||
$ ./bitcoind listaccounts | |||
# show list of accounts and various info for each one | |||
$ ./bitcoind getaccountaddress user889 | |||
# get an address to receive money to that is unique for the account user889 | |||
$ ./bitcoind getbalance user889 | |||
# get the sum of all the money in the addresses owned by the account user889 | |||
</source> | |||
In your shopping system, each user should have a unique username. You then query bitcoin for a unique address using $bitcoin->getaccountaddress("user889"); [gets the first address for user889] or $bitcoin->getnewaddress("user889"); [creates a new address for user889]. | |||
The customer then deposits to this address. | |||
You can check the funds for that customer by doing $bitcoin->getbalance("user889", 4);. The 4 indicates the minimum number of confirmations we will accept before assuming this payment is valid. | |||
=== getnewaddress vs getaccountaddress === | |||
Using getnewaddress helps increase the anonymity of your customers by making it hard to track their payments from the POV of a malicious agent. However running it too often will cause your wallet to become filled with many empty addresses. | |||
I recommend that you run do something like: | |||
<source lang="php"> | |||
<?php | |||
require_once('jsonRPCClient.php'); | |||
$bitcoin = new jsonRPCClient('http://root:root@127.0.0.1:8332/'); | |||
# now check for appropriate funds in user account | |||
try { | |||
$username = ... | |||
if(isset($_SESSION['sendaddress'])) | |||
$sendaddress = $_SESSION['sendaddress']; | |||
else { | |||
$sendaddress = $bitcoin->getnewaddress($username); | |||
$_SESSION['sendaddress'] = $sendaddress; | |||
} | |||
$balance = $bitcoin->getbalance($username); | |||
} | |||
catch (Exception $e) { | |||
die("<p>Server error! Please contact the admin.</p>"); | |||
} | |||
?> | |||
</source> | |||
This creates a new address at the beginning of every new session, and stores it in the session variable. |
Revision as of 10:10, 21 March 2011
Linux Apache MySQL PHP + Bitcoin tutorial.
For the sake of this tutorial we assume an Ubuntu server running with PHP. The use case here is integrating a shopping system to accept Bitcoins. We assume some knowledge of Bitcoin and experience in PHP.
You can substitute any other language here for PHP. See the associated API reference pages for info on other languages.
You will run Bitcoin in daemon mode. The way PHP communicates is through localhost HTTP requests. You use a library called JSON-RPC to call the various functions. It will respond back with a JSON object.
Setting up Bitcoin
Bitcoins are stored internally as 64 bit integers. Because PHP's JSON implementation does not provide a way to use custom types for decimal numbers, we must use an alternative branch of Bitcoin which returns all decimal amounts as strings.
# install needed packages
$ sudo apt-get install build-essential libboost-all-dev libdb4.7++-dev libssl-dev libglib2.0-dev
$ git clone git://github.com/genjix/bitcoin.git
$ cd bitcoin/
$ make -f makefile.unix bitcoind
Before running bitcoind you will need to create a file in ~/.bitcoin/bitcoin.conf
rpcuser=user
rpcpassword=password
Now run bitcoind:
$ ./bitcoind
# wait a few seconds for it to start up
$ ./bitcoind getinfo
# various info shown
$ ./bitcoind help
# help on commands
Bitcoin is now initialising and you must wait until "blocks" is at the current count.
First steps
Assuming Bitcoin has finished the initialisation process; download the file jsonRPCClient.php from JSON-RPC PHP. The other files can be safely discarded.
require_once 'jsonRPCClient.php';
$bitcoin = new jsonRPCClient('http://user:password@127.0.0.1:8332/');
echo "<pre>\n";
print_r($bitcoin->getinfo());
echo "</pre>";
Precision
Because PHP has no option to JSON decode to accurate decimal class, you should internally use GMP. Treat each number like a large int with 8 decimal places (this can be trimmed for display).
Our version of Bitcoin treats every monetary value as int64 8 decimal strings. So 1 BTC will be "100000000". Use PHP's GMP functions to manipulate these values accurately.
For converting between internal GMP numbers and display/user input, you can use these functions:
# converts a user supplied number to our internal representation
# 3.14 => "314000000"
# accepts strings, floats and ints as input
function numstr_to_internal($numstr)
{
return bcmul($numstr, pow(10, 8), 0);
}
# converts an internal number to an end user number for display as defined by precision
# 314100000 => "3.14"
# accepts GMP numbers, ints and strings as input
function internal_to_numstr($num, $precision=2)
{
$repr = gmp_strval($num);
$repr = bcdiv($repr, pow(10, 8), $precision);
# now tidy output...
# trim trailing 0s
$repr = rtrim($repr, '0');
# and a trailing . if it exists
$repr = rtrim($repr, '.');
return $repr;
}
$num_internal = numstr_to_internal("3.141");
echo "<p>".$num_internal."</p>";
$num_display = internal_to_numstr($num_internal);
echo "<p>".$num_display."</p>";
If you need to do a decimal division in GMP, then GMP only supports integer division + a remainder (gmp_div_qr). The work-around for this, is to use the bcmath module:
$a = gmp_init("100");
$b = gmp_init("3");
echo "<p>".bcdiv(gmp_strval($a), gmp_strval($b), 3)."</p>";
Note that bcdiv only uses strings.
See also: GMP and BC Math manuals.
Accounts
In Bitcoin, money is sent to addresses. Your balance is the total of all the money in all the address in your wallet.
Bitcoin goes another step. You can have accounts. Each account holds multiple addresses and adds like a mini-Bitcoin.
$ ./bitcoind listaccounts
# show list of accounts and various info for each one
$ ./bitcoind getaccountaddress user889
# get an address to receive money to that is unique for the account user889
$ ./bitcoind getbalance user889
# get the sum of all the money in the addresses owned by the account user889
In your shopping system, each user should have a unique username. You then query bitcoin for a unique address using $bitcoin->getaccountaddress("user889"); [gets the first address for user889] or $bitcoin->getnewaddress("user889"); [creates a new address for user889].
The customer then deposits to this address.
You can check the funds for that customer by doing $bitcoin->getbalance("user889", 4);. The 4 indicates the minimum number of confirmations we will accept before assuming this payment is valid.
getnewaddress vs getaccountaddress
Using getnewaddress helps increase the anonymity of your customers by making it hard to track their payments from the POV of a malicious agent. However running it too often will cause your wallet to become filled with many empty addresses.
I recommend that you run do something like:
<?php
require_once('jsonRPCClient.php');
$bitcoin = new jsonRPCClient('http://root:root@127.0.0.1:8332/');
# now check for appropriate funds in user account
try {
$username = ...
if(isset($_SESSION['sendaddress']))
$sendaddress = $_SESSION['sendaddress'];
else {
$sendaddress = $bitcoin->getnewaddress($username);
$_SESSION['sendaddress'] = $sendaddress;
}
$balance = $bitcoin->getbalance($username);
}
catch (Exception $e) {
die("<p>Server error! Please contact the admin.</p>");
}
?>
This creates a new address at the beginning of every new session, and stores it in the session variable.