<?php
if ( ! defined( 'ABSPATH' ) ) exit;

class WC_API_Cybersource_SO extends WC_API_Cybersource_SAWM 
{

	public $soap_client;
	public $wsdl_uri;

	function __construct(){
		parent::__construct();

		$this->supports = array(
								 'products',
								 'default_credit_card_form',
								 'tokenization',
								 'refunds'
							  );


		

		add_action( 'woocommerce_thankyou', array( $this , 'cybersource_thankyou_processing_order_status' ), 10 ,1);
		add_action( 'wp_enqueue_scripts', array( $this, 'script' ) );
		add_action('woocommerce_order_status_completed', array( $this, 'capture_order_status_completed' ), 10, 2 );

		
	}

	public function script(){

		if(is_checkout() && $this->cb_api  == 'so' ){
			wp_enqueue_script( 'cybersource-soap-gateway', plugins_url( 'assets/js/wc-cybersource-soap-script.min.js', __FILE__ ), array( 'jquery', 'wc-credit-card-form' ), WC_VERSION, true );
		}
		elseif(is_account_page() && $this->cb_api  == 'so'){
			wp_enqueue_script( 'cybersource-soap-gateway', plugins_url( 'assets/js/wc-cybersource-soap-script.min.js', __FILE__ ), array( 'jquery', 'wc-credit-card-form' ), WC_VERSION, true );
		}
		
	}


	public function payment_fields(){

		if ( $this->mode == 's' ){
				echo '<p>';
					echo esc_html__( 'TEST MODE/SANDBOX ENABLED', 'woocommerce' );
					echo '<br>';
					echo esc_html__( 'Simple Order API (SOAP)', 'woocommerce' );
					echo '<br>';
					echo '<br>';
					echo  esc_html__( 'Test Cards:', 'woocommerce' );
					echo '<br>';
					echo  esc_html__( 'MasterCard: 5555 5555 5555 4444', 'woocommerce' );
					echo '<br>';
					echo  esc_html__( 'VISA: 4111 1111 1111 1111', 'woocommerce' );
				echo '<p>';
			}

		if( $this->description ){
			echo wp_kses_post( wpautop( wptexturize( $this->description ) ) );
		}

		// $this->credit_card_form();
		WC_Payment_Gateway_CC::payment_fields();
		//parent::payment_fields();
		 
		 
	}

