Sunday, 12 May 2019

CoinPayments.net Payments Module for BoxBilling

CoinPayments.net Payments Module for BoxBilling
This module is modified from the PayPal module that comes with BoxBilling.

1. Upload the bb-library folder into your BoxBilling root directory.
2. In your BoxBilling admin page, go to Configuration, Payment gateways, New payment gateway and click the Install button to the right of CoinPayments.
3. Click the Edit Settings button in the Payment Gateways list to the right of CoinPayments.
4. Optionally enter a debug email address to receive IPN errors (recommended during testing).
5. Select "Yes" next to "Enabled:" and "Allow one time payments:".
6. Select "No" next to "Enable test mode:".
7. Click "Update" to save your settings.

And you should be all set.


<------------ Code Start --------------->
<?php
/**
 * CoinPayments.net Payment Gateway for BoxBilling
 * Based on the original PayPal module
 *
 * BoxBilling
 *
 * LICENSE
 *
 * This source file is subject to the license that is bundled
 * with this package in the file LICENSE.txt
 * It is also available through the world-wide-web at this URL:
 * http://www.boxbilling.com/LICENSE.txt
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@boxbilling.com so we can send you a copy immediately.
 *
 * @copyright Copyright (c) 2010-2012 BoxBilling (http://www.boxbilling.com)
 * @license   http://www.boxbilling.com/LICENSE.txt
 * @version   $Id$
 */
class Payment_Adapter_CoinPayments
{
    private $config = array();
    private $error_msg = '';
   
    public function __construct($config)
    {
        $this->config = $config;
       
        if(!$this->config['merchant_id']) {
            throw new Exception('Payment gateway "CoinPayments" is not configured properly. Please update configuration parameter "CoinPayments.net Merchant ID" at "Configuration -> Payments".');
        }
       
        if(!$this->config['ipn_secret']) {
            throw new Exception('Payment gateway "CoinPayments" is not configured properly. Please update configuration parameter "CoinPayments.net IPN Secret" at "Configuration -> Payments".');
        }
       
        if($this->config['test_mode']) {
            throw new Exception('Payment gateway "CoinPayments" does not support Test Mode. You can enable the Litecoin Testnet for testing transactions however.');
        }
    }

    public static function getConfig()
    {
        return array(
            'supports_one_time_payments'   =>  true,
            'supports_subscriptions'     =>  false,
            'description'     =>  'Enter your CoinPayments.net Merchant ID and IPN Secret to start accepting payments by CoinPayments.',
            'form'  => array(
                'merchant_id' => array('text', array(
                            'label' => 'CoinPayments.net Merchant ID',
                            'validators'=>array(),
                    ),
                 ),
                'ipn_secret' => array('text', array(
                            'label' => 'CoinPayments.net IPN Secret',
                            'validators'=>array(),
                    ),
                 ),
                'debug_email' => array('text', array(
                            'label' => 'Debug Email (will receive copies of all IPN errors)',
                            'validators'=>array('EmailAddress'),
                    ),
                 ),
            ),
        );
    }

