Skip to content

Bitcoin JavaScript (frontend) & PHP (backend) payment system

License

Notifications You must be signed in to change notification settings

robclark56/bitcoinPay-PHP

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bitcoinPay-PHP

The files in this project will allow you to safely accept Bitcoin payments on your online PHP-based store (eStore).

Checkout Mode Manual Mode
bitcoinPay GIF bitcoinPayManual GIF

FEATURES

  • Support for:
    • Two Modes
      • Checkout Mode: eStore provides memo & fiat values
      • Manual Mode: User provides memo & fiat values
    • mainnet and testnet
    • Multiple fiat currencies
    • P2PKH addresses (e.g. 1xxxxxxxx).
      • Segwit support is not available as this is written. If/When Segwit address generation is supported at https://www.smartbit.com.au/api then this code (without change) will support Segwit.
    • Exchange Rate fluctuation protection. Protection in cases of late payment broadcasts and/or late transaction mining.
    • Each new payment to an unused bitcoin address. With support for multiple payments to same address.
    • QR Code Payment Request
    • Copy to clipboard
    • Error handling
    • Variable Confirmations. E.g. buying a low value sticker requires only 1 confirmation. Buying a car requires 6 confirmations.
    • Multiple wallets
    • Live exchange rate conversions between Fiat and BTC
    • Encryption protected messaging from bitcoinPay back to the eStore site.
    • CSS formatting

CREDIT

bitcoinPay-PHP is based on LightningTip-PHP, which in turn is based on LightningTip by michael1011.

REQUIREMENTS

A webserver that supports:

SECURITY

At no point do you enter any of your bitcoin private keys. No hacker can spend your bitcoins.

You need to keep your webserver secure, as a hacker with sufficient privileges could exchange his/her xpub for yours and customers would start paying the hacker.

ECOMMERCE EXAMPLE - CHECKOUT MODE

The intended audience for this project is users that have an existing online eCommerce site (eStore). Typically the customer ends up at a checkout confirmation webpage with some Pay Now button(s).

In this project we include a very simple dummy eStore checkout page that serves as an example of how to deploy bitcoinPay.

DESIGN

The basic flow is as follows:

  1. Checkout Mode:
    1. eStore displays a shopping cart page with a total payable (Fiat currency)
    2. User clicks Pay button => Redirected to PHP file which converts fiat value to BTC, and returns a confirmation page
  2. Manual Mode
    1. User enters Memo and fiat value. PHP/Javascript calculates BTC value.
  3. User clicks Get Payment Request => Javascript passes values to PHP file which responds with a Payment Request
  4. The PHP file continuously monitors the blockchain for matching transactions
  5. Customer makes payment with wallet
  6. If/When payment has sufficient confirmations => PHP file sends a secure message back to eStore with payment status ('Paid' or 'Underpaid') and details.
  7. eStore checks message validity, and then takes appropriate action for 2 possible cases: 'Paid' or 'Underpaid'
    [eStore]<----- 'Paid'/'Underpaid'------\ 
        |                                  |
        |                                  ^
        \/                                 |
    [Web Browser,.js,.css]<----HTTP---->[.php]--[database]
        |                                  |
       [QR]                          [Blockchain Explorer]
        |                                  |
        \/                                 |
    [Bitcoin Wallet] -----------------[Blockchain]	

EXTENDED PUBLIC KEYS

This project takes advantage of the concept of Extended Public Keys (xpub). For a full understanding, see Mastering Bitcoin, Andreas M. Antonopoulos, Chapter 5.

HD Wallet Image

The important things to note are:

  • An xpub can generate
    • ALL of the public keys & addresses in your wallet.
    • NONE of the private keys in your wallet, so can not be used to spend your bitcoins.
  • Each level of the tree in the above image has a different xpub.
    • The xpub at the master ('m') level can generate addresses for many different coins (Bitcoin, Litecoin,...). We do not want to use the xpub from this level.
    • The xpub from the Bitcoin (or Bitcoin-Testnet) level is what is needed for this project.

How does bitcoinPay-PHP get the next receiving address from the xpub?

There is an undocumented feature at the smartbit.com.au API. If you give an xpub to the address API call, it returns the next un-used receiving address.

Try it!

What the ? xpub, ypub, zpub, tpub, upub, vpub

The 1st character of an Extended Public Key tells you what sort of wallet it comes from. As this is written, the smartbit.com.au API supports only xpub and tpub.

Address Type mainnet testnet
P2PKH (eg) xpub (1xxxxxx) tpub (mxxxxxx)
P2SH (eg) ypub (3xxxxx) upub (2xxxxx)
Bech32 (eg) zpub (bc1xxx) vpub (tb1xxx)

