<?php
/**
 *
 * Copyright (C) Die Randgruppe GmbH
 *
 * http://www.randshop.com
 * http://www.dierandgruppe.com
 *
 * Unter der Lizenz von Die Randgruppe GmbH:
 * http://www.randshop.com/Lizenz
 *
 */
class PaypalExpress extends PaymentInterface
{
    private $apiUsername;
    private $apiPassword;
    private $apiSignature;
    private $useSandbox;

    private $paymentStep = 'setExpressCheckout';

    const VERSION = '65.0';
    const API_ENDPOINT = 'https://api-3t.paypal.com/nvp';
    const API_ENDPOINT_SANDBOX = 'https://api-3t.sandbox.paypal.com/nvp';
    const PAYPAL_URL = 'https://www.paypal.com/webscr&cmd=_express-checkout&token=';
    const PAYPAL_URL_SANDBOX = 'https://www.sandbox.paypal.com/webscr&cmd=_express-checkout&token=';
    const COMPLETE_CHECKOUT_URL = 'https://www.paypal.com/webscr?cmd=_complete-expresscheckout&token=';
    const COMPLETE_CHECKOUT_URL_SANDBOX = 'https://www.sandbox.paypal.com/webscr?cmd=_complete-expresscheckout&token=';

    private $strings = array(
        'de_DE' => array(
            'rabatt' => 'Rabatt'
        ),
        'en_UK' => array(
            'rabatt' => 'Discount'
        )
    );
    private $localeStrings;

    private $token;

    static $stdPluginCaps = array(
        'createInvoice' => false,
        'checkPending' => false,
        'changeOrder' => false,
        'cancelPayment' => false,
        'plugin_version' => '1.2'
    );

    static public function getPluginCaps($feature = false) {
        if($feature) {
            return self::$stdPluginCaps[$feature];
        } else {
            return self::$stdPluginCaps;
        }
    }

    public function setLocale($locale)
    {
        if(isset($this->strings[$locale]))
            $this->localeStrings = $this->strings[$locale];
        else
            $this->localeStrings = $this->strings['de_DE'];
    }

    public function getDisplayName()
    {
        return 'Paypal Express';
    }

    public function getConfigParams()
    {
        return array(
            new ParamCfg('apiUsername', 'API Username', ParamCfg::TYPE_STRING),
            new ParamCfg('apiPassword', 'API Passwort', ParamCfg::TYPE_STRING),
            new ParamCfg('apiSignature', 'API Signatur', ParamCfg::TYPE_STRING),
            new ParamCfg('useSandbox', 'Sandbox Testbetrieb', ParamCfg::TYPE_CHECK)
        );
    }

    public function setConfig($arrConfig)
    {
        $this->apiUsername = $arrConfig['apiUsername'];
        $this->apiPassword = $arrConfig['apiPassword'];
        $this->apiSignature = $arrConfig['apiSignature'];
        $this->useSandbox = $arrConfig['useSandbox'];
    }

    public function validateConfig($arrConfig)
    {
        if(trim($arrConfig['apiUsername']) == '') {
            $errors['apiUsername'] = 'Bitte Usernamen angeben';
        }
        if(trim($arrConfig['apiPassword']) == '') {
            $errors['apiPassword'] = 'Bitte Passwort angeben';
        }
        if(trim($arrConfig['apiSignature']) == '') {
            $errors['apiSignature'] = 'Bitte Signatur angeben';
        }
        if($errors)
            return $errors;
        else
            return true;
    }

    public function getPaymentChoiceTemplate()
    {
        return array();
    }

    public function validateAndSetPaymentChoice($arrParams)
    {
        return true;
    }

