getMessage(); } } /** * * Handles post/get to BitPay via curl. * * @param string $url, string $apiKey, boolean $post * @return mixed $response * @throws Exception $e * */ function bpCurl($url, $apiKey, $post = false) { global $bpOptions; if((isset($url) && trim($url) != '') && (isset($apiKey) && trim($apiKey) != '')) { try { $curl = curl_init(); $length = 0; if ($post) { curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $post); $length = strlen($post); } $uname = base64_encode($apiKey); if($uname) { $header = array( 'Content-Type: application/json', 'Content-Length: ' . $length, 'Authorization: Basic ' . $uname, ); curl_setopt($curl, CURLOPT_URL, $url); 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)); if($bpOptions['useLogging']) bpLog('Error: ' . curl_error($curl)); } else { $response = json_decode($responseString, true); if (!$response) { $response = array('error' => 'invalid json: '.$responseString); if($bpOptions['useLogging']) bpLog('Error - Invalid JSON: ' . $responseString); } } curl_close($curl); return $response; } else { curl_close($curl); if($bpOptions['useLogging']) bpLog('Invalid data found in apiKey value passed to bpCurl. (Failed: base64_encode(apikey))'); return array('error' => 'Invalid data found in apiKey value passed to bpCurl. (Failed: base64_encode(apikey))'); } } catch (Exception $e) { @curl_close($curl); if($bpOptions['useLogging']) bpLog('Error: ' . $e->getMessage()); return array('error' => $e->getMessage()); } } else { // Invalid parameter specified if($bpOptions['useLogging']) bpLog('Error: You must supply non-empty url and apiKey parameters.'); return array('error' => 'You must supply non-empty url and apiKey parameters.'); } } /** * * Creates BitPay invoice via bpCurl. * * @param string $orderId, string $price, string $posData, array $options * @return array $response * @throws Exception $e * */ function bpCreateInvoice($orderId, $price, $posData, $options = array()) { // $orderId: Used to display an orderID to the buyer. In the account summary view, this value is used to // identify a ledger entry if present. // // $price: by default, $price is expressed in the currency you set in bp_options.php. The currency can be // changed in $options. // // $posData: this field is included in status updates or requests to get an invoice. It is intended to be used by // the merchant to uniquely identify an order associated with an invoice in their system. Aside from that, Bit-Pay does // not use the data in this field. The data in this field can be anything that is meaningful to the merchant. // // $options keys can include any of: // 'itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL', 'apiKey' // 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName', // 'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone' // // If a given option is not provided here, the value of that option will default to what is found in bp_options.php // (see api documentation for information on these options). global $bpOptions; try { $options = array_merge($bpOptions, $options); // $options override any options found in bp_options.php $pos = array('posData' => $posData); if ($bpOptions['verifyPos']) $pos['hash'] = bpHash(serialize($posData), $options['apiKey']); $options['posData'] = json_encode($pos); $options['orderID'] = $orderId; $options['price'] = $price; $postOptions = array('orderID', 'itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL', 'posData', 'price', 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName', 'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone'); foreach($postOptions as $o) { if (array_key_exists($o, $options)) $post[$o] = $options[$o]; } $post = json_encode($post); $response = bpCurl('https://bitpay.com/api/invoice/', $options['apiKey'], $post); if($bpOptions['useLogging']) { bpLog('Create Invoice: '); bpLog($post); bpLog('Response: '); bpLog($response); } return $response; } catch (Exception $e) { if($bpOptions['useLogging']) bpLog('Error: ' . $e->getMessage()); return array('error' => $e->getMessage()); } } /** * * Call from your notification handler to convert $_POST data to an object containing invoice data * * @param boolean $apiKey * @return mixed $json * @throws Exception $e * */ function bpVerifyNotification($apiKey = false) { global $bpOptions; try { if (!$apiKey) $apiKey = $bpOptions['apiKey']; $post = file_get_contents("php://input"); if (!$post) return 'No post data'; $json = json_decode($post, true); if (is_string($json)) return $json; // error if (!array_key_exists('posData', $json)) return 'no posData'; $posData = json_decode($json['posData'], true); if($bpOptions['verifyPos'] and $posData['hash'] != bpHash(serialize($posData['posData']), $apiKey)) return 'authentication failed (bad hash)'; $json['posData'] = $posData['posData']; return $json; } catch (Exception $e) { if($bpOptions['useLogging']) bpLog('Error: ' . $e->getMessage()); return array('error' => $e->getMessage()); } } /** * * Retrieves an invoice from BitPay. $options can include 'apiKey' * * @param string $invoiceId, boolean $apiKey * @return mixed $json * @throws Exception $e * */ function bpGetInvoice($invoiceId, $apiKey=false) { global $bpOptions; try { if (!$apiKey) $apiKey = $bpOptions['apiKey']; $response = bpCurl('https://bitpay.com/api/invoice/'.$invoiceId, $apiKey); if (is_string($response)) return $response; // error $response['posData'] = json_decode($response['posData'], true); $response['posData'] = $response['posData']['posData']; return $response; } catch (Exception $e) { if($bpOptions['useLogging']) bpLog('Error: ' . $e->getMessage()); return 'Error: ' . $e->getMessage(); } } /** * * Generates a base64 encoded keyed hash. * * @param string $data, string $key * @return string $hmac * @throws Exception $e * */ function bpHash($data, $key) { try { $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE)); return strtr($hmac, array('+' => '-', '/' => '_', '=' => '')); } catch (Exception $e) { if($bpOptions['useLogging']) bpLog('Error: ' . $e->getMessage()); return 'Error: ' . $e->getMessage(); } }