More info ...

MONITORING FOR PAYMENTS

This is done by a cron job. The timing logic is as below. EXPIRY_SECONDS & MINE_SECONDS are set in the configuration file.

  • EXPIRY_SECONDS defines a time window that starts as soon as the Payment Pequest is generated, and ends EXPIRY_SECONDS later. For a payment to be received it must be broadcast to the blockchain within that window. It does not have to be confirmed within that window. If the payment is broadcast after EXPIRY_SECONDS, bitcoinPay will not track the payment. This window adds a degree of protection when the FIAT/BTC exchange rate is rapidly changing.
  • MINE_SECONDS defines a time interval that starts as soon as the Payment Request is generated, and ends MINE_SECONDS later. A non-expired payment that is mined (include in a block) within this window, and has sufficient confirmations is accepted as PAID. This window protects for the case when the sender does not include sufficient miner fee and inclusion in the blockchain takes too long, again risking invoice under-payment in fiat value.

The cron job runs periodically to check pending payments. bitcoinPay.phpcan be used as the file for that cron job, if:

  • called as a URL with one GET parameter as follows https://my.estore.com/bitcoinPay/bitcoinPay.php?checksettled, or
  • called from the command-line as follows: $ php bitcoinPay.php checksettled

The logic used is as follows:

Transaction received within EXPIRY_SECONDS Mined within MINE_SECONDS Current Currency Value >= Invoice Currency Value Result
Yes Yes True Paid
Yes Yes False Paid
Yes No True Paid
Yes No False Underpaid
No N/A N/A Not Tracked

PREPARATION

1. Get your xpub & tpub

Your wallet software will give your xpub/tpub. Examples shown below.

  1. Coinomi: Select Bitcoin -> (3-dot menu) -> Account Details
  2. Electrum: Open the wallet you want to receive funds into. Wallet --> Information.
  3. Make your own:
    • Go to https://iancoleman.io/bip39/
    • Generate AND SAVE a new 12-word seed
    • Select Coin: BTC-Bitcoin for mainnet, or BTC-Bitcoin Testnet for testnet
    • Copy the Account Extended Public Key (not the BIP32 Extended Public Key)
  4. Other wallets: Check your documentation.

2. Generate Private/Public key pair

To generate a Private/Public key pair, use one of these options:

  1. Upload generateKeys.php to your host computer. Then run from the command line interface: $ php generateKeys.php
  2. http://travistidwell.com/jsencrypt/demo/ (save page and run offline for extra safety)

Save these keys locally for now. They will look something like this:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCQ6cZssvv0DNrh5qTDq3VnT8c41V34lTa5YFgE3itTEsxBFgUl
[... lines deleted...]
fqE1sl6cOF5yhsoYdQ2L0uJOqBS6rkqtbnN44pSzMDph
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQ6cZssvv0DNrh5qTDq3VnT8c4
[... lines deleted...]
U4UZulZEer8ss8l62QIDAQAB
-----END PUBLIC KEY-----

3. Create SQL Database

You will need to create a mySQL database. Consult your host server documentation.

For example, if you have access to cPanel, these instructions can help.

After you have created your database you should have this information:

-- Parameter -- ------------------ Value ---------------- --- Comment ---
User Give ALL PRIVILEGES
Password
Host Often is localhost
Database name

INSTALLATION

  • Create a folder on your webserver to host the bitcoinPay files. Consult your webserver documentation for details on where html files are stored.

    • e.g.: .../public_html/bitcoinPay
    • e.g.: .../var/www/html/bitcoinPay
    • e.g.: .../htdocs/bitcoinPay
  • Download the latest release, and unzip.

  • Upload all files from the unzipped resources folder to your webserver folder. Note: Due to JavaScript security, bitcoinPay.php must be hosted at the same domain as bitcoinPay.js

  • Edit files.

    • bitcoinPay_conf.php: Edit values as needed. Leave WALLET_DEFAULT set to wallet_testnet.
    • StoreCheckout.php: Edit the CHANGE_ME section.
    • StoreCallback.php: Edit the CHANGE_ME section.
    • bitcoinPay.js: Edit the CHANGE_ME section.
  • Create cron job to periodically check pending payments. Examples on how to run the cron job every 15 minutes are:

    • Servers with normal crontab-style cron jobs:
      • */15 * * * * /usr/bin/php /home/user/public_html/bitcoinPay/bitcoinPay.php checksettled
    • Servers only allowing URL-style cron jobs:
      • Every 15 mins: https://my.estore.com/bitcoinPay/bitcoinPay.php?checksettled
    • Other:
      • Consult your documentation

