<?php
/*
osCommerce, Open Source E-Commerce Solutions 
http://www.oscommerce.com 

Copyright (c) 2003 osCommerce  

osConcert
Copyright (c) 2022 osConcert.com

Released under the GNU General Public License 

PHP code to utilise PayPal API V1
*/

// Check to ensure this file is included in osConcert!
defined('_FEXEC') or die();


  class paypal_api 
  {
    var $code, $title, $description, $enabled, $notify_url, $curl, $add_shipping_to_amount, $add_tax_to_amount, $allowed_currencies, $default_currency, $test_mode;

// class constructor
	function __construct() 
	{
		global $order;
		
		$this->code = 'paypal_api';
		$name = "Paypal";
		$image = "";
		if(MODULE_PAYMENT_PAYPAL_API_DISPLAY_NAME != "MODULE_PAYMENT_PAYPAL_API_DISPLAY_NAME")$name = MODULE_PAYMENT_PAYPAL_API_DISPLAY_NAME;
		if(MODULE_PAYMENT_PAYPAL_API_IMAGE != "MODULE_PAYMENT_PAYPAL_API_IMAGE")$image = MODULE_PAYMENT_PAYPAL_API_IMAGE;
		if($image != "" && file_exists($path . DIR_WS_IMAGES . $image))
		{
			$image = '<img class="img-fluid img-mod" src="' . HTTP_SERVER . DIR_WS_HTTP_CATALOG . DIR_WS_IMAGES . $image . '">';
		}else
		{
			$image_array = array('.png','.jpg','.jpeg','.gif');
			$image_check = true;
			for($i=0;$i<sizeof($image_array);$i++)
			{
				if($image_check && $image != "" && file_exists($path . DIR_WS_IMAGES . $image . $image_array[$i]))
				{
					$image = '<img class="img-fluid img-mod" src="' . HTTP_SERVER . DIR_WS_HTTP_CATALOG . DIR_WS_IMAGES . $image . $image_array[$i] . '">';
					$image_check = false;
				}
			}
			if($image_check)$image = $path;
		}
		define('MODULE_PAYMENT_PAYPAL_API_TEXT_TITLE',  '<span class="modname">' . $name . '</span>&nbsp;&nbsp;' . $image);
		define('MODULE_PAYMENT_PAYPAL_API_TEXT_TEXT_TITLE', $name);

		$this->title = MODULE_PAYMENT_PAYPAL_API_TEXT_TITLE;
		$this->text_title = MODULE_PAYMENT_PAYPAL_API_TEXT_TEXT_TITLE;
		$this->description = MODULE_PAYMENT_PAYPAL_API_TEXT_DESCRIPTION;
		$this->sort_order = MODULE_PAYMENT_PAYPAL_API_SORT_ORDER;
		$this->enabled = ((MODULE_PAYMENT_PAYPAL_API_STATUS == 'True') ? true : false);
		$this->notify_url = MODULE_PAYMENT_PAYPAL_API_NOTIFY_URL;
		$this->general_error_heading = MODULE_PAYMENT_PAYPAL_API_ERROR_TITLE;

		$this->test_mode = ((MODULE_PAYMENT_PAYPAL_API_TEST_MODE == 'True') ? true : false);
		$this->barred=false;
		if ((int)MODULE_PAYMENT_PAYPAL_API_ORDER_STATUS_ID > 0) 
		{
			$this->order_status = MODULE_PAYMENT_PAYPAL_API_ORDER_STATUS_ID;
		}
		
		if (is_object($order)) $this->update_status();
		
		$this->form_action_url = tep_href_link('checkout_process.php','','SSL');
		
		// set timezone
			//date_default_timezone_set('UCT');
			date_default_timezone_set(STORE_TIMEZONE);
		// build API Credentials
			define('API_CREDENTIALS', MODULE_PAYMENT_PAYPAL_API_ID.':'.MODULE_PAYMENT_PAYPAL_API_SECRET);
		//sandbox or live url
			$this->data_url=trim(MODULE_PAYMENT_PAYPAL_API_ADMIN).'/logs/data_live.json';
			$this->ppurl   ='api.paypal.com';
			if (MODULE_PAYMENT_PAYPAL_API_TEST_MODE=='True') 
			{
			$this->data_url=trim(MODULE_PAYMENT_PAYPAL_API_ADMIN).'/logs/data_test.json';
			$this->ppurl   ='api.sandbox.paypal.com';
			}
		if(("ENABLE_SSL_CATALOG"=='true') || ("ENABLE_SSL_CATALOG"==1))
		{$this->store_url=HTTPS_CATALOG_SERVER.DIR_WS_CATALOG;}
		else
		{
		$this->store_url=HTTP_SERVER.DIR_WS_CATALOG;
		}
	}

// class methods
    function update_status() 
	{
      global $order;

  	  tep_check_module_status($this,MODULE_PAYMENT_PAYPAL_API_ZONE,trim(MODULE_PAYMENT_PAYPAL_API_EXCEPT_ZONE),trim(MODULE_PAYMENT_PAYPAL_API_EXCEPT_COUNTRY));	
  	  $this->barred=tep_check_payment_barred(trim(MODULE_PAYMENT_PAYPAL_API_EXCEPT_COUNTRY));

    }

    function javascript_validation() 
	{
      return false;
    }

    function selection() 
	{
	global $FSESSION;

	//if a customer has paid at PayPal and NOT returned to the store then
	//this session may still be active - kill it here for simplicity.
    if (isset($_SESSION['paypal_api_cart'])) 
	{
         $FSESSION->remove('paypal_api_cart');
    }
	
		tep_db_query("CREATE TABLE IF NOT EXISTS `customers_away_basket` LIKE `customers_basket`");
		tep_db_query("DELETE FROM `customers_away_basket` where `customers_id`='".$_SESSION['customer_id']."'");
	//
    return array('id' => $this->code,
				 'barred'=>$this->barred,
                 'module' => $this->title);
    }

    function pre_confirmation_check() 
	{
      return false;
    } 
    function confirmation()
    {
        global $FREQUEST, $FSESSION;
		$this -> GetApprovalURL();
		 

        if (MODULE_PAYMENT_PAYPAL_API_TEST_MODE=='True') 
		{
		$explanation='TEST MODE<br>Paypal Sandbox in operation.(osConcert: Paypal Sandbox is very unreliable, only recommended for Technicians.';}else{$explanation = '';}

        $confirmation = array('title' => $this->title,
        'fields' => array(array('title' => MODULE_PAYMENT_PAYPAL_API_TEXT_CONFIRMATION ,
        						'field' => $explanation . '<div id="ppplus"></div>')));
        return $confirmation;
    }

    function process_button() 
	{
      return false;
    }

    function before_process()
    {   global $FSESSION;

		
	    $FSESSION->remove('paypal_api_cart');
		$FSESSION->remove('paypal_api_return');
		$FSESSION->remove('paypal_api_id');

       	//copy basket contents in case of return
        tep_db_query("DELETE  from `customers_away_basket` where `customers_id`='".$_SESSION['customer_id']."'");

		tep_db_query("INSERT IGNORE `customers_away_basket` SELECT * FROM `customers_basket` where `customers_id`='".$_SESSION['customer_id']."'");
		
		try{
		$this->GetAccessToken();
		   }
		catch (Exception $e) 
		{
			$error = $e->getMessage(); 
			tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code . '&error=' . $error, 'SSL'));
		}
	
		if(empty($_SESSION['webprofilID']) || (empty($_SESSION['access_token']))|| (empty($_SESSION['patch_url'])))
		{
				$error = MODULE_PAYMENT_PAYPAL_API_GENERAL_ERROR;
                tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code . '&error=' . $error, 'SSL'));
		}
		//create the order
        return false;
    }


    function before_email() 
	{
		global $order, $insert_id, $order_total_modules, $currencies, $FSESSION, $fields, $merge_details, $send_details, $_POST;
		$FSESSION->remove('paypal_api_return');
		// check to see that we have the access token and experience for PayPal - if not then cancel the order and refill basket
			if(empty($_SESSION['webprofilID']) || (empty($_SESSION['access_token']))){
				$error = MODULE_PAYMENT_PAYPAL_API_POST_ERROR;
				$this->repopulate_cart($insert_id,'','',MODULE_PAYMENT_PAYPAL_API_POST_ERROR);
						exit();
			}
		// pop order_id into session

	// fill the email data table
	$sql_data_array = array(
				'order_id' => $insert_id,
				'customer_id' => $_SESSION['customer_id'],
				'send_data' => base64_encode( serialize($send_details)),
				'merge_data' => base64_encode( serialize($merge_details))
				);
	tep_db_perform('email_data', $sql_data_array);
				 
	########################################
	# Patch the sale with the invoice number
	######################################
	$url = $FSESSION->get('patch_url');
	$FSESSION->remove('patch_url');

	// Generate a unique invoice number using order ID + timestamp
	$unique_invoice_number = $insert_id . '_' . time();

	$JSONrequest =  '[
		{
			"op": "replace",
			"path": "/transactions/0/invoice_number",
			"value": "'.$unique_invoice_number.'"
		}
	]';

	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HTTPHEADER, array(
		'Content-Type: application/json',
		'Authorization: Bearer '.$_SESSION['access_token']
	));
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
	curl_setopt($ch, CURLOPT_POSTFIELDS, $JSONrequest);

	$result = curl_exec($ch);
	//if(curl_error($ch))	