    public function getHtml($api_admin, $invoice_id, $subscription)
    {
        $invoice = $api_admin->invoice_get(array('id'=>$invoice_id));
        $buyer = $invoice['buyer'];
       
        $p = array(
            ':id'=>sprintf('%05s', $invoice['nr']),
            ':serie'=>$invoice['serie'],
            ':title'=>$invoice['lines'][0]['title']
        );
        $title = __('Payment for invoice :serie:id [:title]', $p);
        $number = $invoice['nr'];
       
        if($subscription) {
            //we don't support recurring subs, but we'll at least do the first payment
            $subs = $invoice['subscription'];
           
            $data = array();
            $data['cmd']                = '_pay';
            $data['reset']              = 1;
            $data['merchant']           = $this->config['merchant_id'];
            $data['item_name']          = $title;
            $data['item_number']        = $number;
            $data['want_shipping']      = 1;
            $data['allow_extra']        = 0; // Do not prompt payers to include a note with their payments. Allowable values for Subscribe buttons:
            $data['currency']           = $invoice['currency'];
            $data['amountf']            = $this->moneyFormat($invoice['total'], $invoice['currency']); // Regular subscription price.
            $data['success_url']        = $this->config['return_url'];
            $data['cancel_url']         = $this->config['cancel_url'];
            $data['ipn_url']            = $this->config['notify_url'];
            $data['invoice']            = $invoice['id'];

            //client data
            $data['address1'] = $buyer['address'];
            $data['city'] = $buyer['city'];
            $data['email'] = $buyer['email'];
            $data['first_name'] = $buyer['first_name'];
            $data['last_name'] = $buyer['last_name'];
            $data['zip'] = $buyer['zip'];
            $data['state'] = $buyer['state'];           
        } else {
            $data = array();
            $data['cmd']                = '_pay';
            $data['reset']              = 1;
            $data['merchant']           = $this->config['merchant_id'];
            $data['item_name']          = $title;
            $data['item_number']        = $number;
            $data['want_shipping']      = 1;
            $data['allow_extra']        = 0; // Do not prompt payers to include a note with their payments. Allowable values for Subscribe buttons:
            $data['currency']           = $invoice['currency'];
            $data['amountf']            = $this->moneyFormat($invoice['total'], $invoice['currency']);
            $data['taxf']               = $this->moneyFormat($invoice['tax'], $invoice['currency']);
            $data['success_url']        = $this->config['return_url'];
            $data['cancel_url']         = $this->config['cancel_url'];
            $data['ipn_url']            = $this->config['notify_url'];
            $data['invoice']            = $invoice['id'];
        }
       
        return $this->_generateForm('https://www.coinpayments.net/index.php', $data);
    }
   
    private function _errorAndDie($error_msg) {
    if ($this->config['debug_email']) {
$report = "AUTH User: |".$_SERVER['PHP_AUTH_USER']."|\n";
$report .= "AUTH Pass: |".$_SERVER['PHP_AUTH_PW']."|\n\n";

$report .= "Error Message: ".$error_msg."\n\n";

$report .= "POST Fields\n\n";
foreach ($_POST as $key => $value) {
$report .= $key . '=' . html_entity_decode($value, ENT_QUOTES, 'UTF-8'). "\n";
}

@mail($this->config['debug_email'], "CoinPayments.net Invalid IPN", $report);
}

throw new Exception('IPN Error: '.$error_msg);
// just in case
die('IPN Error: '.$error_msg);
    }

    public function processTransaction($api_admin, $id, $data, $gateway_id)
    {
      $ipn = $data['post'];
    if (!$this->_is_ipn_valid($ipn)) {
$this->_errorAndDie($this->error_msg);
}

  if (!isset($ipn['ipn_type']) || $ipn['ipn_type'] != 'button') {
$this->_errorAndDie('ipn_type != button, ignored.');
  }

      $tx = $api_admin->invoice_transaction_get(array('id'=>$id));
     
      if ($tx['status'] == 'processed') {
$this->_errorAndDie('Transaction has already been processed...');
      }

      if(!$tx['invoice_id']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'invoice_id'=>$data['get']['bb_invoice_id']));
}
       
if(!$tx['type']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'type'=>$ipn['ipn_type']));
}
       
if(!$tx['txn_id']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'txn_id'=>$ipn['txn_id']));
}
       
if(!$tx['txn_status']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'txn_status'=>$ipn['status_text']));
}
       
if(!$tx['amount']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'amount'=>$ipn['amount1']));
}
       
if(!$tx['currency']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'currency'=>$ipn['currency1']));
}

$invoice = $api_admin->invoice_get(array('id'=>$data['get']['bb_invoice_id']));
$client_id = $invoice['client']['id'];
if ($invoice['currency'] != $ipn['currency1']) {
$api_admin->invoice_transaction_update(array('id'=>$id, 'error'=>'Original currency does not match invoice currency!'));
$this->_errorAndDie('Original currency does not match invoice currency!');
}

