Plugin Directory

Changeset 2552573


Ignore:
Timestamp:
06/23/2021 10:02:32 AM (5 years ago)
Author:
beyondpay
Message:

v1.5.0 Save Card ONLY transaction mode added.

Location:
beyond-pay-for-woocommerce/trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • beyond-pay-for-woocommerce/trunk/README.md

    r2513950 r2552573  
    135135## Changelog
    136136
     137### 1.5.0
     138* 'Save Card ONLY' transaction mode added.
     139
    137140### 1.4.2
    138141* Verbose logging mode support added.
  • beyond-pay-for-woocommerce/trunk/README.txt

    r2513976 r2552573  
    33Tags: credit card, payment, woocommerce, payment gateway, subscriptions, subscription payments, recurring billing
    44Requires at least: 4.7
    5 Tested up to: 5.6
    6 Stable tag: 1.4.2
     5Tested up to: 5.7.2
     6Stable tag: trunk
    77Requires PHP: 7.0
    88License: MIT
     
    122122== Changelog ==
    123123
     124= 1.5.0 =
     125* 'Save Card ONLY' transaction mode added.
     126
    124127= 1.4.2 =
    125128* Debug mode support added.
  • beyond-pay-for-woocommerce/trunk/beyondpay-gateway.php

    r2513950 r2552573  
    66 * Author URI: https://getbeyond.com
    77 * Plugin URI: https://developer.getbeyond.com
    8  * Version: 1.4.2
     8 * Version: 1.5.0
    99 * Text Domain: beyond-pay-for-woocommerce
    1010 *
    11  * Tested up to: 5.7.0
    12  * WC tested up to: 5.1.0
     11 * Tested up to: 5.7.2
     12 * WC tested up to: 5.4.0
    1313 *
    1414 * Copyright (c) 2020 Above and Beyond Business Tools and Services for Entrepreneurs, Inc.
    1515 *
    16  * This program is free software: you can redistribute it and/or modify
    17  * it under the terms of the GNU General Public License as published by
    18  * the Free Software Foundation, either version 3 of the License, or
    19  * (at your option) any later version.
    20  *
    21  * This program is distributed in the hope that it will be useful,
    22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    24  * GNU General Public License for more details.
    25  *
    26  * You should have received a copy of the GNU General Public License
    27  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     16 * Review the LICENSE file for licensing information.
    2817 */
    2918
     
    7867    $beyond_pay_gateway->process_subscription_payment( $amount_to_charge, $order );
    7968}
     69
     70add_filter('woocommerce_register_shop_order_post_statuses', 'beyond_pay_add_saved_card_status');
     71
     72function beyond_pay_add_saved_card_status($statuses) {
     73    $statuses['wc-bp-tokenized'] = array(
     74    'label'                     => 'Saved Card',
     75    'public'                    => false,
     76    'exclude_from_search'       => false,
     77    'show_in_admin_all_list'    => true,
     78    'show_in_admin_status_list' => true,
     79    'label_count'               => _n_noop('Saved Card <span class="count">(%s)</span>', 'Saved Card <span class="count">(%s)</span>', 'beyond-pay-gateway'),
     80    );
     81    return $statuses;
     82}
     83
     84add_filter('wc_order_statuses', 'beyond_pay_add_saved_card_to_order_statuses');
     85
     86function beyond_pay_add_saved_card_to_order_statuses($order_statuses) {
     87    $order_statuses['wc-bp-tokenized'] = 'Saved Card';
     88    return $order_statuses;
     89}
     90
     91
     92add_filter('woocommerce_order_is_pending_statuses', 'beyond_pay_mark_saved_card_as_pending_status');
     93
     94function beyond_pay_mark_saved_card_as_pending_status($statuses) {
     95    array_push($statuses, 'wc-bp-tokenized');
     96    return $statuses;
     97}
     98
     99add_action('woocommerce_order_actions_end', 'beyond_pay_add_process_order_button');
     100
     101function beyond_pay_add_process_order_button($order_id){
     102    $order = wc_get_order($order_id);
     103   
     104    if($order->get_meta('_beyond_pay_tokenized')){
     105    ?>
     106    <li class="wide">
     107        <button type="button" class="button" onclick="beyondPayProcessTokenizedOrder('<?php echo esc_url(get_edit_post_link( $order_id ) ); ?>',<?php echo $order_id ?>)">
     108        Process Payment
     109        </a>
     110    </li>
     111    <?php }
     112}
     113
     114add_action('wp_ajax_beyond_pay_process_tokenized_order', 'beyond_pay_handle_saved_card_processing');
     115
     116function beyond_pay_handle_saved_card_processing(){
     117    $order_id = intval($_POST['order_id']);
     118    if($order_id){
     119    $beyond_pay_gateway = wc_get_payment_gateway_by_order($order_id);
     120    if($beyond_pay_gateway){
     121        $result = $beyond_pay_gateway->process_tokenized_payment($order_id);
     122        die(json_encode($result));
     123    }
     124    }
     125    die(json_encode(array(
     126    'success' => false,
     127    'message' => 'Unable to deterimine order.'
     128    )));
     129}
     130
     131add_action('admin_enqueue_scripts', 'beyond_pay_enqueue_woocommerce_scripts');
     132
     133function beyond_pay_enqueue_woocommerce_scripts(){
     134   wp_enqueue_script('beyondpay_admin_order', plugins_url('assets/js/beyondpay-admin-order.js', __FILE__));
     135}
  • beyond-pay-for-woocommerce/trunk/includes/wc-beyond-pay-gateway.php

    r2513950 r2552573  
    33class WC_Beyond_Pay_Gateway extends WC_Payment_Gateway {
    44
    5     /**
    6      * Class constructor, more about it in Step 3
    7      */
    85    public function __construct() {
    96
     
    5855        $this->get_option('test_password') :
    5956        $this->get_option('password');
    60     $this->transaction_mode = $this->get_option('transaction_mode') == "sale" ? "sale" : "sale-auth";
     57    $this->transaction_mode =
     58        $this->get_option('transaction_mode') == "sale" ? "sale" :
     59        $this->get_option('transaction_mode') == "authorization" ? "sale-auth" :
     60        'tokenize_only';
    6161    $this->merchant_code = $this->get_option('merchant_code');
    6262    $this->merchant_account_code = $this->get_option('merchant_account_code');
     
    161161        'options' => [
    162162            'sale' => 'Sale',
    163             'authorization' => 'Authorization'
     163            'authorization' => 'Authorization',
     164            'tokenize_only' => 'Save Card ONLY'
    164165        ],
    165         'description' => 'Sale mode will capture the payment instantly, '
    166         . 'authorization will only authorize when order is placed and capture'
    167         . ' once order status changes to completed.',
     166        'description' =>
     167            '<ul>'
     168            . '<li>Sale mode will capture the payment instantly;</li> '
     169            . '<li>Authorization will only authorize when order is placed and capture once order status changes to completed;</li>'
     170            . '<li>Save Card ONLY allows you to securely store card numbers without any initial authorization and then later charge the card from the Order Details page. NOTE: You must select “Process Payment” on the Order Details page in order to get paid in Save Card Only mode.</li>'
     171            . '</ul>',
    168172        ),
    169173        'additional_data' => array(
     
    253257        if($this->cart_has_subscription()){
    254258        echo "<p>Your payment method will be saved to process subscription payments.</p>";
     259        } else if($this->transaction_mode == 'tokenize_only') {
     260        echo "<p>Your payment method will be securely saved and charged when your order is completed.</p>";
    255261        } else {
    256262        $this->save_payment_method_checkbox();
     
    307313    return true;
    308314    }
     315    /**
     316     * @param WC_Order $order
     317     * @return boolean
     318     */
     319    public function can_refund_order( $order ) {
     320    return $order && !empty($order->get_transaction_id()) && empty($order->get_meta('_beyond_pay_tokenized'));
     321    }
    309322   
    310323    public function process_refund($order_id, $amount = null, $reason = '') {
    311324    $order = wc_get_order($order_id);
     325    if($order->get_meta('_beyond_pay_tokenized')){
     326        $order->add_order_note("Order refund failed: this order was processed with 'Save Card ONLY' transaction mode and the payment was not processed, no refund should be needed.");
     327        return false;
     328    }
    312329    $request = $this->build_payment_request(
    313330        'refund',
     
    316333        null
    317334    );
    318     $conn = new BeyondPay\BeyondPayConnection();
    319     $response = $conn->processRequest($this->api_url, $request);
     335    $response = $this->send_gateway_request($request, $order);
    320336
    321337    if ($response->ResponseCode == '00000') {
     338        if($reason){
     339        $order->add_order_note("Order refunded, reason: $reason");
     340        }
    322341        return true;
    323342    } else {
     
    335354        intval($_POST['wc-beyondpay-payment-token']) :
    336355        false;
    337     $save_token = !empty($_POST['wc-beyondpay-new-payment-method']) && $_POST['wc-beyondpay-new-payment-method'] === 'true';
     356    $is_tokenize_only = $this->transaction_mode === "tokenize_only";
     357    $save_token =
     358        $is_tokenize_only ||
     359        (!empty($_POST['wc-beyondpay-new-payment-method']) && $_POST['wc-beyondpay-new-payment-method'] === 'true');
    338360    $change_payment_for_order = !empty($_POST['woocommerce_change_payment']) ?
    339361        intval($_POST['woocommerce_change_payment']) :
     
    351373        );
    352374
    353         $conn = new BeyondPay\BeyondPayConnection();
    354         $response = $conn->processRequest($this->api_url, $request);
     375        $response = $this->send_gateway_request($request);
    355376
    356377        if ($response->ResponseCode == '00000') {
     
    377398   
    378399    if($pay_with_token){
     400        /** @var WC_Payment_Token - stored token */
    379401        $token = $this->get_token($pay_with_token);
     402        if($is_tokenize_only) {
     403        $order->add_payment_token($token);
     404        return $this->tokenize_only_order($order);
     405        }
    380406        $request = $this->build_payment_request(
    381407        'token_payment',
     
    383409        $order->get_transaction_id(),
    384410        $token
     411        );
     412    } else if($is_tokenize_only){
     413        $request = $this->build_payment_request(
     414        'save_payment_method',
     415        null,
     416        null,
     417        sanitize_key($_POST['beyond_pay_token'])
    385418        );
    386419    } else {
     
    393426    }
    394427
    395     $address = $order->get_address('billing');
    396     if (!empty($address)) {
    397         $name = trim($address['first_name'] . ' ' . $address['last_name']);
    398         $request->requestMessage->AccountHolderName = $name;
    399         $request->requestMessage->AccountStreet = trim($address['address_1']);
    400         if (!empty($address['phone'])) {
    401         $address['phone'] = str_replace([' ', '-', '#', '+'], '', $address['phone']);
    402         while (strlen($address['phone']) < 10) {
    403             $address['phone'] = '0' . $address['phone'];
    404         }
    405         if (strlen($address['phone']) < 12) {
    406             $request->requestMessage->AccountPhone = trim($address['phone']);
    407         }
    408         }
    409         if (!empty($address['postcode'])) {
    410         $postcode = str_replace('-', '', $address['postcode']);
    411         if (is_numeric($postcode) && strlen($postcode) === 5) {
    412             $request->requestMessage->AccountZip = $postcode;
    413         }
    414         }
    415     }
     428    $this->fill_address_data($request, $order);
    416429
    417430    $customer_id = $order->get_user_id();
     
    420433    }
    421434    $request->requestMessage->InvoiceNum = $order_id;
    422     $this->fill_level_2_3_data($request, $order);
    423    
    424     $conn = new BeyondPay\BeyondPayConnection();
    425     $response = $conn->processRequest($this->api_url, $request);
    426     $this->verbose_logging($order, $request, $response);
     435    if(!$is_tokenize_only){
     436        $this->fill_level_2_3_data($request, $order);
     437    }
     438    $response = $this->send_gateway_request($request, $order);
    427439
    428440    if ($response->ResponseCode == '00000') {
     
    432444        $save_token
    433445        ) {
    434         $this->save_token_from_response(
     446        $token = $this->save_token_from_response(
    435447            $response,
    436448            $save_token || $this->connect_subscription_payments_with_users ? $order->get_user_id() : null,
    437449            $order
    438450        );
     451        if($is_tokenize_only) {
     452            return $this->tokenize_only_order($order, $token);
     453        }
    439454        } elseif ($pay_with_token) {
    440         if (!empty($response->responseMessage->CardType) && $token->get_card_type() === 'Card'){
    441             $token->set_card_type($response->responseMessage->CardType);
    442             $token->save();
    443         }
     455        $this->update_card_type_on_token($response, $token);
    444456        $order->add_payment_token($token);
    445457        }
     
    475487     * @param BeyondPay\BeyondPayResponse $response
    476488     */
    477     private function verbose_logging($order, $request, $response){
     489    private function verbose_logging($order, $request, $response) {
    478490    if($this->debug_mode) {
    479491        $is_successfull = $response->ResponseCode == '00000';
     
    529541        }
    530542        $token->save();
    531     } elseif (!empty($response->responseMessage->CardType) && $token->get_card_type()==='Card'){
     543    } else {
     544        $this->update_card_type_on_token($response, $token);
     545    }
     546   
     547    if(!empty($order)){
     548        $order->add_payment_token($token);
     549    }
     550   
     551    return $token;
     552    }
     553   
     554    /**
     555     * Updates the card type if token has it missing and more accurate value is provided in response.
     556     * @param BeyondPay\BeyondPayResponse $response
     557     * @param WC_Payment_Token $token
     558     */
     559    private function update_card_type_on_token($response, $token) {
     560    if (!empty($response->responseMessage->CardType) && ($token->get_card_type() === 'Card' || empty($token->get_card_type()))) {
    532561        $token->set_card_type($response->responseMessage->CardType);
    533562        $token->save();
    534563    }
    535    
    536     if(!empty($order)){
    537         $order->add_payment_token($token);
    538     }
    539    
    540     return $token;
    541564    }
    542565   
     
    636659    }
    637660    }
     661   
     662    /**
     663     * Update request with address data if available on order.
     664     * @param BeyondPay\BeyondPayRequest $request The request to update
     665     * @param WC_Order $order Order to fetch data from
     666     */
     667    private function fill_address_data($request, $order){
     668    $address = $order->get_address('billing');
     669    if (!empty($address)) {
     670        $name = trim($address['first_name'] . ' ' . $address['last_name']);
     671        $request->requestMessage->AccountHolderName = $name;
     672        $request->requestMessage->AccountStreet = trim($address['address_1']);
     673        if (!empty($address['phone'])) {
     674        $address['phone'] = str_replace([' ', '-', '#', '+'], '', $address['phone']);
     675        while (strlen($address['phone']) < 10) {
     676            $address['phone'] = '0' . $address['phone'];
     677        }
     678        if (strlen($address['phone']) < 12) {
     679            $request->requestMessage->AccountPhone = trim($address['phone']);
     680        }
     681        }
     682        if (!empty($address['postcode'])) {
     683        $postcode = str_replace('-', '', $address['postcode']);
     684        if (is_numeric($postcode) && strlen($postcode) === 5) {
     685            $request->requestMessage->AccountZip = $postcode;
     686        }
     687        }
     688    }
     689    }
    638690
    639691
     
    671723        continue;
    672724        } else {
    673         try{
     725        try {
    674726            $token = new WC_Payment_Token_CC($tokens[0]);
    675727        } catch(exception $e) {
     
    685737        );
    686738        $this->fill_level_2_3_data($request, $order);
    687 
    688         $conn = new BeyondPay\BeyondPayConnection();
    689         $response = $conn->processRequest($this->api_url, $request);
    690         $this->verbose_logging($order, $request, $response);
     739        $response = $this->send_gateway_request($request, $order);
    691740
    692741        if ($response->ResponseCode == '00000') {
     
    720769    public function capture_authorised_payment($order){
    721770    $request = $this->build_payment_request('capture', $order->get_total(), $order->get_transaction_id());
    722 
    723     $conn = new BeyondPay\BeyondPayConnection();
    724     $response = $conn->processRequest($this->api_url, $request);
     771    $response = $this->send_gateway_request($request, $order);
    725772    if ($response->ResponseCode == '00000') {
    726773        $order->update_meta_data('_beyond_pay_processed', 1);
     
    745792     */
    746793    public function build_payment_request($payment_type, $amount_to_charge = null, $reference_number = null, $token = null){
     794    $transaction_type = $this->transaction_mode === 'tokenize_only' ? 'sale' : $this->transaction_mode;
    747795    $configs = array(
    748796        'capture' => array(
     
    754802        'request_type' => '004',
    755803        'auth_with_token' => true,
    756         'transaction_type' => $this->transaction_mode
     804        'transaction_type' => $transaction_type
    757805        ),
    758806        'save_payment_method' => array(
     
    769817        'request_type' => '004',
    770818        'auth_with_token' => false,
    771         'transaction_type' => $this->transaction_mode
     819        'transaction_type' => $transaction_type
    772820        ),
    773821        'refund' => array(
     
    816864    }
    817865    $request->requestMessage->SoftwareVendor = 'WooCommerce Beyond Pay Plugin';
    818     file_put_contents('beyond.log','Transaction type: '.$config['transaction_type']."\n", FILE_APPEND);
    819866    if($config['transaction_type']){
    820867        $request->requestMessage->TransactionType = $config['transaction_type'];
     
    822869   
    823870    $request->requestMessage->TransIndustryType = "EC";
    824     file_put_contents('beyond.log','Transaction type2: '.$config['transaction_type']."\n", FILE_APPEND);
    825871    return $request;
     872    }
     873   
     874    /**
     875     *
     876     * @param BeyondPay\BeyondPayRequest $request
     877     * @param WC_Order $order Optional, if not provided Debug Logging will not be performed for this request.
     878     * @return BeyondPay\BeyondPayResponse
     879     */
     880    public function send_gateway_request($request, $order = null){
     881    $conn = new BeyondPay\BeyondPayConnection();
     882    $response = $conn->processRequest($this->api_url, $request);
     883    if($order){
     884        $this->verbose_logging($order, $request, $response);
     885    }
     886    return $response;
    826887    }
    827888   
     
    844905    );
    845906   
    846     $conn = new BeyondPay\BeyondPayConnection();
    847     $response = $conn->processRequest($this->api_url, $request);
     907    $response = $this->send_gateway_request($request);
    848908
    849909    if ($response->ResponseCode == '00000') {
     
    887947    }
    888948
     949    /**
     950     * Store the token for the "Save Card ONLY" transaction mode.
     951     * @param WC_Order $order
     952     */
     953    public function tokenize_only_order($order) {
     954    global $woocommerce;
     955   
     956    if(empty($order->get_payment_tokens())){
     957        return array(
     958        'result' => 'failure',
     959        'redirect' => $order->get_view_order_url()
     960        );
     961    }
     962   
     963    $order->add_meta_data('_beyond_pay_tokenized', 1);
     964    $order->set_status('bp-tokenized');
     965    $order->payment_complete();
     966    $order->save_meta_data();
     967    $order->save();
     968    wc_reduce_stock_levels($order);
     969    $order->add_order_note('Thank you for your order!', true);
     970    $woocommerce->cart->empty_cart();
     971
     972    return array(
     973        'result' => 'success',
     974        'redirect' => $this->get_return_url($order)
     975    );
     976    }
     977   
     978    /**
     979     * The order processing after merchant presses the "Process Order" button for a saved card.
     980     * @param int $order_id
     981     * @return array An array with success:bool and optional message:string property.
     982     */
     983    public function process_tokenized_payment($order_id) {
     984    $order = wc_get_order($order_id);
     985    $tokens = $order->get_payment_tokens();
     986    if(!empty($tokens)) {
     987        try{
     988        $token = new WC_Payment_Token_CC($tokens[0]);
     989        } catch(Exception $error) {
     990        $order->add_order_note('Failed to process payment, payment method connected to order no longer exists or is not valid. Internal error: '.$error->getMessage());
     991        return array(
     992            'success' => false,
     993            'message' => 'Payment method connected to order no longer exists or is not valid.'
     994        );
     995        }
     996        $request = $this->build_payment_request('token_payment', $order->get_total(), $order->get_transaction_id(), $token);
     997        $customer_id = $order->get_user_id();
     998        if (!empty($customer_id)) {
     999        $request->requestMessage->CustomerAccountCode = $customer_id;
     1000        }
     1001        $request->requestMessage->InvoiceNum = $order_id;
     1002        $this->fill_level_2_3_data($request, $order);
     1003        $response = $this->send_gateway_request($request, $order);
     1004        if ($response->ResponseCode == '00000') {
     1005        $this->update_card_type_on_token($response, $token);
     1006        $order->add_payment_token($token);
     1007        $order->delete_meta_data('_beyond_pay_tokenized');
     1008        $order->save_meta_data();
     1009        $order->add_order_note('Your card has been charged for order #'.$order_id, true);
     1010        $order->set_status('processing');
     1011        $order->set_transaction_id($response->responseMessage->GatewayTransID);
     1012        $order->save();
     1013        $order->payment_complete($response->responseMessage->GatewayTransID);
     1014        return array(
     1015            'success' => true
     1016        );
     1017        } else {
     1018        $reason = $response->ResponseDescription.' ('.$response->ResponseCode.')';
     1019        $order->add_order_note('Processing saved card has failed due to: '.$reason);
     1020        return array(
     1021            'success' => false,
     1022            'message' => $reason
     1023        );
     1024        }
     1025    }
     1026    }
     1027
    8891028}
Note: See TracChangeset for help on using the changeset viewer.