woo_satsale.php (7301B)
1 <?php 2 /* 3 * Plugin Name: SatSale 4 * Plugin URI: https://github.com/nickfarrow/SatSale 5 * Description: Take Bitcoin payments on your store. 6 * Author: Nick Farrow 7 * Author URI: https://nickfarrow.com 8 * Version: 1.0.1 9 * 10 */ 11 12 /* Based. 13 * Based on https://rudrastyh.com/woocommerce/payment-gateway-plugin.html */ 14 15 /* 16 * This action hook registers our PHP class as a WooCommerce payment gateway 17 */ 18 19 // Debugging helper 20 // Writes to wp-content/debug.log 21 if (!function_exists('write_log')) { 22 function write_log($log) { 23 if (true) { 24 if (is_array($log) || is_object($log)) { 25 error_log(print_r($log, true)); 26 } else { 27 error_log($log); 28 } 29 } 30 } 31 } 32 33 // SatSale class 34 add_filter( 'woocommerce_payment_gateways', 'satsale_add_gateway_class' ); 35 function satsale_add_gateway_class( $gateways ) { 36 $gateways[] = 'WC_Satsale_Gateway'; 37 return $gateways; 38 } 39 40 // Extend existing payment gateway 41 add_action( 'plugins_loaded', 'satsale_init_gateway_class' ); 42 function satsale_init_gateway_class() { 43 class WC_Satsale_Gateway extends WC_Payment_Gateway { 44 45 public static $secret = 0; 46 /** 47 * Class constructor 48 */ 49 public function __construct() { 50 51 $this->id = 'satsale'; // payment gateway plugin ID 52 $this->icon = ''; // URL of the icon that will be displayed on checkout page near your gateway name 53 $this->has_fields = true; // in case you need a custom credit card form 54 $this->method_title = 'SatSale Gateway'; 55 $this->method_description = 'SatSale payment gateway'; // will be displayed on the options page 56 57 $this->supports = array( 58 'products' 59 ); 60 61 // Method with all the options fields 62 $this->init_form_fields(); 63 64 // Load the settings. 65 $this->init_settings(); 66 $this->title = $this->get_option( 'title' ); 67 $this->description = $this->get_option( 'description' ); 68 $this->enabled = $this->get_option( 'enabled' ); 69 $this->satsale_server_url = $this->get_option( 'satsale_server_url' ); 70 // $this->redirect_url = $this->get_option( 'redirect_url' ); 71 // $this->testmode = 'yes' === $this->get_option( 'testmode' ); 72 $this->SatSale_API_Key = $this->get_option( 'SatSale_API_Key' ); 73 74 $this->callback_URL = str_replace( 'https:', 'http:', add_query_arg( 'wc-api', 'wc_satsale_gateway', home_url( '/' ) ) ); 75 // $this->callback_URL = home_url( '/' ) . 'wc-api/' . 'WC_SatSale_Gateway/'; 76 77 // This action hook saves the settings 78 add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) ); 79 80 // You can also register a webhook here 81 add_action( 'woocommerce_api_wc_satsale_gateway', array( $this, 'webhook' ) ); 82 } 83 84 /** 85 * Plugin options 86 */ 87 public function init_form_fields(){ 88 89 $this->form_fields = array( 90 'enabled' => array( 91 'title' => 'Enable/Disable', 92 'label' => 'Enable SatSale Gateway', 93 'type' => 'checkbox', 94 'description' => '', 95 'default' => 'no' 96 ), 97 'title' => array( 98 'title' => 'Title', 99 'type' => 'text', 100 'description' => 'This controls the title which the user sees during checkout.', 101 'default' => 'Bitcoin', 102 'desc_tip' => true, 103 ), 104 'description' => array( 105 'title' => 'Description', 106 'type' => 'textarea', 107 'description' => 'This controls the description which the user sees during checkout.', 108 'default' => 'Pay with Bitcoin via SatSale', 109 ), 110 'satsale_server_url' => array( 111 'title' => 'SatSale URL', 112 'type' => 'text', 113 'description' => 'Points towards your instance of SatSale, should be IP or https://SERVER.com', 114 ), 115 'SatSale_API_Key' => array( 116 'title' => 'SatSale_API_Key', 117 'type' => 'text' 118 ) 119 ); 120 } 121 122 /* 123 * Processing the payments 124 */ 125 public function process_payment( $order_id ) { 126 127 global $woocommerce; 128 129 // we need it to get any order details 130 $order = wc_get_order( $order_id ); 131 132 // We need to store a signature of the data, and check it later during the webhook to confirm it is the same! 133 /* 134 * Array with parameters for API interaction 135 */ 136 $args = array( 137 'amount' => $order->get_total(), 138 'w_url' => $this->callback_URL, 139 'id' => $order_id 140 ); 141 142 write_log($args); 143 144 $key = hex2bin($this->SatSale_API_Key); 145 146 $payment_url = add_query_arg( 147 $args, 148 $this->satsale_server_url . '/pay' 149 ); 150 151 // Redirect to SatSale 152 return [ 153 'result' => 'success', 154 'redirect' => $payment_url 155 ]; 156 } 157 158 /* 159 * Webhook to confirm payment 160 */ 161 public function webhook() { 162 $order = wc_get_order( $_GET['id'] ); 163 $headers = getallheaders(); 164 165 $now = time(); // current unix timestamp 166 $json = json_encode($_GET, JSON_FORCE_OBJECT); 167 $key = hex2bin($this->SatSale_API_Key); 168 169 // Order secret must match to ensure inital payment url 170 // had not been tampered when leaving the gateway. 171 // This secret is generated within the python backend (gateways/woo_webhook.py) 172 // For the payment to succeed, this will be provided in the success request header 173 // once the payment has been confirmed by the python backend. 174 // By confirming it matches the order details (amount * id) we know that 175 // the order has not been tampered with after leaving the php payment gateway. 176 $order_secret_seed = (int)($order->get_total() * 100.0); 177 $order_secret_seed_str = (string)$order_secret_seed; 178 $secret = hash_hmac('sha256', $order_secret_seed, $key); 179 180 if ($headers['X-Secret'] != $secret) { 181 header( 'HTTP/1.1 403 Forbidden' ); 182 return 1; 183 } 184 185 // Main Signature. 186 // Get supplied signature 187 $signature = $headers['X-Signature']; 188 189 // Calculate expected signature 190 $valid_signature = hash_hmac('sha256', $_GET['time'] .'.'.$json, $key); 191 192 // Compare signature and timestamps 193 if (hash_equals($signature, $valid_signature) and (abs($now - $_GET['time']) < 5)) { 194 header( 'HTTP/1.1 200 OK' ); 195 // Complete order 196 $order->payment_complete(); 197 $order->reduce_order_stock(); 198 update_option('webhook_debug', $_GET); 199 200 } else { 201 header( 'HTTP/1.1 403 Forbidden' ); 202 return 1; 203 } 204 205 } 206 } 207 }