	public function process_payment( $order_id ){

		try {

			global $woocommerce;


			if(!is_numeric($_REQUEST['cybsawm-cardnumber'])){

				wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Invalid Cardnumber!" ) , 'woocommerce'), "error" );
				return false;
	
			}
	
			if(!is_numeric($_REQUEST['cybsawm-card_cvc'])){
	
				wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Invalid Card code!" ) , 'woocommerce'), "error" );
				return false;
	
			}
	
			if(!is_numeric($_REQUEST['cybsawm-expiry_m'])){
	
				wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Invalid Expiry Month!" ) , 'woocommerce'), "error" );
				return false;
	
			}
	
			if(!is_numeric($_REQUEST['cybsawm-expiry_y'])){
	
				wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Invalid Expiry Year!" ) , 'woocommerce'), "error" );
				return false;
	
			}

			$order = wc_get_order( $order_id );

			$date_completed = $order->get_date_completed();
			$status = $order->get_status();

			if(!empty($date_completed) && $status == 'completed'){

				$duplicate_order_number = array(
					'order_id' => $order_id,
					'status' => $order->get_status(),
					'date_completed' => $order->get_date_completed(),
					'old_billing_email' => $order->get_billing_email(),
					'new_billing_email' => wc_clean($_REQUEST['billing_email']),
				);

				parent::log( 'Cybersource SO Request - Duplicate Order number: ' . print_r( $duplicate_order_number, true ) );

				wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Sorry something went wrong on your order, Please contact the support!" ) , 'woocommerce'), "error" );
				return false;

			}
			
			$save_card 					= (bool) sanitize_text_field($_REQUEST[ 'wc-' . $this->id . '-new-payment-method']);
			$token_id 					= sanitize_text_field($_REQUEST[ 'wc-' . $this->id . '-payment-token' ]);
					
			// Get CC POST Data
			$card_number 		 = wc_clean( $_POST[ $this->id.'-cardnumber'] );
			$card_number 		 = str_replace( ' ', '', $card_number);
			$card_cvc    		 = wc_clean( $_POST[ $this->id.'-card_cvc'] );
			
			$card_exp_year 		 = wc_clean( $_POST[ $this->id.'-expiry_y'] );
			$card_exp_month 		 = wc_clean( $_POST[ $this->id.'-expiry_m'] );

			if($this->dont_send_billing_fields === false ){

				parent::cybersource_custom_validate_address_fields();
			
				$addres_max_length = ! empty($this->address_input_size) ? $this->address_input_size : 61;

				if(! empty($order->get_billing_address_1()) && strlen($order->get_billing_address_1()) >= $addres_max_length ){
					wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Invalid Billing Address 1 Maximum Character Length Exceeded!" ) , 'woocommerce'), "error" );
					return false;
				}

				if(! empty($order->get_billing_address_2()) && strlen($order->get_billing_address_2()) >= $addres_max_length ){
					wc_add_notice( __( sprintf( "Error when processing payment: '%s'", "Invalid Billing Address 2 Maximum Character Length Exceeded!" ) , 'woocommerce'), "error" );
					return false;
				}

			}

			

			//fix for SOAP-ERROR: Parsing WSDL: Couldn't load 
			libxml_disable_entity_loader(false); 

			// Cybersource Request 
			// http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_SO_API/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=request_fields_so.html
			$soap = $this->soaptoolkit_request(); 
			$request = $soap->createRequest( $order->get_order_number() );

			$request->clientLibrary 		= "WooCommerce PHP Client";
			$request->clientLibraryVersion 	= phpversion();
			$request->clientEnvironment 	= php_uname();
			$request->merchantID 			= $this->merchant_id;			
			$request->merchantReferenceCode = $order->get_order_number();
				

			// disable Address Verification Services (AVS)
			$businessRules = new stdClass();

			if( $this->avs === true ){
				$ignore_avs = false;
			} else {
				$ignore_avs = true;
			}

			$businessRules->ignoreAVSResult = $ignore_avs;
			$request->businessRules  = $businessRules;

			

			if( !empty($token_id) && is_numeric($token_id) ){
				$customer_token 			= $this->get_customer_token($token_id);
				$recurringSubscriptionInfo = new stdClass();
				$recurringSubscriptionInfo->subscriptionID = $customer_token;
				$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;

			} else {

				// Cybersource CC object
				$card = new stdClass();
				$card->accountNumber 	= sanitize_text_field($card_number);
				$card->expirationMonth 	= sanitize_text_field($card_exp_month);
				$card->expirationYear 	= sanitize_text_field($card_exp_year);

				$cardtype 				= parent::get_cc_type( $card_number );

				if( ! empty( $cardtype ) ){
					$card->cardType 		= $cardtype;	
				}

				$card->cvNumber 	    = $card_cvc;

				$request->card = $card;

			}

			// Cybersource Billing Address Object 
			$billTo = new stdClass();
			$billTo->firstName 	= sanitize_text_field($order->get_billing_first_name());
			$billTo->lastName 	= sanitize_text_field($order->get_billing_last_name());
			$billTo->street1	= sanitize_text_field($order->get_billing_address_1());
			$billTo->street2 	= sanitize_text_field($order->get_billing_address_2());
			$billTo->city 		= sanitize_text_field($order->get_billing_city());
			$billTo->state 		= sanitize_text_field($order->get_billing_state());
			$billTo->postalCode = sanitize_text_field($order->get_billing_postcode());
			$billTo->country 	= sanitize_text_field($order->get_billing_country());
			$billTo->email 		= sanitize_text_field($order->get_billing_email());
			$billTo->company 	= sanitize_text_field(substr($order->get_billing_company(),0,40));
			$billTo->phoneNumber = sanitize_text_field($order->get_billing_phone());
			$billTo->ipAddress 	= WC_Geolocation::get_ip_address();
			$request->billTo    = $billTo;
			

			// Cybersource Shipping Address Object
			$shipTo = new stdClass();
			$shipTo->city 			= sanitize_text_field($order->get_shipping_city());
			$shipTo->country 		= sanitize_text_field($order->get_shipping_country());
			$shipTo->firstName 		= sanitize_text_field($order->get_shipping_first_name());
			$shipTo->lastName 		= sanitize_text_field($order->get_shipping_last_name());
			$shipTo->phoneNumber 	= sanitize_text_field($order->get_billing_phone());
			$shipTo->postalCode 	= sanitize_text_field($order->get_shipping_postcode());
			$shipTo->state 			= sanitize_text_field($order->get_shipping_state());
			$shipTo->street1 		= sanitize_text_field($order->get_shipping_address_1());
			$shipTo->street2 		= sanitize_text_field($order->get_shipping_address_2());
			$request->shipTo    	= $shipTo;

			
			/* Single Item allowed only if more than one "java.lang.NullPointerException" */
			if( $this->line_items == 'yes' ){
				// Cybersource line Items

				// Get Cart Items
				$cart_items = $woocommerce->cart->get_cart();

				//$cb_sf_line_times = array();		
				$request->item = array();
				if( ! empty( $cart_items ) ){
					$x = 0;
					foreach( $cart_items  as $values ){
						
						if( $values[ 'product_id' ] ){
							$product = wc_get_product( $values['product_id']);
							$product_sku  = $product->get_sku();
							$products_cats = $product->get_category_ids();
						} else {
							$product = wc_get_product( $values['variation_id']);
							$product_sku = $product->get_sku();
							$products_cats = $product->get_category_ids();
						}

						
						if( ! empty( $product_sku ) ){
							$sku = $product_sku;
						} else {
							$sku = "na";
						}
						
						if( $this->round_line_taxamount === false ){
							$line_tax_amount = (double) number_format( $values['line_tax'], 2, ".", "");	
						} else {
							$line_tax_amount = round( $values['line_tax'] );	
						}

						if ( $this->round_line_unitprice === false ) {
							$line_unit_price = (double) number_format( $values['line_total'], 2, ".", "");
						} else {
							$line_unit_price = round($values['line_total']);
						}

						$unitPrice                      	= $values['data']->get_price();
						$_product 							= wc_get_product( $values['product_id'] );

						$cb_sf_line_times 				    = new stdClass();
						$cb_sf_line_times->productName 	    = sanitize_text_field($_product->get_name());
						$cb_sf_line_times->productSKU 	    = $sku;
						$cb_sf_line_times->unitPrice 		= (double) number_format($unitPrice, 2 );
						$cb_sf_line_times->quantity 		= $values['quantity'];
						$cb_sf_line_times->taxAmount 		= (double) number_format($line_tax_amount,2);
						$cb_sf_line_times->id 				= $x;

						$request->item[] = $cb_sf_line_times;
						
						$x++;
					}
				}


			
				//count($cart_items)
				//$request->item = (object) $cb_sf_line_times[0];
			}

			// MDD Fields ( Merchant Custom Fields)	
			if( $this->enable_mdd_fields === true ){
				$mdd_fields = apply_filters( 'cybersource_add_signed_field_names', $order_id );
			} else {
				$mdd_fields = array();
			}

			
			if( is_array(  $mdd_fields ) && ! empty( $mdd_fields ) ){

				$merchantDefinedData = new stdClass();
				$merchantDefinedData->mddField = $mdd_fields;
				$request->merchantDefinedData  = $merchantDefinedData;
			}
			

			// Cybersource purchase totals 
			$purchaseTotals 					= new stdClass();
			$purchaseTotals->currency 			= get_woocommerce_currency();
			$purchaseTotals->grandTotalAmount 	= (double) number_format( $order->get_total(), 2, ".", "");
			$request->purchaseTotals 			= $purchaseTotals;


			// Cybersource Transaction Type

			if( $this->trans_type_so == 'sale' ){

				$ccAuthService 			= new stdClass();
				$ccAuthService->run 	= "true";
				$request->ccAuthService = $ccAuthService;
				
				$ccCaptureService		 = new stdClass();
				$ccCaptureService->run 	 = "true";
				$request->ccCaptureService = $ccCaptureService;

			} else {

				$ccAuthService = new stdClass();
				$ccAuthService->run = "true";
				$request->ccAuthService = $ccAuthService;

			}


			if( $save_card === true && !is_numeric($token_id) ){
				// Add Tokenization 
				$recurringSubscriptionInfo = new stdClass();
				$recurringSubscriptionInfo->frequency = 'on-demand';
				$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;

				$paySubscriptionCreateService = new stdClass();
				$paySubscriptionCreateService->run = "true";
				$request->paySubscriptionCreateService = $paySubscriptionCreateService;
			}
			

			$request->deviceFingerprintID   = $_POST[ 'device_fingerprint_id' ] ;
			$request->deviceFingerprintTrueIPAddress = ! empty( $_SERVER['HTTP_X_FORWARD_FOR'] ) ? $_SERVER['HTTP_X_FORWARD_FOR'] : $_SERVER['REMOTE_ADDR'];


			if(  $order_id > 0 ){

				//$reply = $this->soap_client->runTransaction( $request );
				$reply = $soap->runTransaction( $request );

				$request_logs = $request;
				$request_logs->card->accountNumber = '************'.substr($request_logs->card->accountNumber,-2); 	
				parent::log( 'Cybersource Request - SO: ' . print_r( $request_logs, true ) );
				parent::log( 'Cybersource Response - SO: ' . print_r( $reply, true ) );

				$reason = $this->reason_code( $reply->reasonCode );
				$request_ID 	= wc_clean($reply->requestID); 
				$request_token  = wc_clean($reply->requestToken); 
				
				// update_post_meta( $order_id, '_' . $this->id . '_request_ID', $request_ID );
				// update_post_meta( $order_id, '_' . $this->id . '_request_token', $request_token );
				// update_post_meta( $order_id, '_' . $this->id . '_transaction_id', $request_ID );
				// update_post_meta( $order_id, '_' . $this->id . '_request_token', $request_token );
				// update_post_meta( $order_id, '_' . $this->id . '_card_token_id', $token_id );

				$order->update_meta_data( '_' . $this->id . '_request_ID', $request_ID );
				$order->update_meta_data( '_' . $this->id . '_request_token', $request_token );
				$order->update_meta_data( '_' . $this->id . '_transaction_id', $request_ID );
				$order->update_meta_data( '_' . $this->id . '_request_token', $request_token );
				$order->update_meta_data( '_' . $this->id . '_card_token_id', $token_id );
				
				$cardcode = str_replace( '0', '', $this->get_cc_type($card_number) );

				//update_post_meta( $order_id, '_' . $this->id . '_cardcode', $cardcode );
				$order->update_meta_data( '_' . $this->id . '_cardcode', $cardcode);
				parent::log( 'Cybersource Request CardCode - SO: ' . print_r( array( $cardcode ), true ) );
				
				//update_post_meta( $order_id, '_cyber_decision', $reply->decision );
				$order->update_meta_data( '_cyber_decision', $reply->decision);
				$order->save();

				
				switch ( $reply->decision ){
					case 'ACCEPT':

						$order_status 	= parent::virtual_order_payment_complete_order_status( $order_id );

						if( $this->trans_type_so == 'authorization' ){

							
							if( $this->default_status == 'default' ){
								$order->update_status( 'on-hold' );	
							} else {
								$order->update_status( $this->default_status );
							}

						} else {

							if( $this->default_status == 'default' ){
								$order->update_status( $order_status );
							} else {
								$order->update_status( $this->default_status );
							}	

							$order->payment_complete( $request_ID );

						}

						if( $save_card === true && !is_numeric($token_id) ){
							$token = $reply->paySubscriptionCreateReply->subscriptionID;
							if(!empty($token)){

								$set_last4 = substr($card_number,-4);
								$card_type = parent::get_cc_type($card_number);
								switch($card_type){
									case '001':
										$cc_type = __('Visa','woocommerce');
									break;
									case '002':
										$cc_type = __('MasterCard','woocommerce');
									break;
									case '003':
										$cc_type = __('American Express','woocommerce');
									break;
									case '004':
										$cc_type = __('Discover','woocommerce');
									break;
									case '005':
										$cc_type = __('Diners Club','woocommerce');
									break;
									case '014':
										$cc_type = __('EnRoute','woocommerce');
									break;
									case '007':
										$cc_type = __('JCB','woocommerce');
									break;
									case '024':
										$cc_type = __('Maestro', 'woocommerce');
									break;
									case '033':
										$cc_type = __('Visa Electron','woocommerce');
									break;
									case '035':
										$cc_type = __('Laser','woocommerce');
									break;
									case '006':
										$cc_type = __('Carte Blanche','woocommerce');
									break;
									case '034':
										$cc_type = __('Dankort','woocommerce');
									break;
									default:
										$cc_type = __('unknown','woocommerce');
									break;
								}

								$card_token[ 'token' ] 				= sanitize_text_field( $token );
								$card_token[ 'card_type' ] 			= sanitize_text_field($cc_type);
								$card_token[ 'set_last4' ]			= sanitize_text_field($set_last4);
								$card_token[ 'set_expiry_month' ]	= sanitize_text_field($card_exp_month);
								$card_token[ 'set_expiry_year' ]	= sanitize_text_field($card_exp_year);

								//* Fire save card token
								$ret = $this->save_card_token($card_token);

							}
						}

						
						$order->add_order_note(
							sprintf(
								"%s Payment Completed with Request ID of '%s'",
								$this->method_title,
								$request_ID
							)
						);
								
						
						WC()->cart->empty_cart();

						$redirect_url = $this->get_return_url( $order );
						
						return array(
								'result' => 'success',
								'redirect' => $redirect_url
							);
						
						
						break;
					case 'ERROR':
					case 'REJECT':

						$order->update_status( 'failed' );	
						$order->add_order_note(
							sprintf(
								"%s Payment Failed with message: '%s'",
								$this->method_title,
								$reply->decision .' - '. $reason
							)
						);
					
						//wc_add_notice(__( 'Transaction Error: Could not complete your payment' , 'woocommerce'), "error");
						wc_add_notice(__( sprintf( "Transaction Error: %s with reason code '%s'", $reason, $reply->reasonCode  )  , 'woocommerce'), "error");
						return false;
						
						break;
					case 'REVIEW':
						
						// Decision Manager flagged the order for review.
						$order->update_status( 'on-hold', __(  $reason , 'woocommerce' ));
						$order->reduce_order_stock();
						
						$order->add_order_note(
							sprintf(
								"%s Payment on review with message: '%s'",
								$this->method_title,
								$reply->decision . ' - '. $reason
							)
						);
						
						WC()->cart->empty_cart();
						
						$redirect_url = $this->get_return_url( $order );
						
						return array(
								'result' => 'success',
								'redirect' => $redirect_url
							);
						
						break;			
				}
						
			} 

			else {
				wc_add_notice( 
					__( sprintf( "Error when processing payment: '%s'", "Invalid Order ID!" ) , 'woocommerce' ), "error" );
				return false;

			}



		} catch (Exception $e) {

			wc_add_notice( 
				__( 'Caught exception: ',  $e->getMessage(), "\n" , 'woocommerce' ), "error" );
			return false;
			
		}
		
		


	}
	