Email Special Consideration

Some webserver hosts do not permit use of the PHP mail() function for security reasons. If you are in this category, there is a workaround available in bitcoinPay.

  • You will need to edit the bitcoinPaySendEmail() function in these 2 files:
    • StoreCallback.php
    • bitcoinPay.php
  • Read the comments in the bitcoinPaySendEmail() function in either of these files
  • Install PHPMailer in a folder called PHPMailer
  • Edit the bitcoinPaySendEmail() function in the 2 files as below
    • Change if(false){ //false = use PHPMailer
    • Edit all lines with CHANGE_ME

TESTING - CHECKOUT MODE

Use your browser to visit your URLs like this:

  • https://my.estore.com/bitcoinPay/bitcoinPay.php?checksettled
    • Note: This displays nothing if there are no pending payments, so blank screen is a good response. The only point in trying this is to confirm there are no PHP configuration or syntax errors.
  • https://my.estore.com/bitcoinPay/StoreCallback.php
    • You should receive an email with this is the body: "Hacking Attempt???". This is the expected response when this file is called with the wrong, or no, POST parameters.
  • https://my.estore.com/bitcoinPay/StoreCheckout.php
  • https://my.estore.com/bitcoinPay/StoreCheckout.php?order_id=100
  • https://my.estore.com/bitcoinPay/StoreCheckout.php?wallet=wallet_testnet
  • https://my.estore.com/bitcoinPay/StoreCheckout.php?wallet=wallet_mainnet
  • https://my.estore.com/bitcoinPay/StoreCheckout.php?wallet=wallet_mainnet&order_id=100

or you can check my test site here:

(https not used as this is hosted on a free web server without SSL certificates. You will not be entering any sensitive data.)

TESTING - MANUAL MODE

Confirm Checkout Mode (above) is working.

Use your browser to visit your URL like this:

  • https://my.estore.com/bitcoinPay/bitcoinPay.php

or you can check my test site here:

LIVE USAGE

1a. Checkout Mode: Create a page on your eStore with a form something like this:

<?php
$order_id     = CHANGE_ME; //Some unique order identifier  
$order_value  = CHANGE_ME; //Fiat order value
$amount_format= CHANGE_ME; //eg '$'.number_format($order_value,2)
$currency     = CHANGE_ME; //eg 'USD', 'EUR', ...
$callback_url = CHANGE_ME: //eg 'https://my.estore.com/bitcoinpay/StoreCallback.php'
$bitcoinPayPHP= CHANGE_ME; //eg 'bitcoinPay/bitcoinPay.php'
?>
<form action="<?php echo $bitcoinPayPHP;?>" method="post"> 
 <table cellpadding="10">
  <tr>
   <th>Order Number: </th>
   <td><input type="input" name="memo" value="<?php echo $order_id;?>" readonly ><td>
  </tr>
  <tr>
   <th>Order Total: </th>
   <td><input type="input" name="amount_format" value="<?php echo $amount_format;?>"  readonly ><td>
  </tr>
  <tr><td colspan="2" align="right"><button type="submit">Continue...</button></td></tr>
 </table>
 <input type="hidden" name="amount" value="<?php echo $order_value;?>">
 <input type="hidden" name="currency" value="<?php echo $currency;?>"> 
 <input type="hidden" name="callback" value="<?php echo $callback_url;?>">
</form>

1b. Manual Mode

Just visit your url like: https://my.estore.com/bitcoinPay/bitcoinPay.php

2. Process Payment Notifications

Edit StoreCallback.php and change these two sections as appropriate for your eStore.

case 'fullyPaid':
   //Add code here to process fully paid order
   break;
   
  case 'underPaid':
  //Add code here to process under-paid order
  break;

3. Edit bitcoinPay_conf.php to set the default wallet to your mainnet wallet.

define('DEFAULT_WALLET'   ,'wallet_mainnet');	

4. There is a light theme available for bitcoinPay.

If you want to use it, uncomment this line in your bitcoinPay.php file:

<link rel="stylesheet" href="bitcoinPay_light.css">

5. Do not use bitcoinPay on XHTML sites

That causes some weird scaling issues.

LOCK DOWN SECURITY

1. Set files to Read Only

Example using the shell command line:

$ cd <bitcoinPay folder>
$ chmod 0444 *
$ cd ..
$ chmod 0555 <bitcoinPay folder>

If want to tip me, you can use my LightningTip as below. (https not used as these are hosted on a free web server without SSL certificates. You will not be entering any sensitive data.)