<?php

class Am_Paysystem_Commonpay extends Am_Paysystem_CreditCard
{
    const
        PLUGIN_STATUS = self::STATUS_BETA,
        PLUGIN_DATE = '$Date$',
        PLUGIN_REVISION = '5.6.2',
        SANDBOX_URL = 'https://ipg-test.commonpay.com/lmg/wallet/start.jsf',
        LIVE_URL = 'https://ipg.commonpay.com/lmg/wallet/start.jsf',
        SANDBOX_REST_URL = 'https://ipg-test.commonpay.com/sds/dispatcher3/rest/transaction/',
        LIVE_REST_URL = 'https://ipg.commonpay.com/sds/dispatcher3/rest/transaction/',
        CONFIG_MERCHANT_ID = 'merchant_id',
        CONFIG_SHOP_ID = 'shop_id',
        CONFIG_SECRET = 'secret',
        CONFIG_TEST_MODE = 'test-mode';

    protected
        $defaultTitle = "Pay with your Credit Card",
        $defaultDescription = "accepts all major credit cards",
        $_pciDssNotRequired = true;

    function allowPartialRefunds()
    {
        return true;
    }

    function getSupportedCurrencies()
    {
        return array_keys(Am_Currency::getFullList());
    }

    public function getRecurringType()
    {
        return self::REPORTS_CRONREBILL;
    }

    function _initSetupForm(Am_Form_Setup $form)
    {
        $form->addText(self::CONFIG_MERCHANT_ID)
            ->setLabel(___("Merchant ID\n"
                . "The unique identifier issued to you by your payment provider."));

        $form->addText(self::CONFIG_SHOP_ID)
            ->setLabel(___("Shop ID"));

        $form->addSecretText(self::CONFIG_SECRET, 'class="am-el-wide"')
            ->setLabel(___("Your Secret key"));

        $form->addAdvCheckbox(self::CONFIG_TEST_MODE)->setLabel(___('Test Mode'));
    }

    function getOrderDescription(Invoice $invoice)
    {
        $res = array();
        $i = 1;
        foreach($invoice->getItems() as $item)
        {
            $res[] = array(
                'itemID' => $i,
                'itemQuantity' => $item->qty,
                'itemName' => $item->item_title,
                'itemDescription' => $item->item_description,
                'itemAmountNet' => $item->first_price,
                'itemAmountGross' => $item->first_total
            );
            $i++;
        }
        return json_encode(array(
            'orderDescription' => array(
                'basket' => $res
            )
        ));
    }

    public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result)
    {
        $user = $invoice->getUser();
        $post = array(
            'interfaceVersion' => '1.0.2',
            'transactionID' => $invoice->public_id,
            'merchantID' => $this->getConfig(self::CONFIG_MERCHANT_ID),
            'shopID' => $this->getConfig(self::CONFIG_SHOP_ID),
            'amount' => $invoice->first_total,
            'currency' => $invoice->currency,
            'orderDescription' => $this->getOrderDescription($invoice),
            'paymentProcessingType' => 'immediate',
            'paymentIdentifier' => 992,
            'aboTypeID' => 'external',
            'returnURL' => $this->getPluginUrl('thanks'),
            'timestamp' => date("Y-m-d\TH:i:s") . "," . round(microtime(true) / 10000000),
            'customerFirstName' => $user->name_f,
            'customerLastName' => $user->name_l,
            'customerStreet' => $user->street,
            'customerZIPCode' => $user->zip,
            'customerCity' => $user->city,
            'customerAddressAddition' => $user->street2,
            'customerCountry' => $user->country ? $this->getDi()->db->selectCell("SELECT alpha3 from ?_country where country = ?", $user->country) : '',
            'customerEmail' => $user->email,
            'customerPhone' => $user->phone,
            'hashType' => 'SHA256',
            'hashSalt' => bin2hex(openssl_random_pseudo_bytes(16)),
            'secret' => $this->getConfig(self::CONFIG_SECRET),
        );
        $hash = hash('sha256', implode('', $post));
        $post['hash'] = $hash;

        $a = new Am_Paysystem_Action_Form($this->getConfig(self::CONFIG_TEST_MODE) ? self::SANDBOX_URL : self::LIVE_URL);
        foreach ($post as $key => $value)
            $a->$key = $value;
        $result->setAction($a);
    }

    public function directAction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
    {
        if ($request->getActionName() == 'thanks')
        {
            $log = $this->logRequest($request);
            try {
                $transaction = $this->createThanksTransaction($request, $response, $invokeArgs);
            } catch (Am_Exception_Paysystem_NotImplemented $e) {
                $this->logError("[thanks] page handling is not implemented for this plugin. Define [createThanksTransaction] method in plugin");
                throw $e;
            }
            $transaction->setInvoiceLog($log);
            try {
                $transaction->process();
            } catch (Am_Exception_Paysystem_TransactionAlreadyHandled $e) {
                // ignore this error, as it happens in "thanks" transaction
                // we may have received IPN about the payment earlier
            } catch (Exception $e) {
                throw $e;
                $this->getDi()->errorLogTable->logException($e);
                throw Am_Exception_InputError(___("Error happened during transaction handling. Please contact website administrator"));
            }
            $log->setInvoice($transaction->getInvoice())->update();
            $this->invoice = $transaction->getInvoice();
            $response->setRedirect($this->getReturnUrl());
        } else {
            return parent::directAction($request, $response, $invokeArgs);
        }
    }

    public function createThanksTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
    {
        return new Am_Paysystem_Transaction_Commonpay_Thanks($this, $request, $response, $invokeArgs);
    }

    public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc=null, Am_Paysystem_Result $result)
    {
        $type = 'CCPurchase';
        $request = new Am_HttpRequest(($this->getConfig(self::CONFIG_TEST_MODE) ? self::SANDBOX_REST_URL : self::LIVE_REST_URL) . $type, Am_HttpRequest::METHOD_POST);
        $vars = array(
            'interfaceVersion' => '3.0.0',
            'referenceSequenceNo' => $this->getDi()->invoicePaymentTable->getLastReceiptId($invoice->pk()),
            'transactionType' => $type,
            'transactionID' => $invoice->public_id . '-' . $this->getDi()->security->randomString(3),
            'merchantID' => $this->getConfig(self::CONFIG_MERCHANT_ID),
            'shopID' => $this->getConfig(self::CONFIG_SHOP_ID),
            'amount' => $invoice->second_total,
            'currency' => $invoice->currency,
            'isRecurringTransaction' => 'true',
            'hashType' => 'SHA256',
            'hashSalt' => bin2hex(openssl_random_pseudo_bytes(16))
        );
        $hash = hash('sha256', implode('', $vars) . $this->getConfig(self::CONFIG_SECRET));
        $vars['hash'] = $hash;
        unset($vars['transactionType']);
        $request->setBody($body = json_encode($vars));
        $request->setHeader(array(
            'Content-Type' => 'application/json',
            'Content-Length' => strlen($body)
        ));
        $transaction = new Am_Paysystem_Transaction_Commonpay_Sale($this, $invoice, $request, false);
        $transaction->run($result);
    }

    public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount)
    {
        $type = 'CCRefund';
        $request = new Am_HttpRequest(($this->getConfig(self::CONFIG_TEST_MODE) ? self::SANDBOX_REST_URL : self::LIVE_REST_URL) . $type, Am_HttpRequest::METHOD_POST);
        $vars = array(
            'interfaceVersion' => '3.0.0',
            'referenceSequenceNo' => $payment->receipt_id,
            'transactionType' => $type,
            'transactionID' => $payment->getInvoice()->public_id . '-' . $this->getDi()->security->randomString(3),
            'merchantID' => $this->getConfig(self::CONFIG_MERCHANT_ID),
            'shopID' => $this->getConfig(self::CONFIG_SHOP_ID),
            'amount' => $amount,
            'currency' => $payment->getInvoice()->currency,
            'hashType' => 'SHA256',
            'hashSalt' => bin2hex(openssl_random_pseudo_bytes(16))
        );
        $hash = hash('sha256', implode('', $vars) . $this->getConfig(self::CONFIG_SECRET));
        $vars['hash'] = $hash;
        unset($vars['transactionType']);
        $request->setBody($body = json_encode($vars));
        $request->setHeader(array(
            'Content-Type' => 'application/json',
            'Content-Length' => strlen($body)
        ));
        $tr = new Am_Paysystem_Transaction_Commonpay_Refund($this, $payment->getInvoice(), $request, false);
        $tr->run($result);
    }

}