//	{ 
	 //even though we have a success PayPal will often return an error 'NSS: client certificate not found (nickname not specified)'
	 //on some servers - this should not be allowed to trip the error here as
	 //the sale can still go ahead
//	if (curl_error($ch) != "NSS: client certificate not found (nickname not specified)"){
//	$this->repopulate_cart($insert_id,'','',curl_error($ch).' Patch: '.MODULE_PAYMENT_PAYPAL_API_POST_ERROR); 
//	}
//	}
//	curl_close($ch);

	// Decode PayPal API response
	$resultGetApprovalURL = json_decode($result, true);



	if (empty($resultGetApprovalURL['id']))
	{
	 $this->repopulate_cart($insert_id,'','payment_error=' . $this->code . '&error=Error: '.$resultGetApprovalURL['details'][0]['issue'] .': '. $resultGetApprovalURL['details'][0]['field'],MODULE_PAYMENT_PAYPAL_API_POST_ERROR .' Error: '.$resultGetApprovalURL['details'][0]['issue'] .': '. $resultGetApprovalURL['details'][0]['field']); 
	}
	tep_db_query("update orders set payment_return1 = '".$resultGetApprovalURL['id']."' where orders_id = '".$insert_id."'");

	// paypal token - may be needed for the cancel??? This is POORLY DOCUMENTED by PayPal 
	// i.e. NO documentation at all. It is important that an order cannot be cancelled by subterfuge so we do need some 
	// sort of security check
	$string_to_cut = $resultGetApprovalURL['links'][2]['href'];
	$token = substr($string_to_cut, strrpos($string_to_cut, '=') + 1);
	$FSESSION->set('paypal_api_token',$token); 
		
		
	tep_db_query("update orders set payment_return2 = '" . $token . "' where orders_id = '" . $insert_id . "'");
	// swap out baskets and send to PayPal
	tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET . " where customers_id = '" . (int)$FSESSION->customer_id . "'");

        $FSESSION->set('error_count', 0);
        $FSESSION->set('paypal_api_id', $insert_id);
		// In case of total breakdown 
		//Store session data in the database before redirecting
	//	tep_db_query("INSERT INTO paypal_sessions (session_id, order_id) VALUES ('" . session_id() . "', '" . (int)$insert_id . "')");

		// Append session ID to the PayPal return URL
	//	$payPalReturnUrl = $FSESSION->get('pay_url') . "&session_id=" . session_id();
	//	header("Location: " . $payPalReturnUrl);
	//	exit;
        $FSESSION->set('paypal_api_cart', $_SESSION['cart']);
        $FSESSION->set('cart', '');
        $FSESSION->set('merge_details', $merge_details);
        $FSESSION->set('send_details', $send_details);
        $FSESSION->remove('expiry');
        $FSESSION->remove('expires_in');
        $FSESSION->remove('app_id');
        $FSESSION->remove('token_type');

		// Store the session ID before redirecting to PayPal
	//	$FSESSION->set('paypal_session_id', session_id());

		// Redirect to PayPal, including the session ID in the URL
	//	header("Location: " . $FSESSION->get('pay_url') . "&session_id=" . session_id());
	
	header("Location:". $FSESSION ->get('pay_url'));
		exit('f');
		return false;

	 
    }
    function after_process() 
	{
	//used in paypal webhook not used in main checkout flow
	 global $FSESSION;
	
		try{
		$this->GetAccessToken();
		//$this->GetApprovalURL();
			}
		catch (Exception $e) 
		{
		$error = $e->getMessage(); 
		error_log($error);
		//tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code . '&error=' . $error, 'SSL'));
		}
      return $resultGetAccessToken;
    }

    function output_error() 
	{
      return false;
    }
	 function get_error() 
	 {
      global $FREQUEST;
	  
	  $response_text=$FREQUEST->getvalue('response_text');
	  $error=$FREQUEST->getvalue('error');

      $msg = "";
      if (stripslashes(urldecode($response_text)) != "")
        $msg = stripslashes(urldecode($response_text));

      else if (stripslashes(urldecode($error)) != "") 
        $msg = stripslashes(urldecode($error));
      $error = array('title' => '',
	                       'error' => $msg);

      return $error;
    }
	
    function check() 
	{
      if (!isset($this->_check)) 
	  {
        $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_PAYPAL_API_STATUS'");
        $this->_check = tep_db_num_rows($check_query);
      }
      return $this->_check;
    }
	
    function install() 
	{
      global $FSESSION;

        // OK lets add in a new Customer PayPal  - Cancelled
        $check_query = tep_db_query("select * from " . TABLE_CUSTOMERS . " where customers_lastname = 'PayPal  - cancelled' limit 1");
        if (tep_db_num_rows($check_query) < 1) 
		{
            $sql_data_array = array('customers_lastname' => 'PayPal  - cancelled',
            );
            tep_db_perform(TABLE_CUSTOMERS, $sql_data_array);
            $cust_id = tep_db_insert_id();
        } else 
		{
            $check = tep_db_fetch_array($check_query);
            $cust_id = $check['customers_id'];
        }
	  // OK lets addin a new order status fpr preparing
	  $check_query = tep_db_query("select orders_status_id from " . TABLE_ORDERS_STATUS . " where orders_status_name = 'Preparing [PayPal]' limit 1");

      if (tep_db_num_rows($check_query) < 1) 
	  {
        $status_query = tep_db_query("select max(orders_status_id) as status_id from " . TABLE_ORDERS_STATUS);
        $status = tep_db_fetch_array($status_query);

        $status_id = $status['status_id']+1;
        $languages = tep_get_languages();

        foreach ($languages as $lang) 
		{
          tep_db_query("insert into " . TABLE_ORDERS_STATUS . " (orders_status_id, language_id, orders_status_name) values ('" . $status_id . "', '" . $lang['id'] . "', 'Preparing [PayPal]')");
        }
      } else {
        $check = tep_db_fetch_array($check_query);
        $status_id = $check['orders_status_id'];
      }
	  
	    // OK lets add in a new order status for cancelled
        $check_query1 = tep_db_query("select orders_status_id from " . TABLE_ORDERS_STATUS . " where orders_status_name = 'PayPal  Error/Cancelled' limit 1");
        
        if (tep_db_num_rows($check_query1) < 1) 
		{
            $status_query1 = tep_db_query("select max(orders_status_id) as status_id from " . TABLE_ORDERS_STATUS);
            $status1 = tep_db_fetch_array($status_query1);
            $status_id1 = $status1['status_id']+1;
            $languages = tep_get_languages();
            foreach($languages as $lang) 
			{
                tep_db_query("insert into " . TABLE_ORDERS_STATUS . " (orders_status_id, language_id, orders_status_name) values ('" . $status_id1 . "', '" . $lang['id'] . "', 'PayPal  Error/Cancelled')");
                $status_id1= tep_db_insert_id();
            }
        } else {
            $check1 = tep_db_fetch_array($check_query1);
            
            $status_id1 = $check1['orders_status_id'];
        }
		
	// OK lets addin a new order status for awaiting
	  $check_query2 = tep_db_query("select orders_status_id from " . TABLE_ORDERS_STATUS . " where orders_status_name = 'Awaiting [PayPal]' limit 1");

      if (tep_db_num_rows($check_query2) < 1) 
	  {
        $status_query2 = tep_db_query("select max(orders_status_id) as status_id from " . TABLE_ORDERS_STATUS);
        $status2 = tep_db_fetch_array($status_query2);

        $status_id2 = $status2['status_id']+1;
        $languages = tep_get_languages();

        foreach ($languages as $lang) 
		{
          tep_db_query("insert into " . TABLE_ORDERS_STATUS . " (orders_status_id, language_id, orders_status_name) values ('" . $status_id2 . "', '" . $lang['id'] . "', 'Awaiting [PayPal]')");
        }
      } else {
        $check2 = tep_db_fetch_array($check_query2);
        $status_id2 = $check2['orders_status_id'];
      }

   	//new database table
        tep_db_query("CREATE TABLE IF NOT EXISTS `email_data` (
				  `email_id` int(11) NOT NULL auto_increment,
				  `customer_id` int(11) NOT NULL,
				  `order_id` int(11) NOT NULL,
				  `send_data` longtext NOT NULL,
				  `merge_data` longtext NOT NULL,
				  PRIMARY KEY  (`email_id`)
				)  AUTO_INCREMENT=1 ;");
				 
	if(ENABLE_SSL_CATALOG=='true' || ENABLE_SSL_CATALOG==1)
	{$url=HTTPS_CATALOG_SERVER.DIR_WS_CATALOG;}
	else
	{$url=HTTP_SERVER.DIR_WS_CATALOG;}

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Use PayPal?', 'MODULE_PAYMENT_PAYPAL_API_STATUS', 'True', 'Do you want to accept PayPal payments and notifications?', '6', '10', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Client ID', 'MODULE_PAYMENT_PAYPAL_API_ID', '', 'Your REST API Client ID', '6', '20', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Secret', 'MODULE_PAYMENT_PAYPAL_API_SECRET', '', 'Your REST API Secret', '6', '22', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Sandbox (Test) Mode?', 'MODULE_PAYMENT_PAYPAL_API_TEST_MODE','False', 'Run in TEST MODE? ', '6', '40', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('PayPal Payment Zone', 'MODULE_PAYMENT_PAYPAL_API_ZONE', '1,2,3', 'If a zone is selected, only enable this payment method for that zone.', '6', '42', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Paypal Exclude these Countries', 'MODULE_PAYMENT_PAYPAL_API_EXCEPT_COUNTRY', '', 'If countries are selected, disable this payment method for that countries.', '6', '43', 'tep_cfg_pull_down_zone_except_countries(MODULE_PAYMENT_PAYPAL_API_ZONE,', 'tep_get_zone_except_country', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Paypal exclude these Zones', 'MODULE_PAYMENT_PAYPAL_API_EXCEPT_ZONE', '', 'If a zone is selected, disable this payment method for that zone.', '6', '44', 'tep_cfg_pull_down_zone_classes(','tep_get_zone_class_title', now())");
	//  order status
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Successful Order Status', 'MODULE_PAYMENT_PAYPAL_API_COMP_ORDER_STATUS_ID', '3', 'Set the status of successful orders made with this payment module to this value. Delivered = download tickets.', '6', '45', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())");
	//pending  
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('PayPal Set Order Status', 'MODULE_PAYMENT_PAYPAL_API_ORDER_STATUS_ID', '".$status_id."', 'Set the status of pending orders made with this payment module to this value - <b>do not use</b> Delivered! ', '6', '46', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())");
	//awaiting
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('PayPal Set Awaiting Order Status', 'MODULE_PAYMENT_PAYPAL_API_AWAITS_ORDER_STATUS_ID', '".$status_id2."', 'Set the status of orders with incomplete payment  - <b>do not use</b> Delivered! ', '6', '47', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())");
	//cancelled
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Failed/Error Order Status', 'MODULE_PAYMENT_PAYPAL_API_CANX_ORDER_STATUS_ID', '".$status_id1."', 'Set the status of failed/error orders made with this payment module to this value. Suggested value is PayPal  Error/Cancelled - <b>do not use</b> Delivered! ', '6', '61', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())");
		//////  
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order,  date_added) values ('PayPal  Cancelled Customer ID', 'MODULE_PAYMENT_PAYPAL_API_DUMMY_CUSTOMER_ID', '".$cust_id."', 'Customer id number for failed/cancelled orders - should not need editing', '6', '63', now())");	  
	  
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Display Name', 'MODULE_PAYMENT_PAYPAL_API_DISPLAY_NAME', 'PayPal / Credit Cards', 'Set the Display name to payment module', '6', '70', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Save Token?', 'MODULE_PAYMENT_PAYPAL_API_ADMIN_USE','False', 'Save authorisation data in admin folder? ', '6', '71', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");
	  
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Admin Folder Name', 'MODULE_PAYMENT_PAYPAL_API_ADMIN', 'admin', 'Set the name of your admin folder', '6', '72', now())");

	//debug_backtrace
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Debug?', 'MODULE_PAYMENT_PAYPAL_API_DEBUG','False', ' ', '6', '65', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Debug email', 'MODULE_PAYMENT_PAYPAL_API_DEBUG_EMAIL', '', 'Where do you want debug emails sent to?', '6', '66', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('PayPal Sort order of display.', 'MODULE_PAYMENT_PAYPAL_API_SORT_ORDER', '10', 'Sort order of display. Lowest is displayed first.', '6', '11', now())");

	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Logo to display at PayPal', 'MODULE_PAYMENT_PAYPAL_API_LOGO', '', 'URL (https) to a logo - leave blank if not available', '6', '111', now())");
			
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Name to display at PayPal', 'MODULE_PAYMENT_PAYPAL_API_NAME', '".STORE_NAME."', 'Name to display at PayPal', '6', '111', now())");
    
        tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Add rounding amount at PayPal?', 'MODULE_PAYMENT_PAYPAL_API_ROUNDING','False', ' ', '6', '115', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");
	
	tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Paypal Image', 'MODULE_PAYMENT_PAYPAL_API_IMAGE', 'paypalcards.png', 'Paypal Image', '6', '119', now())");

	//tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Log API calls', 'MODULE_PAYMENT_PAYPAL_API_LOGGING', 'False', 'Do you want to log API data. ?', '6', '200', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())");
	}

    function remove() 
	{
      tep_db_query("delete from " . TABLE_CONFIGURATION . " where configuration_key in ('" . implode("', '", $this->keys()) . "')");
    }

    function keys() 
	{
		return array(
		'MODULE_PAYMENT_PAYPAL_API_STATUS', //
		'MODULE_PAYMENT_PAYPAL_API_ID',//
		'MODULE_PAYMENT_PAYPAL_API_SECRET',//
		'MODULE_PAYMENT_PAYPAL_API_SORT_ORDER',
		'MODULE_PAYMENT_PAYPAL_API_TEST_MODE', //
		'MODULE_PAYMENT_PAYPAL_API_ZONE', //
		'MODULE_PAYMENT_PAYPAL_API_EXCEPT_ZONE', //
		'MODULE_PAYMENT_PAYPAL_API_EXCEPT_COUNTRY', //
		'MODULE_PAYMENT_PAYPAL_API_ORDER_STATUS_ID', //
		'MODULE_PAYMENT_PAYPAL_API_SORT_ORDER',
		'MODULE_PAYMENT_PAYPAL_API_DISPLAY_NAME',//
		'MODULE_PAYMENT_PAYPAL_API_COMP_ORDER_STATUS_ID',//
		'MODULE_PAYMENT_PAYPAL_API_IMAGE',
		// 'MODULE_PAYMENT_PAYPAL_API_LOGGING',
		'MODULE_PAYMENT_PAYPAL_API_DEBUG',
		'MODULE_PAYMENT_PAYPAL_API_DEBUG_EMAIL',
		'MODULE_PAYMENT_PAYPAL_API_CANX_ORDER_STATUS_ID',//
		'MODULE_PAYMENT_PAYPAL_API_ADMIN',
		'MODULE_PAYMENT_PAYPAL_API_ADMIN_USE',
		'MODULE_PAYMENT_PAYPAL_API_NAME', 
		'MODULE_PAYMENT_PAYPAL_API_LOGO', //
                'MODULE_PAYMENT_PAYPAL_API_ROUNDING',
		'MODULE_PAYMENT_PAYPAL_API_DUMMY_CUSTOMER_ID',
		'MODULE_PAYMENT_PAYPAL_API_AWAITS_ORDER_STATUS_ID');//
	}
	 
	//paypal functions
	function GetAccessToken()
	{
		global $FSESSION;
		$FSESSION->remove('access_token');
		$FSESSION->remove('webprofilID');
		$FSESSION->remove('JSONrequest');



	//Get Access Token
	# check for valid access token
	$ts_now = time();
	
	$jsonDATA = (array) json_decode(file_get_contents( $this->data_url, true));
	if (!empty($jsonDATA)) {
		$_SESSION['expiry'] = $jsonDATA['expiry'];
		$_SESSION['access_token'] = $jsonDATA['access_token'];
		$_SESSION['app_id'] = $jsonDATA['app_id'];
		$_SESSION['token_type'] = $jsonDATA['token_type'];
		$_SESSION['webprofilID'] = $jsonDATA['webprofilID'];
		} else { $jsonDATA['expiry'] = 0;
		} 

	if ( $ts_now > $jsonDATA['expiry'] ) 
	{
		$url='https://'.$this->ppurl.'/v1/oauth2/token';
		$JSONrequest= 'grant_type=client_credentials';

		$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $url);
			//curl_setopt($ch, CURLOPT_SSLCERT, $sslcertpath);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array(
				'Accept: application/json',
				));
			curl_setopt($ch, CURLOPT_USERPWD, API_CREDENTIALS);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $JSONrequest);

		$result = curl_exec($ch); 
		
		$resultGetAccessToken = json_decode($result,true);

		curl_close ($ch);
		$_SESSION['expiry'] = time() + $resultGetAccessToken['expires_in'];
		$_SESSION['access_token'] = $resultGetAccessToken['access_token'];
		$_SESSION['app_id'] = $resultGetAccessToken['app_id'];
		$_SESSION['token_type'] = $resultGetAccessToken['token_type'];
	} 

	$url='https://'.$this->ppurl.'/v1/payment-experience/web-profiles';
     $JSONrequest= '{"name": "'.STORE_NAME.time().rand(0,10000).'"
	,"temporary" : true,"presentation": 
	{
	 "brand_name": "'.MODULE_PAYMENT_PAYPAL_API_NAME.'"
	}
	,
	  "flow_config": {
      "landing_page_type": "billing"
      
    },
	
	"input_fields": {
		"allow_note": false,
		"no_shipping": 2,
		"address_override": 1
	}}';
	$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		//curl_setopt($ch, CURLOPT_SSLCERT, $sslcertpath);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array(
			'Content-Type: application/json',
			'Authorization: Bearer '.$_SESSION['access_token']
			));
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $JSONrequest);

	$result = curl_exec($ch); 
	$resultGetExpreienceProfile = json_decode($result,true);//exit (var_dump($resultGetExpreienceProfile));
	
	$check = $resultGetExpreienceProfile['name']; 
	##################################
	# $check may hold a PayPal error
	##################################
		
	if (tep_not_null($check))
	{

	if($check == "VALIDATION_ERROR")
	{
		$check = $resultGetExpreienceProfile['message'].' '. PROBLEM_ERROR .': '.$resultGetExpreienceProfile['details'][0]['issue'].' '.INFIELD_ERROR.': ['.$resultGetExpreienceProfile['details'][0]['field'];
	

	echo "<script>
		window.location = '".tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code . '&error=' . $check, 'SSL')."';
		</script>'";
	exit('error');
 
		}
	}
	
	
	

	curl_close ($ch);
	$_SESSION['webprofilID'] = $resultGetExpreienceProfile['id'];
	$jsonSTRING = '{ "expiry":"'.$_SESSION['expiry'].'" , "access_token":"'.$_SESSION['access_token'].'" , "app_id":"'.$_SESSION['app_id'].'","token_type":"'.$_SESSION['token_type'].'","webprofilID":"'.$_SESSION['webprofilID'].'"}';
	
		if(MODULE_PAYMENT_PAYPAL_API_ADMIN_USE == 'True')
		{
			file_put_contents($this->data_url, $jsonSTRING);
		}
	}

