/** * @param string $url * @param string $apiKey * @param bool|string $post * @return array */ function bpCurl($url, $apiKey, $post = false) { global $bpOptions; $curl = curl_init($url); $length = 0; if ($post) { curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $post); $length = strlen($post); } $uname = base64_encode($apiKey); $header = array('Content-Type: application/json', 'Content-Length: ' . $length, 'Authorization: Basic ' . $uname, 'X-BitPay-Plugin-Info: whmcs3'); curl_setopt($curl, CURLOPT_PORT, 443); curl_setopt($curl, CURLOPT_HTTPHEADER, $header); curl_setopt($curl, CURLOPT_TIMEOUT, 10); curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); // verify certificate curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // check existence of CN and verify that it matches hostname curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_FORBID_REUSE, 1); curl_setopt($curl, CURLOPT_FRESH_CONNECT, 1); $responseString = curl_exec($curl); if ($responseString == false) { $response = array('error' => curl_error($curl)); bpLog('[ERROR] In modules/gateways/bitpay/bp_lib.php::bpCurl(): Invalid response received: ' . var_export($response, true)); } else { $response = json_decode($responseString, true); if (!$response) { bpLog('[ERROR] In modules/gateways/bitpay/bp_lib.php::bpCurl(): Invalid response received: ' . var_export($responseString, true)); $response = array('error' => 'invalid json: ' . $responseString); } } curl_close($curl); return $response; }
if (file_exists('../../../dbconnect.php')) { include '../../../dbconnect.php'; } else { if (file_exists('../../../init.php')) { include '../../../init.php'; } else { bpLog('[ERROR] In modules/gateways/bitpay/createinvoice.php: include error: Cannot find dbconnect.php or init.php'); die('[ERROR] In modules/gateways/bitpay/createinvoice.php: include error: Cannot find dbconnect.php or init.php'); } } require_once '../bit-pay/bp_lib.php'; $gatewaymodule = 'bitpay'; $GATEWAY = getGatewayVariables($gatewaymodule); if (!$GATEWAY['type']) { logTransaction($GATEWAY['name'], $_POST, 'Not activated'); bpLog('[ERROR] In modules/gateways/callback/bitpay.php: bitpay module not activated'); die('[ERROR] In modules/gateways/callback/bitpay.php: Bitpay module not activated.'); } $response = bpVerifyNotification($GATEWAY['apiKey'], $GATEWAY['network']); if (true === is_string($response) || true === empty($response)) { logTransaction($GATEWAY['name'], $_POST, $response); die('[ERROR] In modules/gateways/callback/bitpay.php: Invalid response received: ' . $response); } else { $invoiceid = $response['posData']; // Checks invoice ID is a valid invoice number or ends processing $invoiceid = checkCbInvoiceID($invoiceid, $GATEWAY['name']); $transid = $response['id']; // Checks transaction number isn't already in the database and ends processing if it does checkCbTransID($transid); // Successful $fee = 0;
/** * * Retrieves the current rate based on $code. * The default code us USD, so calling the * function without a parameter will return * the current BTC/USD price. * * @param string $code * @return string $rate * @throws Exception $e * */ function bpGetRate($code = 'USD') { global $bpOptions; $rate_url = 'https://bitpay.com/api/rates'; try { if (function_exists('json_decode')) { $clist = json_decode(file_get_contents($rate_url), true); } else { $clist = bpJSONdecode(file_get_contents($rate_url)); } foreach ($clist as $key => $value) { if ($value['code'] == $code) { $rate = number_format($value['rate'], 2, '.', ''); } } return $rate; } catch (Exception $e) { if ($bpOptions['useLogging']) { bpLog('Error: ' . $e->getMessage()); } return 'Error: ' . $e->getMessage(); } }
bpLog('bitpay callback error: ' . $response); } else { $order_id = $response['posData']; switch ($response['status']) { case 'paid': case 'confirmed': case 'complete': if (function_exists('xtc_db_query')) { xtc_db_query("update " . TABLE_ORDERS . " set orders_status = " . MODULE_PAYMENT_BITPAY_PAID_STATUS_ID . " where orders_id = " . intval($order_id)); } else { bpLog('FATAL: tep_db_query function is missing. Cannot update order_id = ' . $order_id . ' as ' . $response['status']); } break; case 'invalid': case 'expired': if (function_exists('xtc_remove_order')) { xtc_remove_order($order_id, $restock = true); } else { bpLog('FATAL: tep_remove_order function is missing. Cannot update order_id = ' . $order_id . ' as ' . $response['status']); } break; case 'new': break; default: bpLog('INFO: Receieved unknown IPN status of ' . $response['status'] . ' for order_id = ' . $order_id); break; } } ?>
function handle_bitpay_return() { try { $post = file_get_contents("php://input"); if (!$post) { return 'No post data'; } $response = json_decode($post, true); if (is_string($response)) { return $response; } // error if (!array_key_exists('posData', $response)) { return 'No posData'; } $posData = json_decode($response['posData'], true); if ($bpOptions['verifyPos'] and $posData['hash'] != bpHash(serialize($posData['posData']), $bpOptions['apiKey'])) { return 'Authentication failed (bad hash)'; } $response['posData'] = $posData['posData']; } catch (Exception $e) { if ($bpOptions['useLogging']) { bpLog('Error: ' . $e->getMessage()); } return array('error' => $e->getMessage()); } if (isset($response['status'])) { switch ($response['status']) { case 'new': // invoice just created, skip break; case 'paid': case 'complete': case 'confirmed': // payment has been paid, confirmed or marked complete $note = 'Payment ' . $response['status'] . '! BitPay Invoice ID: ' . $response['id']; $amount = $response['price']; $currency = $response['currency']; list($timestamp, $user_id, $sub_id, $key) = explode(':', $response['posData']); // // Update to work with latest 3.5.x Membership version // // and keep backward compatibility with older versions as well // if (!class_exists('Membership_Gateway')) // $isDuplicate = $this->duplicate_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); // else // $isDuplicate = $this->_check_duplicate_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); // if(!$isDuplicate) { // Update to work with latest 3.5.x Membership version // and keep backward compatibility with older versions as well if (!class_exists('Membership_Gateway')) { $this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); } else { $this->_record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); } do_action('membership_payment_processed', $user_id, $sub_id, $amount, $currency, $response['id']); // create_subscription $member = new M_Membership($user_id); if ($member) { $member->create_subscription($sub_id, $this->gateway); } do_action('membership_payment_subscr_signup', $user_id, $sub_id); // } break; case 'invalid': // payment has been deemed invalid. bad transaction! $note = 'This payment has been marked as invalid. Do not process membership! BitPay Invoice ID: ' . $response['id']; $amount = $response['price']; $currency = $response['currency']; list($timestamp, $user_id, $sub_id, $key) = explode(':', $response['posData']); // Update to work with latest 3.5.x Membership version // and keep backward compatibility with older versions as well if (!class_exists('Membership_Gateway')) { $this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); } else { $this->_record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); } $member = new M_Membership($user_id); if ($member) { $member->expire_subscription($sub_id); $member->deactivate(); } do_action('membership_payment_denied', $user_id, $sub_id, $amount, $currency, $response['id']); break; // Since we want instant membership activation, the paid status is combined with the confirmed // and completed statuses above. In the future if you want to change that, remove the paid: switch // above and uncomment this code: /*case 'paid': // payment has been made but confirmation pending $pending_str = 'BitPay payment received. Awaiting confirmation. BitPay Invoice ID: ' . $response['id']; $reason = 'paid'; $note = $pending_str; $amount = $response['price']; $currency = $response['currency']; $timestamp = $response['currentTime']; // Update to work with latest 3.5.x Membership version // and keep backward compatibility with older versions as well if (!class_exists('Membership_Gateway')) $this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); else $this->_record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); do_action('membership_payment_pending', $user_id, $sub_id, $amount, $currency, $response['id']); break; */ // Since we want instant membership activation, the paid status is combined with the confirmed // and completed statuses above. In the future if you want to change that, remove the paid: switch // above and uncomment this code: /*case 'paid': // payment has been made but confirmation pending $pending_str = 'BitPay payment received. Awaiting confirmation. BitPay Invoice ID: ' . $response['id']; $reason = 'paid'; $note = $pending_str; $amount = $response['price']; $currency = $response['currency']; $timestamp = $response['currentTime']; // Update to work with latest 3.5.x Membership version // and keep backward compatibility with older versions as well if (!class_exists('Membership_Gateway')) $this->record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); else $this->_record_transaction($user_id, $sub_id, $amount, $currency, $timestamp, $response['id'], $response['status'], $note); do_action('membership_payment_pending', $user_id, $sub_id, $amount, $currency, $response['id']); break; */ default: // case: various error cases break; } } else { // Did not find expected POST variables. Possible access attempt from a non BitPay site. header('Status: 404 Not Found'); echo 'Error: Missing POST variables. Identification is not possible.'; exit; } }
function after_process() { global $insert_id, $order; require_once DIR_FS_CATALOG . 'callback/bitpay/library/bp_lib.php'; $lut = array("High-0 Confirmations" => 'high', "Medium-1 Confirmations" => 'medium', "Low-6 Confirmations" => 'low'); $network = array("Live" => 'Live', "Test" => 'Test'); // change order status to value selected by merchant xtc_db_query("update " . TABLE_ORDERS . " set orders_status = " . intval(MODULE_PAYMENT_BITPAY_UNPAID_STATUS_ID) . " where orders_id = " . intval($insert_id)); $options = array('physical' => $order->content_type == 'physical' ? 'true' : 'false', 'currency' => $order->info['currency'], 'buyerName' => $order->customer['firstname'] . ' ' . $order->customer['lastname'], 'fullNotifications' => 'true', 'notificationURL' => xtc_href_link('callback/bitpay/bitpay_callback.php', '', 'SSL', true, true), 'redirectURL' => xtc_href_link('account'), 'transactionSpeed' => $lut[MODULE_PAYMENT_BITPAY_TRANSACTION_SPEED], 'apiKey' => MODULE_PAYMENT_BITPAY_APIKEY, 'network' => $network[MODULE_PAYMENT_BITPAY_NETWORK]); $decimal_place = xtc_db_fetch_array(xtc_db_query("SELECT decimal_point FROM " . TABLE_CURRENCIES . " WHERE code = '" . $order->info['currency'] . "'")); $thousands_place = xtc_db_fetch_array(xtc_db_query("SELECT thousands_point FROM " . TABLE_CURRENCIES . " WHERE code = '" . $order->info['currency'] . "'")); $decimal_place = $decimal_place['decimal_point']; $thousands_place = $thousands_place['thousands_point']; $priceString = preg_replace('/[^0-9' . $decimal_place . ']/', '', $order->info['total']); if ($decimal_place != '.') { $priceString = preg_replace('/[' . $decimal_place . ']/', '.', $priceString); } $price = floatval($priceString); $invoice = bpCreateInvoice($insert_id, $price, $insert_id, $options); if (is_array($invoice) && array_key_exists('error', $invoice)) { // error bpLog('Error creating invoice: ' . var_export($invoice, true)); xtc_remove_order($insert_id, $restock = true); xtc_redirect(xtc_href_link(FILENAME_CHECKOUT_PAYMENT, 'error_message=' . urlencode($invoice['error']['message']), 'SSL')); } else { if (!is_array($invoice)) { // error bpLog('Error creating invoice: ' . var_export($this->invoice, true)); xtc_remove_order($insert_id, $restock = true); xtc_redirect(xtc_href_link(FILENAME_CHECKOUT_PAYMENT, 'error_message=' . urlencode('There was a problem processing your payment: invalid response returned from gateway.'), 'SSL')); } else { if (is_array($invoice) && array_key_exists('url', $invoice)) { // success $_SESSION['cart']->reset(true); xtc_redirect($invoice['url']); } else { // unknown problem bpLog('Error creating invoice: ' . var_export($invoice, true)); xtc_remove_order($insert_id, $restock = true); xtc_redirect(xtc_href_link(FILENAME_CHECKOUT_PAYMENT, 'error_message=' . urlencode('There was a problem processing your payment: unknown error or response.'), 'SSL')); } } } return false; }
$currentCurrency = mysql_fetch_assoc($result); if (!$currentCurrency) { bpLog('[ERROR] In modules/gateways/bitpay/createinvoice.php: Invalid invoice currency of ' . $currency); die('[ERROR] In modules/gateways/bitpay/createinvoice.php: Invalid invoice currency of ' . $currency); } $result = mysql_query("SELECT code, rate FROM tblcurrencies where `id` = {$convertTo}"); $convertToCurrency = mysql_fetch_assoc($result); if (!$convertToCurrency) { bpLog('[ERROR] In modules/gateways/bitpay/createinvoice.php: Invalid convertTo currency of ' . $convertTo); die('[ERROR] In modules/gateways/bitpay/createinvoice.php: Invalid convertTo currency of ' . $convertTo); } $currency = $convertToCurrency['code']; $price = $price / $currentCurrency['rate'] * $convertToCurrency['rate']; } // create invoice $options = $_POST; unset($options['invoiceId']); unset($options['systemURL']); $options['notificationURL'] = $_POST['systemURL'] . '/modules/gateways/callback/bitpay.php'; $options['redirectURL'] = $_POST['systemURL']; $options['apiKey'] = $GATEWAY['apiKey']; $options['transactionSpeed'] = $GATEWAY['transactionSpeed']; $options['currency'] = $currency; $options['network'] = $GATEWAY['network']; $invoice = bpCreateInvoice($invoiceId, $price, $invoiceId, $options); if (isset($invoice['error'])) { bpLog('[ERROR] In modules/gateways/bitpay/createinvoice.php: Invoice error: ' . var_export($invoice['error'], true)); die('[ERROR] In modules/gateways/bitpay/createinvoice.php: Invoice error: ' . var_export($invoice['error']['message'], true)); } else { header('Location: ' . $invoice['url']); }
function onPaymentNotification(&$statuses) { $pluginsClass = hikashop_get('class.plugins'); $elements = $pluginsClass->getMethods('payment', 'hikabitcoin'); if (empty($elements)) { return false; } $element = reset($elements); $payment_params = $element->payment_params; $mailer = JFactory::getMailer(); $config =& hikashop_config(); $sender = array($config->get('from_email'), $config->get('from_name')); $mailer->setSender($sender); $mailer->addRecipient(explode(',', $config->get('payment_notification_email'))); if ($payment_params->test) { echo "\n\n params" . print_r($payment_params, true); } if (!$payment_params->notification) { return false; } require dirname(__FILE__) . DIRECTORY_SEPARATOR . 'bitpay/bp_lib.php'; $response = bpVerifyNotification($payment_params->apiKey); if ($payment_params->test) { echo "\n\n response" . print_r($response, true); } if (is_string($response) || !empty($response['error'])) { bpLog($response); if (is_array($response)) { $response = $response['error']; } $mailer->setSubject(JText::sprintf('NOTIFICATION_REFUSED_FOR_THE_ORDER', 'Bitcoin') . 'invalid response Server Response:' . $response); $body = JText::sprintf("Hello,\r\n A bitcoin notification was refused because the response from the bitcoin server was invalid"); $mailer->setBody($body); $mailer->Send(); return false; } else { $id = $orderid = $response['posData']; $orderClass = hikashop_get('class.order'); $dbOrder = $orderClass->get((int) $id); $order = new stdClass(); $order->order_id = $dbOrder->order_id; $order->old_status->order_status = $dbOrder->order_status; $url = HIKASHOP_LIVE . 'administrator/index.php?option=com_hikashop&ctrl=order&task=edit&order_id=' . $order->order_id; $order_text = "\r\n" . JText::sprintf('NOTIFICATION_OF_ORDER_ON_WEBSITE', $dbOrder->order_number, HIKASHOP_LIVE); $order_text .= "\r\n" . str_replace('<br/>', "\r\n", JText::sprintf('ACCESS_ORDER_WITH_LINK', $url)); $isValid = true; if ($id > 0) { if (empty($dbOrder)) { $isValid = false; } } else { $isValid = false; } if (!$isValid) { $mailer->setSubject(JText::sprintf('NOTIFICATION_REFUSED_FOR_THE_ORDER', 'Bitcoin') . 'invalid transaction Server Response:' . $response['message']); $body = JText::sprintf("Hello,\r\n A bitcoin notification was refused because it could not be verified by the bitcoin server") . $order_text; $mailer->setBody($body); $mailer->Send(); if ($element->payment_params->test) { echo 'invalid transaction' . "\n\n\n"; } return false; } echo 'Status: ' . $response['status'] . "\n\n\n"; echo 'Invoice id: ' . $response['id'] . "\n\n\n"; echo 'Url: ' . $response['url'] . "\n\n\n"; echo 'posData: ' . $response['posData'] . "\n\n\n"; echo 'price: ' . $response['price'] . "\n\n\n"; echo 'btcPrice: ' . $response['btcPrice'] . "\n\n\n"; $order->history->history_reason = JText::sprintf('AUTOMATIC_PAYMENT_NOTIFICATION'); $order->history->history_notified = 0; $order->history->history_amount = @$response['price']; $order->history->history_payment_id = $element->payment_id; $order->history->history_payment_method = $element->payment_type; $order->history->history_data = ob_get_clean(); $order->history->history_type = 'payment'; $currencyClass = hikashop_get('class.currency'); $currencies = null; $currencies = $currencyClass->getCurrencies($dbOrder->order_currency_id, $currencies); $currency = $currencies[$dbOrder->order_currency_id]; $price_check = sprintf('%.2f', $dbOrder->order_full_price, (int) $currency->currency_locale['int_frac_digits']); if ($price_check != @$response['price']) { $order->order_status = $element->payment_params->invalid_status; $orderClass->save($order); $mailer->setSubject(JText::sprintf('NOTIFICATION_REFUSED_FOR_THE_ORDER', 'Bitcoin') . JText::_('INVALID_AMOUNT')); $body = str_replace('<br/>', "\r\n", JText::sprintf('AMOUNT_RECEIVED_DIFFERENT_FROM_ORDER', 'Bitcoin', $order->history->history_amount, $price_check . $currency->currency_code)) . "\r\n\r\n" . $order_text; $mailer->setBody($body); $mailer->Send(); return false; } $send_mail = false; switch ($response['status']) { //For low and medium transaction speeds, the order status is set to "Order Received". The customer receives //an initial email stating that the transaction has been paid. case 'paid': $send_mail = true; $order->order_status = $element->payment_params->paid_status; $order_text .= "Payment has been received for order number" . $dbOrder->order_number . ", but the transaction has not been confirmed on the bitcoin network. " . "You will receive another email when the transaction has been confirmed."; //"Payment Received" //false because this is just for email notification break; //For low and medium transaction speeds, the order status will not change. For high transaction speed, the order //status is set to "Order Received" here. For all speeds, an email will be sent stating that the transaction has //been confirmed. //For low and medium transaction speeds, the order status will not change. For high transaction speed, the order //status is set to "Order Received" here. For all speeds, an email will be sent stating that the transaction has //been confirmed. case 'confirmed': $send_mail = true; $order->order_status = $element->payment_params->confirmed_status; //display initial "thank you" if transaction speed is high, as the 'paid' status is skipped on high speed if (get_option('bitpay_transaction_speed') == 'high') { $order_text .= "Payment has been received, and the transaction has been confirmed on the bitcoin network for order number" . $dbOrder->order_number . ". " . "You will receive another email when the transaction is complete."; //"Payment Received" } else { $order_text .= "Transaction has now been confirmed on the bitcoin network order number" . $dbOrder->order_number . ". " . "You will receive another email when the transaction is complete."; //"Transaction Confirmed" } //false because this is just for email notification break; //The purchase receipt email is sent upon the invoice status changing to "complete", and the order //status is changed to Accepted Payment //The purchase receipt email is sent upon the invoice status changing to "complete", and the order //status is changed to Accepted Payment case 'complete': $send_mail = true; $order->order_status = $element->payment_params->complete_status; $order->history->history_notified = 1; $order_text .= "Transaction is now complete! for order number" . $dbOrder->order_number; //"Transaction Complete" //false because this is just for email notification break; case 'invalid': $send_mail = true; $order->order_status = $element->payment_params->invalid_status; $order_text .= "Invalid transaction for order number" . $dbOrder->order_number; //false because this is just for email notification break; } if ($dbOrder->order_status == $order->order_status) { return true; } if ($send_mail != true) { return true; } $order->mail_status = $statuses[$order->order_status]; $mailer->setSubject(JText::sprintf('PAYMENT_NOTIFICATION_FOR_ORDER', 'Bitcoin', $response['status'], $dbOrder->order_number)); $body = str_replace('<br/>', "\r\n", JText::sprintf('PAYMENT_NOTIFICATION_STATUS', 'Bitcoin', $response['status'])) . ' ' . JText::sprintf('ORDER_STATUS_CHANGED', $order->mail_status) . "\r\n\r\n" . $order_text; $mailer->setBody($body); $mailer->Send(); $orderClass->save($order); return true; } }
/** * @return false */ function after_process() { global $insert_id, $order; require_once 'bitpay/bp_lib.php'; $lut = array("High-0 Confirmations" => 'high', "Medium-1 Confirmations" => 'medium', "Low-6 Confirmations" => 'low'); // change order status to value selected by merchant tep_db_query("update " . TABLE_ORDERS . " set orders_status = " . intval(MODULE_PAYMENT_BITPAY_UNPAID_STATUS_ID) . " where orders_id = " . intval($insert_id)); $options = array('physical' => $order->content_type == 'physical' ? 'true' : 'false', 'currency' => $order->info['currency'], 'buyerName' => $order->customer['firstname'] . ' ' . $order->customer['lastname'], 'fullNotifications' => 'true', 'notificationURL' => tep_href_link('bitpay_callback.php', '', 'SSL', true, true), 'redirectURL' => tep_href_link(FILENAME_ACCOUNT), 'transactionSpeed' => $lut[MODULE_PAYMENT_BITPAY_TRANSACTION_SPEED], 'apiKey' => MODULE_PAYMENT_BITPAY_APIKEY); $invoice = bpCreateInvoice($insert_id, $order->info['total'], $insert_id, $options); if (is_array($invoice) && array_key_exists('error', $invoice)) { // error bpLog('Error creating invoice: ' . var_export($invoice, true)); tep_remove_order($insert_id, $restock = true); tep_redirect(tep_href_link(FILENAME_SHOPPING_CART, 'error_message=' . urlencode($invoice['error']['message']), 'SSL')); } else { if (!is_array($invoice)) { // error bpLog('Error creating invoice: ' . var_export($invoice, true)); tep_remove_order($insert_id, $restock = true); tep_redirect(tep_href_link(FILENAME_SHOPPING_CART, 'error_message=' . urlencode('There was a problem processing your payment: invalid response returned from gateway.'), 'SSL')); } else { if (is_array($invoice) && array_key_exists('url', $invoice)) { // success $_SESSION['cart']->reset(true); tep_redirect($invoice['url']); } else { // unknown problem bpLog('Error creating invoice: ' . var_export($invoice, true)); tep_remove_order($insert_id, $restock = true); tep_redirect(tep_href_link(FILENAME_SHOPPING_CART, 'error_message=' . urlencode('There was a problem processing your payment: unknown error or response.'), 'SSL')); } } } return false; }