Plugin Directory

Changeset 2790425


Ignore:
Timestamp:
09/26/2022 05:35:24 PM (3 years ago)
Author:
rixeo
Message:

Code fixes and new version

Location:
thebunch-ke-pesapal-woocommerce/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • thebunch-ke-pesapal-woocommerce/trunk/lib/OAuth.php

    r2224659 r2790425  
    11<?php
     2/**
     3 * Gateway helpers.
     4 *
     5 * @package TheBunchKEPesaPal
     6 */
     7
     8if ( ! defined( 'ABSPATH' ) ) {
     9    exit; // Exit if accessed directly.
     10}
    211
    312/*
  • thebunch-ke-pesapal-woocommerce/trunk/lib/gateway.php

    r2725075 r2790425  
    11<?php
     2/**
     3 * Gateway class.
     4 *
     5 * @package TheBunchKEPesaPal
     6 */
     7
    28if ( ! defined( 'ABSPATH' ) ) {
    3     exit; // Exit if accessed directly
     9    exit; // Exit if accessed directly.
    410}
    511
    612if ( class_exists( 'WC_Payment_Gateway' ) ) {
    7     if ( !class_exists( 'WC_TheBunchKE_PesaPal_Pay_Gateway' ) ) {
    8        
    9         class WC_TheBunchKE_PesaPal_Pay_Gateway extends WC_Payment_Gateway {
    10            
    11             function __construct(){
    12            
    13                 //Settings
    14                 $this->id           = 'pesapal';
    15                 $this->method_title = 'Pesapal';
    16                 $this->has_fields   = false;
    17                 $this->testmode     = ( $this->get_option('testmode') === 'yes' ) ? true : false;
    18                 $this->debug        = $this->get_option( 'debug' );
    19                 $this->title        = $this->get_option('title');
    20                 $this->description  = $this->get_option('description');
    21                 $this->use_cron     = ( $this->get_option('use_cron') === 'yes' );
    22                 $default_prefix     = substr( str_shuffle( str_repeat( "ABCDEFGHJKMNPQRSTUVWXYZ", 4 ) ), 0, 4 );
    23                 $order_prefix       = $this->get_option( 'order_prefix', $default_prefix );
    24                
    25                 $this->order_prefix = strtoupper( str_replace( "-","", str_replace( " ", "", $order_prefix ) ) );
    26                
    27                 //Set up logging
    28                 $this->log = wc_get_logger();
    29                
    30                 //Set up API details
    31                 if( $this->testmode ) {
    32                     $api = 'https://demo.pesapal.com/';
    33                     $this->consumer_key     = $this->get_option('testconsumerkey');
    34                     $this->consumer_secret  = $this->get_option('testsecretkey');
    35                 } else {
    36                     $api  = 'https://www.pesapal.com/';
    37                     $this->consumer_key     = $this->get_option('consumerkey');
    38                     $this->consumer_secret  = $this->get_option('secretkey');
    39                 }
    40                
    41                 //OAuth Signatures
    42                 $this->consumer             = new PesaPalOAuthConsumer($this->consumer_key, $this->consumer_secret);
    43                 $this->signature_method     = new PesaPalOAuthSignatureMethod_HMAC_SHA1();
    44                 $this->token                = $this->params = NULL;
    45                
    46                 //PesaPal End Points
    47                 $this->gatewayURL                       = $api.'api/PostPesapalDirectOrderV4';
    48                 $this->QueryPaymentStatus               = $api.'API/QueryPaymentStatus';
    49                 $this->QueryPaymentStatusByMerchantRef  = $api.'API/QueryPaymentStatusByMerchantRef';
    50                 $this->querypaymentdetails              = $api.'API/querypaymentdetails';
    51                
    52                 //IPN URL
    53                 $this->notify_url                       = add_query_arg( 'wc-api', strtolower( get_class( $this ) ), home_url( '/' ) );
    54                 $this->rest_ipn_url                     = rest_url( 'thebunchke-pespal/v1/ipn-listener' );
    55                
    56                 $this->init_form_fields();
    57                 $this->init_settings();
    58                
    59                 if ( is_admin() ) {
    60                     add_action('woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
    61                 }
    62                
    63                 add_action('woocommerce_receipt_'.$this->id, array( $this, 'payment_page' ) );
    64                
    65                 add_action( 'woocommerce_api_callback', array( $this, 'ipn_response' ) );
    66                
     13    if ( ! class_exists( 'WC_TheBunchKE_PesaPal_Pay_Gateway' ) ) {
     14
     15        /**
     16         * Main gateway class.
     17         */
     18        class WC_TheBunchKE_PesaPal_Pay_Gateway extends WC_Payment_Gateway {
     19
     20            /**
     21             * If logging is enabled.
     22             *
     23             * @var bool
     24             */
     25            public static bool $log_enabled = false;
     26
     27            /**
     28             * Logger instance.
     29             *
     30             * @var bool|WC_Logger
     31             */
     32            public static $log = false;
     33
     34            /**
     35             * Main constructor.
     36             */
     37            public function __construct() {
     38
     39                // Settings.
     40                $this->id           = 'pesapal';
     41                $this->method_title = 'Pesapal';
     42                $this->has_fields   = false;
     43                $this->testmode     = ( $this->get_option( 'testmode' ) === 'yes' ) ? true : false;
     44                $this->debug        = 'yes' === $this->get_option( 'debug', 'no' );
     45                $this->title        = $this->get_option( 'title' );
     46                $this->description  = $this->get_option( 'description' );
     47                $this->use_cron     = ( $this->get_option( 'use_cron' ) === 'yes' );
     48                $default_prefix     = substr( str_shuffle( str_repeat( 'ABCDEFGHJKMNPQRSTUVWXYZ', 4 ) ), 0, 4 );
     49                $order_prefix       = $this->get_option( 'order_prefix', $default_prefix );
     50
     51                $this->order_prefix = strtoupper( str_replace( '-', '', str_replace( ' ', '', $order_prefix ) ) );
     52                self::$log_enabled  = $this->debug;
     53
     54
     55                // Set up API details.
     56                if ( $this->testmode ) {
     57                    $api                   = 'https://demo.pesapal.com/';
     58                    $this->consumer_key    = $this->get_option( 'testconsumerkey' );
     59                    $this->consumer_secret = $this->get_option( 'testsecretkey' );
     60                } else {
     61                    $api                   = 'https://www.pesapal.com/';
     62                    $this->consumer_key    = $this->get_option( 'consumerkey' );
     63                    $this->consumer_secret = $this->get_option( 'secretkey' );
     64                }
     65
     66                // OAuth Signatures.
     67                $this->consumer         = new PesaPalOAuthConsumer( $this->consumer_key, $this->consumer_secret );
     68                $this->signature_method = new PesaPalOAuthSignatureMethod_HMAC_SHA1();
     69                $this->token            = null;
     70                $this->params           = null;
     71
     72                // PesaPal End Points.
     73                $this->gateway_url                          = $api . 'api/PostPesapalDirectOrderV4';
     74                $this->query_payment_status                 = $api . 'API/QueryPaymentStatus';
     75                $this->query_payment_status_by_merchant_ref = $api . 'API/QueryPaymentStatusByMerchantRef';
     76                $this->query_payment_details                = $api . 'API/querypaymentdetails';
     77
     78                // IPN URL.
     79                $this->notify_url   = add_query_arg( 'wc-api', strtolower( get_class( $this ) ), home_url( '/' ) );
     80                $this->rest_ipn_url = rest_url( 'thebunchke-pespal/v1/ipn-listener' );
     81
     82                $this->init_form_fields();
     83                $this->init_settings();
     84
     85                if ( is_admin() ) {
     86                    add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
     87                }
     88
     89                add_action( 'woocommerce_receipt_' . $this->id, array( $this, 'payment_page' ) );
     90
     91                add_action( 'woocommerce_api_callback', array( $this, 'ipn_response' ) );
     92
    6793                add_action( 'woocommerce_api_wc_gateway_' . $this->id, array( $this, 'ipn_response' ) );
    68                
    69                 add_action('thebunchke_pesapal_woo_bckground_cron', array( $this, 'check_order_status' ) );
    70                
    71             }
    72            
    73            
    74             /**
    75              * Handle logging
    76              *
    77              * @since 1.0.0
    78              *
    79              * @param string $message
    80              */
    81             function pesapal_do_log( $message ) {
    82                 if ( 'yes' == $this->debug ) {
    83                     $this->log->debug( $message, array( 'source' => $this->id ) );
    84                 }
    85             }
    86            
    87            
    88             function init_form_fields() {
    89                 $this->form_fields = array(
    90                     'enabled' => array(
    91                         'title' => __( 'Enable/Disable', 'woothemes' ),
    92                         'type' => 'checkbox',
    93                         'label' => __( 'Enable Pesapal Payment', 'woothemes' ),
    94                         'default' => 'no'
    95                     ),
    96                     'title' => array(
    97                         'title' => __( 'Title', 'woothemes' ),
    98                         'type' => 'text',
    99                         'description' => __( 'This controls the title which the user sees during checkout.', 'woothemes' ),
    100                         'default' => __( 'Pesapal Payment', 'woothemes' )
    101                     ),
    102                     'order_prefix' => array(
    103                         'title'         => __( 'Order Prefix', 'woothemes' ),
    104                         'type'          => 'text',
    105                         'description'   => __( 'This is the prefix appended to all order to ensure you do not have duplicate pesapal merchant references generated by other systems connected to your Pesapal account.', 'woothemes' ),
    106                         'default'       => $this->order_prefix
    107                     ),
    108                     'description' => array(
    109                         'title' => __( 'Description', 'woocommerce' ),
    110                         'type' => 'textarea',
    111                         'description' => __( 'This is the description which the user sees during checkout.', 'woocommerce' ),
    112                         'default' => __("Payment via Pesapal Gateway, you can pay by either credit/debit card or use mobile payment option such as Mpesa.", 'woocommerce')
    113                     ),
    114                     'testmode' => array(
    115                         'title' => __( 'Use Demo Gateway', 'woothemes' ),
    116                         'type' => 'checkbox',
    117                         'label' => __( 'Use Demo Gateway', 'woothemes' ),
    118                         'description' => __( 'Use demo pesapal gateway for testing from your account at <a href="https://demo.pesapal.com">https://demo.pesapal.com</a>', 'woothemes' ),
    119                         'default' => 'no'
    120                     ),
    121                     'consumerkey' => array(
    122                         'title' => __( 'Pesapal Consumer Key', 'woothemes' ),
    123                         'type' => 'text',
    124                         'description' => __( 'Your Pesapal consumer key which should have been emailed to you.', 'woothemes' ),
    125                         'default' => ''
    126                     ),
    127                     'secretkey' => array(
    128                         'title' => __( 'Pesapal Secret Key', 'woothemes' ),
    129                         'type' => 'text',
    130                         'description' => __( 'Your Pesapal secret key which should have been emailed to you.', 'woothemes' ),
    131                         'default' => ''
    132                     ),
    133                    
    134                     'testconsumerkey' => array(
    135                         'title' => __( 'Pesapal Demo Consumer Key', 'woothemes' ),
    136                         'type' => 'text',
    137                         'description' => __( 'Your demo Pesapal consumer key which can be seen at demo.pesapal.com.', 'woothemes' ),
    138                         'default' => ''
    139                     ),
    140                    
    141                     'testsecretkey' => array(
    142                         'title' => __( 'Pesapal Demo Secret Key', 'woothemes' ),
    143                         'type' => 'text',
    144                         'description' => __( 'Your demo Pesapal secret key which can be seen at demo.pesapal.com.', 'woothemes' ),
    145                         'default' => ''
    146                     ),
    147                    
    148                     'debug' => array(
    149                         'title' => __( 'Debug Log', 'woocommerce' ),
    150                         'type' => 'checkbox',
    151                         'label' => __( 'Enable logging', 'woocommerce' ),
    152                         'default' => 'no',
    153                         'description' => sprintf( __( 'Log PesaPal events, such as IPN requests, inside <code>woocommerce/logs/pesapal-%s.txt</code>', 'woocommerce' ), sanitize_file_name( wp_hash( 'pesapal' ) ) ),
    154                     ),
    155 
    156                     'use_cron' => array(
    157                         'title'          => __( 'Use Background Cron', 'woocommerce' ),
    158                         'type'          => 'checkbox',
    159                         'label'         => __( 'Use Background Cron', 'woocommerce' ),
    160                         'default'       => 'no',
    161                         'description'   => __( 'In case IPN has issues, you can add a cron job on your server that will be a back-up process to ensure transactions get completed. Enable this to set the cron to run every 5 mins. However, its advisable to ensure IPN is up and running', 'woocommerce' ),
    162                     )
    163                 );
    164             }
    165            
    166             public function admin_options() {
    167                 ?>
    168                 <h3><?php _e('Pesapal', 'woothemes'); ?></h3>
    169                 <p>
    170                     <?php _e('PesaPal requires Full names and email/phone number. To handle IPN return requests, please set the url '); ?>
    171                     <strong><?php echo $this->notify_url; ?></strong><br/> <?php _e( 'or' ); ?>
    172                     <strong><?php echo $this->rest_ipn_url; ?></strong>
    173                    
    174                     <?php _e(' in your <a href="https://www.pesapal.com/merchantdashboard" target="_blank">PesaPal</a> account settings'); ?>
    175                 </p>
    176                 <table class="form-table">
    177                     <?php $this->generate_settings_html(); ?>
    178                 </table>
    179                 <script type="text/javascript">
    180                 jQuery(function(){
    181                     var testMode = jQuery("#woocommerce_pesapal_testmode");
    182                     var live_consumer = jQuery("#woocommerce_pesapal_consumerkey");
    183                     var live_secrect = jQuery("#woocommerce_pesapal_secretkey");
    184                     var test_consumer = jQuery("#woocommerce_pesapal_testconsumerkey");
    185                     var test_secrect = jQuery("#woocommerce_pesapal_testsecretkey");
    186                     if (testMode.is(":not(:checked)")){
    187                         test_consumer.parents("tr").hide();
    188                         test_secrect.parents("tr").hide();
    189                        
    190                         live_consumer.parents("tr").show();
    191                         live_secrect.parents("tr").show();
    192                     }
    193                     testMode.click(function(){           
    194                         // If checked
    195                         if (testMode.is(":checked")) {
    196                             //show the hidden div
    197                             test_consumer.parents("tr").show("fast");
    198                             test_secrect.parents("tr").show("fast");
    199                            
    200                             live_consumer.parents("tr").hide("fast");
    201                             live_secrect.parents("tr").hide("fast");
    202                         } else {
    203                             //otherwise, hide it
    204                             test_consumer.parents("tr").hide("fast");
    205                             test_secrect.parents("tr").hide("fast");
    206                            
    207                             live_consumer.parents("tr").show("fast");
    208                             live_secrect.parents("tr").show("fast");
    209                         }
    210                     });
    211                 });
    212                 </script>
    213                 <?php
    214             }
    215            
    216            
    217             function process_payment( $order_id ) {
    218                 global $woocommerce;
    219            
    220                 $order = wc_get_order( $order_id );
    221                
    222                 if($order->get_status() === 'completed'){
    223                     //Redirect to payment page
    224                     return array(
    225                         'result'    => 'success',
    226                         'redirect'  => $this->get_return_url( $order )
    227                     );
    228                 }else{
    229                     return array(
    230                         'result'    => 'success',
    231                         'redirect'  => $order->get_checkout_payment_url(true)
    232                     );
    233                 }
    234             }
    235            
    236            
    237             //Create Payment Page
    238             function payment_page($order_id){
    239                 if(isset($_REQUEST['pesapal_merchant_reference'])){
    240                     $order = wc_get_order( $order_id );
    241                     $pesapalMerchantReference = $_REQUEST['pesapal_merchant_reference'];
    242                     $pesapalTrackingId = $_REQUEST['pesapal_transaction_tracking_id'];
    243                    
    244                     $transactionDetails = $this->getTransactionDetails($pesapalMerchantReference,$pesapalTrackingId);
    245                    
    246                     add_post_meta( $order_id, '_order_pesapal_transaction_tracking_id', $transactionDetails['pesapal_transaction_tracking_id']);
    247                     add_post_meta( $order_id, '_order_thebunchke_pesapalment_method', $transactionDetails['payment_method']);
    248                    
    249                     add_post_meta( $order_id, '_order_payment_method', $transactionDetails['payment_method']);
    250                    
    251                     if($transactionDetails['status'] === 'COMPLETED'){
    252                         $order->payment_complete();
    253                     }else{
    254                         $order->update_status('wc-processing', 'Payment accepted, awaiting confirmation');
    255                     }
    256                     return array(
    257                         'result'   => 'success',
    258                         'redirect' => $this->get_return_url( $order )
    259                     );
    260                    
    261                 }else{
    262                     $url = $this->create_url($order_id);
    263                     ?>
    264                     <div class="pesapal_container" style="position:relative;">
    265                         <img class="pesapal_loading_preloader" src="<?php echo THEBUNCHKE_PESAPAL_WOO_PLUGIN_URL; ?>/assets/img/loader.gif" alt="loading" style="position:absolute;"/>
    266                         <iframe class="pesapal_loading_frame" src="<?php echo $url; ?>" width="100%" height="700px"  scrolling="yes" frameBorder="0">
    267                             <p><?php _e('Browser unable to load iFrame', 'woothemes'); ?></p>
    268                         </iframe>
    269                     </div>
    270                     <script>
    271                         jQuery(function(){
    272                             jQuery('.pesapal_loading_frame').on('load', function () {
    273                                 jQuery('.pesapal_loading_preloader').hide();
    274                             });
    275                         });
    276                     </script>
    277                     <?php
    278                 }
    279             }
    280            
    281             function thankyou_page($order_id) {
    282 
    283                 if(isset($_REQUEST['pesapal_transaction_tracking_id'])){
    284                     $order = wc_get_order( $order_id );
    285                     $pesapalMerchantReference = $_REQUEST['pesapal_merchant_reference'];
    286                     $pesapalTrackingId = $_REQUEST['pesapal_transaction_tracking_id'];
    287                    
    288                     $transactionDetails = $this->getTransactionDetails($pesapalMerchantReference,$pesapalTrackingId);
    289                    
    290                     $order->add_order_note( __('Payment accepted, awaiting confirmation.', 'woothemes') );
    291                     add_post_meta( $order_id, '_order_pesapal_transaction_tracking_id', $transactionDetails['pesapal_transaction_tracking_id']);
    292                     add_post_meta( $order_id, '_order_thebunchke_pesapalment_method', $transactionDetails['payment_method']);
    293                    
    294                     add_post_meta( $order_id, '_order_payment_method', $transactionDetails['payment_method']);
    295                    
    296                     if($transactionDetails['status'] === 'COMPLETED'){
    297                         $order->payment_complete();
    298                     }else{
    299                         $order->update_status('wc-processing', 'Payment accepted, awaiting confirmation');
    300                     }
    301                 }
    302                 WC()->cart->empty_cart();
    303             }
    304            
    305            
    306             /**
    307              * Create iframe URL
    308              *
    309              */
    310             function create_url($order_id){
    311                 $order = wc_get_order( $order_id );
    312                 $order_xml = $this->pesapal_xml($order,$order_id);
    313                 $callback_url = $this->get_return_url( $order ); //add_query_arg('key', $order->order_key, add_query_arg('order', $order_id, $this->get_return_url( $order )));
    314                 //$callback_url = add_query_arg('key', $order->order_key, add_query_arg('order', $order_id, $order->get_checkout_order_received_url()));
    315                
    316                 $url = PesaPalOAuthRequest::from_consumer_and_token($this->consumer, $this->token, "GET", $this->gatewayURL, $this->params);
    317                 $url->set_parameter("oauth_callback", $callback_url);
    318                 $url->set_parameter("pesapal_request_data", $order_xml);
    319                 $url->sign_request($this->signature_method, $this->consumer, $this->token);
    320                 return $url;
    321             }
    322            
    323             /**
    324              * Generate PesaPal XML
    325              */
    326             function pesapal_xml($order,$order_id) {
    327                 $pesapal_args['total']      = $order->get_total();
    328                 $pesapal_args['reference']  = $this->order_prefix . '-' .$order_id;
    329                 $pesapal_args['first_name'] = $order->get_billing_first_name();
    330                 $pesapal_args['last_name']  = $order->get_billing_last_name();
    331                 $pesapal_args['email']      = $order->get_billing_email();
    332                 $pesapal_args['phone']      = preg_replace( "/[^0-9]/", "", str_replace( ' ', '', $order->get_billing_phone() ) );
    333              
    334                 $description                = sprintf( __( 'Order at %s, from %s'), bloginfo('name'), $order->billing_first_name . " " . $order->billing_email . " " . $order->billing_phone );
    335                 $lineitem                   = '';
     94
     95                add_action( 'thebunchke_pesapal_woo_bckground_cron', array( $this, 'check_order_status' ) );
     96
     97            }
     98
     99            /**
     100             * Logging method.
     101             *
     102             * @param string|array|object $message Log message.
     103             * @param string              $level Optional. Default 'info'. Possible values:
     104             *                      emergency|alert|critical|error|warning|notice|info|debug.
     105             */
     106            public static function log( $message, $level = 'info' ) {
     107                if ( self::$log_enabled ) {
     108                    if ( empty( self::$log ) ) {
     109                        self::$log = wc_get_logger();
     110                    }
     111                    if ( ! is_string( $message ) ) {
     112                        $message = print_r( $message, true );
     113                    }
     114                    self::$log->log( $level, $message, array( 'source' => 'pesapal' ) );
     115                }
     116            }
     117
     118            /**
     119             * Set up form fields.
     120             *
     121             * @return void
     122             */
     123            public function init_form_fields() {
     124                $this->form_fields = array(
     125                    'enabled'         => array(
     126                        'title'   => __( 'Enable/Disable', 'woothemes' ),
     127                        'type'    => 'checkbox',
     128                        'label'   => __( 'Enable Pesapal Payment', 'woothemes' ),
     129                        'default' => 'no',
     130                    ),
     131                    'title'           => array(
     132                        'title'       => __( 'Title', 'woothemes' ),
     133                        'type'        => 'text',
     134                        'description' => __( 'This controls the title which the user sees during checkout.', 'woothemes' ),
     135                        'default'     => __( 'Pesapal Payment', 'woothemes' ),
     136                    ),
     137                    'order_prefix'    => array(
     138                        'title'       => __( 'Order Prefix', 'woothemes' ),
     139                        'type'        => 'text',
     140                        'description' => __( 'This is the prefix appended to all order to ensure you do not have duplicate pesapal merchant references generated by other systems connected to your Pesapal account.', 'woothemes' ),
     141                        'default'     => $this->order_prefix,
     142                    ),
     143                    'description'     => array(
     144                        'title'       => __( 'Description', 'woocommerce' ),
     145                        'type'        => 'textarea',
     146                        'description' => __( 'This is the description which the user sees during checkout.', 'woocommerce' ),
     147                        'default'     => __( 'Payment via Pesapal Gateway, you can pay by either credit/debit card or use mobile payment option such as Mpesa.', 'woocommerce' ),
     148                    ),
     149                    'testmode'        => array(
     150                        'title'       => __( 'Use Demo Gateway', 'woothemes' ),
     151                        'type'        => 'checkbox',
     152                        'label'       => __( 'Use Demo Gateway', 'woothemes' ),
     153                        'description' => __( 'Use demo pesapal gateway for testing from your account at <a href="https://demo.pesapal.com">https://demo.pesapal.com</a>', 'woothemes' ),
     154                        'default'     => 'no',
     155                    ),
     156                    'consumerkey'     => array(
     157                        'title'       => __( 'Pesapal Consumer Key', 'woothemes' ),
     158                        'type'        => 'text',
     159                        'description' => __( 'Your Pesapal consumer key which should have been emailed to you.', 'woothemes' ),
     160                        'default'     => '',
     161                    ),
     162                    'secretkey'       => array(
     163                        'title'       => __( 'Pesapal Secret Key', 'woothemes' ),
     164                        'type'        => 'text',
     165                        'description' => __( 'Your Pesapal secret key which should have been emailed to you.', 'woothemes' ),
     166                        'default'     => '',
     167                    ),
     168
     169                    'testconsumerkey' => array(
     170                        'title'       => __( 'Pesapal Demo Consumer Key', 'woothemes' ),
     171                        'type'        => 'text',
     172                        'description' => __( 'Your demo Pesapal consumer key which can be seen at demo.pesapal.com.', 'woothemes' ),
     173                        'default'     => '',
     174                    ),
     175
     176                    'testsecretkey'   => array(
     177                        'title'       => __( 'Pesapal Demo Secret Key', 'woothemes' ),
     178                        'type'        => 'text',
     179                        'description' => __( 'Your demo Pesapal secret key which can be seen at demo.pesapal.com.', 'woothemes' ),
     180                        'default'     => '',
     181                    ),
     182
     183                    'debug'           => array(
     184                        'title'       => __( 'Debug Log', 'woocommerce' ),
     185                        'type'        => 'checkbox',
     186                        'label'       => __( 'Enable logging', 'woocommerce' ),
     187                        'default'     => 'no',
     188                        'description' => sprintf( __( 'Log PesaPal events, such as IPN requests, inside <code>woocommerce/logs/pesapal-%s.txt</code>', 'woocommerce' ), sanitize_file_name( wp_hash( 'pesapal' ) ) ),
     189                    ),
     190
     191                    'use_cron'        => array(
     192                        'title'       => __( 'Use Background Cron', 'woocommerce' ),
     193                        'type'        => 'checkbox',
     194                        'label'       => __( 'Use Background Cron', 'woocommerce' ),
     195                        'default'     => 'no',
     196                        'description' => __( 'In case IPN has issues, you can add a cron job on your server that will be a back-up process to ensure transactions get completed. Enable this to set the cron to run every 5 mins. However, its advisable to ensure IPN is up and running', 'woocommerce' ),
     197                    ),
     198                );
     199            }
     200
     201            /**
     202             * Admin options.
     203             *
     204             * @return void
     205             */
     206            public function admin_options() {
     207                ?>
     208                <h3><?php esc_html_e( 'Pesapal', 'woothemes' ); ?></h3>
     209                <p>
     210                    <?php esc_html_e( 'PesaPal requires Full names and email/phone number. To handle IPN return requests, please set the url ' ); ?>
     211                    <strong><?php echo esc_url( $this->notify_url ); ?></strong><br/> <?php esc_html_e( 'or' ); ?>
     212                    <strong><?php echo esc_url( $this->rest_ipn_url ); ?></strong>
     213                    <?php esc_html_e( ' in your <a href="https://www.pesapal.com/merchantdashboard" target="_blank">PesaPal</a> account settings' ); ?>
     214                </p>
     215                <table class="form-table">
     216                    <?php $this->generate_settings_html(); ?>
     217                </table>
     218                <script type="text/javascript">
     219                jQuery(function(){
     220                    var testMode = jQuery("#woocommerce_pesapal_testmode");
     221                    var live_consumer = jQuery("#woocommerce_pesapal_consumerkey");
     222                    var live_secrect = jQuery("#woocommerce_pesapal_secretkey");
     223                    var test_consumer = jQuery("#woocommerce_pesapal_testconsumerkey");
     224                    var test_secrect = jQuery("#woocommerce_pesapal_testsecretkey");
     225                    if (testMode.is(":not(:checked)")){
     226                        test_consumer.parents("tr").hide();
     227                        test_secrect.parents("tr").hide();
     228                        live_consumer.parents("tr").show();
     229                        live_secrect.parents("tr").show();
     230                    }
     231                    testMode.click(function(){           
     232                        // If checked
     233                        if (testMode.is(":checked")) {
     234                            //show the hidden div
     235                            test_consumer.parents("tr").show("fast");
     236                            test_secrect.parents("tr").show("fast");
     237                            live_consumer.parents("tr").hide("fast");
     238                            live_secrect.parents("tr").hide("fast");
     239                        } else {
     240                            //otherwise, hide it
     241                            test_consumer.parents("tr").hide("fast");
     242                            test_secrect.parents("tr").hide("fast");
     243                            live_consumer.parents("tr").show("fast");
     244                            live_secrect.parents("tr").show("fast");
     245                        }
     246                    });
     247                });
     248                </script>
     249                <?php
     250            }
     251
     252            /**
     253             * Process payment.
     254             *
     255             * @param int $order_id The order id.
     256             *
     257             * @return array
     258             */
     259            public function process_payment( $order_id ) {
     260                global $woocommerce;
     261
     262                $order = wc_get_order( $order_id );
     263
     264                if ( in_array( $order->get_status(), array( 'processing', 'completed' ) ) ) {
     265                    // Redirect to payment page
     266                    return array(
     267                        'result'   => 'success',
     268                        'redirect' => $this->get_return_url( $order ),
     269                    );
     270                } else {
     271                    return array(
     272                        'result'   => 'success',
     273                        'redirect' => $order->get_checkout_payment_url( true ),
     274                    );
     275                }
     276            }
     277
     278
     279            /**
     280             * Create the payment page.
     281             *
     282             * @param int $order_id The order id.
     283             *
     284             * @return array
     285             */
     286            public function payment_page( $order_id ) {
     287                if ( isset( $_REQUEST['pesapal_merchant_reference'] ) && isset( $_REQUEST['pesapal_transaction_tracking_id'] ) ) {
     288                    $order                           = wc_get_order( $order_id );
     289                    $pesapal_merchant_reference      = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_merchant_reference'] ) );
     290                    $pesapal_transaction_tracking_id = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_transaction_tracking_id'] ) );
     291
     292                    $transaction_details = $this->getTransactionDetails( $pesapal_merchant_reference, $pesapal_transaction_tracking_id );
     293
     294                    add_post_meta( $order_id, '_order_pesapal_transaction_tracking_id', $transaction_details['pesapal_transaction_tracking_id'] );
     295                    add_post_meta( $order_id, '_order_thebunchke_pesapalment_method', $transaction_details['payment_method'] );
     296
     297                    add_post_meta( $order_id, '_order_payment_method', $transaction_details['payment_method'] );
     298
     299                    if ( 'COMPLETED' === $transaction_details['status'] ) {
     300                        $order->payment_complete();
     301                    } else {
     302                        $order->update_status( 'wc-processing', 'Payment accepted, awaiting confirmation' );
     303                    }
     304                    return array(
     305                        'result'   => 'success',
     306                        'redirect' => $this->get_return_url( $order ),
     307                    );
     308
     309                } else {
     310                    $url = $this->create_url( $order_id );
     311                    ?>
     312                    <div class="pesapal_container" style="position:relative;">
     313                        <img class="pesapal_loading_preloader" src="<?php echo esc_url( THEBUNCHKE_PESAPAL_WOO_PLUGIN_URL ); ?>/assets/img/loader.gif" alt="loading" style="position:absolute;"/>
     314                        <iframe class="pesapal_loading_frame" src="<?php echo esc_url( $url ); ?>" width="100%" height="700px"  scrolling="yes" frameBorder="0">
     315                            <p><?php esc_html_e( 'Browser unable to load iFrame', 'woothemes' ); ?></p>
     316                        </iframe>
     317                    </div>
     318                    <script>
     319                        jQuery(function(){
     320                            jQuery('.pesapal_loading_frame').on('load', function () {
     321                                jQuery('.pesapal_loading_preloader').hide();
     322                            });
     323                        });
     324                    </script>
     325                    <?php
     326                }
     327            }
     328
     329            /**
     330             * Generate the thank you page.
     331             *
     332             * @param int $order_id The order id.
     333             *
     334             * @return void
     335             */
     336            public function thankyou_page( $order_id ) {
     337
     338                if ( isset( $_REQUEST['pesapal_merchant_reference'] ) && isset( $_REQUEST['pesapal_transaction_tracking_id'] ) ) {
     339                    $order                           = wc_get_order( $order_id );
     340                    $pesapal_merchant_reference      = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_merchant_reference'] ) );
     341                    $pesapal_transaction_tracking_id = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_transaction_tracking_id'] ) );
     342
     343                    $transaction_details = $this->getTransactionDetails( $pesapal_merchant_reference, $pesapal_transaction_tracking_id );
     344
     345                    $order->add_order_note( __( 'Payment accepted, awaiting confirmation.', 'woothemes' ) );
     346                    add_post_meta( $order_id, '_order_pesapal_transaction_tracking_id', $transaction_details['pesapal_transaction_tracking_id'] );
     347                    add_post_meta( $order_id, '_order_thebunchke_pesapalment_method', $transaction_details['payment_method'] );
     348
     349                    add_post_meta( $order_id, '_order_payment_method', $transaction_details['payment_method'] );
     350
     351                    if ( 'COMPLETED' === $transaction_details['status'] ) {
     352                        $order->payment_complete();
     353                    } else {
     354                        $order->update_status( 'wc-processing', 'Payment accepted, awaiting confirmation' );
     355                    }
     356                }
     357                WC()->cart->empty_cart();
     358            }
     359
     360
     361            /**
     362             * Create iframe URL.
     363             *
     364             * @param int $order_id The order id.
     365             *
     366             * @return string.
     367             */
     368            public function create_url( $order_id ) {
     369                $order        = wc_get_order( $order_id );
     370                $order_xml    = $this->pesapal_xml( $order, $order_id );
     371                $callback_url = $this->get_return_url( $order ); // add_query_arg('key', $order->order_key, add_query_arg('order', $order_id, $this->get_return_url( $order )));
     372                // $callback_url = add_query_arg('key', $order->order_key, add_query_arg('order', $order_id, $order->get_checkout_order_received_url()));
     373
     374                $url = PesaPalOAuthRequest::from_consumer_and_token( $this->consumer, $this->token, 'GET', $this->gateway_url, $this->params );
     375                $url->set_parameter( 'oauth_callback', $callback_url );
     376                $url->set_parameter( 'pesapal_request_data', $order_xml );
     377                $url->sign_request( $this->signature_method, $this->consumer, $this->token );
     378                return $url;
     379            }
     380
     381            /**
     382             * Generate PesaPal XML.
     383             *
     384             * @param WC_Order $order The current order.
     385             * @param int      $order_id The order id.
     386             *
     387             * @return string
     388             */
     389            public function pesapal_xml( $order, $order_id ) {
     390                $pesapal_args['total']      = $order->get_total();
     391                $pesapal_args['reference']  = $this->order_prefix . '-' . $order_id;
     392                $pesapal_args['first_name'] = $order->get_billing_first_name();
     393                $pesapal_args['last_name']  = $order->get_billing_last_name();
     394                $pesapal_args['email']      = $order->get_billing_email();
     395                $pesapal_args['phone']      = preg_replace( '/[^0-9]/', '', str_replace( ' ', '', $order->get_billing_phone() ) );
     396
     397                $description = sprintf( __( 'Order at %1$s, from %2$s' ), bloginfo( 'name' ), $order->billing_first_name . ' ' . $order->billing_email . ' ' . $order->billing_phone );
     398                $lineitem    = '';
    336399                foreach ( $order->get_items() as $item_key => $item_values ) {
    337                     $item_name      = $item_values->get_name();
    338                     $item_name      = trim( preg_replace( '/ +/', ' ', preg_replace( '/[^A-Za-z0-9 ]/', ' ', urldecode( html_entity_decode( strip_tags( $item_name ) ) ) ) ) );
    339                     $item_name      = str_replace( array( '(', ')' ), '', htmlentities( substr( $item_name, 0, 99 ) ) );
    340                     $item_quantity  = $item_values->get_quantity();
    341                     $item_total     = $item_values->get_total();
    342                     $item_price     = $item_total/ $item_quantity;
    343                     $lineitem       .= "<lineitem uniqueid='".( $item_key + 1 )."' particulars='" . $item_name . "' quantity='" . $item_quantity . "' unitcost='" . $item_price . "' subtotal='" . $item_total . "'></lineitem>";
    344                 }
    345              
    346                 $xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
    347                     <PesapalDirectOrderInfo
    348                         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
    349                         xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
    350                         Amount=\"" . $pesapal_args['total'] . "\"
    351                         Description=\"Order from " . $description . ".\"
    352                         Type=\"MERCHANT\"
    353                         Reference=\"" . $pesapal_args['reference'] . "\"
    354                         FirstName=\"" . $pesapal_args['first_name'] . "\"
    355                         LastName=\"" . $pesapal_args['last_name'] . "\"
    356                         Email=\"" . $pesapal_args['email'] . "\"
    357                         PhoneNumber=\"" . $pesapal_args['phone'] . "\"
    358                         Currency=\"" . get_woocommerce_currency() . "\"
    359                         xmlns=\"http://www.pesapal.com\">
    360                         <lineitems>".$lineitem."</lineitems>
    361                     </PesapalDirectOrderInfo>";
    362              
    363                 return htmlentities( $xml );
    364             }
    365            
    366            
    367             function status_request($transaction_id, $merchant_ref){
    368                 $request_status = PesaPalOAuthRequest::from_consumer_and_token($this->consumer, $this->token, "GET", $this->gatewayURL, $this->params);
    369                 $request_status->set_parameter("pesapal_merchant_reference", $merchant_ref);
    370                 $request_status->set_parameter("pesapal_transaction_tracking_id", $transaction_id);
    371                 $request_status->sign_request($this->signature_method, $this->consumer, $this->token);
    372              
    373                 return $this->checkTransactionStatus($merchant_ref);
    374             }
    375            
    376            
    377             /**
    378              * Check Transaction status
    379              *
    380              * @return PENDING/FAILED/INVALID
    381              **/
    382             function checkTransactionStatus($pesapalMerchantReference,$pesapalTrackingId = NULL){
    383                 if($pesapalTrackingId)
    384                     $queryURL = $this->QueryPaymentStatus;
    385                 else
    386                     $queryURL = $this->QueryPaymentStatusByMerchantRef;
    387          
    388                 //get transaction status
    389                 $request_status = PesaPalOAuthRequest::from_consumer_and_token(
    390                                     $this->consumer,
    391                                     $this->token,
    392                                     "GET",
    393                                     $queryURL,
    394                                     $this->params
    395                                   );
    396        
    397                 $request_status->set_parameter("pesapal_merchant_reference", $pesapalMerchantReference);
    398        
    399                 if($pesapalTrackingId)
    400                     $request_status->set_parameter("pesapal_transaction_tracking_id",$pesapalTrackingId);
    401          
    402                 $request_status->sign_request($this->signature_method, $this->consumer, $this->token);
    403      
    404                 return $this->curlRequest($request_status);
    405             }
    406 
    407             /**
    408              * Check Transaction status
    409              *
    410              * @return PENDING/FAILED/INVALID
    411             **/
    412             function getTransactionDetails($pesapalMerchantReference,$pesapalTrackingId){
    413 
    414                 $request_status = PesaPalOAuthRequest::from_consumer_and_token(
    415                                     $this->consumer,
    416                                     $this->token,
    417                                     "GET",
    418                                     $this->querypaymentdetails,
    419                                     $this->params
    420                                   );
    421        
    422                 $request_status->set_parameter("pesapal_merchant_reference", $pesapalMerchantReference);
    423                 $request_status->set_parameter("pesapal_transaction_tracking_id",$pesapalTrackingId);
    424                 $request_status->sign_request($this->signature_method, $this->consumer, $this->token);
    425      
    426                 $responseData = $this->curlRequest($request_status);
    427              
    428                 $pesapalResponse = explode(",", $responseData);
    429                 $pesapalResponseArray=array('pesapal_transaction_tracking_id'=>$pesapalResponse[0],
    430                                     'payment_method'=>$pesapalResponse[1],
    431                                     'status'=>$pesapalResponse[2],
    432                                     'pesapal_merchant_reference'=>$pesapalResponse[3]
    433                                   );
    434                                  
    435                 return $pesapalResponseArray;
    436             }
    437 
    438             /**
    439              * Check Transaction status
    440              *
    441              * @return ARRAY
    442              **/
    443             function curlRequest($request_status){
    444                
    445                 $response = wp_remote_get( $request_status );
    446                 if ( is_wp_error( $response) ) {
    447                     return __( 'An Error Occurred' );
    448                 } else {
    449                     $resp_body  = wp_remote_retrieve_body( $response );
    450                     return $resp_body;
    451                 }
    452 
    453             }
    454    
    455             /**
    456              * IPN Response
    457              *
    458              * @return null
    459              **/
    460             function ipn_response(){
    461                
    462                 $this->pesapal_do_log( 'IPN Begin' );
    463                
    464                 $this->pesapal_do_log( var_export( $_REQUEST, true ) );
    465                
    466                 $pesapalTrackingId          = '';
    467                 $pesapalNotification        = '';
    468                 $pesapalMerchantReference   = '';
    469 
    470                 if ( isset( $_REQUEST['pesapal_merchant_reference'] ) ) {
    471                     $pesapalMerchantReference = $_REQUEST['pesapal_merchant_reference'];
    472                 }
    473                      
    474                 if ( isset( $_REQUEST['pesapal_transaction_tracking_id'] ) ) {
    475                     $pesapalTrackingId = $_REQUEST['pesapal_transaction_tracking_id'];
    476                 }
    477                      
    478                 if ( isset( $_REQUEST['pesapal_notification_type'] ) ) {
    479                     $pesapalNotification=$_REQUEST['pesapal_notification_type'];
    480                 }
    481 
    482                 $transactionDetails = $this->getTransactionDetails( $pesapalMerchantReference,$pesapalTrackingId );
    483 
    484                 $this->pesapal_do_log( 'IPN Response' );
    485 
    486                 $this->pesapal_do_log( var_export( $transactionDetails, true ) );
    487                 //Get original order id
    488                 $order_id           = str_replace( strtoupper( $this->order_prefix ).'-','', $pesapalMerchantReference );
    489                 $order              = wc_get_order( $order_id );
    490                 if ( $order ) {
    491                     // We are here so lets check status and do actions
    492                     switch ( $transactionDetails['status'] ) {
    493                         case 'COMPLETED' :
    494                         case 'PENDING' :
    495 
    496                             // Check order not already completed
    497                             if ( $order->get_status() == 'completed' ) {
    498                                 if ( 'yes' == $this->debug )
    499                                     $this->log->add( 'pesapal', 'Aborting, Order #' . $order->id . ' is already complete.' );
    500                                 exit;
    501                             }
    502 
    503                             if ( $transactionDetails['status'] == 'COMPLETED' ) {
    504                                 $order->add_order_note( __( 'IPN payment completed', 'woocommerce' ) );
    505                                 $order->payment_complete();
    506                             } else {
    507                                 $order->update_status( 'on-hold', sprintf( __( 'Payment pending: %s', 'woocommerce' ), 'Waiting PesaPal confirmation' ) );
    508                             }
    509 
    510                             if ( 'yes' == $this->debug )
    511                                 $this->log->add( 'pesapal', 'Payment complete.' );
    512 
    513                             break;
    514                         case 'INVALID' :
    515                         case 'FAILED' :
    516                             // Order failed
    517                             $order->update_status( 'failed', sprintf( __( 'Payment %s via IPN.', 'woocommerce' ), strtolower( $transactionDetails['status'] ) ) );
    518                             break;
    519 
    520                         default :
    521                             // No action
    522                             break;
    523                     }
    524                 }
    525 
    526                 if ( $pesapalNotification =="CHANGE" && $transactionDetails['status'] != "PENDING" ){   
    527                     $resp = "pesapal_notification_type=$pesapalNotification".       
    528                             "&pesapal_transaction_tracking_id=$pesapalTrackingId".
    529                             "&pesapal_merchant_reference=$pesapalMerchantReference";
    530                                        
    531                     ob_start();
    532                     echo $resp;
    533                     ob_flush();
    534                    
    535                 }
    536                 exit();                 
     400                    $item_name     = $item_values->get_name();
     401                    $item_name     = trim( preg_replace( '/ +/', ' ', preg_replace( '/[^A-Za-z0-9 ]/', ' ', urldecode( html_entity_decode( strip_tags( $item_name ) ) ) ) ) );
     402                    $item_name     = str_replace( array( '(', ')' ), '', htmlentities( substr( $item_name, 0, 99 ) ) );
     403                    $item_quantity = $item_values->get_quantity();
     404                    $item_total    = $item_values->get_total();
     405                    $item_price    = $item_total / $item_quantity;
     406                    $lineitem     .= "<lineitem uniqueid='" . ( $item_key + 1 ) . "' particulars='" . $item_name . "' quantity='" . $item_quantity . "' unitcost='" . $item_price . "' subtotal='" . $item_total . "'></lineitem>";
     407                }
     408
     409                $xml = '<?xml version="1.0" encoding="utf-8"?>
     410                    <PesapalDirectOrderInfo
     411                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     412                        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     413                        Amount="' . $pesapal_args['total'] . '"
     414                        Description="Order from ' . $description . '."
     415                        Type="MERCHANT"
     416                        Reference="' . $pesapal_args['reference'] . '"
     417                        FirstName="' . $pesapal_args['first_name'] . '"
     418                        LastName="' . $pesapal_args['last_name'] . '"
     419                        Email="' . $pesapal_args['email'] . '"
     420                        PhoneNumber="' . $pesapal_args['phone'] . '"
     421                        Currency="' . get_woocommerce_currency() . '"
     422                        xmlns="http://www.pesapal.com">
     423                        <lineitems>' . $lineitem . '</lineitems>
     424                    </PesapalDirectOrderInfo>';
     425
     426                return htmlentities( $xml );
     427            }
     428
     429            /**
     430             * Process the status request.
     431             *
     432             * @param string $transaction_id The transaction id.
     433             * @param string $merchant_ref Te merchant ref.
     434             *
     435             * @return string
     436             */
     437            public function status_request( $transaction_id, $merchant_ref ) {
     438                $request_status = PesaPalOAuthRequest::from_consumer_and_token( $this->consumer, $this->token, 'GET', $this->gateway_url, $this->params );
     439                $request_status->set_parameter( 'pesapal_merchant_reference', $merchant_ref );
     440                $request_status->set_parameter( 'pesapal_transaction_tracking_id', $transaction_id );
     441                $request_status->sign_request( $this->signature_method, $this->consumer, $this->token );
     442
     443                return $this->checkTransactionStatus( $merchant_ref );
     444            }
     445
     446
     447            /**
     448             * Check Transaction status
     449             *
     450             * @return PENDING/FAILED/INVALID
     451             **/
     452            public function checkTransactionStatus( $pesapal_merchant_reference, $pesapal_transaction_tracking_id = null ) {
     453                if ( $pesapal_transaction_tracking_id ) {
     454                    $query_url = $this->query_payment_status;
     455                } else {
     456                    $query_url = $this->query_payment_status_by_merchant_ref;
     457                }
     458
     459                // get transaction status
     460                $request_status = PesaPalOAuthRequest::from_consumer_and_token(
     461                    $this->consumer,
     462                    $this->token,
     463                    'GET',
     464                    $query_url,
     465                    $this->params
     466                );
     467
     468                $request_status->set_parameter( 'pesapal_merchant_reference', $pesapal_merchant_reference );
     469
     470                if ( $pesapal_transaction_tracking_id ) {
     471                    $request_status->set_parameter( 'pesapal_transaction_tracking_id', $pesapal_transaction_tracking_id );
     472                }
     473
     474                $request_status->sign_request( $this->signature_method, $this->consumer, $this->token );
     475
     476                return $this->curlRequest( $request_status );
     477            }
     478
     479            /**
     480             * Check Transaction status
     481             *
     482             * @return PENDING/FAILED/INVALID
     483             **/
     484            public function getTransactionDetails( $pesapal_merchant_reference, $pesapal_transaction_tracking_id ) {
     485
     486                $request_status = PesaPalOAuthRequest::from_consumer_and_token(
     487                    $this->consumer,
     488                    $this->token,
     489                    'GET',
     490                    $this->query_payment_details,
     491                    $this->params
     492                );
     493
     494                $request_status->set_parameter( 'pesapal_merchant_reference', $pesapal_merchant_reference );
     495                $request_status->set_parameter( 'pesapal_transaction_tracking_id', $pesapal_transaction_tracking_id );
     496                $request_status->sign_request( $this->signature_method, $this->consumer, $this->token );
     497
     498                $response_data = $this->curlRequest( $request_status );
     499
     500                $pesapal_response       = explode( ',', $response_data );
     501                return array(
     502                    'pesapal_transaction_tracking_id' => $pesapal_response[0],
     503                    'payment_method'                  => $pesapal_response[1],
     504                    'status'                          => $pesapal_response[2],
     505                    'pesapal_merchant_reference'      => $pesapal_response[3],
     506                );
     507            }
     508
     509            /**
     510             * Check Transaction status.
     511             *
     512             * @return ARRAY
     513             **/
     514            public function curlRequest( $request_status ) {
     515
     516                $response = wp_remote_get( $request_status );
     517                if ( is_wp_error( $response ) ) {
     518                    return __( 'An Error Occurred' );
     519                } else {
     520                    $resp_body = wp_remote_retrieve_body( $response );
     521                    return $resp_body;
     522                }
     523
     524            }
     525
     526            /**
     527             * IPN Response
     528             *
     529             * @return null
     530             **/
     531            public function ipn_response() {
     532
     533                self::log( 'IPN Begin' );
     534
     535                self::log( $_REQUEST );
     536
     537                $pesapal_transaction_tracking_id = '';
     538                $pesapal_notification            = '';
     539                $pesapal_merchant_reference      = '';
     540
     541                if ( isset( $_REQUEST['pesapal_merchant_reference'] ) ) {
     542                    $pesapal_merchant_reference = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_merchant_reference'] ) );
     543                }
     544
     545                if ( isset( $_REQUEST['pesapal_transaction_tracking_id'] ) ) {
     546                    $pesapal_transaction_tracking_id = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_transaction_tracking_id'] ) );
     547                }
     548
     549                if ( isset( $_REQUEST['pesapal_notification_type'] ) ) {
     550                    $pesapal_notification = sanitize_text_field( wp_unslash( $_REQUEST['pesapal_notification_type'] ) );
     551                }
     552
     553                $transaction_details = $this->getTransactionDetails( $pesapal_merchant_reference, $pesapal_transaction_tracking_id );
     554
     555                self::log( 'IPN Response' );
     556
     557                self::log( $transaction_details );
     558                // Get original order id.
     559                $order_id = str_replace( strtoupper( $this->order_prefix ) . '-', '', $pesapal_merchant_reference );
     560                $order    = wc_get_order( $order_id );
     561                if ( $order ) {
     562                    // We are here so lets check status and do actions.
     563                    switch ( $transaction_details['status'] ) {
     564                        case 'COMPLETED':
     565                        case 'PENDING':
     566                            // Check order not already completed.
     567                            if ( in_array( $order->get_status(), array( 'processing', 'completed' ) ) ) {
     568                                if ( 'yes' == $this->debug ) {
     569                                    $this->log->add( 'pesapal', 'Aborting, Order #' . $order->id . ' is already complete.' );
     570                                }
     571                                exit;
     572                            }
     573
     574                            if ( 'COMPLETED' === $transaction_details['status'] ) {
     575                                $order->add_order_note( __( 'IPN payment completed', 'woocommerce' ) );
     576                                $order->payment_complete();
     577                            } else {
     578                                $order->update_status( 'on-hold', sprintf( __( 'Payment pending: %s', 'woocommerce' ), 'Waiting PesaPal confirmation' ) );
     579                            }
     580
     581                            if ( 'yes' == $this->debug ) {
     582                                $this->log->add( 'pesapal', 'Payment complete.' );
     583                            }
     584
     585                            break;
     586                        case 'INVALID':
     587                        case 'FAILED':
     588                            // Order failed.
     589                            $order->update_status( 'failed', sprintf( __( 'Payment %s via IPN.', 'woocommerce' ), strtolower( $transactionDetails['status'] ) ) );
     590                            break;
     591
     592                        default:
     593                            // No action.
     594                            break;
     595                    }
     596                }
     597
     598                if ( 'CHANGE' === $pesapal_notification && 'PENDING' !== $transaction_details['status'] ) {
     599                    $resp = "pesapal_notification_type=$pesapal_notification" .
     600                            "&pesapal_transaction_tracking_id=$pesapal_transaction_tracking_id" .
     601                            "&pesapal_merchant_reference=$pesapal_merchant_reference";
     602
     603                    ob_start();
     604                    echo $resp;
     605                    ob_flush();
     606
     607                }
     608                exit();
    537609            }
    538610
    539611            /**
    540612             * Cron to run and check order status
    541              * 
     613             *
    542614             * @since 1.0.0
    543615             */
    544             function check_order_status() {
     616            public function check_order_status() {
    545617                if ( $this->use_cron ) {
    546                     $this->pesapal_do_log( 'Beginning cron' );
    547                     $fetch  = array( 'pending','on-hold','processing' );
    548                     $args   = array(
    549                         'status'        => $fetch,
    550                         'date_created'  => '>' . ( time() - DAY_IN_SECONDS ),
    551                         'return'        => 'ids',
     618                    self::log( 'Beginning cron' );
     619                    $fetch = array( 'pending', 'on-hold', 'processing' );
     620                    $args  = array(
     621                        'status'       => $fetch,
     622                        'date_created' => '>' . ( time() - DAY_IN_SECONDS ),
     623                        'return'       => 'ids',
    552624                    );
    553                    
     625
    554626                    $orders = wc_get_orders( $args );
    555                     foreach ( $orders as $order ){
     627                    foreach ( $orders as $order ) {
    556628                        if ( is_int( $order ) ) {
    557629                            $order = wc_get_order( $order );
    558630                        }
    559631                        $method = $order->get_payment_method();
    560                         if ( $method == $this->id ) {
    561                             $pesapalMerchantReference   = $this->order_prefix . '-'. $order->get_id();
    562                             $status                     = $this->checkTransactionStatus( $pesapalMerchantReference );
    563                             if ( $status=="COMPLETED" ) {
     632                        if ( $method == $this->id ) {
     633                            $pesapal_merchant_reference = $this->order_prefix . '-' . $order->get_id();
     634                            $status                     = $this->checkTransactionStatus( $pesapal_merchant_reference );
     635                            if ( $status == 'COMPLETED' ) {
    564636                                $order->add_order_note( __( 'Payment completed via CRON', 'woocommerce' ) );
    565637                                $order->payment_complete();
    566                             } else if ( $status=="FAILED" || $status=="REVERSED" ) {
     638                            } elseif ( in_array( $status, array( 'FAILED', 'REVERSED' ) ) ) {
    567639                                $order->update_status( 'failed', sprintf( __( 'Payment %s via CRON.', 'woocommerce' ), strtolower( $status ) ) );
    568640                            }
    569641                        }
    570642                    }
    571                     $this->pesapal_do_log( 'Completed cron' );
    572                 }
    573             }
    574         }
    575     }
     643                    self::log( 'Completed cron' );
     644                }
     645            }
     646        }
     647    }
    576648}
    577 ?>
  • thebunch-ke-pesapal-woocommerce/trunk/readme.txt

    r2725075 r2790425  
    66Tested up to: 5.9.3
    77Requires PHP: 5.6
    8 Stable tag: 1.4.7
     8Stable tag: 1.4.8
    99License: GPLv3 or later
    1010License URI: http://www.gnu.org/licenses/gpl-3.0.html
     
    3232
    3333== Changelog ==
     34
     35= 1.4.8 =
     36* Fix: Remove currencies that are already supported in WooCommerce.
     37* Fix: Code improvements.
    3438
    3539= 1.4.7 =
  • thebunch-ke-pesapal-woocommerce/trunk/thebunchke_pesapal_woocommerce.php

    r2725075 r2790425  
    11<?php
    2 /*
    3 Plugin Name: TheBunch KE Pesapal Woocommerce
    4 Description: Add PesaPal payment gateway to your Woocommerce plugin
    5 Version: 1.4.7
    6 Author: rixeo
    7 Author URI: http://thebunch.co.ke/
    8 Plugin URI: http://dev.thebunch.co.ke/
    9 */
     2/**
     3 * Plugin Name:         TheBunch KE Pesapal Woocommerce
     4 * Plugin URI:          https://www.hubloy.com
     5 * Description:         Add PesaPal payment gateway to your Woocommerce plugin
     6 * Version:             1.4.8
     7 * Author:              rixeo
     8 * Author URI:          https://www.hubloy.com
     9 * License:             GPLv2
     10 * License URI:         https://www.gnu.org/licenses/gpl-2.0.html
     11 *
     12 * @package TheBunchKEPesaPal
     13 */
    1014
    11 if ( ! defined( 'ABSPATH' ) )
    12     exit; // Exit if accessed directly
     15if ( ! defined( 'ABSPATH' ) ) {
     16    exit; // Exit if accessed directly.
     17}
    1318
    14 //Define constants
    15 define( 'THEBUNCHKE_PESAPAL_WOO_PLUGIN_DIR', dirname(__FILE__).'/' );
    16 define( 'THEBUNCHKE_PESAPAL_WOO_PLUGIN_URL', plugin_dir_url(__FILE__) );
    17    
    18    
     19// Define constants.
     20define( 'THEBUNCHKE_PESAPAL_WOO_PLUGIN_DIR', dirname( __FILE__ ) . '/' );
     21define( 'THEBUNCHKE_PESAPAL_WOO_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     22
     23/**
     24 * Functions called to initiate the plugin.
     25 */
    1926function thebunchke_pesapal_woo_init() {
    20    
    21     //Load required files
    2227
    23     require_once( THEBUNCHKE_PESAPAL_WOO_PLUGIN_DIR . 'lib/OAuth.php' );
    24     require_once( THEBUNCHKE_PESAPAL_WOO_PLUGIN_DIR . 'lib/gateway.php' );
    25    
    26     add_filter('woocommerce_payment_gateways', 'add_pesapal_gateway_class' );
     28    // Load required files.
     29
     30    require_once THEBUNCHKE_PESAPAL_WOO_PLUGIN_DIR . 'lib/OAuth.php';
     31    require_once THEBUNCHKE_PESAPAL_WOO_PLUGIN_DIR . 'lib/gateway.php';
     32
     33    add_filter( 'woocommerce_payment_gateways', 'add_pesapal_gateway_class' );
     34    /**
     35     * Add the gateway class.
     36     *
     37     * @param array $methods The current methods.
     38     *
     39     * @return array
     40     */
    2741    function add_pesapal_gateway_class( $methods ) {
    28         $methods[] = 'WC_TheBunchKE_PesaPal_Pay_Gateway'; 
     42        $methods[] = 'WC_TheBunchKE_PesaPal_Pay_Gateway';
    2943        return $methods;
    3044    }
    31    
    32     /**
    33      * Add Currencies
    34      *
    35      */
    36     add_filter( 'woocommerce_currencies', 'thebunchke_pesapal_woo_add_shilling' );
    37     function thebunchke_pesapal_woo_add_shilling( $currencies ) {
    38         if( !isset( $currencies['KES'] ) ||!isset( $currencies['KSH'] ) ) {
    39             $currencies['KES'] = __( 'Kenyan Shilling', 'woocommerce' );
    40             $currencies['TZA'] = __( 'Tanzanian Shilling', 'woocommerce' );
    41             $currencies['UGX'] = __( 'Ugandan Shilling', 'woocommerce' );
    42             return $currencies;
    43         }
    44     }
    4545
    46     /**
    47      * Add Currency Symbols
    48      *
    49      */
    50     add_filter('woocommerce_currency_symbol', 'thebunchke_pesapal_woo_add_shilling_symbol', 10, 2);
    51     function thebunchke_pesapal_woo_add_shilling_symbol( $currency_symbol, $currency ) {
    52         switch( $currency ) {
    53             case 'KES':
    54                 $currency_symbol = 'KShs';
    55             break;
    56             case 'TZA':
    57                 $currency_symbol = 'TZs';
    58             break;
    59             case 'UGX':
    60                 $currency_symbol = 'UShs';
    61             break;
    62         }
    63         return $currency_symbol;
    64     }
     46    add_action( 'init', 'thebunchke_pesapal_woo_check_cron_status' );
    6547}
    6648
    67 //Initialize the plugin
     49// Initialize the plugin.
    6850add_action( 'plugins_loaded', 'thebunchke_pesapal_woo_init' );
    6951
    7052
     53
     54add_filter( 'cron_schedules', 'thebunchke_pesapal_woo_cron_recurrence_interval' );
    7155/**
    72  * Set cron schedules
     56 * Add custom cron schedule.
     57 *
     58 * @param array $schedules The current schedules.
     59 *
     60 * @return array
    7361 */
    74 add_filter( 'cron_schedules', 'thebunchke_pesapal_woo_cron_recurrence_interval' );
    75 
    7662function thebunchke_pesapal_woo_cron_recurrence_interval( $schedules ) {
    7763    $schedules['every_five_minutes'] = array(
     
    8369
    8470
    85  /**
    86  * Activation hook
     71
     72/**
     73 * Check status of cron and set it.
     74 *
     75 * @return void
    8776 */
    88 register_activation_hook( __FILE__, 'thebunchke_pesapal_woo_activation_hook' );
    89 function thebunchke_pesapal_woo_activation_hook() {
     77function thebunchke_pesapal_woo_check_cron_status() {
    9078    if ( ! wp_next_scheduled( 'thebunchke_pesapal_woo_check_order_status' ) ) {
    9179        wp_schedule_event( time(), 'every_five_minutes', 'thebunchke_pesapal_woo_check_order_status' );
     
    9381}
    9482
     83
     84add_action( 'thebunchke_pesapal_woo_check_order_status', 'thebunchke_pesapal_woo_check_order_status_function' );
    9585/**
    96  * Check the order status
     86 * Check the order status.
     87 *
     88 * @return void
    9789 */
    98 add_action( 'thebunchke_pesapal_woo_check_order_status', 'thebunchke_pesapal_woo_check_order_status_function' );
    9990function thebunchke_pesapal_woo_check_order_status_function() {
    10091    if ( class_exists( 'WC_TheBunchKE_PesaPal_Pay_Gateway' ) ) {
     
    10697
    10798add_action( 'rest_api_init', 'thebunchke_pesapal_woo_rest_api' );
     99/**
     100 * Register rest route.
     101 *
     102 * @param object $wp_rest_server The rest server.
     103 *
     104 * @return void
     105 */
    108106function thebunchke_pesapal_woo_rest_api( $wp_rest_server ) {
    109     register_rest_route( 'thebunchke-pespal/v1', 'ipn-listener', array(
    110         'methods'   => 'GET',
    111         'callback'  => 'thebunchke_pesapal_woo_check_ipn_function',
    112     ) );
     107    register_rest_route(
     108        'thebunchke-pespal/v1',
     109        'ipn-listener',
     110        array(
     111            'methods'  => 'GET',
     112            'callback' => 'thebunchke_pesapal_woo_check_ipn_function',
     113        )
     114    );
    113115}
    114116
    115117/**
    116  * IPN
     118 * IPN check function.
    117119 */
    118120function thebunchke_pesapal_woo_check_ipn_function() {
    119      if ( class_exists( 'WC_TheBunchKE_PesaPal_Pay_Gateway' ) ) {
    120         $newInstance = new WC_TheBunchKE_PesaPal_Pay_Gateway();
    121         $newInstance->ipn_response();
    122     } else {
    123         return "invalid";
    124     }   
     121    if ( class_exists( 'WC_TheBunchKE_PesaPal_Pay_Gateway' ) ) {
     122        $wc_thebunch_pesapal_instance = new WC_TheBunchKE_PesaPal_Pay_Gateway();
     123        $wc_thebunch_pesapal_instance->ipn_response();
     124    } else {
     125        return 'invalid';
     126    }
    125127}
    126 ?>
Note: See TracChangeset for help on using the changeset viewer.