    /**
     *
     * @param $cart
     * @param $customer
     * @param $requestParams
     * @return \PaymentResult
     */
    public function doPayment(OrderData $order, array $requestParams)
    {
        switch($this->paymentStep) {
            case 'setExpressCheckout':
                $gesamtsumme = $order->totalAmount;
                $paymentAmount = round($gesamtsumme, 2);
                $currencyCodeType = $order->currency->ISOCode;

                $nvpstr = "&PAYMENTREQUEST_0_AMT=".$paymentAmount;
                $index = 0;
                foreach($order->cartItems as $cartItem) {
                    $nvpstr.= '&L_PAYMENTREQUEST_0_NAME'.$index.'='.urlencode($cartItem->name);
                    $nvpstr.= '&L_PAYMENTREQUEST_0_AMT'.$index.'='.$cartItem->amount;
                    $nvpstr.= '&L_PAYMENTREQUEST_0_NUMBER'.$index.'='.$cartItem->number;
                    $nvpstr.= '&L_PAYMENTREQUEST_0_QTY'.$index.'='.$cartItem->quantity;
                    $index++;
                }

                $itemAmount = $order->itemAmount;
                if($order->discount) {
                    $nvpstr.= '&L_PAYMENTREQUEST_0_NAME'.$index.'='.urlencode($this->localeStrings['rabatt']);
                    $nvpstr.= '&L_PAYMENTREQUEST_0_AMT'.$index.'='.round($order->discount->amount, 2);
                    $nvpstr.= '&L_PAYMENTREQUEST_0_QTY'.$index.'=1';
                    $index++;
                    $itemAmount += $order->discount->amount;
                }

                if($order->voucher) {
                    $nvpstr.= '&L_PAYMENTREQUEST_0_NAME'.$index.'='.urlencode($order->voucher->name);
                    $nvpstr.= '&L_PAYMENTREQUEST_0_AMT'.$index.'='.round($order->voucher->amount, 2);
                    $nvpstr.= '&L_PAYMENTREQUEST_0_QTY'.$index.'=1';
                    $itemAmount += $order->voucher->amount;
                }

                $nvpstr .= "&PAYMENTREQUEST_0_ITEMAMT=".round($itemAmount, 2);
                $nvpstr .= "&PAYMENTREQUEST_0_SHIPPINGAMT=".round($order->shippingFeeAmount, 2);
                $nvpstr .= "&PAYMENTREQUEST_0_HANDLINGAMT=".round($order->paymentFeeAmount, 2);
                $taxAmount = round($order->taxAmount, 2);
                if(!$order->amountsAreGross) { // Mwst. wird nur bei Geschäftskunden explizit angegeben, bei Endkunden müsste man sonst auch Nettopreise an Paypal übermitteln, was den Kunden nur verwirrt
                    $nvpstr .= "&PAYMENTREQUEST_0_TAXAMT=".$taxAmount;
                }

                $nvpstr .= "&ReturnUrl=".urlencode(self::getReturnURL());
                $nvpstr .= "&CANCELURL=".urlencode(self::getReturnURL() . '?action=cancel');
                $nvpstr .= "&GIROPAYSUCCESSURL=".urlencode(self::getReturnURL() . '?action=giropaysuccess');
                $nvpstr .= "&GIROPAYCANCELURL=".urlencode(self::getReturnURL() . '?action=giropaycancel');
                $nvpstr .= "&BANKTXNPENDINGURL=".urlencode(self::getReturnURL() . '?action=bankTxnPending');
                $nvpstr .= "&PAYMENTREQUEST_0_CURRENCYCODE=".$currencyCodeType;
                $nvpstr .= "&PAYMENTREQUEST_0_LOCALECODE=".$order->customer->invoiceAddress->country->isocode;
                if($order->customer->deliveryAddress) {
                    $address = $order->customer->deliveryAddress;
                } else {
                    $address = $order->customer->invoiceAddress;
                }
                $nvpstr .= "&SHIPTONAME=" . $address->firstName . " " . $address->lastName;
                $nvpstr .= "&SHIPTOSTREET=" . $address->street . " " . $address->houseNumber;
                $nvpstr .= "&SHIPTOCITY=" . $address->city;
                if($address->state) {
                    $nvpstr .= "&SHIPTOSTATE=" . $address->state->code;
                }
                $nvpstr .= "&SHIPTOCOUNTRYCODE=" . $address->country->isocode;
                $nvpstr .= "&SHIPTOZIP=" . $address->zipCode;
                $nvpstr .= "&ADDROVERRIDE=1";

//                			echo $nvpstr;

                $resArray=$this->hash_call("SetExpressCheckout",$nvpstr);
                if(strtoupper($resArray['ACK']) == 'SUCCESS')
                {
                    $this->token = $resArray['TOKEN'];
                    $this->paymentStep = 'doExpressCheckout';
                    return new PaymentResult(PaymentResult::REDIRECT_REQUIRED, $this->getPaypalURL() . $resArray['TOKEN']);
                }
                else
                {
                    $this->paymentStep = 'errorOccured';
                    return new PaymentResult(PaymentResult::PAYMENT_FAILED, '', array(), $resArray['L_LONGMESSAGE0']);
                }
            break;
            case 'doExpressCheckout':
                $nvpstr = '&TOKEN='.$this->token;
                $resArray=$this->hash_call("GetExpressCheckoutDetails",$nvpstr);
                if(strtoupper($resArray['ACK']) == 'SUCCESS')
                {
                    $paymentAction = 'Sale';
                    $payerID = $resArray['PAYERID'];

                    $paymentAmount = round($order->totalAmount, 2);

                    $shippingAmount = round($order->shippingFeeAmount, 2);
                    $handlingAmount = round($order->paymentFeeAmount, 2);

                    $waehrungObject = GetWaehrungDetail();
                    $currencyCodeType = $order->currency->ISOCode;

                    $nvpstr = "&TOKEN=".$this->token;
                    $nvpstr .= "&PAYMENTACTION=".$paymentAction;
                    $nvpstr .= "&PAYERID=".$payerID;
                    $nvpstr .= "&PAYMENTREQUEST_0_AMT=".$paymentAmount;
                    $index = 0;
                    foreach($order->cartItems as $cartItem) {
                        $nvpstr.= '&L_PAYMENTREQUEST_0_NAME'.$index.'='.urlencode($cartItem->name);
                        $nvpstr.= '&L_PAYMENTREQUEST_0_AMT'.$index.'='.$cartItem->amount;
                        $nvpstr.= '&L_PAYMENTREQUEST_0_NUMBER'.$index.'='.urlencode($cartItem->number);
                        $nvpstr.= '&L_PAYMENTREQUEST_0_QTY'.$index.'='.$cartItem->quantity;
                        $index++;
                    }

                    $itemAmount = $order->itemAmount;
                    if($order->discount) {
                        $nvpstr.= '&L_PAYMENTREQUEST_0_NAME'.$index.'='.urlencode($this->localeStrings['rabatt']);
                        $nvpstr.= '&L_PAYMENTREQUEST_0_AMT'.$index.'='.round($order->discount->amount, 2);
                        $nvpstr.= '&L_PAYMENTREQUEST_0_QTY'.$index.'=1';
                        $index++;
                        $itemAmount += $order->discount->amount;
                    }

                    if($order->voucher) {
                        $nvpstr.= '&L_PAYMENTREQUEST_0_NAME'.$index.'='.urlencode($order->voucher->name);
                        $nvpstr.= '&L_PAYMENTREQUEST_0_AMT'.$index.'='.round($order->voucher->amount, 2);
                        $nvpstr.= '&L_PAYMENTREQUEST_0_QTY'.$index.'=1';
                        $itemAmount += $order->voucher->amount;
                    }

                    $nvpstr .= "&PAYMENTREQUEST_0_ITEMAMT=".round($itemAmount, 2);
                    $nvpstr .= "&PAYMENTREQUEST_0_SHIPPINGAMT=".$shippingAmount;
                    $nvpstr .= "&PAYMENTREQUEST_0_HANDLINGAMT=".$handlingAmount;

                    $taxAmount = round($order->taxAmount,2);
                    if(!$order->amountsAreGross)
                        $nvpstr .= "&PAYMENTREQUEST_0_TAXAMT=".$taxAmount;
                    $nvpstr .= "&PAYMENTREQUEST_0_CURRENCYCODE=".$currencyCodeType;
                    $nvpstr .= "&PAYMENTREQUEST_0_INVNUM=".$order->orderID;
                    $nvpstr .= "&PAYMENTREQUEST_0_DESC=".urlencode("Kundennr.: ".$order->customer->customerID. "");
                    $nvpstr .= "&PAYMENTREQUEST_0_NOTETEXT=".urlencode("Kundennr.: ".$order->customer->customerID. "");
                    $nvpstr .= "&NOTIFYURL=".urlencode(URLPFAD_NOSSL."interfaces/PayPalExpressIPN.php");

                    //				echo $nvpstr;
                    $resArray=$this->hash_call("DoExpressCheckoutPayment", $nvpstr);
                    if(strtoupper($resArray['ACK']) == "SUCCESS") {
                        if($resArray['PAYMENTINFO_0_PAYMENTSTATUS'] == 'Completed') {
                            $this->paymentStep = 'completed';
                            return new PaymentResult(PaymentResult::PAYMENT_COMPLETE);
                        } else {
                            $this->paymentStep = 'completed';
                            return new PaymentResult(PaymentResult::PAYMENT_PENDING);
                        }
                    } else {
                        $this->paymentStep = 'errorOccured';

                        return new PaymentResult(PaymentResult::PAYMENT_FAILED, '', array(), $resArray['L_LONGMESSAGE0']);
                    }
                } else {
                    return new PaymentResult(PaymentResult::PAYMENT_FAILED, '', array(), $resArray['L_LONGMESSAGE0']);
                }
                break;
            case 'errorOccured':
                return new PaymentResult(PaymentResult::PAYMENT_FAILED);
                break;
            default:

                break;
        }
    }