class Am_Paysystem_Transaction_Commonpay_Thanks extends Am_Paysystem_Transaction_Incoming_Thanks
{
    public function findInvoiceId()
    {
        return $this->request->getFiltered('transactionID');
    }

    public function getUniqId()
    {
        return $this->request->get('sequenceNo');
    }

    public function validateStatus()
    {
        return in_array($this->request->get('resultCode'), array(0,1000));
    }

    public function validateTerms()
    {
        return doubleval($this->request->get('amount')) == doubleval($this->invoice->first_total);
    }
    public function validateSource()
    {
        $text = $this->request->get('sequenceNo') . $this->request->get('transactionID') . $this->request->get('merchantID') . $this->request->get('shopID') .
            $this->request->get('amount') . $this->request->get('currency') . $this->request->get('orderDescription') . $this->request->get('paymentType') .
            $this->request->get('timestamp') . $this->request->get('resultCode') .
            $this->request->get('hashType') . $this->request->get('hashSalt');
        if(substr($this->request->get('hashType'), 0, 5) === "HMac-") {
            $h = substr($this->request->get('hashType'), 5);
            $hash = hash_hmac(strtolower($h), $text, $this->getPlugin()->getConfig(Am_Paysystem_Commonpay::CONFIG_SECRET));
        } else {
            $hash = hash(strtolower($this->request->get('hashType')), $text . $this->getPlugin()->getConfig(Am_Paysystem_Commonpay::CONFIG_SECRET));
        }
        return strtoupper($hash) == strtoupper($this->request->get('hash'));
    }
}

class Am_Paysystem_Transaction_Commonpay_Sale extends Am_Paysystem_Transaction_CreditCard
{
    public function validate()
    {
        print_rre($this->vars);
        if(($this->vars['resultCode'] != 'FAILURE') && in_array($this->vars['resultCode'], array(0,1000)))
        {
            $this->result->setSuccess($this);
        }
        else
        {
            $this->result->setFailed("Payment failed");
        }
    }

    public function parseResponse(){
        $this->vars = json_decode($this->response->getBody(), true);
    }

    public function getUniqId()
    {
        return $this->vars['sequenceNo'];
    }
}

class Am_Paysystem_Transaction_Commonpay_Refund extends Am_Paysystem_Transaction_CreditCard
{
    public function validate()
    {
        if(($this->vars['resultCode'] != 'FAILURE') && in_array($this->vars['resultCode'], array(0,1000)))
        {
            $this->result->setSuccess($this);
        }
        else
        {
            $this->result->setFailed("Refund failed");
        }
    }

    public function parseResponse(){
        $this->vars = json_decode($this->response->getBody(), true);
    }

    public function getUniqId()
    {
        return $this->vars['sequenceNo'];
    }
}
