Plugin Directory

Changeset 2712100


Ignore:
Timestamp:
04/20/2022 10:50:54 AM (4 years ago)
Author:
quadpay
Message:

version 1.7.0 (defer funds capture)

Location:
quadpay-gateway-for-woocommerce
Files:
4 added
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • quadpay-gateway-for-woocommerce/tags/1.7.0/includes/class-quadpay-gateway.php

    r2688749 r2712100  
    44
    55    /**
    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
    79     */
    810    private static $_instance = NULL;
    911
    1012    /**
    11      * @var boolean Whether or not logging is enabled
    12      */
    13     public static $log_enabled = false;
    14 
    15     /**
    16      * @var WC_Logger Logger instance
    17      */
    18     public static $log = false;
    19 
    20     /**
    2113     * @var string
    2214     */
    23     private $territory = 'US';
     15    private $client_id;
     16
    2417    /**
    2518     * @var string
    2619     */
    27     private $client_id;
     20    private $client_secret;
     21
    2822    /**
    2923     * @var string
    3024     */
    31     private $client_secret;
     25    private $mode;
     26
    3227    /**
    3328     * @var string
    3429     */
    35     private $mode;
     30    private $minimum_amount;
     31
    3632    /**
    3733     * @var string
    3834     */
    39     private $minimum_amount;
    40     /**
    41      * @var string
    42      */
    4335    private $maximum_amount;
    44     /**
    45      * @var string
    46      */
    47     public $orderurl;
    48     /**
    49      * @var string
    50      */
    51     private $configurationUrl;
    52     /**
    53      * @var string
    54      */
    55     private $order_check_time;
    5636
    5737    /**
     
    8060        $this->method_title = __( 'Zip - Pay in 4 installments', 'woo_quadpay' );
    8161        $this->method_description = __( 'Use Zip as a credit or debit card processor for WooCommerce.', 'woo_quadpay' );
    82 
    8362        $this->title = __( 'Zip - Pay in 4 installments', 'woo_quadpay');
    84 
    8563        $this->description = __('Pay in 4 installments.','woo_quadpay');
    86 
    87         $this->supports = array( 'products', 'refunds' );
    88 
     64        $this->supports = [ 'products', 'refunds' ];
    8965        $this->icon = QUADPAY_WC_PLUGIN_URL . 'assets/images/zip-logo-color.svg';
    9066
     
    9975        $this->init_settings();
    10076
    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() ) {
    10978            $this->title .= ' (TEST MODE)';
    11079        }
     
    11584    public function init()
    11685    {
    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 met
    122         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 );
    12594    }
    12695
     
    12998     */
    13099    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
    144109     */
    145110    public function is_available() {
     111
    146112        if ( 'yes' !== $this->enabled ) {
    147113            return false;
     
    149115
    150116        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 ) ) {
    151140            return false;
    152141        }
     
    166155     */
    167156    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 
    176157        ?>
    177158        <h2><?php _e( 'Zip Gateway', 'woo_quadpay' ); ?></h2>
    178159        <?php
    179         $login = (bool) ( false === $this->get_quadpay_authorization_code( 'login check' ) ? false : true );
     160        $login = ( $this->get_quadpay_authorization_code() !== false );
    180161        ?>
    181162        <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            ?>
    183167        </p>
    184168
     
    191175                </th>
    192176                <td class="forminp">
    193                     <?php echo $min_amount ?>
     177                    <?php echo $this->minimum_amount ?>
    194178                </td>
    195179            </tr>
     
    199183                </th>
    200184                <td class="forminp">
    201                     <?php echo $max_amount ?>
     185                    <?php echo $this->maximum_amount ?>
    202186                </td>
    203187            </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; ?>
    204196            </tbody>
    205197        </table>
     
    244236     * Request an order token from QuadPay
    245237     *
    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();
    279272
    280273        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        ]);
    323282
    324283        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 limits
    338         $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;
    370284    }
    371285
     
    375289     *
    376290     * @param int $order_id
    377      *
    378291     * @return array
    379292     */
    380293    public function process_payment( $order_id ) {
    381294
    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 );
    390296
    391297        //Process here
    392         $orderitems = $order->get_items();
     298        $order_items = $order->get_items();
    393299        $items = array();
    394300
    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'] );
    416306
    417307                $items[] = array(
     
    465355                'postcode'     => $this->get_order_prop( $order, 'shipping_postcode' ),
    466356            ),
    467             'description'       => 'string',
     357            'description'       => "Order #$order_id",
    468358            'items'             => $items,
    469359            'merchant'          => array(
     
    474364            'taxAmount'         => $order->get_total_tax(),
    475365            'shippingAmount'    => $shipping_total,
    476             'merchantFeeForPaymentPlan' => $mfpp_total
     366            'merchantFeeForPaymentPlan' => $mfpp_total,
     367            'metadata' => array(
     368                'platform' => 'WooCommerce'
     369            )
    477370        );
    478371
    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
    482397        );
    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     */
    527406    public function get_quadpay_order_id( $order_id ) {
    528407
     
    533412        }
    534413
    535         $access_token = $this->get_quadpay_authorization_code( 'get_quadpay_order_id');
    536414        $order_token  = get_post_meta( $order_id, '_quadpay_order_token', true );
    537415
     
    540418        }
    541419
    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 );
    583433
    584434        if ( 'quadpay' !== $order->get_payment_method() ) {
     
    587437
    588438        //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
    688454     * @return bool
    689455     */
    690456    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
    698465     * @param float $amount
    699466     * @param string $reason
    700467     *
    701      * @return  boolean True or false based on success
     468     * @return bool
    702469     */
    703470    public function process_refund( $order_id, $amount = null, $reason = '' ) {
    704471
    705472        $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 );
    708474
    709475        if ( empty( $quadpay_order_id ) ) {
    710 
    711476            $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) ) {
    748485            $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;
    752598    }
    753599
     
    755601     * Logging method
    756602     *
    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)
    774614     */
    775615    public function check_pending_orders() {
     616
     617        // Default value 24h
     618        $order_check_time = $this->get_option( 'order_check_time', 'DAY_IN_SECONDS');
    776619
    777620        // Get PENDING orders that may have been abandoned, or browser window closed after approved
     
    779622            'status'         => 'pending',
    780623            'type'           => 'shop_order',
    781             'date_created'   => '>' . ( time() - DAY_IN_SECONDS ),
     624            'date_created'   => '>' . ( time() - constant( $order_check_time ) ),
    782625            'limit'          => -1,
    783626            'payment_method' => 'quadpay',
    784627        );
     628
    785629        $pending_orders = wc_get_orders( $args );
    786630
     
    790634
    791635        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
    833688                }
    834689
    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');
    842695                }
    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');
    918703                }
    919704
    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');
    929710                }
    930711
    931             }
    932 
    933         }
    934 
     712                break;
     713        }
     714
     715        return true;
    935716    }
    936717
  • quadpay-gateway-for-woocommerce/tags/1.7.0/includes/class-quadpay-settings.php

    r2688749 r2712100  
    2626        'api_url_v2' => 'https://gateway.quadpay.com'
    2727    ];
     28
     29    const ALLOWED_COUNTRIES = [ 'US', 'CA' ];
    2830
    2931    /**
     
    152154                    'WEEK_IN_SECONDS'  => __( 'One Week', 'woo_quadpay' ),
    153155                    'MONTH_IN_SECONDS' => __( 'One Month', 'woo_quadpay' ),
    154                     'YEAR_IN_SECONDS'  => __( 'One Year', 'woo_quadpay' ),
    155156                ),
    156157                'description' => __( 'How far back should authorized, not captured orders be checked for status change.', 'woo_quadpay' ),
     158                'default' => 'DAY_IN_SECONDS'
    157159            ),
    158160            'widget_settings' => array(
     
    240242        $quadpay_settings = get_option( self::SETTINGS_KEY, [] );
    241243
    242         if (isset ($quadpay_settings[$key]) ) {
     244        if ( isset( $quadpay_settings[$key] ) ) {
    243245            return $quadpay_settings[$key];
    244246        }
     
    249251
    250252        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 );
    251265    }
    252266
  • quadpay-gateway-for-woocommerce/tags/1.7.0/quadpay.php

    r2688749 r2712100  
    55Author: Zip Co Limited
    66Author URI: https://zip.co
    7 Version: 1.6.0
     7Version: 1.7.0
    88WC requires at least: 3.1.0
    9 WC tested up to: 5.9
     9WC tested up to: 6.3
    1010*/
    1111
    12 define( 'QUADPAY_WC_VERSION', '1.6.0' );
     12define( 'QUADPAY_WC_VERSION', '1.7.0' );
    1313define( 'QUADPAY_WC_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    1414
    1515require_once __DIR__ . '/includes/class-quadpay-settings.php';
     16require_once __DIR__ . '/includes/class-quadpay-logger.php';
     17require_once __DIR__ . '/includes/class-quadpay-api.php';
    1618require_once __DIR__ . '/includes/class-quadpay-widget.php';
    1719require_once __DIR__ . '/includes/class-quadpay-mfpp.php';
     
    4648
    4749            $gateway = WC_Gateway_QuadPay::instance();
    48 
    4950            $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 );
    5652
    5753            if ( $order && $order->get_payment_method() === 'quadpay' ) {
     
    8076
    8177    /**
    82      * Call the cron task to check all on-hold orders status in the gateway
    83      **/
    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     /**
    9178     * Call the cron task to check the merchant payment limits on QuadPay
    9279     **/
     
    125112        }
    126113
    127         $order_actions['check_quadpay_status'] = 'Check payment status on Zip';
     114        $order_actions['check_quadpay_status'] = __( 'Check payment status on Zip' , 'woo_quadpay' );
    128115
    129116        return $order_actions;
     
    159146
    160147        $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.' );
    197150        }
    198151
  • quadpay-gateway-for-woocommerce/tags/1.7.0/readme.txt

    r2688749 r2712100  
    44* Requires at least: 4.7
    55* Tested up to: 5.9
    6 * Stable tag: 1.6.0
     6* Stable tag: 1.7.0
    77* Requires PHP: 7.0
    88* License: GPLv3
     
    5959== Changelog ==
    6060
     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
    6166= 1.6.0 =
    6267* Added payment widget on checkout
  • quadpay-gateway-for-woocommerce/trunk/includes/class-quadpay-gateway.php

    r2688749 r2712100  
    44
    55    /**
    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
    79     */
    810    private static $_instance = NULL;
    911
    1012    /**
    11      * @var boolean Whether or not logging is enabled
    12      */
    13     public static $log_enabled = false;
    14 
    15     /**
    16      * @var WC_Logger Logger instance
    17      */
    18     public static $log = false;
    19 
    20     /**
    2113     * @var string
    2214     */
    23     private $territory = 'US';
     15    private $client_id;
     16
    2417    /**
    2518     * @var string
    2619     */
    27     private $client_id;
     20    private $client_secret;
     21
    2822    /**
    2923     * @var string
    3024     */
    31     private $client_secret;
     25    private $mode;
     26
    3227    /**
    3328     * @var string
    3429     */
    35     private $mode;
     30    private $minimum_amount;
     31
    3632    /**
    3733     * @var string
    3834     */
    39     private $minimum_amount;
    40     /**
    41      * @var string
    42      */
    4335    private $maximum_amount;
    44     /**
    45      * @var string
    46      */
    47     public $orderurl;
    48     /**
    49      * @var string
    50      */
    51     private $configurationUrl;
    52     /**
    53      * @var string
    54      */
    55     private $order_check_time;
    5636
    5737    /**
     
    8060        $this->method_title = __( 'Zip - Pay in 4 installments', 'woo_quadpay' );
    8161        $this->method_description = __( 'Use Zip as a credit or debit card processor for WooCommerce.', 'woo_quadpay' );
    82 
    8362        $this->title = __( 'Zip - Pay in 4 installments', 'woo_quadpay');
    84 
    8563        $this->description = __('Pay in 4 installments.','woo_quadpay');
    86 
    87         $this->supports = array( 'products', 'refunds' );
    88 
     64        $this->supports = [ 'products', 'refunds' ];
    8965        $this->icon = QUADPAY_WC_PLUGIN_URL . 'assets/images/zip-logo-color.svg';
    9066
     
    9975        $this->init_settings();
    10076
    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() ) {
    10978            $this->title .= ' (TEST MODE)';
    11079        }
     
    11584    public function init()
    11685    {
    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 met
    122         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 );
    12594    }
    12695
     
    12998     */
    13099    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
    144109     */
    145110    public function is_available() {
     111
    146112        if ( 'yes' !== $this->enabled ) {
    147113            return false;
     
    149115
    150116        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 ) ) {
    151140            return false;
    152141        }
     
    166155     */
    167156    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 
    176157        ?>
    177158        <h2><?php _e( 'Zip Gateway', 'woo_quadpay' ); ?></h2>
    178159        <?php
    179         $login = (bool) ( false === $this->get_quadpay_authorization_code( 'login check' ) ? false : true );
     160        $login = ( $this->get_quadpay_authorization_code() !== false );
    180161        ?>
    181162        <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            ?>
    183167        </p>
    184168
     
    191175                </th>
    192176                <td class="forminp">
    193                     <?php echo $min_amount ?>
     177                    <?php echo $this->minimum_amount ?>
    194178                </td>
    195179            </tr>
     
    199183                </th>
    200184                <td class="forminp">
    201                     <?php echo $max_amount ?>
     185                    <?php echo $this->maximum_amount ?>
    202186                </td>
    203187            </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; ?>
    204196            </tbody>
    205197        </table>
     
    244236     * Request an order token from QuadPay
    245237     *
    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();
    279272
    280273        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        ]);
    323282
    324283        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 limits
    338         $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;
    370284    }
    371285
     
    375289     *
    376290     * @param int $order_id
    377      *
    378291     * @return array
    379292     */
    380293    public function process_payment( $order_id ) {
    381294
    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 );
    390296
    391297        //Process here
    392         $orderitems = $order->get_items();
     298        $order_items = $order->get_items();
    393299        $items = array();
    394300
    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'] );
    416306
    417307                $items[] = array(
     
    465355                'postcode'     => $this->get_order_prop( $order, 'shipping_postcode' ),
    466356            ),
    467             'description'       => 'string',
     357            'description'       => "Order #$order_id",
    468358            'items'             => $items,
    469359            'merchant'          => array(
     
    474364            'taxAmount'         => $order->get_total_tax(),
    475365            'shippingAmount'    => $shipping_total,
    476             'merchantFeeForPaymentPlan' => $mfpp_total
     366            'merchantFeeForPaymentPlan' => $mfpp_total,
     367            'metadata' => array(
     368                'platform' => 'WooCommerce'
     369            )
    477370        );
    478371
    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
    482397        );
    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     */
    527406    public function get_quadpay_order_id( $order_id ) {
    528407
     
    533412        }
    534413
    535         $access_token = $this->get_quadpay_authorization_code( 'get_quadpay_order_id');
    536414        $order_token  = get_post_meta( $order_id, '_quadpay_order_token', true );
    537415
     
    540418        }
    541419
    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 );
    583433
    584434        if ( 'quadpay' !== $order->get_payment_method() ) {
     
    587437
    588438        //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
    688454     * @return bool
    689455     */
    690456    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
    698465     * @param float $amount
    699466     * @param string $reason
    700467     *
    701      * @return  boolean True or false based on success
     468     * @return bool
    702469     */
    703470    public function process_refund( $order_id, $amount = null, $reason = '' ) {
    704471
    705472        $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 );
    708474
    709475        if ( empty( $quadpay_order_id ) ) {
    710 
    711476            $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) ) {
    748485            $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;
    752598    }
    753599
     
    755601     * Logging method
    756602     *
    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)
    774614     */
    775615    public function check_pending_orders() {
     616
     617        // Default value 24h
     618        $order_check_time = $this->get_option( 'order_check_time', 'DAY_IN_SECONDS');
    776619
    777620        // Get PENDING orders that may have been abandoned, or browser window closed after approved
     
    779622            'status'         => 'pending',
    780623            'type'           => 'shop_order',
    781             'date_created'   => '>' . ( time() - DAY_IN_SECONDS ),
     624            'date_created'   => '>' . ( time() - constant( $order_check_time ) ),
    782625            'limit'          => -1,
    783626            'payment_method' => 'quadpay',
    784627        );
     628
    785629        $pending_orders = wc_get_orders( $args );
    786630
     
    790634
    791635        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
    833688                }
    834689
    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');
    842695                }
    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');
    918703                }
    919704
    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');
    929710                }
    930711
    931             }
    932 
    933         }
    934 
     712                break;
     713        }
     714
     715        return true;
    935716    }
    936717
  • quadpay-gateway-for-woocommerce/trunk/includes/class-quadpay-settings.php

    r2688749 r2712100  
    2626        'api_url_v2' => 'https://gateway.quadpay.com'
    2727    ];
     28
     29    const ALLOWED_COUNTRIES = [ 'US', 'CA' ];
    2830
    2931    /**
     
    152154                    'WEEK_IN_SECONDS'  => __( 'One Week', 'woo_quadpay' ),
    153155                    'MONTH_IN_SECONDS' => __( 'One Month', 'woo_quadpay' ),
    154                     'YEAR_IN_SECONDS'  => __( 'One Year', 'woo_quadpay' ),
    155156                ),
    156157                'description' => __( 'How far back should authorized, not captured orders be checked for status change.', 'woo_quadpay' ),
     158                'default' => 'DAY_IN_SECONDS'
    157159            ),
    158160            'widget_settings' => array(
     
    240242        $quadpay_settings = get_option( self::SETTINGS_KEY, [] );
    241243
    242         if (isset ($quadpay_settings[$key]) ) {
     244        if ( isset( $quadpay_settings[$key] ) ) {
    243245            return $quadpay_settings[$key];
    244246        }
     
    249251
    250252        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 );
    251265    }
    252266
  • quadpay-gateway-for-woocommerce/trunk/quadpay.php

    r2688749 r2712100  
    55Author: Zip Co Limited
    66Author URI: https://zip.co
    7 Version: 1.6.0
     7Version: 1.7.0
    88WC requires at least: 3.1.0
    9 WC tested up to: 5.9
     9WC tested up to: 6.3
    1010*/
    1111
    12 define( 'QUADPAY_WC_VERSION', '1.6.0' );
     12define( 'QUADPAY_WC_VERSION', '1.7.0' );
    1313define( 'QUADPAY_WC_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    1414
    1515require_once __DIR__ . '/includes/class-quadpay-settings.php';
     16require_once __DIR__ . '/includes/class-quadpay-logger.php';
     17require_once __DIR__ . '/includes/class-quadpay-api.php';
    1618require_once __DIR__ . '/includes/class-quadpay-widget.php';
    1719require_once __DIR__ . '/includes/class-quadpay-mfpp.php';
     
    4648
    4749            $gateway = WC_Gateway_QuadPay::instance();
    48 
    4950            $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 );
    5652
    5753            if ( $order && $order->get_payment_method() === 'quadpay' ) {
     
    8076
    8177    /**
    82      * Call the cron task to check all on-hold orders status in the gateway
    83      **/
    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     /**
    9178     * Call the cron task to check the merchant payment limits on QuadPay
    9279     **/
     
    125112        }
    126113
    127         $order_actions['check_quadpay_status'] = 'Check payment status on Zip';
     114        $order_actions['check_quadpay_status'] = __( 'Check payment status on Zip' , 'woo_quadpay' );
    128115
    129116        return $order_actions;
     
    159146
    160147        $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.' );
    197150        }
    198151
  • quadpay-gateway-for-woocommerce/trunk/readme.txt

    r2688749 r2712100  
    44* Requires at least: 4.7
    55* Tested up to: 5.9
    6 * Stable tag: 1.6.0
     6* Stable tag: 1.7.0
    77* Requires PHP: 7.0
    88* License: GPLv3
     
    5959== Changelog ==
    6060
     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
    6166= 1.6.0 =
    6267* Added payment widget on checkout
Note: See TracChangeset for help on using the changeset viewer.