    private function getApiEndpoint() {
        if($this->useSandbox)
            return self::API_ENDPOINT_SANDBOX;
        else
            return self::API_ENDPOINT;
    }

    private function getPaypalURL() {
        if($this->useSandbox)
            return self::PAYPAL_URL_SANDBOX;
        else
            return self::PAYPAL_URL;
    }

    private function getCompleteCheckoutURL() {
        if($this->useSandbox)
            return self::COMPLETE_CHECKOUT_URL_SANDBOX;
        else
            return self::COMPLETE_CHECKOUT_URL;
    }

    private function hash_call($methodName,$nvpStr)
    {
        //setting the curl parameters.
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$this->getApiEndpoint());
        curl_setopt($ch, CURLOPT_VERBOSE, 1);

        //turning off the server and peer verification(TrustManager Concept).
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_SSLVERSION, 3);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_POST, 1);
        //if USE_PROXY constant set to TRUE in Constants.php, then only proxy will be enabled.
        //Set proxy name to PROXY_HOST and port number to PROXY_PORT in constants.php
//        if(USE_PROXY)
//            curl_setopt ($ch, CURLOPT_PROXY, PROXY_HOST.":".PROXY_PORT);

        //NVPRequest for submitting to server
        $nvpreq="METHOD=".urlencode($methodName)."&VERSION=".urlencode(self::VERSION)."&PWD=".urlencode($this->apiPassword)."&USER=".urlencode($this->apiUsername)."&SIGNATURE=".urlencode($this->apiSignature).$nvpStr;

        //setting the nvpreq as POST FIELD to curl
        curl_setopt($ch,CURLOPT_POSTFIELDS,$nvpreq);

        //getting response from server
        $response = curl_exec($ch);

        //convrting NVPResponse to an Associative Array
        $nvpResArray=$this->deformatNVP($response);
        $nvpReqArray=$this->deformatNVP($nvpreq);
        $_SESSION['nvpReqArray']=$nvpReqArray;

        //closing the curl
        curl_close($ch);

        return $nvpResArray;
    }

    /** This function will take NVPString and convert it to an Associative Array and it will decode the response.
     * It is usefull to search for a particular key and displaying arrays.
     * @nvpstr is NVPString.
     * @nvpArray is Associative Array.
     */

    function deformatNVP($nvpstr)
    {

        $intial=0;
        $nvpArray = array();


        while(strlen($nvpstr)){
            //postion of Key
            $keypos= strpos($nvpstr,'=');
            //position of value
            $valuepos = strpos($nvpstr,'&') ? strpos($nvpstr,'&'): strlen($nvpstr);

            /*getting the Key and Value values and storing in a Associative Array*/
            $keyval=substr($nvpstr,$intial,$keypos);
            $valval=substr($nvpstr,$keypos+1,$valuepos-$keypos-1);
            //decoding the respose
            $nvpArray[urldecode($keyval)] =urldecode( $valval);
            $nvpstr=substr($nvpstr,$valuepos+1,strlen($nvpstr));
        }
        return $nvpArray;
    }


}