	public function save_card_token( $params ){
				
		$customer_token 	= get_user_meta( get_current_user_id(), $this->id . '_customer_token', true );
		$customer_token_id 	= null;
		
		// Add New Customer Card Token
		$customer_tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id(), $this->id );

		if( ! empty($customer_tokens)){
			foreach ( $customer_tokens as $c_token ) {
				if ( $c_token->get_token() == $customer_token ) {
					$customer_token_id = $c_token->get_id();
					break;
				}
			}
		}

		if( $customer_token_id  == null ){
			$token = new WC_Payment_Token_CC();
		} else {
			$token = WC_Payment_Tokens::get( $customer_token_id );
		}

		$token->set_token( $params[ 'token' ] );
		$token->set_gateway_id( $this->id );
		$token->set_card_type( $params[ 'card_type' ] );
		$token->set_last4( $params[ 'set_last4' ] );
		$token->set_expiry_month(  $params['set_expiry_month'] );
		$token->set_expiry_year(  $params['set_expiry_year'] );

		if ( is_user_logged_in() ) {
			$token->set_user_id( get_current_user_id() );
		}

		//var_dump( $token->validate() ); // bool(true)
		$result = $token->save();

		if( $result == true ){
			return $token;
		} else {
			return null;
		}

	}

	public function get_customer_token( $id ){

		$ret = '';

		if( !empty($id)){
			$customer_token = WC_Payment_Tokens::get( $id );
			$ret = $customer_token->get_token();
		} 

		return $ret;
	}

	public function add_payment_method() {
				
		// Get CC POST Data
    	$card_number 		 = wc_clean( $_POST[ $this->id.'-cardnumber'] );
        $card_number 		 = str_replace( ' ', '', $card_number);
        $card_cvc    		 = wc_clean( $_POST[ $this->id.'-card_cvc'] );
		
		$card_exp_year 		 = wc_clean( $_POST[ $this->id.'-expiry_y'] );
		$card_exp_month 		 = wc_clean( $_POST[ $this->id.'-expiry_m'] );
		
		//fix for SOAP-ERROR: Parsing WSDL: Couldn't load 
		libxml_disable_entity_loader(false); 

		// Cybersource Request 
		// http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_SO_API/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=request_fields_so.html
		$soap = $this->soaptoolkit_request(); 
		$temp_orderno = uniqid();
		$request = $soap->createRequest( $temp_orderno );

		$request->clientLibrary 		= "WooCommerce PHP Client";
		$request->clientLibraryVersion 	= phpversion();
		$request->clientEnvironment 	= php_uname();
		$request->merchantID 			= $this->merchant_id;			
		$request->merchantReferenceCode = $temp_orderno;			

		// disable Address Verification Services (AVS)
		$businessRules = new stdClass();

		if( $this->avs === true ){
			$ignore_avs = false;
		} else {
			$ignore_avs = true;
		}

		$businessRules->ignoreAVSResult = $ignore_avs;
		$request->businessRules  = $businessRules;

		// Cybersource CC object
		$card = new stdClass();
		$card->accountNumber 	= sanitize_text_field($card_number);
		$card->expirationMonth 	= sanitize_text_field($card_exp_month);
		$card->expirationYear 	= sanitize_text_field($card_exp_year);

		$cardtype 				= parent::get_cc_type( $card_number );

		if( ! empty( $cardtype ) ){
			$card->cardType 		= $cardtype;	
		}

		$card->cvNumber 	    = $card_cvc;

		$request->card = $card;

		$customer_id = get_current_user_id();
		
		// Cybersource Billing Address Object 
		$billTo = new stdClass();
		$billTo->firstName 	= sanitize_text_field(get_user_meta( $customer_id, 'billing_first_name', true ));
		$billTo->lastName 	= sanitize_text_field(get_user_meta( $customer_id, 'billing_last_name', true ));
		$billTo->street1	= sanitize_text_field(get_user_meta( $customer_id, 'billing_address_1', true ));
		$billTo->street2 	= sanitize_text_field(get_user_meta( $customer_id, 'billing_address_2', true ));
		$billTo->city 		= sanitize_text_field(get_user_meta( $customer_id, 'billing_city', true ));
		$billTo->state 		= sanitize_text_field(get_user_meta( $customer_id, 'billing_state', true ));
		$billTo->postalCode = sanitize_text_field(get_user_meta( $customer_id, 'billing_postcode', true ));
		$billTo->country 	= sanitize_text_field(get_user_meta( $customer_id, 'billing_country', true ));
		$billTo->email 		= sanitize_text_field(get_user_meta( $customer_id, 'billing_email', true ));
		$billTo->company 	= sanitize_text_field(get_user_meta( $customer_id, 'billing_company', true ));
		$billTo->phoneNumber = sanitize_text_field(get_user_meta( $customer_id, 'billing_phone', true ));
		$billTo->ipAddress 	= WC_Geolocation::get_ip_address();
		$request->billTo    = $billTo;

		// Cybersource Shipping Address Object
		$shipTo = new stdClass();
		$shipTo->city 			= sanitize_text_field(get_user_meta( $customer_id, 'shipping_city', true ));
		$shipTo->country 		= sanitize_text_field(get_user_meta( $customer_id, 'shipping_country', true ));
		$shipTo->firstName 		= sanitize_text_field(get_user_meta( $customer_id, 'shipping_first_name', true ));
		$shipTo->lastName 		= sanitize_text_field(get_user_meta( $customer_id, 'shipping_last_name', true ));
		$shipTo->phoneNumber 	= sanitize_text_field(get_user_meta( $customer_id, 'billing_phone', true ));
		$shipTo->postalCode 	= sanitize_text_field(get_user_meta( $customer_id, 'shipping_postcode', true ));
		$shipTo->state 			= sanitize_text_field(get_user_meta( $customer_id, 'shipping_state', true ));
		$shipTo->street1 		= sanitize_text_field(get_user_meta( $customer_id, 'shipping_address_1', true ));
		$shipTo->street2 		= sanitize_text_field(get_user_meta( $customer_id, 'shipping_address_2', true ));
		$request->shipTo    	= $shipTo;	


		// Add Tokenization 
		$recurringSubscriptionInfo = new stdClass();
		$recurringSubscriptionInfo->frequency = 'on-demand';
		$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;

		$paySubscriptionCreateService = new stdClass();
		$paySubscriptionCreateService->run = "true";
		$request->paySubscriptionCreateService = $paySubscriptionCreateService;

		$purchaseTotals 					= new stdClass();
		$purchaseTotals->currency 			= get_woocommerce_currency();
		$request->purchaseTotals 			= $purchaseTotals;

		$reply = $soap->runTransaction( $request );
		parent::log( 'Cybersource Request - SO: ' . print_r( $request, true ) );
		parent::log( 'Cybersource Response - SO: ' . print_r( $reply, true ) );

		if($reply->decision == 'ACCEPT' ){

			$token = $reply->paySubscriptionCreateReply->subscriptionID;

			if(!empty($token)){

				$set_last4 = substr($card_number,-4);
				$card_type = parent::get_cc_type($card_number);
				switch($card_type){
					case '001':
						$cc_type = __('Visa','woocommerce');
					break;
					case '002':
						$cc_type = __('MasterCard','woocommerce');
					break;
					case '003':
						$cc_type = __('American Express','woocommerce');
					break;
					case '004':
						$cc_type = __('Discover','woocommerce');
					break;
					case '005':
						$cc_type = __('Diners Club','woocommerce');
					break;
					case '014':
						$cc_type = __('EnRoute','woocommerce');
					break;
					case '007':
						$cc_type = __('JCB','woocommerce');
					break;
					case '024':
						$cc_type = __('Maestro', 'woocommerce');
					break;
					case '033':
						$cc_type = __('Visa Electron','woocommerce');
					break;
					case '035':
						$cc_type = __('Laser','woocommerce');
					break;
					case '006':
						$cc_type = __('Carte Blanche','woocommerce');
					break;
					case '034':
						$cc_type = __('Dankort','woocommerce');
					break;
					default:
						$cc_type = __('unknown','woocommerce');
					break;
				}

				$card_token[ 'token' ] 				= sanitize_text_field( $token );
				$card_token[ 'card_type' ] 			= sanitize_text_field($cc_type);
				$card_token[ 'set_last4' ]			= sanitize_text_field($set_last4);
				$card_token[ 'set_expiry_month' ]	= sanitize_text_field($card_exp_month);
				$card_token[ 'set_expiry_year' ]	= sanitize_text_field($card_exp_year);

				//* Fire save card token
				$ret = $this->save_card_token($card_token);

			}

		} else {

			wc_add_notice( __( $this->reason_code( $reply->reasonCode ) , 'woocommerce' ), 'error' );
		  	return;

		}

		
		
		if ( is_null( $ret ) ) {
		  wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' );
		  return;
		}
			
		return array(
		  'result'   => 'success',
		  'redirect' => wc_get_endpoint_url( 'payment-methods' ),
		);
	}

    public function reason_code( $code = '' ){
			
			$reason_codes['100'] = __( "Successful transaction.", "woocommerce" );
			$reason_codes['101'] = __( "The request is missing one or more required fields. ", "woocommerce" );
			$reason_codes['102'] = __( "One or more fields in the request contains invalid data.", "woocommerce" );
			$reason_codes['104'] = __( "The merchant reference code for this authorization request matches the merchant reference code of another authorization request that you sent within the past 15 minutes.", "woocommerce" );
			$reason_codes['110'] = __( "Only a partial amount was approved.", "woocommerce" );
			$reason_codes['150'] = __( sprintf("General system failure. Recurring Billing or Secure Storage service is not enabled for the merchant please contact the %s support", $this->method_title ), "woocommerce" );
			$reason_codes['151'] = __( "The request was received but there was a server timeout. This error does not include timeouts between the client and the server.", "woocommerce" );
			$reason_codes['152'] = __( "The request was received, but a service did not finish running in time. ", "woocommerce" );
			$reason_codes['200'] = __( "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the Address Verification System (AVS) check.", "woocommerce" );
			$reason_codes['201'] = __( "The issuing bank has questions about the request. You do not receive an authorization code programmatically, but you might receive one verbally by calling the processor.", "woocommerce" );
			$reason_codes['202'] = __( "Expired card. You might also receive this value if the expiration date you provided does not match the date the issuing bank has on file.", "woocommerce" );
			$reason_codes['203'] = __( "General decline of the card. No other information was provided by the issuing bank.", "woocommerce" );
			$reason_codes['204'] = __( "Insufficient funds in the account.", "woocommerce" );
			$reason_codes['205'] = __( "Stolen or lost card.", "woocommerce" );
			$reason_codes['207'] = __( "Issuing bank unavailable.", "woocommerce" );
			$reason_codes['208'] = __( "Inactive card or card not authorized for card-not-present transactions.", "woocommerce" );
			$reason_codes['209'] = __( "CVN did not match.", "woocommerce" );
			$reason_codes['210'] = __( "The card has reached the credit limit. ", "woocommerce" );
			$reason_codes['211'] = __( "Invalid CVN.", "woocommerce" );
			$reason_codes['221'] = __( "The customer matched an entry on the processorÕs negative file. ", "woocommerce" );
			$reason_codes['230'] = __( "The authorization request was approved by the issuing bank but declined by CyberSorce because it did not pass the CVN check.", "woocommerce" );
			$reason_codes['231'] = __( "Invalid account number.", "woocommerce" );
			$reason_codes['232'] = __( "The card type is not accepted by the payment processor.", "woocommerce" );
			$reason_codes['233'] = __( "General decline by the processor.", "woocommerce" );
			$reason_codes['234'] = __( "There is a problem with the information in your CyberSource account.", "woocommerce" );
			$reason_codes['235'] = __( "The requested capture amount exceeds the originally authorized amount. ", "woocommerce" );
			$reason_codes['236'] = __( "Processor failure. ", "woocommerce" );
			$reason_codes['237'] = __( "The authorization has already been reversed.", "woocommerce" );
			$reason_codes['238'] = __( "The authorization has already been captured.", "woocommerce" );
			$reason_codes['239'] = __( "The requested transaction amount must match the previous transaction amount. ", "woocommerce" );
			$reason_codes['240'] = __( "The card type sent is invalid or does not correlate with the credit card number.", "woocommerce" );
			$reason_codes['241'] = __( "The request ID is invalid.", "woocommerce" );
			$reason_codes['242'] = __( "You requested a capture, but there is no corresponding, unused authorization record. Occurs if there was not a previously successful authorization request or if the previously successful authorization has already been used by another capture request.", "woocommerce" );
			$reason_codes['243'] = __( "The transaction has already been settled or reversed.", "woocommerce" );
			$reason_codes['246'] = __( "The capture or credit is not voidable because the capture or credit information has already been submitted to your processor. or You requested a void for a type of transaction that cannot be voided.
Possible action: No action required.", "woocommerce" );
			$reason_codes['247'] = __( "You requested a credit for a capture that was previously voided.", "woocommerce" );
			$reason_codes['250'] = __( "The request was received, but there was a timeout at the payment processor.", "woocommerce" );
			$reason_codes['254'] = __( "Stand-alone credits are not allowed.", "woocommerce" );
			
			if( ! empty( $code )){
				$result = $reason_codes[$code];
			}else{
				$result = 'Unknown Error!';
			}
			
			return $result;
			
		}


		public function cybersource_thankyou_processing_order_status( $order_id ){

			//$order = new WC_Order( $order_id );
			$order = wc_get_order( $order_id );
			$order_data = $order->get_data();

			$transaction_id = get_post_meta( $order_id, '_cyber_transaction_id' , true );
			$decision_id = get_post_meta( $order_id, '_cyber_decision_id',  true );

			$order_status = parent::virtual_order_payment_complete_order_status( $order_id );

			// some paid transaction not change the order status 'processing'
			if ( 'pending' ==  $order_data['status'] && $decision_id == 'ACCEPT' && ! empty($transaction_id) ) {
				if($order_status == 'completed' ){
				    $order->update_status( 'completed' );
            	} else {
            		$order->update_status( 'processing' );
            	}	
			}
				
		}

    public function processing_level_payment($order_id,$order,$soap, $request){
	
			$order_data 	= $order->get_data();

			
			$transaction_id = get_post_meta( $order_id, '_'. $this->id .'_transaction_id' , true );
			$request_token 	= get_post_meta( $order_id, '_'. $this->id .'_request_token' , true );
			$cardcode 		= strtolower ( get_post_meta( $order_id, '_'. $this->id .'_cardcode' , true ) );

			if(empty($cardcode)){
				$card_token_id 	= get_post_meta( $order_id, '_'. $this->id .'_card_token_id' , true );
				$card_token_id 	= WC_Payment_Tokens::get( $card_token_id );
				$cardcode       = strtolower( $card_token_id->get_card_type() );

				switch($cardcode){
					case 'visa':
						$cardcode = "1";
						break;
					case 'mastercard':	
						$cardcode = "2";
						break;
				}
			}
			
			$currency = $order_data['currency'];

			$meta_capture = get_post_meta( $order_id, '_' . $this->id . '_capture', true );
				
			$amount = $order->get_total();

			//parent::log( 'Cybersource Request CardCode - SO: ' . print_r( array( $cardcode ), true ) );
			// $this->processing_level != 'none'
			if(  in_array($cardcode, $this->pl_cards ) ){		

				$capture_service = new \stdClass();
				$capture_service->run = 'true';
				$capture_service->purchasingLevel = $this->processing_level;

				$shipTo = new \stdClass();
				$shipTo->country = $order_data['shipping']['country'];
				$shipTo->postalCode = $order_data['shipping']['postcode'];
				$shipTo->state = $order_data['shipping']['state'];
				$request->shipTo = $shipTo;
				
				// Get Cart Items
				$cart_items = $order_data['line_items'];
				$request->item =array();	

				if( ! empty( $cart_items ) ){
					$x = 0;
					foreach( $cart_items as $values ){

						$data = $values->get_data();


						if( $data['product_id'] ){
							$product = wc_get_product( $data['product_id']);
							$product_sku  = $product->get_sku();
							$products_cats = $product->get_category_ids();
						} else {
							$product = wc_get_product(  $data['variation_id']);
							$product_sku = $product->get_sku();
							$products_cats = $product->get_category_ids();
						}

						
						if( ! empty( $product_sku ) ){
							$sku = $product_sku;
						} else {
							$sku = "na";
						}
						
						
						if( $this->round_line_taxamount === false ){
							$line_tax_amount = (double) number_format( $data['total_tax'], 2, ".", "");	
						} else {
							$line_tax_amount = round( $data['total_tax'] );	
						}

						if ( $this->round_line_unitprice === false ) {
							$line_unit_price = (double) number_format( $data['total'], 2, ".", "");
						} else {
							$line_unit_price = round($data['total']);
						}

						$units_of_measurement = !empty($this->settings['units_of_measurement']) ? $this->settings['units_of_measurement'] : '';
						$item_commodity_code = !empty($this->settings['item_commodity_code']) ? $this->settings['item_commodity_code'] : ''; 
						

						$_product 					= wc_get_product( $data['product_id'] );
						$unitPrice                  = ($data['total']/$data['quantity']);
						$line_items 				= new stdClass();
						$line_items->productName 	= sanitize_text_field($_product->get_name());
						$line_items->unitPrice 		= (double) number_format($unitPrice, 2, ".", "");
						$line_items->quantity 		= $data['quantity'];
						$line_items->taxAmount 		= (double) number_format($line_tax_amount, 2, ".", "");
						$line_items->totalAmount 	= (double) number_format($data['total'], 2, ".", "");

						if(!empty($units_of_measurement)){
							$line_items->unitOfMeasure 	= $units_of_measurement;
						}

						if(!empty($item_commodity_code)){
							$line_items->commodityCode 	= $item_commodity_code;
						}

						$line_items->id 					= $x;
						$request->item[] 					= $line_items;

						$x++;
					}
				}

				
				$request->purchaseTotals->currency = $order_data['currency'];

				$dutyAmount 	= ! empty($order_data['total_tax']) ? $order_data['total_tax'] : 0;
				$freightAmount 	= ! empty($order_data['shipping_total']) ? $order_data['shipping_total'] : 0 ;
				$discountAmount = ! empty($order_data['discount_total']) ? $order_data['discount_total'] : 0;;

				$request->purchaseTotals->dutyAmount 		= (double) number_format($dutyAmount, 2, ".", "");
				$request->purchaseTotals->freightAmount 	= (double) number_format($freightAmount, 2, ".", "");
				$request->purchaseTotals->discountAmount 	= (double) number_format($discountAmount, 2, ".", "");

				$invoiceHeader = new \stdClass();
				$invoiceHeader->userPO = $order_id;
				$request->invoiceHeader = $invoiceHeader;

				if ( isset( $transaction_id ) )
				{
					$capture_service->authRequestID = $transaction_id;
				}

				$request->ccCaptureService = $capture_service;
				$request->purchaseTotals->grandTotalAmount = (double) number_format( $amount, 2, ".", "");
				
				parent::log( 'Cybersource Request - SOP: ' . print_r( $request, true ) );

				$response = $soap->runTransaction( $request );

				parent::log( 'Cybersource Response - SOP: ' . print_r( $response, true ) );

				if(!empty($response->requestID) && $response->reasonCode == 100 && $response->decision == 'ACCEPT' ){

					// $request_ID 	= $response->requestID; 
					// $request_token  = $response->requestToken; 
					// update_post_meta( $order_id, '_' . $this->id . '_capture_request_id', $request_ID );
					// update_post_meta( $order_id, '_' . $this->id . '_request_token', $request_token );
					// update_post_meta( $order_id, '_' . $this->id . '_capture', 1 );

					$order->update_meta_data( '_' . $this->id . '_capture_request_id', $request_ID );
					$order->update_meta_data( '_' . $this->id . '_request_token', $request_token );
					$order->update_meta_data( '_' . $this->id . '_capture', 1 );
					$order->save();
					
					$order->add_order_note(
						esc_html__( sprintf(
							'%1$s Capture %2$s with Request ID of %3$s',
							$this->method_title,
							sanitize_text_field($response->decision),
							sanitize_text_field($response->requestID)
						), 'woocommerce' )
					);

					return true;

				} else {

					$err_msg = esc_html__($this->reason_code($response->ccCaptureReply->reasonCode));

					$order->update_status( 'failed' );
					$order->add_order_note(
						esc_html__( sprintf(
							'%1$s Capture %2$s, %3$s',
							$this->method_title,
							sanitize_text_field($response->decision),
							sanitize_text_field($err_msg)
						), 'woocommerce' )
					);
					
					return new WP_Error( $this->id . '_capture_error', esc_html__( ucwords($this->method_title). ' ' . 'Payment Capture Failed, ' . $err_msg  , '' ,'woocommerce' ) );

				}
				return false;


			} //in_array($cardcode, $this->pl_cards )

			return $response;
	}		

	public function capture_order_status_completed( $order_id, $order ){

		if( $this->transaction_type == 'sale' ) return;

		$transaction_id = get_post_meta( $order_id, '_'. $this->id .'_transaction_id' , true );
		$request_token = get_post_meta( $order_id, '_'. $this->id .'_request_token' , true );
		$amount = $order->get_total();

		if ( ! $transaction_id ) {
			return new WP_Error( $this->id . '_capture_error',
				esc_html__( sprintf(
					'%s Capture failed because the Request ID is missing.',
					ucwords($this->id)
				), 'woocommerce' )
			);
		}

		$soap = $this->soaptoolkit_request(); 
		$request = $soap->createRequest( $order->get_currency());
		
		$request->clientLibrary 		= "WooCommerce PHP Client";
		$request->clientLibraryVersion 	= phpversion();
		$request->clientEnvironment 	= php_uname();
		$request->merchantID 			= $this->merchant_id;			
		$request->merchantReferenceCode = $order_id;

		if($this->processing_level != 'none' ){

			$response = $this->processing_level_payment($order_id,$order,$soap,$request);
			return $response;

		} else { // Default payment process

			if( !empty($transaction_id) && !empty($request_token)  ){

				$capture_service = new stdClass();
				$capture_service->run = 'true';
				$capture_service->authRequestToken = $request_token;

				if ( isset( $transaction_id ) )
				{
					$capture_service->authRequestID = $transaction_id;
				}

				$request->ccCaptureService = $capture_service;
				$request->purchaseTotals->grandTotalAmount = (double) number_format( $amount, 2, ".", "");
				
				parent::log( 'Cybersource Request - SOP: ' . print_r( $request, true ) );

				$response = $soap->runTransaction( $request );

				parent::log( 'Cybersource Response - SOP: ' . print_r( $response, true ) );

				if(!empty($response->requestID) && $response->reasonCode == 100 && $response->decision == 'ACCEPT' ){

					$request_ID 	= $response->requestID; 
					$request_token  = $response->requestToken; 
					// update_post_meta( $order_id, '_' . $this->id . '_capture_request_id', $request_ID );
					// update_post_meta( $order_id, '_' . $this->id . '_request_token', $request_token );
					// update_post_meta( $order_id, '_' . $this->id . '_capture', 1 );

					$order->update_meta_data( '_' . $this->id . '_capture_request_id', $request_ID );
					$order->update_meta_data( '_' . $this->id . '_request_token', $request_token );
					$order->update_meta_data( '_' . $this->id . '_capture', 1 );
					$order->save();
					
					$order->add_order_note(
						esc_html__( sprintf(
							'%1$s Capture %2$s with Request ID of %3$s',
							$this->method_title,
							sanitize_text_field($response->decision),
							sanitize_text_field($response->requestID)
						), 'woocommerce' )
					);

					return true;

				} else {

					$err_msg = esc_html__($this->reason_code($response->ccCaptureReply->reasonCode));

					$order->update_status( 'failed' );
					$order->add_order_note(
						esc_html__( sprintf(
							'%1$s Capture %2$s, %3$s',
							$this->method_title,
							sanitize_text_field($response->decision),
							sanitize_text_field($err_msg)
						), 'woocommerce' )
					);
					
					return new WP_Error( $this->id . '_capture_error', esc_html__( ucwords($this->method_title). ' ' . 'Payment Capture Failed, ' . $err_msg  , '' ,'woocommerce' ) );

				}
				return false;
			}

		}
		return $response;
	}

	public function process_refund( $order_id, $amount = null, $reason = '' ){

		$order = wc_get_order( $order_id );
		$order_data = $order->get_data();
	
		if( $this->transaction_type == 'authorization' ){
			return parent::reversal_payment ( $order_id, $amount = null, $reason = '' );
		} else {
			return parent::void_payment ( $order_id, $amount = null, $reason = '' );
		}
		
	}
		
}