Changeset 2712100
- Timestamp:
- 04/20/2022 10:50:54 AM (4 years ago)
- Location:
- quadpay-gateway-for-woocommerce
- Files:
-
- 4 added
- 8 edited
- 1 copied
-
tags/1.7.0 (copied) (copied from quadpay-gateway-for-woocommerce/trunk)
-
tags/1.7.0/includes/class-quadpay-api.php (added)
-
tags/1.7.0/includes/class-quadpay-gateway.php (modified) (19 diffs)
-
tags/1.7.0/includes/class-quadpay-logger.php (added)
-
tags/1.7.0/includes/class-quadpay-settings.php (modified) (4 diffs)
-
tags/1.7.0/quadpay.php (modified) (5 diffs)
-
tags/1.7.0/readme.txt (modified) (2 diffs)
-
trunk/includes/class-quadpay-api.php (added)
-
trunk/includes/class-quadpay-gateway.php (modified) (19 diffs)
-
trunk/includes/class-quadpay-logger.php (added)
-
trunk/includes/class-quadpay-settings.php (modified) (4 diffs)
-
trunk/quadpay.php (modified) (5 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
quadpay-gateway-for-woocommerce/tags/1.7.0/includes/class-quadpay-gateway.php
r2688749 r2712100 4 4 5 5 /** 6 * @var $_instance WC_Gateway_QuadPay The reference to the singleton instance of this class 6 * The reference to the singleton instance of this class 7 * 8 * @var $_instance WC_Gateway_QuadPay 7 9 */ 8 10 private static $_instance = NULL; 9 11 10 12 /** 11 * @var boolean Whether or not logging is enabled12 */13 public static $log_enabled = false;14 15 /**16 * @var WC_Logger Logger instance17 */18 public static $log = false;19 20 /**21 13 * @var string 22 14 */ 23 private $territory = 'US'; 15 private $client_id; 16 24 17 /** 25 18 * @var string 26 19 */ 27 private $client_id; 20 private $client_secret; 21 28 22 /** 29 23 * @var string 30 24 */ 31 private $client_secret; 25 private $mode; 26 32 27 /** 33 28 * @var string 34 29 */ 35 private $mode; 30 private $minimum_amount; 31 36 32 /** 37 33 * @var string 38 34 */ 39 private $minimum_amount;40 /**41 * @var string42 */43 35 private $maximum_amount; 44 /**45 * @var string46 */47 public $orderurl;48 /**49 * @var string50 */51 private $configurationUrl;52 /**53 * @var string54 */55 private $order_check_time;56 36 57 37 /** … … 80 60 $this->method_title = __( 'Zip - Pay in 4 installments', 'woo_quadpay' ); 81 61 $this->method_description = __( 'Use Zip as a credit or debit card processor for WooCommerce.', 'woo_quadpay' ); 82 83 62 $this->title = __( 'Zip - Pay in 4 installments', 'woo_quadpay'); 84 85 63 $this->description = __('Pay in 4 installments.','woo_quadpay'); 86 87 $this->supports = array( 'products', 'refunds' ); 88 64 $this->supports = [ 'products', 'refunds' ]; 89 65 $this->icon = QUADPAY_WC_PLUGIN_URL . 'assets/images/zip-logo-color.svg'; 90 66 … … 99 75 $this->init_settings(); 100 76 101 $api_url = $this->quadpay_settings->get_environment('api_url'); 102 103 $this->orderurl = $api_url . '/order'; 104 $this->configurationUrl = $api_url . '/configuration'; 105 106 self::$log_enabled = ($this->get_option( 'logging' ) === 'yes'); 107 108 if ($this->mode !== 'production') { 77 if ( $this->mode !== 'production' && !is_admin() ) { 109 78 $this->title .= ' (TEST MODE)'; 110 79 } … … 115 84 public function init() 116 85 { 117 add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ));118 119 add_action( 'woocommerce_thankyou', array( $this, 'payment_callback' ));120 121 // Don't enable QuadPay if the amount limits are not met122 add_filter( 'woocommerce_available_payment_gateways', array( $this, 'check_cart_within_limits' ), 99, 1 ); 123 124 add_action( 'woocommerce_ update_options_checkout', array( $this, 'save_options_page'));86 add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] ); 87 88 add_action( 'woocommerce_thankyou', [ $this, 'payment_callback' ] ); 89 90 add_action( 'woocommerce_update_options_checkout', [ $this, 'save_options_page' ] ); 91 92 add_action( 'woocommerce_order_status_changed', [ $this, 'process_capture' ], null, 4 ); 93 add_action( 'woocommerce_order_status_changed', [ $this, 'process_void' ], null, 4 ); 125 94 } 126 95 … … 129 98 */ 130 99 private function init_options() { 131 $this->territory = $this->get_option( 'territory', 'US' ); 132 $this->client_id = $this->get_option( 'client_id' ); 133 $this->client_secret = $this->get_option( 'client_secret' ); 134 $this->mode = $this->get_option( 'testmode' ); 135 $this->minimum_amount = $this->get_option( 'quadpay-amount-minimum' ); 136 $this->maximum_amount = $this->get_option( 'quadpay-maximum-amount' ); 137 // Default value 24h 138 $this->order_check_time = 139 ( !empty( $this->get_option( 'order_check_time' ) ) ? $this->get_option( 'order_check_time' ) : 'DAY_IN_SECONDS'); 140 } 141 142 /** 143 * Check if this gateway is enabled 100 $this->client_id = $this->get_option( 'client_id' ); 101 $this->client_secret = $this->get_option( 'client_secret' ); 102 $this->mode = $this->get_option( 'testmode' ); 103 $this->minimum_amount = $this->get_option( 'quadpay-amount-minimum', 35 ); 104 $this->maximum_amount = $this->get_option( 'quadpay-maximum-amount', 1500 ); 105 } 106 107 /** 108 * @inheritDoc 144 109 */ 145 110 public function is_available() { 111 146 112 if ( 'yes' !== $this->enabled ) { 147 113 return false; … … 149 115 150 116 if ( ! ( $this->client_id && $this->client_secret ) ) { 117 return false; 118 } 119 120 if ( is_admin() || !WC()->cart ) { 121 return true; 122 } 123 124 $total = $this->get_order_total(); 125 if ( $total < $this->minimum_amount || $total > $this->maximum_amount ) { 126 return false; 127 } 128 129 $billing_country = ''; 130 if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.0', '<' ) ) { 131 global $woocommerce; 132 $billing_country = $woocommerce->customer->country; 133 } else if ( null !== WC()->customer && null !== WC()->customer->get_billing_country() ) { 134 // make sure to call the member function only if not null 135 // @since 1.3.11 136 $billing_country = WC()->customer->get_billing_country(); 137 } 138 139 if ( ! in_array( $billing_country, Quadpay_WC_Settings::ALLOWED_COUNTRIES ) ) { 151 140 return false; 152 141 } … … 166 155 */ 167 156 public function admin_options() { 168 169 wp_cache_flush();170 171 $settings = get_option( 'woocommerce_quadpay_settings' );172 $min_amount = $settings['quadpay-amount-minimum'];173 $max_amount = $settings['quadpay-maximum-amount'];174 unset( $settings );175 176 157 ?> 177 158 <h2><?php _e( 'Zip Gateway', 'woo_quadpay' ); ?></h2> 178 159 <?php 179 $login = ( bool) ( false === $this->get_quadpay_authorization_code( 'login check' ) ? false : true );160 $login = ( $this->get_quadpay_authorization_code() !== false ); 180 161 ?> 181 162 <p style="color: <?php echo $login ? 'green' : 'red' ?>"> 182 <?php echo( !$login ? __( 'Zip login failed. Please check your Client ID and Client Secret.', 'woo_quadpay' ) : __( 'Validation of Zip login credentials passed successful.', 'woo_quadpay' ) ); ?> 163 <?php echo $login ? 164 __( 'Validation of Zip login credentials passed successful.', 'woo_quadpay' ) : 165 __( 'Zip login failed. Please check your Client ID and Client Secret.', 'woo_quadpay' ); 166 ?> 183 167 </p> 184 168 … … 191 175 </th> 192 176 <td class="forminp"> 193 <?php echo $ min_amount ?>177 <?php echo $this->minimum_amount ?> 194 178 </td> 195 179 </tr> … … 199 183 </th> 200 184 <td class="forminp"> 201 <?php echo $ max_amount ?>185 <?php echo $this->maximum_amount ?> 202 186 </td> 203 187 </tr> 188 <?php if ($this->get_option('defer_funds_capture', false)): ?> 189 <tr valign="top"> 190 <th scope="row" class="titledesc"> 191 <label for="woocommerce_quadpay_max_amount"><?php _e( 'Defer Funds Capture', 'woo_quadpay' ) ?></label> 192 </th> 193 <td class="forminp"><?php _e( 'Yes', 'woo_quadpay' ) ?></td> 194 </tr> 195 <?php endif; ?> 204 196 </tbody> 205 197 </table> … … 244 236 * Request an order token from QuadPay 245 237 * 246 * @param string $reference where does the request come from 247 * 248 * @return string or boolean false if no token generated 249 */ 250 public function get_quadpay_authorization_code( $reference = 'get_quadpay_authorization_code' ) { 251 252 $access_token = wp_cache_get( 'quadpay_access_token', 'quadpay' ); 253 254 if ( false !== $access_token ) { 255 self::log( 'Access token from cache' ); 256 return $access_token; 257 } 258 259 if ( false === $this->apiKeysAvailable( $reference ) ) { 260 return false; 261 } 262 263 $AuthURL = $this->quadpay_settings->get_environment( 'auth_url' ); 264 265 $AuthBody = array( 266 'client_id' => $this->client_id, 267 'client_secret' => $this->client_secret, 268 'audience' => $this->quadpay_settings->get_environment( 'auth_audience' ), 269 'grant_type' => 'client_credentials', 270 ); 271 272 $args = array( 273 'method' => 'POST', 274 'headers' => array( 'Content-Type' => 'application/json'), 275 'body' => json_encode( $AuthBody ) 276 ); 277 278 $response = wp_remote_post($AuthURL ,$args); 238 * @return string|false 239 * @deprecated 240 */ 241 public function get_quadpay_authorization_code() { 242 return Quadpay_WC_Api::instance()->get_auth_token() ?: false; 243 } 244 245 /** 246 * Save options page in admin, update payment limits 247 */ 248 public function save_options_page() { 249 self::log( __METHOD__ ); 250 wp_cache_flush(); 251 // reload settings and options 252 $this->init_settings(); 253 $this->init_options(); 254 // update configuration from api 255 $this->update_payment_limits(); 256 } 257 258 /** 259 * Update settings from configuration api (min, max and dfc) 260 * 261 * @return bool 262 */ 263 public function update_payment_limits() { 264 265 if ( !$this->client_id || !$this->client_secret ) { 266 return false; 267 } 268 269 self::log( __METHOD__ ); 270 271 $response = Quadpay_WC_Api::instance()->configuration(); 279 272 280 273 if ( is_wp_error( $response ) ) { 281 self::log( '[Authentication] QuadPay API Error: ' . $response->get_error_message() ); 282 return false; 283 } 284 285 $body = json_decode(wp_remote_retrieve_body($response)); 286 287 $response_code = wp_remote_retrieve_response_code( $response ); 288 289 if ( $response_code == 200 ) { 290 291 self::log( '[Retrieve response] Response code ' . $response_code . ' valid'); 292 293 //store this in cache 294 wp_cache_set('quadpay_access_token', $body->access_token, 'quadpay', $body->expires_in); 295 296 self::log( '[Retrieve response] Access token available.' ); 297 298 return $body->access_token; 299 300 } else { 301 302 if ( 401 == $response_code ) { 303 self::log( '[Retrieve response] QuadPay login failed. Please check login credentials.' ); 304 } else { 305 self::log( '[Retrieve response] Error response code ' . $response_code ); 306 } 307 308 return false; 309 } 310 } 311 312 private function apiKeysAvailable( $reference ) { 313 314 if ( empty( $this->client_id ) || empty( $this->client_secret ) ) { 315 self::log( '[API key availability check] API keys not available. Reference: ' . $reference ); 316 317 return false; 318 } 319 320 self::log( '[API key availability check] API keys available. Reference: ' . $reference ); 321 //self::log( '[API key availability check] Client ID: ' . $this->client_id ); 322 //self::log( '[API key availability check] Client secret: ' . $this->client_secret ); 274 return false; 275 } 276 277 $this->quadpay_settings->update_options([ 278 'quadpay-amount-minimum' => $response->minimumAmount, 279 'quadpay-maximum-amount' => $response->maximumAmount, 280 'defer_funds_capture' => $response->deferFundsCapture 281 ]); 323 282 324 283 return true; 325 }326 327 public function save_options_page() {328 self::log( '[Saved settings] start' );329 wp_cache_flush();330 $this->init_options();331 $this->update_payment_limits();332 self::log( '[Saved settings] end' );333 }334 335 public function update_payment_limits() {336 337 // Get existing limits338 $settings = get_option('woocommerce_quadpay_settings');339 340 if( false === $this->apiKeysAvailable( 'update_payment_limits' ) ) {341 return false;342 }343 344 self::log( '[Updating payment limits] request values');345 346 $headers = array(347 'headers' => array(348 'Authorization' => 'Bearer ' . $this->get_quadpay_authorization_code( 'update_payment_limits' ),349 )350 );351 352 $response = wp_remote_get( $this->configurationUrl, $headers );353 $body = json_decode( wp_remote_retrieve_body( $response ) );354 $response_code = wp_remote_retrieve_response_code( $response );355 356 self::log( '[Updating payment limits] response code: ' . $response_code );357 self::log( '[Updating payment limits] response: ' . print_r( $body, true ) );358 359 if ( $response_code == 200 ) {360 361 $settings['quadpay-amount-minimum'] = $body->minimumAmount;362 $settings['quadpay-maximum-amount'] = $body->maximumAmount;363 364 update_option( 'woocommerce_quadpay_settings', $settings );365 366 return true;367 }368 369 return false;370 284 } 371 285 … … 375 289 * 376 290 * @param int $order_id 377 *378 291 * @return array 379 292 */ 380 293 public function process_payment( $order_id ) { 381 294 382 if ( function_exists( 'wc_get_order' ) ) { 383 $order = wc_get_order( $order_id ); 384 } else { 385 $order = new WC_Order( $order_id ); 386 } 387 388 // Get the authorization token 389 $access_token = $this->get_quadpay_authorization_code( 'process_payment'); 295 $order = wc_get_order( $order_id ); 390 296 391 297 //Process here 392 $order items = $order->get_items();298 $order_items = $order->get_items(); 393 299 $items = array(); 394 300 395 if ( count($orderitems ) ) { 396 397 foreach ( $orderitems as $item ) { 398 399 if ( $item['variation_id'] ) { 400 401 if ( function_exists( "wc_get_product" ) ) { 402 $product = wc_get_product( $item['variation_id'] ); 403 } else { 404 $product = new WC_Product( $item['variation_id'] ); 405 } 406 407 } else { 408 409 if ( function_exists( "wc_get_product" ) ) { 410 $product = wc_get_product( $item['product_id'] ); 411 } else { 412 $product = new WC_Product( $item['product_id'] ); 413 } 414 415 } 301 if ( count($order_items ) ) { 302 303 foreach ( $order_items as $item ) { 304 305 $product = wc_get_product( $item['variation_id'] ?: $item['product_id'] ); 416 306 417 307 $items[] = array( … … 465 355 'postcode' => $this->get_order_prop( $order, 'shipping_postcode' ), 466 356 ), 467 'description' => 'string',357 'description' => "Order #$order_id", 468 358 'items' => $items, 469 359 'merchant' => array( … … 474 364 'taxAmount' => $order->get_total_tax(), 475 365 'shippingAmount' => $shipping_total, 476 'merchantFeeForPaymentPlan' => $mfpp_total 366 'merchantFeeForPaymentPlan' => $mfpp_total, 367 'metadata' => array( 368 'platform' => 'WooCommerce' 369 ) 477 370 ); 478 371 479 $headers = array( 480 'Content-Type' => 'application/json', 481 'Authorization' => 'Bearer ' . $access_token, 372 self::log( 'POST Order request'); 373 $response = Quadpay_WC_Api::instance()->order( $body ); 374 375 // Couldn't generate token 376 if ( is_wp_error($response) ) { 377 378 $order->add_order_note( 379 __('Unable to generate the order token. Payment could not proceed.', 'woo_quadpay' ) 380 ); 381 382 wc_add_notice( 383 __('Sorry, there was a problem preparing your payment. Please try again.', 'woo_quadpay'), 384 'error' 385 ); 386 387 return []; 388 } 389 390 // Order token successful, save it so we can confirm it later 391 update_post_meta( $order_id, '_quadpay_order_token', $response->token ); 392 update_post_meta( $order_id, '_quadpay_order_id', $response->orderId ); 393 394 return array( 395 'result' => 'success', 396 'redirect' => $response->redirectUrl 482 397 ); 483 484 $order_args = array( 485 'method' => 'POST', 486 'headers' => $headers, 487 'body' => json_encode( $body ), 488 'timeout' => 30, 489 ); 490 491 self::log( 'POST Order request: '.print_r( $order_args,true ) ); 492 493 $APIURL = $this->orderurl; 494 495 $order_response = wp_remote_post( $APIURL, $order_args ); 496 497 $response_code = wp_remote_retrieve_response_code( $order_response ); 498 499 $order_body = json_decode( wp_remote_retrieve_body( $order_response ) ); 500 501 self::log( 'POST Order response: ' . print_r( $order_body,true ) ); 502 503 $valid_response_code = array( 200, 201 ); 504 505 if ( ! in_array( $response_code, $valid_response_code ) ) { 506 // Couldn't generate token 507 $order->add_order_note(__('Unable to generate the order token. Payment couldn\'t proceed.', 'woo_quadpay' ) ); 508 509 wc_add_notice(__('Sorry, there was a problem preparing your payment. Please try again.', 'woo_quadpay'),'error'); 510 511 return; 512 513 } else { 514 // Order token successful, save it so we can confirm it later 515 update_post_meta( $order_id,'_quadpay_order_token', $order_body->token ); 516 517 $redirect = $order_body->redirectUrl; 518 519 return array( 520 'result' => 'success', 521 'redirect' => $redirect 522 ); 523 } 524 525 } 526 398 } 399 400 /** 401 * Resolves Zip order id from order metadata 402 * 403 * @param $order_id 404 * @return string|false 405 */ 527 406 public function get_quadpay_order_id( $order_id ) { 528 407 … … 533 412 } 534 413 535 $access_token = $this->get_quadpay_authorization_code( 'get_quadpay_order_id');536 414 $order_token = get_post_meta( $order_id, '_quadpay_order_token', true ); 537 415 … … 540 418 } 541 419 542 //check if quadpay order id already exists 543 544 //get the order id and save in postmeta 545 $get_args = array( 546 'headers' => array( 547 'Content-Type' => 'application/json', 548 'Authorization' => 'Bearer '. $access_token 549 ), 550 ); 551 552 $getOrderUrl = $this->orderurl . '?token=' . $order_token; 553 554 $get_response = wp_remote_get( $getOrderUrl, $get_args ); 555 $get_body = json_decode( wp_remote_retrieve_body( $get_response ) ); 556 557 self::log( 'Get Order result: ' . print_r( $get_body, true ) ); 558 559 $quadpay_order_id = $get_body->orderId; 560 $quadpay_order_status = $get_body->orderStatus; 561 562 //store order id for future purposes 563 if ( $quadpay_order_status !== 'Approved' ) { 564 return false; 565 } 566 567 update_post_meta($order_id,'_quadpay_order_id', $quadpay_order_id); 568 update_post_meta($order_id,'_quadpay_order_status', $quadpay_order_status); 569 570 return $quadpay_order_id; 571 } 572 573 /** 574 * @param $order_id 575 */ 576 public function payment_callback($order_id) { 577 578 if ( function_exists( "wc_get_order" ) ) { 579 $order = wc_get_order( $order_id ); 580 } else { 581 $order = new WC_Order( $order_id ); 582 } 420 update_post_meta($order_id, '_quadpay_order_id', $order_token ); 421 422 return $order_token; 423 } 424 425 /** 426 * Thank You page callback 427 * 428 * @param int $order_id 429 */ 430 public function payment_callback( $order_id ) { 431 432 $order = wc_get_order( $order_id ); 583 433 584 434 if ( 'quadpay' !== $order->get_payment_method() ) { … … 587 437 588 438 //save the order id 589 $quadpay_order_id = $this->get_quadpay_order_id($order_id); 590 591 $order_token = get_post_meta($order_id, '_quadpay_order_token', true); 592 if ($quadpay_order_id == false) { 593 $order->add_order_note(sprintf(__('Failure to process the Zip payment.','woo_quadpay') )); 594 } else if ( !empty($order_token) ) { 595 596 $quadpay_order_id = $this->get_quadpay_order_id($order_id); 597 598 //Update status for order 599 if (isset($_GET['status'])) { 600 601 $query_string = $_GET['status']; 602 603 if ($query_string != NULL) { 604 if ($query_string === 'confirmed') { 605 606 $order->add_order_note(sprintf(__('Payment approved. Zip Order ID: '.$quadpay_order_id.' ','woo_quadpay') )); 607 $order->payment_complete($quadpay_order_id); 608 wc_empty_cart(); 609 610 } elseif ($query_string === 'cancelled') { 611 612 $order->add_order_note(sprintf(__('Zip payment is pending approval. Zip Order ID: '.$quadpay_order_id.' ','woo_quadpay') )); 613 $order->update_status( 'cancelled' ); 614 615 616 } elseif ($query_string === 'failure') { 617 618 $order->add_order_note(sprintf(__('Zip payment declined. Order ID from Zip: '.$quadpay_order_id.' ','woo_quadpay') )); 619 $order->update_status('failed'); 620 } 621 622 } else { 623 $order->update_status('pending'); 624 } 625 } 626 627 } 628 629 } 630 631 /** 632 * Check whether the cart amount is within payment limits 633 * 634 * @param array $gateways Enabled gateways 635 * @return array Enabled gateways, possibly with QuadPay removed 636 */ 637 public function check_cart_within_limits($gateways) { 638 639 if ( is_admin() ) { 640 return $gateways; 641 } 642 643 $total = WC()->cart->total; 644 645 $minAmount = isset( $this->minimum_amount ) ? $this->minimum_amount : null; 646 $maxAmount = isset( $this->maximum_amount ) ? $this->maximum_amount : null; 647 648 if ( $minAmount == null || $maxAmount == null ) { 649 self::log('Requesting payment limit update'); 650 651 $status = $this->update_payment_limits(); 652 653 if ( $status ) { 654 655 $settings = get_option( 'woocommerce_quadpay_settings' ); 656 657 $minAmount = $settings['quadpay-amount-minimum']; 658 $maxAmount = $settings['quadpay-maximum-amount']; 659 660 } 661 } else { 662 self::log('Using cached payment limits'); 663 } 664 665 $pbi = ( $total >= $minAmount && $total <= $maxAmount ); 666 667 $billing_country = ''; 668 if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.0', '<' ) ) { 669 global $woocommerce; 670 $billing_country = $woocommerce->customer->country; 671 } else if ( null !== WC()->customer && null !== WC()->customer->get_billing_country() ) { 672 // make sure to call the member function only if not null 673 // @since 1.3.11 674 $billing_country = WC()->customer->get_billing_country(); 675 } 676 677 if ( ! $pbi || ! in_array( $billing_country, array( 'US', 'CA' ) ) ) { 678 unset( $gateways['quadpay'] ); 679 } 680 681 return $gateways; 682 } 683 684 /** 685 * Can the order be refunded? 686 * 687 * @param object $order WC_Order 439 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 440 441 if ( $quadpay_order_id === false ) { 442 $order->add_order_note(sprintf(__('Failure to process the Zip payment.', 'woo_quadpay'))); 443 return; 444 } 445 446 // check response and not query 447 $this->sync_order_status( $order ); 448 } 449 450 /** 451 * Can the order be refunded 452 * 453 * @param WC_Order $order 688 454 * @return bool 689 455 */ 690 456 public function can_refund_order( $order ) { 691 return $order && $order->get_transaction_id(); 692 } 693 694 /** 695 * Process a refund if supported 696 * 697 * @param object $order_id WC_Order 457 //return $order && $order->get_transaction_id(); 458 return $order && in_array( $order->get_status(), wc_get_is_paid_statuses() ); 459 } 460 461 /** 462 * Process a refund 463 * 464 * @param int $order_id 698 465 * @param float $amount 699 466 * @param string $reason 700 467 * 701 * @return boolean True or false based on success468 * @return bool 702 469 */ 703 470 public function process_refund( $order_id, $amount = null, $reason = '' ) { 704 471 705 472 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 706 707 $order = new WC_Order( $order_id ); 473 $order = wc_get_order( $order_id ); 708 474 709 475 if ( empty( $quadpay_order_id ) ) { 710 711 476 $order->add_order_note( sprintf( __( 'There was an error submitting the refund to Zip.', 'woo_quadpay' ) ) ); 712 713 return false; 714 } 715 716 $access_token = $this->get_quadpay_authorization_code( 'process_refund' ); 717 $random_string = wp_generate_password( 8, false, false ); 718 719 $refund_args = array( 720 'headers' => array( 721 'Content-Type' => 'application/json', 722 'Authorization' => 'Bearer ' . $access_token, 723 ), 724 'body' => json_encode( array( 725 'requestId' => 'Order #' . $order_id . '-' . $random_string, 726 'amount' => $amount, 727 'merchantRefundReference' => 'Order #' . $order_id . '-' . $random_string, 728 ) ), 729 ); 730 731 $refundOrderUrl = $this->orderurl.'/'.$quadpay_order_id.'/refund'; 732 733 $refund_response = wp_remote_post( $refundOrderUrl, $refund_args ); 734 $refund_body = json_decode( wp_remote_retrieve_body( $refund_response ) ); 735 736 self::log( 'Refund body: ' . print_r( $refund_body, true ) ); 737 738 $response_code = (int)$refund_response['response']['code']; 739 740 if ( $response_code === 201 || $response_code === 200 ) { 741 $order->add_order_note( sprintf( __( 'Refund of $%s successfully sent to Zip.', 'woo_quadpay' ), $amount ) ); 742 return true; 743 } 744 745 if ( $response_code === 404 ) { 746 $order->add_order_note( sprintf( __( 'Order not found on Zip.', 'woo_quadpay' ) ) ); 747 } else { 477 return false; 478 } 479 480 $merchant_refund_reference = 'Order #' . $order_id . '-' . wp_generate_password( 8, false, false ); 481 482 $response = Quadpay_WC_Api::instance()->refund( $quadpay_order_id, $amount, $merchant_refund_reference ); 483 484 if ( is_wp_error($response) ) { 748 485 $order->add_order_note( sprintf( __( 'There was an error submitting the refund to Zip.', 'woo_quadpay' ) ) ); 749 } 750 751 return false; 486 return false; 487 } 488 489 $order->add_order_note(sprintf( 490 __( 'Refund of %s successfully sent to Zip.', 'woo_quadpay' ), 491 wc_price( $amount, array( 'currency' => $order->get_currency() ) ) 492 ) ); 493 return true; 494 } 495 496 /** 497 * Capture order on Zip side, DFC 498 * 499 * @param int $order_id 500 * @param string $from 501 * @param string $to 502 * @return bool 503 */ 504 public function process_capture( $order_id, $from, $to ) { 505 506 // no need for manual capture 507 if ( !$this->get_option('defer_funds_capture') ) { 508 return false; 509 } 510 511 if ( ! ( in_array( $from, [ 'pending', 'on-hold' ] ) && in_array( $to, wc_get_is_paid_statuses() ) ) ) { 512 return false; 513 } 514 515 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 516 if ( empty( $quadpay_order_id ) ) { 517 return false; 518 } 519 520 $order = wc_get_order( $order_id ); 521 $amount = $order->get_total() - $order->get_total_refunded(); 522 523 if ($amount < 0.01) { 524 return false; 525 } 526 527 $merchant_reference = 'Order #' . $order_id . '-' . wp_generate_password( 8, false, false ); 528 529 $response = Quadpay_WC_Api::instance()->capture( $quadpay_order_id, $amount, $merchant_reference ); 530 531 if ( is_wp_error($response) ) { 532 $order->add_order_note( 533 sprintf( __( 'There was an error submitting the capture to Zip.', 'woo_quadpay' ) ) . 534 ' ' . 535 $response->get_error_message() 536 ); 537 return false; 538 } 539 540 $order->payment_complete( $quadpay_order_id ); 541 $order->add_order_note(sprintf( 542 __( 'Capture of %s successfully sent to Zip.', 'woo_quadpay' ), 543 wc_price( $amount, array( 'currency' => $order->get_currency() ) ) 544 ) ); 545 546 return true; 547 } 548 549 /** 550 * Void order on Zip side, DFC 551 * 552 * @param $order_id 553 * @param string $from 554 * @param string $to 555 * @return bool 556 */ 557 public function process_void( $order_id, $from, $to ) { 558 559 if ( !$this->get_option('defer_funds_capture') ) { 560 return false; 561 } 562 563 if ( ! ( in_array( $from, [ 'pending', 'on-hold' ] ) && in_array( $to, [ 'cancelled', 'failed' ] ) ) ) { 564 return false; 565 } 566 567 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 568 if ( empty( $quadpay_order_id ) ) { 569 return false; 570 } 571 572 $order = wc_get_order( $order_id ); 573 $amount = $order->get_total() - $order->get_total_refunded(); 574 575 if ($amount < 0.01) { 576 return false; 577 } 578 579 $merchant_reference = 'Order #' . $order_id . '-' . wp_generate_password( 8, false, false ); 580 581 /** @var WP_Error $response */ 582 $response = Quadpay_WC_Api::instance()->void( $quadpay_order_id, $amount, $merchant_reference ); 583 584 if ( is_wp_error($response) ) { 585 $order->add_order_note( 586 sprintf( __( 'There was an error submitting the void to Zip.', 'woo_quadpay' ) ) . 587 ' ' . 588 $response->get_error_message() 589 ); 590 return false; 591 } 592 593 $order->add_order_note(sprintf( 594 __( 'Void of %s successfully sent to Zip.', 'woo_quadpay' ), 595 wc_price( $amount, array( 'currency' => $order->get_currency() ) ) 596 ) ); 597 return true; 752 598 } 753 599 … … 755 601 * Logging method 756 602 * 757 * @param string $message 758 */ 759 public static function log( $message ) { 760 if ( self::$log_enabled ) { 761 if ( empty( self::$log ) ) { 762 self::$log = new WC_Logger(); 763 } 764 self::$log->add( 'quadpay', $message ); 765 } 766 767 if( WP_DEBUG && WP_DEBUG_LOG ) { 768 error_log($message); 769 } 770 } 771 772 /** 773 * Check the order status of all pending orders that didn't return to the thank you page or marked as Pending by QuadPay 603 * @param string $message 604 * @param string|null $source 605 */ 606 public static function log( $message, $source = null ) { 607 Quadpay_WC_Logger::log( $message, $source ); 608 } 609 610 /** 611 * Check the order status of all pending orders that didn't return to the thank you page or marked as Pending by Zip 612 * 613 * @note: old code was also checking on-hold orders, but we don't have any (only DFC) 774 614 */ 775 615 public function check_pending_orders() { 616 617 // Default value 24h 618 $order_check_time = $this->get_option( 'order_check_time', 'DAY_IN_SECONDS'); 776 619 777 620 // Get PENDING orders that may have been abandoned, or browser window closed after approved … … 779 622 'status' => 'pending', 780 623 'type' => 'shop_order', 781 'date_created' => '>' . ( time() - DAY_IN_SECONDS),624 'date_created' => '>' . ( time() - constant( $order_check_time ) ), 782 625 'limit' => -1, 783 626 'payment_method' => 'quadpay', 784 627 ); 628 785 629 $pending_orders = wc_get_orders( $args ); 786 630 … … 790 634 791 635 foreach ( $pending_orders as $order ) { 792 793 $order_id = $order->get_id(); 794 795 $quadpay_order_token = get_post_meta( $order_id, '_quadpay_order_token', true ); 796 797 // Check if there's a stored order token. If not, it's not an QuadPay order. 798 if ( ! $quadpay_order_token ) continue; 799 800 self::log( 'Checking abandoned order for WC Order ID ' . $order_id . ', Zip Token ' . $quadpay_order_token ); 801 802 $response = wp_remote_get( 803 $this->orderurl.'?token=' . $quadpay_order_token, 804 array( 805 'headers'=>array( 806 'Authorization' => 'Bearer ' . $this->get_quadpay_authorization_code( 'check_pending_orders'), 807 ) 808 ) 809 ); 810 811 $body = json_decode( wp_remote_retrieve_body( $response ) ); 812 813 self::log( 'Checking abandoned order result: ' . print_r( $body,true ) ); 814 815 $response_code = wp_remote_retrieve_response_code( $response ); 816 817 // Check status of order 818 if ( $response_code == 200 ) { 819 820 // Check status of order 821 if ( $body->orderStatus === 'Approved' ) { 822 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment approved. Zip Order ID: %s', 'woo_quadpay' ), $body->orderId ) ); 823 $order->payment_complete( $order_id ); 824 } elseif ( $body->orderStatus === 'Created' ) { 825 $order->add_order_note( __( 'Checked payment status with Zip. Still pending approval.', 'woo_quadpay' ) ); 826 $order->update_status( 'pending' ); 827 } elseif ( $body->orderStatus === 'Abandoned' ) { 828 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 829 $order->update_status( 'cancelled' ); 830 } else { 831 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 832 $order->update_status( 'failed' ); 636 $this->sync_order_status($order); 637 } 638 } 639 640 /** 641 * Checks order status on Zip and updates order accordingly 642 * 643 * @param WC_Order $order 644 * @param bool $forceStatus 645 * @return bool 646 */ 647 public function sync_order_status( $order, $forceStatus = false ) { 648 649 $payment_method = $order->get_payment_method(); 650 651 if ( 'quadpay' !== $payment_method ) { 652 return false; 653 } 654 655 $order_id = $order->get_id(); 656 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 657 658 if ( ! $quadpay_order_id ) { 659 return false; 660 } 661 662 self::log( 'Order ID ' . $order_id . ', Zip Order ID ' . $quadpay_order_id, __METHOD__ ); 663 $response = Quadpay_WC_Api::instance()->get_order( $quadpay_order_id ); 664 665 if ( is_wp_error( $response ) ) { 666 return false; 667 } 668 669 $order->add_order_note( 670 sprintf( 671 __('Zip payment %s. Zip Order ID: %s', 'woo_quadpay'), 672 strtolower($response->orderStatus), 673 $response->orderId 674 ) 675 ); 676 677 // Check status of order 678 switch ($response->orderStatus) { 679 case 'Approved': 680 if ( $forceStatus || $order->get_status() === 'pending' ) { 681 682 if ( $this->get_option('defer_funds_capture') ) { 683 $order->update_status('on-hold'); 684 } else { 685 $order->payment_complete($quadpay_order_id); 686 } 687 833 688 } 834 689 835 } else { 836 837 $order_date = method_exists( $order, 'get_date_created' ) ? $order->get_date_created() : $order->order_date; 838 839 if ( strtotime( 'now' ) - strtotime( $order_date ) > 3600 ) { 840 $order->add_order_note( sprintf( __( 'Pending Order Expired' ) ) ); 841 $order->update_status( 'cancelled' ); 690 break; 691 692 case 'Created': 693 if ( $forceStatus && $order->get_status() !== 'pending' ) { 694 $order->update_status('pending'); 842 695 } 843 } 844 845 } 846 } 847 848 /** 849 * Check the order status of all on-hold orders that didn't return to the thank you page or marked as Pending by QuadPay 850 */ 851 public function check_on_hold_orders() { 852 853 self::log( 'Start checking On Hold orders' ); 854 855 // Get ON-HOLD orders that are "pending" at QuadPay that need to be checked whether approved or denied 856 $args = array( 857 'status' => 'on-hold', 858 'type' => 'shop_order', 859 'date_created' => '>' . ( time() - constant( $this->order_check_time ) ), 860 'limit' => -1, 861 'payment_method' => 'quadpay', 862 ); 863 864 $on_hold_orders = wc_get_orders( $args ); 865 866 if ( empty( $on_hold_orders ) ) { 867 868 self::log( 'No On Hold orders found (' . $this->order_check_time . ')' ); 869 self::log( 'End checking On Hold orders' ); 870 871 return; 872 } 873 874 self::log( count( $on_hold_orders ) . ' On Hold orders found' ); 875 876 foreach ( $on_hold_orders as $order ) { 877 878 $order_id = $order->get_id(); 879 880 $quadpay_order_token = get_post_meta( $order_id, '_quadpay_order_token', true ); 881 882 // Check if there's a stored order token. If not, it's not an QuadPay order. 883 if ( ! $quadpay_order_token ) continue; 884 885 self::log( 'Checking abandoned order for WC Order ID ' . $order_id . ', QuadPay Token ' . $quadpay_order_token ); 886 887 $response = wp_remote_get( 888 $this->orderurl.'?token=' . $quadpay_order_token, 889 array( 890 'headers'=>array( 891 'Authorization' => 'Bearer ' . $this->get_quadpay_authorization_code( 'check_on_hold_orders' ), 892 ) 893 ) 894 ); 895 896 $body = json_decode( wp_remote_retrieve_body( $response ) ); 897 898 self::log( 'Checking abandoned order result: ' . print_r( $body, true ) ); 899 900 $response_code = wp_remote_retrieve_response_code( $response ); 901 902 // Check status of order 903 if ( $response_code == 200 ) { 904 905 // Check status of order 906 if ( $body->orderStatus === 'Approved' ) { 907 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment approved. Zip Order ID: %s', 'woo_quadpay' ), $body->orderId ) ); 908 $order->payment_complete( $order_id ); 909 } elseif ( $body->orderStatus === 'Created' ) { 910 $order->add_order_note( __( 'Checked payment status with Zip. Still pending approval.', 'woo_quadpay' ) ); 911 $order->update_status( 'pending' ); 912 } elseif ( $body->orderStatus === 'Abandoned' ) { 913 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 914 $order->update_status( 'cancelled' ); 915 } else { 916 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 917 $order->update_status( 'failed' ); 696 697 break; 698 699 case 'Abandoned': 700 case 'Declined': 701 if ( $forceStatus || $order->get_status() === 'pending' ) { 702 $order->update_status('cancelled'); 918 703 } 919 704 920 } else { 921 922 $order_date = method_exists( $order, 'get_date_created' ) ? $order->get_date_created() : $order->order_date; 923 924 if ( strtotime( 'now' ) - strtotime( $order_date ) > 3600 ) { 925 926 $order->add_order_note( sprintf( __( 'On Hold Order Expired at Zip' ) ) ); 927 $order->update_status( 'cancelled' ); 928 705 break; 706 707 default: 708 if ( $forceStatus ) { 709 $order->update_status('failed'); 929 710 } 930 711 931 }932 933 } 934 712 break; 713 } 714 715 return true; 935 716 } 936 717 -
quadpay-gateway-for-woocommerce/tags/1.7.0/includes/class-quadpay-settings.php
r2688749 r2712100 26 26 'api_url_v2' => 'https://gateway.quadpay.com' 27 27 ]; 28 29 const ALLOWED_COUNTRIES = [ 'US', 'CA' ]; 28 30 29 31 /** … … 152 154 'WEEK_IN_SECONDS' => __( 'One Week', 'woo_quadpay' ), 153 155 'MONTH_IN_SECONDS' => __( 'One Month', 'woo_quadpay' ), 154 'YEAR_IN_SECONDS' => __( 'One Year', 'woo_quadpay' ),155 156 ), 156 157 'description' => __( 'How far back should authorized, not captured orders be checked for status change.', 'woo_quadpay' ), 158 'default' => 'DAY_IN_SECONDS' 157 159 ), 158 160 'widget_settings' => array( … … 240 242 $quadpay_settings = get_option( self::SETTINGS_KEY, [] ); 241 243 242 if ( isset ($quadpay_settings[$key]) ) {244 if ( isset( $quadpay_settings[$key] ) ) { 243 245 return $quadpay_settings[$key]; 244 246 } … … 249 251 250 252 return $default; 253 } 254 255 /** 256 * @param $options 257 * @return bool 258 */ 259 public function update_options( $options ) 260 { 261 $quadpay_settings = get_option( self::SETTINGS_KEY, [] ); 262 $quadpay_settings = array_merge( $quadpay_settings, $options ); 263 264 return update_option( 'woocommerce_quadpay_settings', $quadpay_settings ); 251 265 } 252 266 -
quadpay-gateway-for-woocommerce/tags/1.7.0/quadpay.php
r2688749 r2712100 5 5 Author: Zip Co Limited 6 6 Author URI: https://zip.co 7 Version: 1. 6.07 Version: 1.7.0 8 8 WC requires at least: 3.1.0 9 WC tested up to: 5.99 WC tested up to: 6.3 10 10 */ 11 11 12 define( 'QUADPAY_WC_VERSION', '1. 6.0' );12 define( 'QUADPAY_WC_VERSION', '1.7.0' ); 13 13 define( 'QUADPAY_WC_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 14 14 15 15 require_once __DIR__ . '/includes/class-quadpay-settings.php'; 16 require_once __DIR__ . '/includes/class-quadpay-logger.php'; 17 require_once __DIR__ . '/includes/class-quadpay-api.php'; 16 18 require_once __DIR__ . '/includes/class-quadpay-widget.php'; 17 19 require_once __DIR__ . '/includes/class-quadpay-mfpp.php'; … … 46 48 47 49 $gateway = WC_Gateway_QuadPay::instance(); 48 49 50 $order_id = wc_get_order_id_by_order_key( $_GET['key'] ); 50 51 if ( function_exists( "wc_get_order" ) ) { 52 $order = wc_get_order( $order_id ); 53 } else { 54 $order = new WC_Order( $order_id ); 55 } 51 $order = wc_get_order( $order_id ); 56 52 57 53 if ( $order && $order->get_payment_method() === 'quadpay' ) { … … 80 76 81 77 /** 82 * Call the cron task to check all on-hold orders status in the gateway83 **/84 function quadpay_check_on_hold_orders_cron_jobs() {85 $gateway = WC_Gateway_QuadPay::instance();86 $gateway->check_on_hold_orders();87 }88 add_action( 'quadpay_forty_five_minutes_cron_jobs', 'quadpay_check_on_hold_orders_cron_jobs' );89 90 /**91 78 * Call the cron task to check the merchant payment limits on QuadPay 92 79 **/ … … 125 112 } 126 113 127 $order_actions['check_quadpay_status'] = 'Check payment status on Zip';114 $order_actions['check_quadpay_status'] = __( 'Check payment status on Zip' , 'woo_quadpay' ); 128 115 129 116 return $order_actions; … … 159 146 160 147 $gateway = WC_Gateway_QuadPay::instance(); 161 162 $response = wp_remote_get( 163 $gateway->orderurl.'?token=' . $quadpay_order_token, 164 array( 165 'headers'=>array( 166 'Authorization' => 'Bearer ' . $gateway->get_quadpay_authorization_code( 'quadpay_woocommerce_check_order_payment_status' ), 167 ) 168 ) 169 ); 170 171 $body = json_decode( wp_remote_retrieve_body( $response ) ); 172 173 $response_code = wp_remote_retrieve_response_code( $response ); 174 175 $response_message = wp_remote_retrieve_response_message( $response ); 176 177 // Check status of order 178 if ( $response_code == 200 ) { 179 180 // Check status of order 181 if ( 'Approved' === $body->orderStatus ) { 182 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment approved. Zip Order ID: %s', 'woo_quadpay' ), $body->orderId ) ); 183 $order->payment_complete( $order_id ); 184 } elseif ( 'Created' === $body->orderStatus ) { 185 $order->add_order_note( __( 'Checked payment status with Zip. Still pending approval.', 'woo_quadpay' ) ); 186 $order->update_status( 'pending' ); 187 } elseif ( 'Abandoned' === $body->orderStatus ) { 188 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 189 $order->update_status( 'cancelled' ); 190 } else { 191 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 192 $order->update_status( 'failed' ); 193 } 194 195 } else { 196 $order->add_order_note( 'Can\'t check order payment status on Zip (response message: ' . $response_message . ').' ); 148 if ( !$gateway->sync_order_status( $order ) ) { 149 $order->add_order_note( 'Can\'t check order payment status on Zip.' ); 197 150 } 198 151 -
quadpay-gateway-for-woocommerce/tags/1.7.0/readme.txt
r2688749 r2712100 4 4 * Requires at least: 4.7 5 5 * Tested up to: 5.9 6 * Stable tag: 1. 6.06 * Stable tag: 1.7.0 7 7 * Requires PHP: 7.0 8 8 * License: GPLv3 … … 59 59 == Changelog == 60 60 61 = 1.7.0 = 62 * Defer funds capture support 63 * Multiple place order and API improvements 64 * Compatibility - tested up to WooCommerce 6.3 65 61 66 = 1.6.0 = 62 67 * Added payment widget on checkout -
quadpay-gateway-for-woocommerce/trunk/includes/class-quadpay-gateway.php
r2688749 r2712100 4 4 5 5 /** 6 * @var $_instance WC_Gateway_QuadPay The reference to the singleton instance of this class 6 * The reference to the singleton instance of this class 7 * 8 * @var $_instance WC_Gateway_QuadPay 7 9 */ 8 10 private static $_instance = NULL; 9 11 10 12 /** 11 * @var boolean Whether or not logging is enabled12 */13 public static $log_enabled = false;14 15 /**16 * @var WC_Logger Logger instance17 */18 public static $log = false;19 20 /**21 13 * @var string 22 14 */ 23 private $territory = 'US'; 15 private $client_id; 16 24 17 /** 25 18 * @var string 26 19 */ 27 private $client_id; 20 private $client_secret; 21 28 22 /** 29 23 * @var string 30 24 */ 31 private $client_secret; 25 private $mode; 26 32 27 /** 33 28 * @var string 34 29 */ 35 private $mode; 30 private $minimum_amount; 31 36 32 /** 37 33 * @var string 38 34 */ 39 private $minimum_amount;40 /**41 * @var string42 */43 35 private $maximum_amount; 44 /**45 * @var string46 */47 public $orderurl;48 /**49 * @var string50 */51 private $configurationUrl;52 /**53 * @var string54 */55 private $order_check_time;56 36 57 37 /** … … 80 60 $this->method_title = __( 'Zip - Pay in 4 installments', 'woo_quadpay' ); 81 61 $this->method_description = __( 'Use Zip as a credit or debit card processor for WooCommerce.', 'woo_quadpay' ); 82 83 62 $this->title = __( 'Zip - Pay in 4 installments', 'woo_quadpay'); 84 85 63 $this->description = __('Pay in 4 installments.','woo_quadpay'); 86 87 $this->supports = array( 'products', 'refunds' ); 88 64 $this->supports = [ 'products', 'refunds' ]; 89 65 $this->icon = QUADPAY_WC_PLUGIN_URL . 'assets/images/zip-logo-color.svg'; 90 66 … … 99 75 $this->init_settings(); 100 76 101 $api_url = $this->quadpay_settings->get_environment('api_url'); 102 103 $this->orderurl = $api_url . '/order'; 104 $this->configurationUrl = $api_url . '/configuration'; 105 106 self::$log_enabled = ($this->get_option( 'logging' ) === 'yes'); 107 108 if ($this->mode !== 'production') { 77 if ( $this->mode !== 'production' && !is_admin() ) { 109 78 $this->title .= ' (TEST MODE)'; 110 79 } … … 115 84 public function init() 116 85 { 117 add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ));118 119 add_action( 'woocommerce_thankyou', array( $this, 'payment_callback' ));120 121 // Don't enable QuadPay if the amount limits are not met122 add_filter( 'woocommerce_available_payment_gateways', array( $this, 'check_cart_within_limits' ), 99, 1 ); 123 124 add_action( 'woocommerce_ update_options_checkout', array( $this, 'save_options_page'));86 add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] ); 87 88 add_action( 'woocommerce_thankyou', [ $this, 'payment_callback' ] ); 89 90 add_action( 'woocommerce_update_options_checkout', [ $this, 'save_options_page' ] ); 91 92 add_action( 'woocommerce_order_status_changed', [ $this, 'process_capture' ], null, 4 ); 93 add_action( 'woocommerce_order_status_changed', [ $this, 'process_void' ], null, 4 ); 125 94 } 126 95 … … 129 98 */ 130 99 private function init_options() { 131 $this->territory = $this->get_option( 'territory', 'US' ); 132 $this->client_id = $this->get_option( 'client_id' ); 133 $this->client_secret = $this->get_option( 'client_secret' ); 134 $this->mode = $this->get_option( 'testmode' ); 135 $this->minimum_amount = $this->get_option( 'quadpay-amount-minimum' ); 136 $this->maximum_amount = $this->get_option( 'quadpay-maximum-amount' ); 137 // Default value 24h 138 $this->order_check_time = 139 ( !empty( $this->get_option( 'order_check_time' ) ) ? $this->get_option( 'order_check_time' ) : 'DAY_IN_SECONDS'); 140 } 141 142 /** 143 * Check if this gateway is enabled 100 $this->client_id = $this->get_option( 'client_id' ); 101 $this->client_secret = $this->get_option( 'client_secret' ); 102 $this->mode = $this->get_option( 'testmode' ); 103 $this->minimum_amount = $this->get_option( 'quadpay-amount-minimum', 35 ); 104 $this->maximum_amount = $this->get_option( 'quadpay-maximum-amount', 1500 ); 105 } 106 107 /** 108 * @inheritDoc 144 109 */ 145 110 public function is_available() { 111 146 112 if ( 'yes' !== $this->enabled ) { 147 113 return false; … … 149 115 150 116 if ( ! ( $this->client_id && $this->client_secret ) ) { 117 return false; 118 } 119 120 if ( is_admin() || !WC()->cart ) { 121 return true; 122 } 123 124 $total = $this->get_order_total(); 125 if ( $total < $this->minimum_amount || $total > $this->maximum_amount ) { 126 return false; 127 } 128 129 $billing_country = ''; 130 if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.0', '<' ) ) { 131 global $woocommerce; 132 $billing_country = $woocommerce->customer->country; 133 } else if ( null !== WC()->customer && null !== WC()->customer->get_billing_country() ) { 134 // make sure to call the member function only if not null 135 // @since 1.3.11 136 $billing_country = WC()->customer->get_billing_country(); 137 } 138 139 if ( ! in_array( $billing_country, Quadpay_WC_Settings::ALLOWED_COUNTRIES ) ) { 151 140 return false; 152 141 } … … 166 155 */ 167 156 public function admin_options() { 168 169 wp_cache_flush();170 171 $settings = get_option( 'woocommerce_quadpay_settings' );172 $min_amount = $settings['quadpay-amount-minimum'];173 $max_amount = $settings['quadpay-maximum-amount'];174 unset( $settings );175 176 157 ?> 177 158 <h2><?php _e( 'Zip Gateway', 'woo_quadpay' ); ?></h2> 178 159 <?php 179 $login = ( bool) ( false === $this->get_quadpay_authorization_code( 'login check' ) ? false : true );160 $login = ( $this->get_quadpay_authorization_code() !== false ); 180 161 ?> 181 162 <p style="color: <?php echo $login ? 'green' : 'red' ?>"> 182 <?php echo( !$login ? __( 'Zip login failed. Please check your Client ID and Client Secret.', 'woo_quadpay' ) : __( 'Validation of Zip login credentials passed successful.', 'woo_quadpay' ) ); ?> 163 <?php echo $login ? 164 __( 'Validation of Zip login credentials passed successful.', 'woo_quadpay' ) : 165 __( 'Zip login failed. Please check your Client ID and Client Secret.', 'woo_quadpay' ); 166 ?> 183 167 </p> 184 168 … … 191 175 </th> 192 176 <td class="forminp"> 193 <?php echo $ min_amount ?>177 <?php echo $this->minimum_amount ?> 194 178 </td> 195 179 </tr> … … 199 183 </th> 200 184 <td class="forminp"> 201 <?php echo $ max_amount ?>185 <?php echo $this->maximum_amount ?> 202 186 </td> 203 187 </tr> 188 <?php if ($this->get_option('defer_funds_capture', false)): ?> 189 <tr valign="top"> 190 <th scope="row" class="titledesc"> 191 <label for="woocommerce_quadpay_max_amount"><?php _e( 'Defer Funds Capture', 'woo_quadpay' ) ?></label> 192 </th> 193 <td class="forminp"><?php _e( 'Yes', 'woo_quadpay' ) ?></td> 194 </tr> 195 <?php endif; ?> 204 196 </tbody> 205 197 </table> … … 244 236 * Request an order token from QuadPay 245 237 * 246 * @param string $reference where does the request come from 247 * 248 * @return string or boolean false if no token generated 249 */ 250 public function get_quadpay_authorization_code( $reference = 'get_quadpay_authorization_code' ) { 251 252 $access_token = wp_cache_get( 'quadpay_access_token', 'quadpay' ); 253 254 if ( false !== $access_token ) { 255 self::log( 'Access token from cache' ); 256 return $access_token; 257 } 258 259 if ( false === $this->apiKeysAvailable( $reference ) ) { 260 return false; 261 } 262 263 $AuthURL = $this->quadpay_settings->get_environment( 'auth_url' ); 264 265 $AuthBody = array( 266 'client_id' => $this->client_id, 267 'client_secret' => $this->client_secret, 268 'audience' => $this->quadpay_settings->get_environment( 'auth_audience' ), 269 'grant_type' => 'client_credentials', 270 ); 271 272 $args = array( 273 'method' => 'POST', 274 'headers' => array( 'Content-Type' => 'application/json'), 275 'body' => json_encode( $AuthBody ) 276 ); 277 278 $response = wp_remote_post($AuthURL ,$args); 238 * @return string|false 239 * @deprecated 240 */ 241 public function get_quadpay_authorization_code() { 242 return Quadpay_WC_Api::instance()->get_auth_token() ?: false; 243 } 244 245 /** 246 * Save options page in admin, update payment limits 247 */ 248 public function save_options_page() { 249 self::log( __METHOD__ ); 250 wp_cache_flush(); 251 // reload settings and options 252 $this->init_settings(); 253 $this->init_options(); 254 // update configuration from api 255 $this->update_payment_limits(); 256 } 257 258 /** 259 * Update settings from configuration api (min, max and dfc) 260 * 261 * @return bool 262 */ 263 public function update_payment_limits() { 264 265 if ( !$this->client_id || !$this->client_secret ) { 266 return false; 267 } 268 269 self::log( __METHOD__ ); 270 271 $response = Quadpay_WC_Api::instance()->configuration(); 279 272 280 273 if ( is_wp_error( $response ) ) { 281 self::log( '[Authentication] QuadPay API Error: ' . $response->get_error_message() ); 282 return false; 283 } 284 285 $body = json_decode(wp_remote_retrieve_body($response)); 286 287 $response_code = wp_remote_retrieve_response_code( $response ); 288 289 if ( $response_code == 200 ) { 290 291 self::log( '[Retrieve response] Response code ' . $response_code . ' valid'); 292 293 //store this in cache 294 wp_cache_set('quadpay_access_token', $body->access_token, 'quadpay', $body->expires_in); 295 296 self::log( '[Retrieve response] Access token available.' ); 297 298 return $body->access_token; 299 300 } else { 301 302 if ( 401 == $response_code ) { 303 self::log( '[Retrieve response] QuadPay login failed. Please check login credentials.' ); 304 } else { 305 self::log( '[Retrieve response] Error response code ' . $response_code ); 306 } 307 308 return false; 309 } 310 } 311 312 private function apiKeysAvailable( $reference ) { 313 314 if ( empty( $this->client_id ) || empty( $this->client_secret ) ) { 315 self::log( '[API key availability check] API keys not available. Reference: ' . $reference ); 316 317 return false; 318 } 319 320 self::log( '[API key availability check] API keys available. Reference: ' . $reference ); 321 //self::log( '[API key availability check] Client ID: ' . $this->client_id ); 322 //self::log( '[API key availability check] Client secret: ' . $this->client_secret ); 274 return false; 275 } 276 277 $this->quadpay_settings->update_options([ 278 'quadpay-amount-minimum' => $response->minimumAmount, 279 'quadpay-maximum-amount' => $response->maximumAmount, 280 'defer_funds_capture' => $response->deferFundsCapture 281 ]); 323 282 324 283 return true; 325 }326 327 public function save_options_page() {328 self::log( '[Saved settings] start' );329 wp_cache_flush();330 $this->init_options();331 $this->update_payment_limits();332 self::log( '[Saved settings] end' );333 }334 335 public function update_payment_limits() {336 337 // Get existing limits338 $settings = get_option('woocommerce_quadpay_settings');339 340 if( false === $this->apiKeysAvailable( 'update_payment_limits' ) ) {341 return false;342 }343 344 self::log( '[Updating payment limits] request values');345 346 $headers = array(347 'headers' => array(348 'Authorization' => 'Bearer ' . $this->get_quadpay_authorization_code( 'update_payment_limits' ),349 )350 );351 352 $response = wp_remote_get( $this->configurationUrl, $headers );353 $body = json_decode( wp_remote_retrieve_body( $response ) );354 $response_code = wp_remote_retrieve_response_code( $response );355 356 self::log( '[Updating payment limits] response code: ' . $response_code );357 self::log( '[Updating payment limits] response: ' . print_r( $body, true ) );358 359 if ( $response_code == 200 ) {360 361 $settings['quadpay-amount-minimum'] = $body->minimumAmount;362 $settings['quadpay-maximum-amount'] = $body->maximumAmount;363 364 update_option( 'woocommerce_quadpay_settings', $settings );365 366 return true;367 }368 369 return false;370 284 } 371 285 … … 375 289 * 376 290 * @param int $order_id 377 *378 291 * @return array 379 292 */ 380 293 public function process_payment( $order_id ) { 381 294 382 if ( function_exists( 'wc_get_order' ) ) { 383 $order = wc_get_order( $order_id ); 384 } else { 385 $order = new WC_Order( $order_id ); 386 } 387 388 // Get the authorization token 389 $access_token = $this->get_quadpay_authorization_code( 'process_payment'); 295 $order = wc_get_order( $order_id ); 390 296 391 297 //Process here 392 $order items = $order->get_items();298 $order_items = $order->get_items(); 393 299 $items = array(); 394 300 395 if ( count($orderitems ) ) { 396 397 foreach ( $orderitems as $item ) { 398 399 if ( $item['variation_id'] ) { 400 401 if ( function_exists( "wc_get_product" ) ) { 402 $product = wc_get_product( $item['variation_id'] ); 403 } else { 404 $product = new WC_Product( $item['variation_id'] ); 405 } 406 407 } else { 408 409 if ( function_exists( "wc_get_product" ) ) { 410 $product = wc_get_product( $item['product_id'] ); 411 } else { 412 $product = new WC_Product( $item['product_id'] ); 413 } 414 415 } 301 if ( count($order_items ) ) { 302 303 foreach ( $order_items as $item ) { 304 305 $product = wc_get_product( $item['variation_id'] ?: $item['product_id'] ); 416 306 417 307 $items[] = array( … … 465 355 'postcode' => $this->get_order_prop( $order, 'shipping_postcode' ), 466 356 ), 467 'description' => 'string',357 'description' => "Order #$order_id", 468 358 'items' => $items, 469 359 'merchant' => array( … … 474 364 'taxAmount' => $order->get_total_tax(), 475 365 'shippingAmount' => $shipping_total, 476 'merchantFeeForPaymentPlan' => $mfpp_total 366 'merchantFeeForPaymentPlan' => $mfpp_total, 367 'metadata' => array( 368 'platform' => 'WooCommerce' 369 ) 477 370 ); 478 371 479 $headers = array( 480 'Content-Type' => 'application/json', 481 'Authorization' => 'Bearer ' . $access_token, 372 self::log( 'POST Order request'); 373 $response = Quadpay_WC_Api::instance()->order( $body ); 374 375 // Couldn't generate token 376 if ( is_wp_error($response) ) { 377 378 $order->add_order_note( 379 __('Unable to generate the order token. Payment could not proceed.', 'woo_quadpay' ) 380 ); 381 382 wc_add_notice( 383 __('Sorry, there was a problem preparing your payment. Please try again.', 'woo_quadpay'), 384 'error' 385 ); 386 387 return []; 388 } 389 390 // Order token successful, save it so we can confirm it later 391 update_post_meta( $order_id, '_quadpay_order_token', $response->token ); 392 update_post_meta( $order_id, '_quadpay_order_id', $response->orderId ); 393 394 return array( 395 'result' => 'success', 396 'redirect' => $response->redirectUrl 482 397 ); 483 484 $order_args = array( 485 'method' => 'POST', 486 'headers' => $headers, 487 'body' => json_encode( $body ), 488 'timeout' => 30, 489 ); 490 491 self::log( 'POST Order request: '.print_r( $order_args,true ) ); 492 493 $APIURL = $this->orderurl; 494 495 $order_response = wp_remote_post( $APIURL, $order_args ); 496 497 $response_code = wp_remote_retrieve_response_code( $order_response ); 498 499 $order_body = json_decode( wp_remote_retrieve_body( $order_response ) ); 500 501 self::log( 'POST Order response: ' . print_r( $order_body,true ) ); 502 503 $valid_response_code = array( 200, 201 ); 504 505 if ( ! in_array( $response_code, $valid_response_code ) ) { 506 // Couldn't generate token 507 $order->add_order_note(__('Unable to generate the order token. Payment couldn\'t proceed.', 'woo_quadpay' ) ); 508 509 wc_add_notice(__('Sorry, there was a problem preparing your payment. Please try again.', 'woo_quadpay'),'error'); 510 511 return; 512 513 } else { 514 // Order token successful, save it so we can confirm it later 515 update_post_meta( $order_id,'_quadpay_order_token', $order_body->token ); 516 517 $redirect = $order_body->redirectUrl; 518 519 return array( 520 'result' => 'success', 521 'redirect' => $redirect 522 ); 523 } 524 525 } 526 398 } 399 400 /** 401 * Resolves Zip order id from order metadata 402 * 403 * @param $order_id 404 * @return string|false 405 */ 527 406 public function get_quadpay_order_id( $order_id ) { 528 407 … … 533 412 } 534 413 535 $access_token = $this->get_quadpay_authorization_code( 'get_quadpay_order_id');536 414 $order_token = get_post_meta( $order_id, '_quadpay_order_token', true ); 537 415 … … 540 418 } 541 419 542 //check if quadpay order id already exists 543 544 //get the order id and save in postmeta 545 $get_args = array( 546 'headers' => array( 547 'Content-Type' => 'application/json', 548 'Authorization' => 'Bearer '. $access_token 549 ), 550 ); 551 552 $getOrderUrl = $this->orderurl . '?token=' . $order_token; 553 554 $get_response = wp_remote_get( $getOrderUrl, $get_args ); 555 $get_body = json_decode( wp_remote_retrieve_body( $get_response ) ); 556 557 self::log( 'Get Order result: ' . print_r( $get_body, true ) ); 558 559 $quadpay_order_id = $get_body->orderId; 560 $quadpay_order_status = $get_body->orderStatus; 561 562 //store order id for future purposes 563 if ( $quadpay_order_status !== 'Approved' ) { 564 return false; 565 } 566 567 update_post_meta($order_id,'_quadpay_order_id', $quadpay_order_id); 568 update_post_meta($order_id,'_quadpay_order_status', $quadpay_order_status); 569 570 return $quadpay_order_id; 571 } 572 573 /** 574 * @param $order_id 575 */ 576 public function payment_callback($order_id) { 577 578 if ( function_exists( "wc_get_order" ) ) { 579 $order = wc_get_order( $order_id ); 580 } else { 581 $order = new WC_Order( $order_id ); 582 } 420 update_post_meta($order_id, '_quadpay_order_id', $order_token ); 421 422 return $order_token; 423 } 424 425 /** 426 * Thank You page callback 427 * 428 * @param int $order_id 429 */ 430 public function payment_callback( $order_id ) { 431 432 $order = wc_get_order( $order_id ); 583 433 584 434 if ( 'quadpay' !== $order->get_payment_method() ) { … … 587 437 588 438 //save the order id 589 $quadpay_order_id = $this->get_quadpay_order_id($order_id); 590 591 $order_token = get_post_meta($order_id, '_quadpay_order_token', true); 592 if ($quadpay_order_id == false) { 593 $order->add_order_note(sprintf(__('Failure to process the Zip payment.','woo_quadpay') )); 594 } else if ( !empty($order_token) ) { 595 596 $quadpay_order_id = $this->get_quadpay_order_id($order_id); 597 598 //Update status for order 599 if (isset($_GET['status'])) { 600 601 $query_string = $_GET['status']; 602 603 if ($query_string != NULL) { 604 if ($query_string === 'confirmed') { 605 606 $order->add_order_note(sprintf(__('Payment approved. Zip Order ID: '.$quadpay_order_id.' ','woo_quadpay') )); 607 $order->payment_complete($quadpay_order_id); 608 wc_empty_cart(); 609 610 } elseif ($query_string === 'cancelled') { 611 612 $order->add_order_note(sprintf(__('Zip payment is pending approval. Zip Order ID: '.$quadpay_order_id.' ','woo_quadpay') )); 613 $order->update_status( 'cancelled' ); 614 615 616 } elseif ($query_string === 'failure') { 617 618 $order->add_order_note(sprintf(__('Zip payment declined. Order ID from Zip: '.$quadpay_order_id.' ','woo_quadpay') )); 619 $order->update_status('failed'); 620 } 621 622 } else { 623 $order->update_status('pending'); 624 } 625 } 626 627 } 628 629 } 630 631 /** 632 * Check whether the cart amount is within payment limits 633 * 634 * @param array $gateways Enabled gateways 635 * @return array Enabled gateways, possibly with QuadPay removed 636 */ 637 public function check_cart_within_limits($gateways) { 638 639 if ( is_admin() ) { 640 return $gateways; 641 } 642 643 $total = WC()->cart->total; 644 645 $minAmount = isset( $this->minimum_amount ) ? $this->minimum_amount : null; 646 $maxAmount = isset( $this->maximum_amount ) ? $this->maximum_amount : null; 647 648 if ( $minAmount == null || $maxAmount == null ) { 649 self::log('Requesting payment limit update'); 650 651 $status = $this->update_payment_limits(); 652 653 if ( $status ) { 654 655 $settings = get_option( 'woocommerce_quadpay_settings' ); 656 657 $minAmount = $settings['quadpay-amount-minimum']; 658 $maxAmount = $settings['quadpay-maximum-amount']; 659 660 } 661 } else { 662 self::log('Using cached payment limits'); 663 } 664 665 $pbi = ( $total >= $minAmount && $total <= $maxAmount ); 666 667 $billing_country = ''; 668 if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '3.0', '<' ) ) { 669 global $woocommerce; 670 $billing_country = $woocommerce->customer->country; 671 } else if ( null !== WC()->customer && null !== WC()->customer->get_billing_country() ) { 672 // make sure to call the member function only if not null 673 // @since 1.3.11 674 $billing_country = WC()->customer->get_billing_country(); 675 } 676 677 if ( ! $pbi || ! in_array( $billing_country, array( 'US', 'CA' ) ) ) { 678 unset( $gateways['quadpay'] ); 679 } 680 681 return $gateways; 682 } 683 684 /** 685 * Can the order be refunded? 686 * 687 * @param object $order WC_Order 439 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 440 441 if ( $quadpay_order_id === false ) { 442 $order->add_order_note(sprintf(__('Failure to process the Zip payment.', 'woo_quadpay'))); 443 return; 444 } 445 446 // check response and not query 447 $this->sync_order_status( $order ); 448 } 449 450 /** 451 * Can the order be refunded 452 * 453 * @param WC_Order $order 688 454 * @return bool 689 455 */ 690 456 public function can_refund_order( $order ) { 691 return $order && $order->get_transaction_id(); 692 } 693 694 /** 695 * Process a refund if supported 696 * 697 * @param object $order_id WC_Order 457 //return $order && $order->get_transaction_id(); 458 return $order && in_array( $order->get_status(), wc_get_is_paid_statuses() ); 459 } 460 461 /** 462 * Process a refund 463 * 464 * @param int $order_id 698 465 * @param float $amount 699 466 * @param string $reason 700 467 * 701 * @return boolean True or false based on success468 * @return bool 702 469 */ 703 470 public function process_refund( $order_id, $amount = null, $reason = '' ) { 704 471 705 472 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 706 707 $order = new WC_Order( $order_id ); 473 $order = wc_get_order( $order_id ); 708 474 709 475 if ( empty( $quadpay_order_id ) ) { 710 711 476 $order->add_order_note( sprintf( __( 'There was an error submitting the refund to Zip.', 'woo_quadpay' ) ) ); 712 713 return false; 714 } 715 716 $access_token = $this->get_quadpay_authorization_code( 'process_refund' ); 717 $random_string = wp_generate_password( 8, false, false ); 718 719 $refund_args = array( 720 'headers' => array( 721 'Content-Type' => 'application/json', 722 'Authorization' => 'Bearer ' . $access_token, 723 ), 724 'body' => json_encode( array( 725 'requestId' => 'Order #' . $order_id . '-' . $random_string, 726 'amount' => $amount, 727 'merchantRefundReference' => 'Order #' . $order_id . '-' . $random_string, 728 ) ), 729 ); 730 731 $refundOrderUrl = $this->orderurl.'/'.$quadpay_order_id.'/refund'; 732 733 $refund_response = wp_remote_post( $refundOrderUrl, $refund_args ); 734 $refund_body = json_decode( wp_remote_retrieve_body( $refund_response ) ); 735 736 self::log( 'Refund body: ' . print_r( $refund_body, true ) ); 737 738 $response_code = (int)$refund_response['response']['code']; 739 740 if ( $response_code === 201 || $response_code === 200 ) { 741 $order->add_order_note( sprintf( __( 'Refund of $%s successfully sent to Zip.', 'woo_quadpay' ), $amount ) ); 742 return true; 743 } 744 745 if ( $response_code === 404 ) { 746 $order->add_order_note( sprintf( __( 'Order not found on Zip.', 'woo_quadpay' ) ) ); 747 } else { 477 return false; 478 } 479 480 $merchant_refund_reference = 'Order #' . $order_id . '-' . wp_generate_password( 8, false, false ); 481 482 $response = Quadpay_WC_Api::instance()->refund( $quadpay_order_id, $amount, $merchant_refund_reference ); 483 484 if ( is_wp_error($response) ) { 748 485 $order->add_order_note( sprintf( __( 'There was an error submitting the refund to Zip.', 'woo_quadpay' ) ) ); 749 } 750 751 return false; 486 return false; 487 } 488 489 $order->add_order_note(sprintf( 490 __( 'Refund of %s successfully sent to Zip.', 'woo_quadpay' ), 491 wc_price( $amount, array( 'currency' => $order->get_currency() ) ) 492 ) ); 493 return true; 494 } 495 496 /** 497 * Capture order on Zip side, DFC 498 * 499 * @param int $order_id 500 * @param string $from 501 * @param string $to 502 * @return bool 503 */ 504 public function process_capture( $order_id, $from, $to ) { 505 506 // no need for manual capture 507 if ( !$this->get_option('defer_funds_capture') ) { 508 return false; 509 } 510 511 if ( ! ( in_array( $from, [ 'pending', 'on-hold' ] ) && in_array( $to, wc_get_is_paid_statuses() ) ) ) { 512 return false; 513 } 514 515 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 516 if ( empty( $quadpay_order_id ) ) { 517 return false; 518 } 519 520 $order = wc_get_order( $order_id ); 521 $amount = $order->get_total() - $order->get_total_refunded(); 522 523 if ($amount < 0.01) { 524 return false; 525 } 526 527 $merchant_reference = 'Order #' . $order_id . '-' . wp_generate_password( 8, false, false ); 528 529 $response = Quadpay_WC_Api::instance()->capture( $quadpay_order_id, $amount, $merchant_reference ); 530 531 if ( is_wp_error($response) ) { 532 $order->add_order_note( 533 sprintf( __( 'There was an error submitting the capture to Zip.', 'woo_quadpay' ) ) . 534 ' ' . 535 $response->get_error_message() 536 ); 537 return false; 538 } 539 540 $order->payment_complete( $quadpay_order_id ); 541 $order->add_order_note(sprintf( 542 __( 'Capture of %s successfully sent to Zip.', 'woo_quadpay' ), 543 wc_price( $amount, array( 'currency' => $order->get_currency() ) ) 544 ) ); 545 546 return true; 547 } 548 549 /** 550 * Void order on Zip side, DFC 551 * 552 * @param $order_id 553 * @param string $from 554 * @param string $to 555 * @return bool 556 */ 557 public function process_void( $order_id, $from, $to ) { 558 559 if ( !$this->get_option('defer_funds_capture') ) { 560 return false; 561 } 562 563 if ( ! ( in_array( $from, [ 'pending', 'on-hold' ] ) && in_array( $to, [ 'cancelled', 'failed' ] ) ) ) { 564 return false; 565 } 566 567 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 568 if ( empty( $quadpay_order_id ) ) { 569 return false; 570 } 571 572 $order = wc_get_order( $order_id ); 573 $amount = $order->get_total() - $order->get_total_refunded(); 574 575 if ($amount < 0.01) { 576 return false; 577 } 578 579 $merchant_reference = 'Order #' . $order_id . '-' . wp_generate_password( 8, false, false ); 580 581 /** @var WP_Error $response */ 582 $response = Quadpay_WC_Api::instance()->void( $quadpay_order_id, $amount, $merchant_reference ); 583 584 if ( is_wp_error($response) ) { 585 $order->add_order_note( 586 sprintf( __( 'There was an error submitting the void to Zip.', 'woo_quadpay' ) ) . 587 ' ' . 588 $response->get_error_message() 589 ); 590 return false; 591 } 592 593 $order->add_order_note(sprintf( 594 __( 'Void of %s successfully sent to Zip.', 'woo_quadpay' ), 595 wc_price( $amount, array( 'currency' => $order->get_currency() ) ) 596 ) ); 597 return true; 752 598 } 753 599 … … 755 601 * Logging method 756 602 * 757 * @param string $message 758 */ 759 public static function log( $message ) { 760 if ( self::$log_enabled ) { 761 if ( empty( self::$log ) ) { 762 self::$log = new WC_Logger(); 763 } 764 self::$log->add( 'quadpay', $message ); 765 } 766 767 if( WP_DEBUG && WP_DEBUG_LOG ) { 768 error_log($message); 769 } 770 } 771 772 /** 773 * Check the order status of all pending orders that didn't return to the thank you page or marked as Pending by QuadPay 603 * @param string $message 604 * @param string|null $source 605 */ 606 public static function log( $message, $source = null ) { 607 Quadpay_WC_Logger::log( $message, $source ); 608 } 609 610 /** 611 * Check the order status of all pending orders that didn't return to the thank you page or marked as Pending by Zip 612 * 613 * @note: old code was also checking on-hold orders, but we don't have any (only DFC) 774 614 */ 775 615 public function check_pending_orders() { 616 617 // Default value 24h 618 $order_check_time = $this->get_option( 'order_check_time', 'DAY_IN_SECONDS'); 776 619 777 620 // Get PENDING orders that may have been abandoned, or browser window closed after approved … … 779 622 'status' => 'pending', 780 623 'type' => 'shop_order', 781 'date_created' => '>' . ( time() - DAY_IN_SECONDS),624 'date_created' => '>' . ( time() - constant( $order_check_time ) ), 782 625 'limit' => -1, 783 626 'payment_method' => 'quadpay', 784 627 ); 628 785 629 $pending_orders = wc_get_orders( $args ); 786 630 … … 790 634 791 635 foreach ( $pending_orders as $order ) { 792 793 $order_id = $order->get_id(); 794 795 $quadpay_order_token = get_post_meta( $order_id, '_quadpay_order_token', true ); 796 797 // Check if there's a stored order token. If not, it's not an QuadPay order. 798 if ( ! $quadpay_order_token ) continue; 799 800 self::log( 'Checking abandoned order for WC Order ID ' . $order_id . ', Zip Token ' . $quadpay_order_token ); 801 802 $response = wp_remote_get( 803 $this->orderurl.'?token=' . $quadpay_order_token, 804 array( 805 'headers'=>array( 806 'Authorization' => 'Bearer ' . $this->get_quadpay_authorization_code( 'check_pending_orders'), 807 ) 808 ) 809 ); 810 811 $body = json_decode( wp_remote_retrieve_body( $response ) ); 812 813 self::log( 'Checking abandoned order result: ' . print_r( $body,true ) ); 814 815 $response_code = wp_remote_retrieve_response_code( $response ); 816 817 // Check status of order 818 if ( $response_code == 200 ) { 819 820 // Check status of order 821 if ( $body->orderStatus === 'Approved' ) { 822 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment approved. Zip Order ID: %s', 'woo_quadpay' ), $body->orderId ) ); 823 $order->payment_complete( $order_id ); 824 } elseif ( $body->orderStatus === 'Created' ) { 825 $order->add_order_note( __( 'Checked payment status with Zip. Still pending approval.', 'woo_quadpay' ) ); 826 $order->update_status( 'pending' ); 827 } elseif ( $body->orderStatus === 'Abandoned' ) { 828 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 829 $order->update_status( 'cancelled' ); 830 } else { 831 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 832 $order->update_status( 'failed' ); 636 $this->sync_order_status($order); 637 } 638 } 639 640 /** 641 * Checks order status on Zip and updates order accordingly 642 * 643 * @param WC_Order $order 644 * @param bool $forceStatus 645 * @return bool 646 */ 647 public function sync_order_status( $order, $forceStatus = false ) { 648 649 $payment_method = $order->get_payment_method(); 650 651 if ( 'quadpay' !== $payment_method ) { 652 return false; 653 } 654 655 $order_id = $order->get_id(); 656 $quadpay_order_id = $this->get_quadpay_order_id( $order_id ); 657 658 if ( ! $quadpay_order_id ) { 659 return false; 660 } 661 662 self::log( 'Order ID ' . $order_id . ', Zip Order ID ' . $quadpay_order_id, __METHOD__ ); 663 $response = Quadpay_WC_Api::instance()->get_order( $quadpay_order_id ); 664 665 if ( is_wp_error( $response ) ) { 666 return false; 667 } 668 669 $order->add_order_note( 670 sprintf( 671 __('Zip payment %s. Zip Order ID: %s', 'woo_quadpay'), 672 strtolower($response->orderStatus), 673 $response->orderId 674 ) 675 ); 676 677 // Check status of order 678 switch ($response->orderStatus) { 679 case 'Approved': 680 if ( $forceStatus || $order->get_status() === 'pending' ) { 681 682 if ( $this->get_option('defer_funds_capture') ) { 683 $order->update_status('on-hold'); 684 } else { 685 $order->payment_complete($quadpay_order_id); 686 } 687 833 688 } 834 689 835 } else { 836 837 $order_date = method_exists( $order, 'get_date_created' ) ? $order->get_date_created() : $order->order_date; 838 839 if ( strtotime( 'now' ) - strtotime( $order_date ) > 3600 ) { 840 $order->add_order_note( sprintf( __( 'Pending Order Expired' ) ) ); 841 $order->update_status( 'cancelled' ); 690 break; 691 692 case 'Created': 693 if ( $forceStatus && $order->get_status() !== 'pending' ) { 694 $order->update_status('pending'); 842 695 } 843 } 844 845 } 846 } 847 848 /** 849 * Check the order status of all on-hold orders that didn't return to the thank you page or marked as Pending by QuadPay 850 */ 851 public function check_on_hold_orders() { 852 853 self::log( 'Start checking On Hold orders' ); 854 855 // Get ON-HOLD orders that are "pending" at QuadPay that need to be checked whether approved or denied 856 $args = array( 857 'status' => 'on-hold', 858 'type' => 'shop_order', 859 'date_created' => '>' . ( time() - constant( $this->order_check_time ) ), 860 'limit' => -1, 861 'payment_method' => 'quadpay', 862 ); 863 864 $on_hold_orders = wc_get_orders( $args ); 865 866 if ( empty( $on_hold_orders ) ) { 867 868 self::log( 'No On Hold orders found (' . $this->order_check_time . ')' ); 869 self::log( 'End checking On Hold orders' ); 870 871 return; 872 } 873 874 self::log( count( $on_hold_orders ) . ' On Hold orders found' ); 875 876 foreach ( $on_hold_orders as $order ) { 877 878 $order_id = $order->get_id(); 879 880 $quadpay_order_token = get_post_meta( $order_id, '_quadpay_order_token', true ); 881 882 // Check if there's a stored order token. If not, it's not an QuadPay order. 883 if ( ! $quadpay_order_token ) continue; 884 885 self::log( 'Checking abandoned order for WC Order ID ' . $order_id . ', QuadPay Token ' . $quadpay_order_token ); 886 887 $response = wp_remote_get( 888 $this->orderurl.'?token=' . $quadpay_order_token, 889 array( 890 'headers'=>array( 891 'Authorization' => 'Bearer ' . $this->get_quadpay_authorization_code( 'check_on_hold_orders' ), 892 ) 893 ) 894 ); 895 896 $body = json_decode( wp_remote_retrieve_body( $response ) ); 897 898 self::log( 'Checking abandoned order result: ' . print_r( $body, true ) ); 899 900 $response_code = wp_remote_retrieve_response_code( $response ); 901 902 // Check status of order 903 if ( $response_code == 200 ) { 904 905 // Check status of order 906 if ( $body->orderStatus === 'Approved' ) { 907 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment approved. Zip Order ID: %s', 'woo_quadpay' ), $body->orderId ) ); 908 $order->payment_complete( $order_id ); 909 } elseif ( $body->orderStatus === 'Created' ) { 910 $order->add_order_note( __( 'Checked payment status with Zip. Still pending approval.', 'woo_quadpay' ) ); 911 $order->update_status( 'pending' ); 912 } elseif ( $body->orderStatus === 'Abandoned' ) { 913 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 914 $order->update_status( 'cancelled' ); 915 } else { 916 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 917 $order->update_status( 'failed' ); 696 697 break; 698 699 case 'Abandoned': 700 case 'Declined': 701 if ( $forceStatus || $order->get_status() === 'pending' ) { 702 $order->update_status('cancelled'); 918 703 } 919 704 920 } else { 921 922 $order_date = method_exists( $order, 'get_date_created' ) ? $order->get_date_created() : $order->order_date; 923 924 if ( strtotime( 'now' ) - strtotime( $order_date ) > 3600 ) { 925 926 $order->add_order_note( sprintf( __( 'On Hold Order Expired at Zip' ) ) ); 927 $order->update_status( 'cancelled' ); 928 705 break; 706 707 default: 708 if ( $forceStatus ) { 709 $order->update_status('failed'); 929 710 } 930 711 931 }932 933 } 934 712 break; 713 } 714 715 return true; 935 716 } 936 717 -
quadpay-gateway-for-woocommerce/trunk/includes/class-quadpay-settings.php
r2688749 r2712100 26 26 'api_url_v2' => 'https://gateway.quadpay.com' 27 27 ]; 28 29 const ALLOWED_COUNTRIES = [ 'US', 'CA' ]; 28 30 29 31 /** … … 152 154 'WEEK_IN_SECONDS' => __( 'One Week', 'woo_quadpay' ), 153 155 'MONTH_IN_SECONDS' => __( 'One Month', 'woo_quadpay' ), 154 'YEAR_IN_SECONDS' => __( 'One Year', 'woo_quadpay' ),155 156 ), 156 157 'description' => __( 'How far back should authorized, not captured orders be checked for status change.', 'woo_quadpay' ), 158 'default' => 'DAY_IN_SECONDS' 157 159 ), 158 160 'widget_settings' => array( … … 240 242 $quadpay_settings = get_option( self::SETTINGS_KEY, [] ); 241 243 242 if ( isset ($quadpay_settings[$key]) ) {244 if ( isset( $quadpay_settings[$key] ) ) { 243 245 return $quadpay_settings[$key]; 244 246 } … … 249 251 250 252 return $default; 253 } 254 255 /** 256 * @param $options 257 * @return bool 258 */ 259 public function update_options( $options ) 260 { 261 $quadpay_settings = get_option( self::SETTINGS_KEY, [] ); 262 $quadpay_settings = array_merge( $quadpay_settings, $options ); 263 264 return update_option( 'woocommerce_quadpay_settings', $quadpay_settings ); 251 265 } 252 266 -
quadpay-gateway-for-woocommerce/trunk/quadpay.php
r2688749 r2712100 5 5 Author: Zip Co Limited 6 6 Author URI: https://zip.co 7 Version: 1. 6.07 Version: 1.7.0 8 8 WC requires at least: 3.1.0 9 WC tested up to: 5.99 WC tested up to: 6.3 10 10 */ 11 11 12 define( 'QUADPAY_WC_VERSION', '1. 6.0' );12 define( 'QUADPAY_WC_VERSION', '1.7.0' ); 13 13 define( 'QUADPAY_WC_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 14 14 15 15 require_once __DIR__ . '/includes/class-quadpay-settings.php'; 16 require_once __DIR__ . '/includes/class-quadpay-logger.php'; 17 require_once __DIR__ . '/includes/class-quadpay-api.php'; 16 18 require_once __DIR__ . '/includes/class-quadpay-widget.php'; 17 19 require_once __DIR__ . '/includes/class-quadpay-mfpp.php'; … … 46 48 47 49 $gateway = WC_Gateway_QuadPay::instance(); 48 49 50 $order_id = wc_get_order_id_by_order_key( $_GET['key'] ); 50 51 if ( function_exists( "wc_get_order" ) ) { 52 $order = wc_get_order( $order_id ); 53 } else { 54 $order = new WC_Order( $order_id ); 55 } 51 $order = wc_get_order( $order_id ); 56 52 57 53 if ( $order && $order->get_payment_method() === 'quadpay' ) { … … 80 76 81 77 /** 82 * Call the cron task to check all on-hold orders status in the gateway83 **/84 function quadpay_check_on_hold_orders_cron_jobs() {85 $gateway = WC_Gateway_QuadPay::instance();86 $gateway->check_on_hold_orders();87 }88 add_action( 'quadpay_forty_five_minutes_cron_jobs', 'quadpay_check_on_hold_orders_cron_jobs' );89 90 /**91 78 * Call the cron task to check the merchant payment limits on QuadPay 92 79 **/ … … 125 112 } 126 113 127 $order_actions['check_quadpay_status'] = 'Check payment status on Zip';114 $order_actions['check_quadpay_status'] = __( 'Check payment status on Zip' , 'woo_quadpay' ); 128 115 129 116 return $order_actions; … … 159 146 160 147 $gateway = WC_Gateway_QuadPay::instance(); 161 162 $response = wp_remote_get( 163 $gateway->orderurl.'?token=' . $quadpay_order_token, 164 array( 165 'headers'=>array( 166 'Authorization' => 'Bearer ' . $gateway->get_quadpay_authorization_code( 'quadpay_woocommerce_check_order_payment_status' ), 167 ) 168 ) 169 ); 170 171 $body = json_decode( wp_remote_retrieve_body( $response ) ); 172 173 $response_code = wp_remote_retrieve_response_code( $response ); 174 175 $response_message = wp_remote_retrieve_response_message( $response ); 176 177 // Check status of order 178 if ( $response_code == 200 ) { 179 180 // Check status of order 181 if ( 'Approved' === $body->orderStatus ) { 182 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment approved. Zip Order ID: %s', 'woo_quadpay' ), $body->orderId ) ); 183 $order->payment_complete( $order_id ); 184 } elseif ( 'Created' === $body->orderStatus ) { 185 $order->add_order_note( __( 'Checked payment status with Zip. Still pending approval.', 'woo_quadpay' ) ); 186 $order->update_status( 'pending' ); 187 } elseif ( 'Abandoned' === $body->orderStatus ) { 188 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 189 $order->update_status( 'cancelled' ); 190 } else { 191 $order->add_order_note( sprintf( __( 'Checked payment status with Zip. Payment %s. Zip Order ID: %s', 'woo_quadpay' ), strtolower( $body->orderStatus ), $body->orderId ) ); 192 $order->update_status( 'failed' ); 193 } 194 195 } else { 196 $order->add_order_note( 'Can\'t check order payment status on Zip (response message: ' . $response_message . ').' ); 148 if ( !$gateway->sync_order_status( $order ) ) { 149 $order->add_order_note( 'Can\'t check order payment status on Zip.' ); 197 150 } 198 151 -
quadpay-gateway-for-woocommerce/trunk/readme.txt
r2688749 r2712100 4 4 * Requires at least: 4.7 5 5 * Tested up to: 5.9 6 * Stable tag: 1. 6.06 * Stable tag: 1.7.0 7 7 * Requires PHP: 7.0 8 8 * License: GPLv3 … … 59 59 == Changelog == 60 60 61 = 1.7.0 = 62 * Defer funds capture support 63 * Multiple place order and API improvements 64 * Compatibility - tested up to WooCommerce 6.3 65 61 66 = 1.6.0 = 62 67 * Added payment widget on checkout
Note: See TracChangeset
for help on using the changeset viewer.