.
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BitcoinPay WooCommerce payment module
2 |
3 | BitcoinPay payment module for WooCommerce
4 |
5 | ### Version
6 |
7 | 1.0
8 |
9 | ### Installation guide
10 |
11 | 1. Extract ZIP file
12 | 2. Copy the extracted contents into your Wordpress installation under wp-content/plugins
13 | 3. Go to your Wordpress administration panel and navigate: Plugins > Installed plugins > WooCommerce Bitcoinpay Payment Gateway. Activate Bitcoinpay plugin.
14 | 4. In Wordpress Menu go to WooCommerce > Settings > Checkout > Payment gateways. Enable BitcoinPay option.
15 | 5. Configure BitcoinPay plugin.
16 | 6. You are ready to accept Bitcoins!
17 |
--------------------------------------------------------------------------------
/bitcoinpay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitcoinpay/bitcoinpay-woocommerce/59f1a60b4f752291436f567f2a90d985cc6b4c88/bitcoinpay.png
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | id = 'bitcoinpay';
24 | $this->icon_path = WP_PLUGIN_URL . "/" . plugin_basename(dirname(__FILE__)) . '/bitcoinpay.png';
25 | $this->method_title = 'Bitcoin';
26 | $this->has_fields = false;
27 |
28 | $this->init_form_fields();
29 | $this->init_settings();
30 |
31 | $this->title = $this->settings['title'];
32 | $this->description = $this->settings['description'];
33 | $this->icon_enable = $this->settings['icon'];
34 | $this->apikey = $this->settings['apikey'];
35 | $this->callback = $this->settings['callback'];
36 | $this->email = $this->settings['email'];
37 | $this->payout = $this->settings['payout'];
38 |
39 | $this->liveurl = 'https://bitcoinpaycom.apiary-mock.com/api/v1/payment/btc';
40 |
41 | $this->msg['message'] = "";
42 | $this->msg['class'] = "";
43 |
44 | add_action('woocommerce_thankyou', function()
45 | {
46 |
47 | $returnStatus = $_GET["bitcoinpay-status"];
48 | $doit = true;
49 |
50 | if (strcmp($returnStatus, "true") == 0) {
51 | $doit = false;
52 | } elseif (strcmp($returnStatus, "received") == 0) {
53 | $bcp_thanks_title = "Your Order Has Not Been Processed Yet!";
54 | $bcp_thanks_msg = "Your order has not been successfully processed yet! We received your payment, but we are waiting for confirmation. You will be notified by email.";
55 | } elseif (strcmp($returnStatus, "cancel") == 0) {
56 | $bcp_thanks_title = "Your Order Has Been Cancelled!";
57 | $bcp_thanks_msg = "Your order has been cancelled at BitcoinPay payment gate! You may place a new one.";
58 | } else {
59 | $bcp_thanks_title = "Your Order Has Not Been Processed!";
60 | $bcp_thanks_msg = "Your order has not been successfully processed!";
61 | }
62 |
63 | if ($doit) {
64 | echo "";
70 | }
71 | });
72 |
73 | add_action('woocommerce_api_' . strtolower(get_class($this)), array(
74 | &$this,
75 | 'handle_callback'
76 | ));
77 |
78 | if (version_compare(WOOCOMMERCE_VERSION, '2.0.0', '>=')) {
79 | add_action('woocommerce_update_options_payment_gateways_' . $this->id, array(
80 | &$this,
81 | 'process_admin_options'
82 | ));
83 | } else {
84 | add_action('woocommerce_update_options_payment_gateways', array(
85 | &$this,
86 | 'process_admin_options'
87 | ));
88 | }
89 |
90 | if (strlen($this->apikey) == 0 || strlen($this->payout) == 0) {
91 | static $count = 0;
92 | $count++;
93 |
94 | $this->enabled = 'no';
95 | $this->settings['enabled'] = 'no';
96 |
97 | if ($count > 1)
98 | $this->errors[] = "Payment gateway has been disabled!";
99 | }
100 |
101 | }
102 |
103 | // custom link and icons
104 | public function get_icon()
105 | {
106 | if (strcmp($this->icon_enable, 'yes') == 0)
107 | $icon_html = "
icon_path}\" alt=\"BitcoinPay\">";
108 | else
109 | $icon_html = '';
110 |
111 | $icon_html .= 'What is BitcoinPay?';
112 |
113 | return apply_filters('woocommerce_gateway_icon', $icon_html, $this->id);
114 | }
115 |
116 | // validation functions
117 | function validate_apikey_field($key)
118 | {
119 | static $count = 0;
120 | $count++;
121 |
122 | // get the posted value
123 | $value = $_POST[$this->plugin_id . $this->id . '_' . $key];
124 |
125 | if (isset($value) && 24 != strlen($value)) {
126 | if ($count > 1)
127 | return "";
128 | else
129 | $this->errors[] = "Your API key is not VALID!";
130 | }
131 | return $value;
132 | }
133 |
134 | function validate_payout_field($key)
135 | {
136 | static $count = 0;
137 | $count++;
138 |
139 | // get the posted value
140 | $value = $_POST[$this->plugin_id . $this->id . '_' . $key];
141 |
142 | if (isset($value) && (strlen($value) != 3)) {
143 | if ($count > 1)
144 | return "";
145 | else
146 | $this->errors[] = "Your Payout currency is not VALID! Use 3 letter currency code.";
147 | } elseif (isset($value) && strlen($valid_curr = $this->check_currency($value)) != 0) {
148 | if ($count > 1)
149 | return "";
150 | else {
151 | strlen($valid_curr) == 1 ? $curr_list = "You must select your payout currency in BitcoinPay.com administration first" : $curr_list = $valid_curr;
152 | $this->errors[] = "Your Payout currency is not VALID! Select form: {$valid_curr}";
153 | }
154 | }
155 | return $value;
156 | }
157 |
158 | public function check_currency($user_curr)
159 | {
160 | static $count;
161 | $count++;
162 |
163 | $isValid = false;
164 | $settlement_url = 'https://www.bitcoinpay.com/api/v1/settlement/';
165 | $apiID = $this->apikey;
166 |
167 | $curlheaders = array(
168 | "Content-type: application/json",
169 | "Authorization: Token {$apiID}"
170 | );
171 |
172 | $curl = curl_init($settlement_url);
173 | curl_setopt($curl, CURLOPT_HEADER, true);
174 | curl_setopt($curl, CURLOPT_VERBOSE, true);
175 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
176 | curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheaders);
177 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // bypassing ssl verification, because of bad compatibility
178 |
179 | $response = curl_exec($curl);
180 |
181 | $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
182 | $jHeader = substr($response, 0, $header_size);
183 | $jBody = substr($response, $header_size);
184 |
185 | // http response code
186 | $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
187 |
188 | if ($status != 200) {
189 | if ($status == 401) {
190 | if ($count > 1)
191 | $this->errors[] = "API key is not VALID! Cannot connect to gate to check payout currency!";
192 | } else {
193 | if ($count > 1)
194 | $this->errors[] = "API key is not VALID! Cannot connect to gate to check payout currency!";
195 | }
196 | curl_close($curl);
197 | return "";
198 | }
199 |
200 | $answer = json_decode($jBody);
201 | $active_currencies = $answer->data->active_settlement_currencies;
202 |
203 | if (count($active_currencies) == 0) {
204 | curl_close($curl);
205 | return "1";
206 | }
207 |
208 | foreach ($active_currencies as $value) {
209 | if (strcmp($value, $user_curr) == 0) {
210 | $isValid = true;
211 | break;
212 | }
213 | }
214 |
215 | if (!$isValid) {
216 | $valid_currencies = '';
217 | foreach ($active_currencies as $value) {
218 | $valid_currencies .= '
' . $value;
219 | }
220 | return $valid_currencies;
221 | }
222 |
223 | curl_close($curl);
224 | }
225 |
226 | public function display_errors()
227 | {
228 | // loop through each error and display it
229 | foreach ($this->errors as $key => $value) {
230 | echo '';
231 | _e($value, 'bcp-error');
232 | echo '
';
233 | }
234 | unset($this->errors);
235 | }
236 |
237 | // callback function
238 | function handle_callback()
239 | {
240 | $inputData = file_get_contents('php://input');
241 | $payResponse = json_decode($inputData);
242 |
243 | // callback password
244 | if (($callbackPass = $this->callback) != NULL) {
245 | $paymentHeaders = getallheaders();
246 | $digest = $paymentHeaders["Bpsignature"];
247 |
248 | $hashMsg = $inputData . $callbackPass;
249 | $checkDigest = hash('sha256', $hashMsg);
250 |
251 | if (strcmp($digest, $checkDigest) == 0) {
252 | $security = 1;
253 | } else {
254 | $security = 0;
255 | }
256 | } else {
257 | $security = 1;
258 | }
259 |
260 | // payment status
261 | $paymentStatus = $payResponse->status;
262 |
263 | // order id
264 | $preOrderId = json_decode($payResponse->reference);
265 | $orderId = $preOrderId->order_number;
266 |
267 | // confirmation process
268 | $order = new WC_Order($orderId);
269 |
270 | if ($security) {
271 | if ($paymentStatus != NULL) {
272 | error_log($paymentStatus);
273 | switch ($paymentStatus) {
274 | case 'confirmed':
275 | $order->update_status('processing', __('BCP Payment processing', 'bcp'));
276 | break;
277 | case 'pending':
278 | $order->update_status('pending', __('BCP Payment pending', 'bcp'));
279 | break;
280 | case 'received':
281 | $order->update_status('pending', __('BCP Payment received but still pending', 'bcp'));
282 | break;
283 | case 'insufficient_amount':
284 | $order->update_status('failed', __('BCP Payment failed. Insufficient amount', 'bcp'));
285 | break;
286 | case 'invalid':
287 | $order->update_status('cancelled', __('BCP Payment failed. Invalid', 'bcp'));
288 | break;
289 | case 'timeout':
290 | $order->update_status('cancelled', __('BCP Payment failed. Timeout', 'bcp'));
291 | break;
292 | case 'refund':
293 | $order->update_status('refunded', __('BCP Payment refunded', 'bcp'));
294 | break;
295 | case 'paid_after_timeout':
296 | $order->update_status('failed', __('BCP Payment failed. Paid after timeout', 'bcp'));
297 | break;
298 | }
299 | }
300 | }
301 | }
302 |
303 | function add_content()
304 | {
305 | echo 'Get 20% off
Thank you for making this purchase! Come back and use the code "Back4More" to receive a 20% discount on your next purchase! Click here to continue shopping.
';
306 | }
307 |
308 | function init_form_fields()
309 | {
310 | $this->form_fields = array(
311 | 'enabled' => array(
312 | 'title' => __('Enable/Disable', 'bcp'),
313 | 'type' => 'checkbox',
314 | 'label' => __('Enable BitcoinPay Payment Module.', 'bcp'),
315 | 'default' => 'no'
316 | ),
317 | 'title' => array(
318 | 'title' => __('Title:', 'bcp'),
319 | 'type' => 'text',
320 | 'description' => __('This controls the title which the user sees during checkout.', 'bcp'),
321 | 'default' => __('BitcoinPay', 'bcp')
322 | ),
323 | 'description' => array(
324 | 'title' => __('Description:', 'bcp'),
325 | 'type' => 'textarea',
326 | 'description' => __('This controls the description which the user sees during checkout.', 'bcp'),
327 | 'default' => __('Pay securely with Bitcoins through BitcoinPay Secure Servers.', 'bcp')
328 | ),
329 | 'icon' => array(
330 | 'title' => __('Frontend icon', 'bcp'),
331 | 'type' => 'checkbox',
332 | 'label' => __('Display Bitcoin icon in frontend.', 'bcp'),
333 | 'default' => 'no'
334 | ),
335 | 'apikey' => array(
336 | 'title' => __('* API key:', 'bcp'),
337 | 'type' => 'text',
338 | 'description' => __('API key is used for backed authentication and you should keep it private. You will find your API key in your account under settings > API', 'bcp'),
339 | 'desc_tip' => true
340 | ),
341 | 'callback' => array(
342 | 'title' => __('Callback password:', 'bcp'),
343 | 'type' => 'text',
344 | 'description' => __('We recommend using a callback password. It is used as a data validation for stronger security. Callback password can be set under Settings > API in your account at BitcoinPay.com', 'bcp'),
345 | 'desc_tip' => true
346 | ),
347 | 'email' => array(
348 | 'title' => __('E-Mail:', 'bcp'),
349 | 'type' => 'text',
350 | 'description' => __('Email where notifications about Payment changes are sent.', 'bcp'),
351 | 'desc_tip' => true
352 | ),
353 | 'payout' => array(
354 | 'title' => __('* Payout currency:', 'bcp'),
355 | 'type' => 'text',
356 | 'description' => __('Currency of settlement. You must first set a payout for currency in your account Settings > Payout in your account at BitcoinPay.com. If the currency is not set in payout, the request will return an error.', 'bcp'),
357 | 'desc_tip' => true
358 | )
359 | );
360 | }
361 |
362 | public function admin_options()
363 | {
364 | echo '' . __('BitcoinPay Payment Gateway', 'bcp') . '
';
365 | echo '' . __('BitcoinPay is secure payment gateway for Bitcoin transactions') . '
';
366 | echo '';
370 | }
371 |
372 | /**
373 | * There are no payment fields for payu, but we want to show the description if set.
374 | **/
375 | function payment_fields()
376 | {
377 | if ($this->description)
378 | echo wpautop(wptexturize($this->description));
379 | }
380 |
381 | /**
382 | * Process the payment and return the result
383 | **/
384 | function process_payment($order_id)
385 | {
386 | global $woocommerce;
387 | $order = new WC_Order($order_id);
388 |
389 | // gate logic start
390 | // Getting API-ID from config
391 | $apiID = $this->settings['apikey'];
392 |
393 | // test mode check
394 | $testMode = 0; // if set to 1, test mode will be set
395 | if (!$testMode) {
396 | $payurl = 'https://www.bitcoinpay.com/api/v1/payment/btc';
397 | } else {
398 | $payurl = 'https://bitcoinpaycom.apiary-mock.com/api/v1/payment/btc';
399 | }
400 |
401 | // data preparation
402 | $bcp_order_id = $order_id;
403 | $bcp_price = $order->get_total();
404 | $bcp_fname = $order->billing_first_name;
405 | $bcp_lname = $order->billing_last_name;
406 | $bcp_name = "{$bcp_fname} {$bcp_lname}";
407 | $bcp_email = $order->billing_email;
408 | $bcp_currency = get_woocommerce_currency();
409 |
410 | // data finalize
411 | $customData = array(
412 | 'customer_name' => $bcp_name,
413 | 'order_number' => intval($bcp_order_id),
414 | 'customer_email' => $bcp_email
415 | );
416 | $jCustomData = json_encode($customData);
417 |
418 | $notiEmail = $this->settings['email'];
419 | $lang = "";
420 | $settCurr = $this->settings['payout'];
421 |
422 | if (strlen($settCurr) != 3) {
423 | $settCurr = "BTC";
424 | }
425 |
426 | $bcp_callback_url = str_replace('https:', 'http:', add_query_arg('wc-api', 'wc_bcp_payment', home_url('/')));
427 | $bcp_return_url = $this->get_return_url($order);
428 |
429 | $postData = array(
430 | 'settled_currency' => $settCurr,
431 | 'return_url' => $bcp_return_url,
432 | 'notify_url' => $bcp_callback_url,
433 | 'price' => floatval($bcp_price),
434 | 'currency' => $bcp_currency,
435 | 'reference' => json_decode($jCustomData)
436 | );
437 |
438 | if (($notiEmail !== NULL) && (strlen($notiEmail) > 5)) {
439 | $postData['notify_email'] = $notiEmail;
440 | }
441 | if ((strcmp($lang, "cs") !== 0) || (strcmp($lang, "en") !== 0) || (strcmp($lang, "de") !== 0)) {
442 | $postData['lang'] = "en";
443 | } else {
444 | $postData['lang'] = $lang;
445 | }
446 |
447 | $content = json_encode($postData);
448 |
449 | // sending data via cURL
450 | $curlheaders = array(
451 | "Content-type: application/json",
452 | "Authorization: Token {$apiID}"
453 | );
454 | $curl = curl_init($payurl);
455 | curl_setopt($curl, CURLOPT_HEADER, true);
456 | curl_setopt($curl, CURLOPT_VERBOSE, true);
457 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
458 | curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheaders);
459 | curl_setopt($curl, CURLOPT_POST, true);
460 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // bypassing ssl verification, because of bad compatibility
461 | curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
462 |
463 | // sending to server, and waiting for response
464 | $response = curl_exec($curl);
465 |
466 | $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
467 | $jHeader = substr($response, 0, $header_size);
468 | $jBody = substr($response, $header_size);
469 |
470 | $jHeaderArr = $this->get_headers_from_curl_response($jHeader);
471 |
472 | // http response code
473 | $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
474 |
475 | // callback password check
476 | if (($callbackPass = $this->settings['callback']) != NULL) {
477 | $digest = $jHeaderArr[0]["BPSignature"];
478 |
479 | $hashMsg = $jBody . $callbackPass;
480 | $checkDigest = hash('sha256', $hashMsg);
481 |
482 | if (strcmp($digest, $checkDigest) == 0) {
483 | $security = 1;
484 | } else {
485 | $security = 0;
486 | }
487 | } else {
488 | $security = 1;
489 | }
490 |
491 | if ($status != 200) {
492 | die("Error: call to URL {$payurl} failed with status {$status}, curl_error " . curl_error($curl) . ", curl_errno " . curl_errno($curl) . "
Please contact shop administrator...");
493 | curl_close($curl);
494 | } elseif (!$security) {
495 | die("Error: Callback password does not match!
Please contact shop administrator...");
496 | curl_close($curl);
497 | } else {
498 | curl_close($curl);
499 |
500 | $response = json_decode($jBody);
501 | // adding paymentID to payment method
502 | $BCPPaymentId = $response->data->payment_id;
503 | $bcp_pre_inv = "https://bitcoinpay.com/en/sci/invoice/btc/" . $BCPPaymentId;
504 | $BCPInvoiceUrl = "
BitcoinPay Invoice: " . $bcp_pre_inv . "";
505 | // $prePaymentMethod = html_entity_decode($order_info['payment_method'], ENT_QUOTES, 'UTF-8');
506 | $finPaymentMethod = "PaymentID: " . $BCPPaymentId . $BCPInvoiceUrl;
507 |
508 | // redirect to pay gate
509 | $paymentUrl = $response->data->payment_url;
510 | $order->add_order_note(__($finPaymentMethod, 'bcp'));
511 |
512 | // Mark as on-hold (we're awaiting the cheque)
513 | $order->update_status('pending', __('BCP Payment pending', 'bcp'));
514 | // Reduce stock levels
515 | $order->reduce_order_stock();
516 |
517 | // Remove cart
518 | $woocommerce->cart->empty_cart();
519 |
520 | // Return thankyou redirect
521 | return array(
522 | 'result' => 'success',
523 | 'redirect' => $paymentUrl
524 | );
525 | }
526 | }
527 |
528 | private function get_headers_from_curl_response($headerContent)
529 | {
530 | $headers = array();
531 |
532 | // Split the string on every "double" new line.
533 | $arrRequests = explode("\r\n\r\n", $headerContent);
534 |
535 | // Loop of response headers. The "count() -1" is to
536 | // avoid an empty row for the extra line break before the body of the response.
537 | for ($index = 0; $index < count($arrRequests) - 1; $index++) {
538 |
539 | foreach (explode("\r\n", $arrRequests[$index]) as $i => $line) {
540 | if ($i === 0)
541 | $headers[$index]['http_code'] = $line;
542 | else {
543 | list($key, $value) = explode(': ', $line);
544 | $headers[$index][$key] = $value;
545 | }
546 | }
547 | }
548 |
549 | return $headers;
550 | }
551 |
552 | function showMessage($content)
553 | {
554 | return '' . $this->msg['message'] . '
' . $content;
555 | }
556 |
557 | // get all pages
558 | function get_pages($title = false, $indent = true)
559 | {
560 | $wp_pages = get_pages('sort_column=menu_order');
561 | $page_list = array();
562 | if ($title)
563 | $page_list[] = $title;
564 | foreach ($wp_pages as $page) {
565 | $prefix = '';
566 | // show indented child pages?
567 | if ($indent) {
568 | $has_parent = $page->post_parent;
569 | while ($has_parent) {
570 | $prefix .= ' - ';
571 | $next_page = get_page($has_parent);
572 | $has_parent = $next_page->post_parent;
573 | }
574 | }
575 | // add to page list array array
576 | $page_list[$page->ID] = $prefix . $page->post_title;
577 | }
578 | return $page_list;
579 | }
580 |
581 | }
582 | class bcp_handle_callback
583 | {
584 | public function __construct()
585 | {
586 | }
587 | }
588 |
589 | /**
590 | * Add the Gateway to WooCommerce
591 | **/
592 | function woocommerce_add_bcp_payment_gateway($methods)
593 | {
594 | $methods[] = 'WC_bcp_payment';
595 | return $methods;
596 | }
597 |
598 | add_filter('woocommerce_payment_gateways', 'woocommerce_add_bcp_payment_gateway');
599 | }
600 |
--------------------------------------------------------------------------------