###################################
# repopulate cart
###################################
	function repopulate_cart($order_id,$redirect_page=FILENAME_CHECKOUT_PAYMENT,$extras='', $note='')
	{
    global $FSESSION;

    // if we have the order_id then do stuff
    if (tep_not_null($order_id)) 
	{
        //grab customers name from order
        $cust_query = tep_db_query("select customers_name from " . TABLE_ORDERS . " where orders_id = '" . (int)$order_id . "'");
        if (tep_db_num_rows($cust_query) ) {
            $cust_query_result = tep_db_fetch_array($cust_query);
            $cust_name = $cust_query_result['customers_name'];
        }
        //change the order status
        $sql_data_array = array('orders_status' => MODULE_PAYMENT_PAYPAL_API_CANX_ORDER_STATUS_ID,
        'customers_id'  => MODULE_PAYMENT_PAYPAL_API_DUMMY_CUSTOMER_ID,
        'customers_name'=> 'PayPal -cancelled::'.$cust_name)
        ;
        tep_db_perform(TABLE_ORDERS, $sql_data_array, "update", "orders_id='".$order_id."'");

        
        $sql_data_array = array('orders_id' => $order_id,
        'orders_status_id' => MODULE_PAYMENT_PAYPAL_API_CANX_ORDER_STATUS_ID,
        'date_added' => 'now()',
        'customer_notified' => 0,
        'comments' => $note );
        tep_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
      
		include_once('includes/functions/ga_tickets.php');

        $order_query = tep_db_query("select products_id, products_quantity, events_type from " . TABLE_ORDERS_PRODUCTS . " where orders_id = '" . $order_id. "'");
        while ($order = tep_db_fetch_array($order_query)) 
		{
            tep_db_query("update " . TABLE_PRODUCTS . " set products_quantity = products_quantity + " . $order['products_quantity'] . ", products_ordered = products_ordered - " . $order['products_quantity'] . ", products_status='1' where products_id = '" . (int)$order['products_id'] . "'");

			if (function_exists('ga_check_process_restock'))
			{

			ga_check_process_restock((int)$order['products_id'], $order['products_quantity'], $order['events_type']);	
			}													

        }

        tep_db_query("delete from " . TABLE_ORDERS_TOTAL . " where orders_id = '" . $order_id. "'");
        tep_db_query("update " . TABLE_ORDERS_PRODUCTS . " set products_quantity = '0', orders_products_status = MODULE_PAYMENT_PAYPAL_API_CANX_ORDER_STATUS_ID  where orders_id = '" . $order_id . "'");
        tep_db_query("insert into " . TABLE_ORDERS_TOTAL. " (orders_id, title, text, value, class, sort_order) values ('" . $order_id . "', 'Total', '0.00', '0','ot_total', '99')");

	    	$FSESSION->remove('paypal_api_temp_id');
	
		if ($FSESSION->is_registered('paypal_api_cart')) 
		{
		$FSESSION->set('cart',$_SESSION['paypal_api_cart']);
		$FSESSION->remove('paypal_api_cart');

		tep_db_query("DELETE FROM `customers_basket` where `customers_id`='".$_SESSION['customer_id']."'");
		tep_db_query("INSERT IGNORE `customers_basket` SELECT * FROM `customers_away_basket` where `customers_id`='".$_SESSION['customer_id']."'");
		tep_db_query("DELETE FROM `customers_away_basket` where `customers_id`='".$_SESSION['customer_id']."'");
		}
        tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT,$extras, 'SSL'));
        exit();
        return false;
    }
	}