$status = intval($ipn['status']);
if ($status >= 100 || $status == 2) {
        $bd = array(
            'id'            =>  $client_id,
            'amount'        =>  $ipn['amount1'],
            'description'   =>  'CoinPayments transaction '.$ipn['txn_id'],
            'type'          =>  'CoinPayments',
            'rel_id'        =>  $ipn['txn_id'],
        );
        $api_admin->client_balance_add_funds($bd);
        $api_admin->invoice_batch_pay_with_credits(array('client_id'=>$client_id));
        $d = array(
            'id'        => $id,
            'error'     => '',
            'error_code'=> '',
            'status'    => 'processed',
            'updated_at'=> date('c'),
        );
        $api_admin->invoice_transaction_update($d);                   
} else if ($status >= 0) {
        $d = array(
            'id'        => $id,
            'error'     => '',
            'error_code'=> '',
            'status'    => 'pending',
            'updated_at'=> date('c'),
        );
        $api_admin->invoice_transaction_update($d);                   
} else {
        $d = array(
            'id'        => $id,
            'error'     => '',
            'error_code'=> '',
            'status'    => 'canceled',
            'updated_at'=> date('c'),
        );
        $api_admin->invoice_transaction_update($d);                   
}
    }

    private function _is_ipn_valid($ipn)
    {   
if (!isset($ipn['ipn_mode'])) {
$this->error_msg  = 'IPN received with no ipn_mode.';
return FALSE;
}
if ($ipn['ipn_mode'] == 'hmac') {
if (!isset($_SERVER['HTTP_HMAC']) || empty($_SERVER['HTTP_HMAC'])) {
$this->error_msg  = 'No HMAC signature received.';
return FALSE;
}

$request = file_get_contents('php://input');
if ($request === FALSE || empty($request)) {
$this->error_msg  = 'Error reading POST data';
return FALSE;
}

$merchant = isset($ipn['merchant']) ? $ipn['merchant']:'';
if (empty($merchant)) {
$this->error_msg  = 'No Merchant ID passed';
return FALSE;
}
if ($merchant != trim($this->config['merchant_id'])) {
$this->error_msg  = 'Invalid Merchant ID';
return FALSE;
}

$hmac = hash_hmac("sha512", $request, trim($this->config['ipn_secret']));
if ($hmac != $_SERVER['HTTP_HMAC']) {
$this->error_msg  = 'HMAC signature does not match';
return FALSE;
}

return TRUE;
} else if ($ipn['ipn_mode'] == 'httpauth') {
if (isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] == trim($this->config['merchant_id'])) {
if (isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_PW'] == trim($this->config['ipn_secret'])) {
$merchant = isset($ipn['merchant']) ? $ipn['merchant']:'';
if (empty($merchant)) {
$this->error_msg  = 'No Merchant ID passed';
return FALSE;
}
if ($merchant != trim($this->config['merchant_id'])) {
$this->error_msg  = 'Invalid Merchant ID';
return FALSE;
}
return TRUE;
} else {
$this->error_msg  = 'IPN Secret not correct or no HTTP Auth variables passed. If you are using PHP in CGI mode try the HMAC method.';
}
} else {
$this->error_msg  = 'Merchant ID not correct or no HTTP Auth variables passed. If you are using PHP in CGI mode try the HMAC method.';
}
} else {
$this->error_msg  = 'Unknown ipn_mode.';
}
return FALSE;   
    }

    private function moneyFormat($amount, $currency)
    {
        return number_format($amount, 2, '.', '');
    }
   
private function _escapeForForm($data) {
if (defined('ENT_HTML401')) {
return htmlspecialchars($data,ENT_QUOTES|ENT_HTML401,'UTF-8');
} else {
return htmlspecialchars($data,ENT_QUOTES,'UTF-8');
}
}

    private function _generateForm($url, $data, $method = 'post')
    {
        $form  = '';
        $form .= '<form name="coinpayments_payment_form" action="'.$url.'" method="'.$method.'">' . PHP_EOL;
        foreach($data as $key => $value) {
            $form .= sprintf('<input type="hidden" name="%s" value="%s" />', $this->_escapeForForm($key), $this->_escapeForForm($value)) . PHP_EOL;
        }
        $form .=  '<input class="bb-button bb-button-submit" type="submit" value="Pay with Bitcoin, Litecoin, or other altcoins via CoinPayments.net" id="coinpayments_payment_button"/>'. PHP_EOL;
        $form .=  '</form>' . PHP_EOL . PHP_EOL;

        if(isset($this->config['auto_redirect']) && $this->config['auto_redirect']) {
            $form .= sprintf('<h2>%s</h2>', __('Redirecting to CoinPayments.net'));
            $form .= "<script type='text/javascript'>$(document).ready(function(){    document.getElementById('coinpayments_payment_button').style.display = 'none';    document.forms['coinpayments_payment_form'].submit();});</script>";
        }

        return $form;
    }
}


<-------------- Code End -------------------->

No comments:

Post a Comment