protected function paypal_sanitize_name($str) {
    // Remove HTML tags
    $str = strip_tags($str);

    // Collapse whitespace (including newlines) to single spaces
    $str = preg_replace("/\s+/", ' ', $str);

    // Replace double quotes with single quotes so they don't break anything
    $str = str_replace('"', "'", $str);

    // Limit for safety (PayPal's item name limit is 127 chars)
    if (function_exists('mb_substr')) {
        $str = mb_substr($str, 0, 127, 'UTF-8');
    } else {
        $str = substr($str, 0, 127);
    }

    return $str;
}


####################################
# Payment URL
####################################
function GetApprovalURL()
{
global  $FSESSION, $order_total_modules, $order_totals, $order;

//$FSESSION ->remove('patch_url');
$FSESSION ->remove('paypal_api_token');
$FSESSION ->remove('pay_url');

$this -> GetAccessToken();
//$order_totals_pp=$order_total_modules->process();
//////////////Fix to remove duplicates//////////////
// $ids = array_column($order_totals_pp, 'code');
// $ids = array_unique($ids);
// $order_totals_pp = array_filter($order_totals_pp, function ($key, $value) use ($ids) {
    // return in_array($value, array_keys($ids));
// }, ARRAY_FILTER_USE_BOTH);
///////////////////////////
//$order_totals_pp = array_map("unserialize", array_unique(array_map("serialize", $order_totals_pp))); 
//build the item list and totals
require_once('includes/functions/categories_lookup.php');

$items    = [];
$subtotal = 0;
$tax      = 0;
$shipping = 0; // will be set from order_totals_pp if present

//
// 1) Base line items from products
//
for ($i = 0, $n = sizeof($order->products); $i < $n; $i++) {

    $current_product_id = tep_db_input($order->products[$i]['id']);

    list($heading_name, $heading_venue, $heading_date, $heading_time) =
        getProductCategoryDetails($current_product_id);

    $item_price_array = $this->format_raw($order->products[$i]['final_price']);
    $item_price       = $item_price_array[0]; // numeric

    $subtotal += ($item_price * $order->products[$i]['qty']);

    $raw_name =
        $heading_name . ', ' .
        $heading_venue . ', ' .
        $heading_date . ', ' .
        $heading_time . ', ' .
        $order->products[$i]['name'];

    // Make name safe for PayPal (quotes, newlines, length)
    $safe_name = $this->paypal_sanitize_name($raw_name);

    $items[] = [
        'quantity' => (string)$order->products[$i]['qty'],
        'name'     => $safe_name,
        'price'    => number_format($item_price, 2, '.', ''), // "10.00"
        'currency' => $order->info['currency'],
    ];
}

//
// 2) Handle rounding difference as a line item (if enabled)
//
if ($price_diff > 0 && MODULE_PAYMENT_PAYPAL_API_ROUNDING == "True") {
    $price_diff_array = $this->format_raw($price_diff);
    // In original code this was multiplied by -1 before adding to subtotal
    $price_diff = ($price_diff_array[0] * -1);

    $subtotal += $price_diff;

    $items[] = [
        'quantity' => '1',
        'name'     => $this->paypal_sanitize_name(MODULE_PAYMENT_PAYPAL_API_PRICE_DIFF),
        'price'    => number_format($price_diff, 2, '.', ''),
        'currency' => $order->info['currency'],
    ];
}

//
// 3) Build order_totals_pp array (unchanged logic, just don’t touch $items as a string)
//

// (1) pass the stdclass object to an array then build 
$working_array = array($order_total_modules);
$working_array = json_decode(json_encode($order_total_modules), true);

$order_total_array = []; // make sure it's initialized

foreach ($working_array['modules'] as $key => $value) {
    $class = substr($value, 0, strrpos($value, '.'));
    if ($GLOBALS[$class]->enabled) {
        for ($i = 0, $n = sizeof($GLOBALS[$class]->output); $i < $n; $i++) {
            if (tep_not_null($GLOBALS[$class]->output[$i]['title']) && tep_not_null($GLOBALS[$class]->output[$i]['text'])) {
                $order_total_array[] = array(
                    'code'      => $GLOBALS[$class]->code,
                    'class'     => $GLOBALS[$class]->credit_class,
                    'title'     => $GLOBALS[$class]->output[$i]['title'],
                    'text'      => $GLOBALS[$class]->output[$i]['text'],
                    'value'     => $GLOBALS[$class]->output[$i]['value'],
                    'net_value' => $GLOBALS[$class]->output[$i]['net_value'],
                    'sort_order'=> $GLOBALS[$class]->sort_order
                );
            }
        }
    }
}
$order_totals_pp = $order_total_array;

// (2) strip duplicates keeping the first 'code' field
$order_totals_pp = array_reverse(array_values(array_column(
    array_reverse($order_total_array),
    null,
    'code'
)));

//
// 4) Original PayPal code recommences – but we now push extra items into $items[]
//
for ($i = 0, $n = sizeof($order_totals_pp); $i < $n; $i++) {

    // ignore total and subtotal (and raw tax/shipping here – handled separately)
    if (
        $order_totals_pp[$i]['code'] != 'ot_total' &&
        $order_totals_pp[$i]['code'] != 'ot_subtotal' &&
        $order_totals_pp[$i]['code'] != 'ot_tax' &&
        $order_totals_pp[$i]['code'] != 'ot_shipping' &&
        $order_totals_pp[$i]['code'] != ''
    ) {

        // ot_coupon / ot_qty_discount / ot_gv as line items
        if (
            $order_totals_pp[$i]['code'] == 'ot_coupon' ||
            $order_totals_pp[$i]['code'] == 'ot_qty_discount' ||
            $order_totals_pp[$i]['code'] == 'ot_gv'
        ) {
            if (PHP_VERSION >= 5.3) {
                $amount = number_format(round($order_totals_pp[$i]['value'], 2, PHP_ROUND_HALF_EVEN), 2);
            } else {
                $amount = number_format(floor($order_totals_pp[$i]['value'] * 100) / 100, 2);
            }

            // Subtotal adjusted by amount (note: your original code used +)
            $subtotal += $amount;

            $items[] = [
                'quantity' => '1',
                'name'     => $this->paypal_sanitize_name($order_totals_pp[$i]['title']),
                'price'    => number_format($amount, 2, '.', ''),
                'currency' => $order->info['currency'],
            ];

        } else {
            // 2021 PayPal needs net values (ot_service_ff, ot_loworderfee, etc.)
            $amount_to_use = $order_totals_pp[$i]['value'];

            if (isset($order_totals_pp[$i]['net_value'])) {
                $amount_to_use = $order_totals_pp[$i]['net_value'];
            }

            if (PHP_VERSION >= 5.3) {
                $amount = number_format(round($amount_to_use, 2, PHP_ROUND_HALF_EVEN), 2);
            } else {
                $amount = number_format(floor($amount_to_use * 100) / 100, 2);
            }

            $subtotal += $amount;

            $items[] = [
                'quantity' => '1',
                'name'     => $this->paypal_sanitize_name($order_totals_pp[$i]['title']),
                'price'    => number_format($amount, 2, '.', ''),
                'currency' => $order->info['currency'],
            ];
        }
    }

    // add in tax and shipping here (not as items, just into amount.details)
    if ($order_totals_pp[$i]['code'] == 'ot_tax') {
        if (PHP_VERSION >= 5.3) {
            $tax = number_format(round($order_totals_pp[$i]['value'], 2, PHP_ROUND_HALF_EVEN), 2);
        } else {
            $tax = number_format(floor($order_totals_pp[$i]['value'] * 100) / 100, 2);
        }
    }

    if ($order_totals_pp[$i]['code'] == 'ot_shipping') {
        $value = (float)$order_totals_pp[$i]['value'];

        if (PHP_VERSION >= 5.3) {
            $shipping = number_format(round($value, 2, PHP_ROUND_HALF_EVEN), 2);
        } else {
            $shipping = number_format(floor($value * 100) / 100, 2);
        }
    }
}

// paypal requires different state codes for certain countries
$state = $order->delivery['state'];

// US
if ($order->delivery['country']['iso_code_2'] == 'US') {
    $state_query = tep_db_query("select zone_code from " . TABLE_ZONES . " where zone_country_id = '223' and zone_name = '" . $state . "' limit 1");
    if (tep_db_num_rows($state_query)) {
        $state_values               = tep_db_fetch_array($state_query);
        $order->delivery['state']   = $state_values['zone_code'];
    }
}

// IT (as in original comments)
if ($order->delivery['country']['iso_code_2'] == 'IT') {
    $state_query = tep_db_query("select zone_code from " . TABLE_ZONES . " where zone_country_id = '105' and zone_name = '" . $state . "' limit 1");
    if (tep_db_num_rows($state_query)) {
        $state_values               = tep_db_fetch_array($state_query);
        $order->delivery['state']   = $state_values['zone_code'];
    }
}

// Ensure PayPal-required address fields are not empty
foreach ($order->delivery as $key => $value) {

    if ($key == 'city' && !tep_not_null($value)) {
        $order->delivery['city'] = 'city';
    }

    if ($key == 'postcode' && !tep_not_null($value)) {
        $order->delivery['postcode'] = 'zipcode';
    }

    if ($key == 'street_address' && !tep_not_null($value)) {
        $order->delivery['street_address'] = 'street address';
    }
}

// Make sure totals add up as PayPal expects
$total = $order->info['total']; 
$dif   = ($total - ($tax + $shipping + $subtotal));

if ($dif != 0) {
    $total = $total - $dif;
}

//
// 5) Build item_list, amount, transaction, payload
//
$shipping_address = [
    'recipient_name' => $order->delivery['firstname'] . ' ' . $order->delivery['lastname'],
    'line1'          => $order->delivery['street_address'],
    'line2'          => $order->delivery['suburb'],
    'city'           => $order->delivery['city'],
    'state'          => $order->delivery['state'],
    'postal_code'    => $order->delivery['postcode'],
    'country_code'   => $order->delivery['country']['iso_code_2'],
];

$item_list = [
    'items'            => $items,
    'shipping_address' => $shipping_address,
];

$amount = [
    'currency' => $order->info['currency'],
    'total'    => number_format($total, 2, '.', ''),
    'details'  => [
        'subtotal' => number_format($subtotal, 2, '.', ''),
        'tax'      => number_format($tax, 2, '.', ''),
        'shipping' => number_format($shipping, 2, '.', ''),
    ],
];

$transaction = [
    'amount'        => $amount,
    'invoice_number'=> 'xxxx', // you can replace with your dynamic invoice number
    'item_list'     => $item_list,
];

$payload = [
    'intent'               => 'sale',
    'payer'                => ['payment_method' => 'paypal'],
    'transactions'         => [$transaction],
    'redirect_urls'        => [
        'return_url' => $this->store_url . 'paypal_api_return.php',
        'cancel_url' => $this->store_url . 'paypal_api_cancel.php',
    ],
    'experience_profile_id'=> $_SESSION['webprofilID'],
];

$JSONrequest = json_encode($payload);

// For debugging:
error_log("PayPal create payment request:\n" . $JSONrequest);

//
// 6) Send to PayPal
//
$url = 'https://' . $this->ppurl . '/v1/payments/payment';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Authorization: Bearer ' . $_SESSION['access_token']
));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $JSONrequest);

$result              = curl_exec($ch); 
$resultGetApprovalURL= json_decode($result, true);
curl_close($ch);

$extras = $resultGetApprovalURL['name'];
$check  = $resultGetApprovalURL['name'];

	if (tep_not_null($check))
	{

	if($check == "VALIDATION_ERROR")
	{
		
		$check_your_zip = "";
		if ($resultGetApprovalURL['details'][0]['field'] == 'city'){
				$check_your_zip = MODULE_PAYMENT_PAYPAL_API_VAL_ERROR;
		}
		$check = $resultGetApprovalURL['message'].' '. PROBLEM_ERROR .': '.$resultGetApprovalURL['details'][0]['issue'].' '.INFIELD_ERROR.': ['.$resultGetApprovalURL['details'][0]['field'] . '] '.$check_your_zip; 
		
	

	echo "<script>
		window.location = '".tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code . '&error=' . $check, 'SSL')."';
		</script>'";
	exit('error');
 
		}
	}

	
        if ($resultGetApprovalURL['state'] != 'created')
		{
			$extras = MODULE_PAYMENT_PAYPAL_API_GET_ERROR;
			tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT,$extras, 'SSL'));
			exit('error');
		}else{
		$sandbox='"mode": "live",';
		if (MODULE_PAYMENT_PAYPAL_API_TEST_MODE=='True') 
		{
			$sandbox='"mode": "sandbox",';
		}
		   $FSESSION ->set('patch_url', $resultGetApprovalURL['links'][0]['href']);
		   $FSESSION ->set('pay_url', $resultGetApprovalURL['links'][1]['href']);
		}

	
	}

	####################################number_format(round($price,2,PHP_ROUND_HALF_EVEN),2)

		// format prices without currency formatting
		function format_raw($number, $currency_code = '', $currency_value = '') 
		{
		  global $currencies, $currency, $FSESSION, $order;
			  $currency_code = $order->info['currency'];
			  $currency_value = $order->info['currency_value'];


		  $rounded = number_format(tep_round($number * $currency_value, $currencies->currencies[$currency_code]['decimal_places']), $currencies->currencies[$currency_code]['decimal_places'], '.', '');
          
            $original = $number * $currency_value;
            $diff = tep_round(($rounded - $original),4);
            
            $result = (array($rounded,$diff));
            
            return $result;
		}
	}
  
?>