<?php
/* * ***********************************************************************************************************************************
 * 														INITIALISE
 * *********************************************************************************************************************************** */

use Carbon\Carbon;
use Mtc\Modules\Members\Models\Member;
use Mtc\Shop\Basket\Protx;
use Mtc\Shop\Checkout\SagepayResponse;
use Mtc\Shop\Order\Note;

$path = "../../../";

$checkoutServer = true;

require_once($path . 'core/includes/header.inc.php');
require_once('function.sagepay.php');

SagepayResponse::query()
    ->create([
        'date' => Carbon::now(),
        'VendorTxCode' => $_REQUEST['VendorTxCode'],
        'StatusDetail' => $_REQUEST['StatusDetail'],
        'response' => serialize($_REQUEST),
    ]);

/**
 * ** Information is POSTed to this page from Sage Pay Server. The POST will ALWAYS contain the VendorTxCode, **
 * ** VPSTxID and Status fields.  We'll extract these first and use them to decide how to respond to the POST. * */
//Define end of line character used to correctly format response to Sage Pay Server
$eoln = chr(13) . chr(10);

$strStatus = cleaninput($_REQUEST["Status"], "Text");
//$strSagePayVendorTxCode will match the VendorTxCode in MySagePay
$strSagePayVendorTxCode = cleaninput($_REQUEST["VendorTxCode"], "VendorTxCode");
//$strWebVendorTxCode is the VendorTxCode before transaction prefix added
$strWebVendorTxCode = preg_replace('/^' . config('site.tnx_prefix') . '/', '', $strSagePayVendorTxCode);
$strVPSTxId = cleaninput($_REQUEST["VPSTxId"], "Text");

// Using the VPSTxId and VendorTxCode, we can retrieve our SecurityKey from our database
// This enables us to validate the POST to ensure it came from the Sage Pay Systems
// Lets check the Basket_PROTX Table

// 2015-01-27 GB: Looking up by VendorTxCode is never going to work because ID is an INTEGER and TxCode is a STRING
// => use VPSTxId for the lookup and ditch the old basket.seccode hack

$basketProtx = Protx::query()
    ->where('VPSTxId', $strVPSTxId)
    ->first();

$basketid = $basketProtx->basket_id;
$strSecurityKey = $basketProtx->secKey;

ob_start();

if (empty($basketProtx)) {
    /** We cannot find a record of this order in the database, so something isn't right **
     * * To protect the customer, we should send back an INVALID response.  This will prevent **
     * * the Sage Pay Server systems from settling any authorised transactions.  We will also send a **
     * * RedirectURL that points to our orderFailure page, passing details of the error **
     * * in the Query String so that the page knows how to respond to the customer * */
    echo "Status=INVALID" . $eoln;

    /** Only use the Internal FQDN value during development.  In LIVE systems, always use the actual FQDN * */
    if ($strConnectTo == "LIVE") {
        echo "RedirectURL=" . $strYourSiteFQDN . $strVirtualDir . "/server/order-failed.php" . $eoln;
    } else {
        echo "RedirectURL=" . $strYourSiteFQDN . $strVirtualDir . "/server/order-failed.php" . $eoln;
    }

    echo "StatusDetail=Unable to find the transaction in our database." . $eoln;
} else {

    /** We've found the order in the database, so now we can validate the message **
     * * First blank out our result variables * */
    $strStatusDetail = "";
    $strTxAuthNo = "";
    $strAVSCV2 = "";
    $strAddressResult = "";
    $strPostCodeResult = "";
    $strCV2Result = "";
    $strGiftAid = "";
    $str3DSecureStatus = "";
    $strCAVV = "";
    $strAddressStatus = "";
    $strPayerStatus = "";
    $strCardType = "";
    $strLast4Digits = "";
    $strMySignature = "";
	$token = "";

    /** Now get the VPSSignature value from the POST, and the StatusDetail in case we need it * */
    $strVPSSignature = cleaninput($_REQUEST["VPSSignature"], "Text");
    $strStatusDetail = cleaninput($_REQUEST["StatusDetail"], "Text");

    /** Retrieve the other fields, from the POST if they are present * */
    if (strlen($_REQUEST["TxAuthNo"] > 0))
        $strTxAuthNo = cleaninput($_REQUEST["TxAuthNo"], "Number");

    $strAVSCV2 = cleaninput($_REQUEST["AVSCV2"], "Text");
    $strAddressResult = cleaninput($_REQUEST["AddressResult"], "Text");
    $strPostCodeResult = cleaninput($_REQUEST["PostCodeResult"], "Text");
    $strCV2Result = cleaninput($_REQUEST["CV2Result"], "Text");
    $strGiftAid = cleaninput($_REQUEST["GiftAid"], "Number");
    $str3DSecureStatus = cleaninput($_REQUEST["3DSecureStatus"], "Text");
    $strCAVV = cleaninput($_REQUEST["CAVV"], "Text");
    $strAddressStatus = cleaninput($_REQUEST["AddressStatus"], "Text");
    $strPayerStatus = cleaninput($_REQUEST["PayerStatus"], "Text");

    $strCardType = cleaninput($_REQUEST["CardType"], "Text");
    $strLast4Digits = cleaninput($_REQUEST["Last4Digits"], "Text");
    $token=cleaninput($_REQUEST["Token"],"Text");

    // for protocol 3.00
    $strDeclineCode = cleaninput($_REQUEST['DeclineCode'], 'Text');
    $strExpiryDate = cleaninput($_REQUEST['ExpiryDate'], 'Text');
    $strFraudResponse = cleaninput($_REQUEST['FraudResponse'], 'Text');
    $strBankAuthCode = cleaninput($_REQUEST['BankAuthCode'], 'Text');

    /** Now we rebuilt the POST message, including our security key, and use the MD5 Hash **
     * * component that is included to create our own signature to compare with **
     * * the contents of the VPSSignature field in the POST.  Check the Sage Pay Server protocol **
     * * if you need clarification on this process * */
    $strMessage = $strVPSTxId . $strSagePayVendorTxCode . $strStatus . $strTxAuthNo . $strVendorName . $strAVSCV2 . $strSecurityKey
        . $strAddressResult . $strPostCodeResult . $strCV2Result . $strGiftAid . $str3DSecureStatus . $strCAVV
        . $strAddressStatus . $strPayerStatus . $strCardType . $strLast4Digits
        . $strDeclineCode . $strExpiryDate . $strFraudResponse . $strBankAuthCode;

    $strMySignature = strtoupper(md5($strMessage));

    /** We can now compare our MD5 Hash signature with that from Sage Pay Server * */
    if ($strMySignature !== $strVPSSignature) {
        /** If the signatures DON'T match, we should mark the order as tampered with, and **
         * * send back a Status of INVALID and failure page RedirectURL * */
        $strSQL = "";
        $rsPrimary = "";

        echo "Status=INVALID" . $eoln;

        /** Only use the Internal FQDN value during development.  In LIVE systems, always use the actual FQDN * */
        if ($strConnectTo == "LIVE") {
            echo "RedirectURL=" . $strYourSiteFQDN . $strVirtualDir . "/server/order-failed.php" . $eoln;
        } else {
            echo "RedirectURL=" . $strYourSiteInternalFQDN . $strVirtualDir . "/server/order-failed.php" . $eoln;
        }

        echo "StatusDetail=Cannot match the MD5 Hash. Order might be tampered with." . $eoln;
    } else {

        /** Great, the signatures DO match, so we can update the database and redirect the user appropriately * */
        if ($strStatus == "OK")
            $strDBStatus = "AUTHORISED - The transaction was successfully authorised with the bank.";
        elseif ($strStatus == "NOTAUTHED")
            $strDBStatus = "DECLINED - The transaction was not authorised by the bank.";
        elseif ($strStatus == "ABORT")
            $strDBStatus = "ABORTED - The customer clicked Cancel on the payment pages, or the transaction was timed out due to customer inactivity.";
        elseif ($strStatus == "REJECTED")
            $strDBStatus = "REJECTED - The transaction was failed by your 3D-Secure or AVS/CV2 rule-bases.";
        elseif ($strStatus == "AUTHENTICATED")
            $strDBStatus = "AUTHENTICATED - The transaction was successfully 3D-Secure Authenticated and can now be Authorised.";
        elseif ($strStatus == "REGISTERED")
            $strDBStatus = "REGISTERED - The transaction was could not be 3D-Secure Authenticated, but has been registered to be Authorised.";
        elseif ($strStatus == "ERROR")
            $strDBStatus = 'ERROR - There was an error during the payment process.  The error details are: ' . $strStatusDetail;
        else
            $strDBStatus = 'UNKNOWN - An unknown status was returned from Sage Pay.  The Status was: ' . $strStatus . ', with StatusDetail:' . $strStatusDetail;

        /** Update our database with the results from the Notification POST * */
        $strSQL = "";
        $rsPrimary = "";

        /** New reply to Sage Pay Server to let the system know we've received the Notification POST * */
        /** Always send a Status of OK if we've read everything correctly.  Only INVALID for messages with a Status of ERROR * */
        if ($strStatus == "ERROR") {
            echo "Status=INVALID" . $eoln;
            echo $strDBStatus . $eoln;
        } else {
            echo "Status=OK" . $eoln;
            $strResponse = "Status=OK" . $eoln;
        }

        /** Now decide where to redirect the customer * */
        if ($strStatus == "OK" || $strStatus == "AUTHENTICATED" || $strStatus == "REGISTERED" || $strStatus == "ATTEMPTONLY") {

            $VPSTxId = $strVPSTxId;

            $orderProtx = \Mtc\Shop\Order\Protx::query()
                ->where('VPSTxId', $VPSTxId)
                ->first();

            if (empty($orderProtx)) {

                $order = \Mtc\Shop\Order::query()
                    ->where('basket_id', $basketid)
                    ->orderByDesc('id')
                    ->first();

                //get the very last order for the current basket letting system update that as paid.
                $orderid = $_SESSION['order_id'] = $order->id;

                $newbasket = new Order($orderid);
                // google analytics tracking is done in the OrderPaid event handler
                // fired in the markPaid method
                $newbasket->markPaid(\Order::PAYMENT_TYPE_SAGEPAY_SERVER);

                // This isn't provided by Sagepay Server, so to save us trouble we're adding it in here manually.
                // While it may seem a little bit of an issue relying on the order total, this notification is
                // protected by a security code so should be correct
                $_REQUEST['Amount'] = $newbasket->getTotalCost();
                $_REQUEST['SecurityKey'] = Protx::query()
                    ->where('VPSTxId', $_REQUEST['VPSTxId'])
                    ->first()
                    ->secKey ?? '';
                $newbasket->saveProtX($_REQUEST);
                $newbasket->sendOrderConfirmation();

                $order->checkout = 'Server';
                $order->save();

                if(SAGEPAY_TOKEN && !empty($token)){

                    $order->checkout = 'Server';
                    $order->save();

                	$member_id=$newbasket->member;

                    $params_token = array( ":token" => $token,
                                        ":cardtype" => $strCardType,
                                        ":last4" => $strLast4Digits,
                                        ":member_id" => $member_id
                                        );

                    Member::query()
                        ->where('id', $member_id)
                        ->update([
                            'sagepay_token' => $token,
                            'sagepay_card' => $strCardType,
                            'sagepay_last4' => $strLast4Digits,
                        ]);

                    mail(DEV_EMAIL, "Sagepay token payment on" . config('app.name'), print_r($params_token, true) . " - " . $member_id);
                }

                if (PPC_TRACKING) {
                    track('sale', $newbasket->getTotalCost(), $newbasket->ref, $newbasket->keywords);
                }

            }

            $strRedirectPage = "/server/order-complete.php?order_id=" . $orderid;

        } elseif ($strStatus == "ERROR") {

            $payments = Protx::query()
                ->where('VendorTxCode', $strSagePayVendorTxCode)
                ->get();

            if ($payments->count() == 1) {
                $payment = $payments->first();
                Note::query()
                    ->create([
                        'note' => 'Payment failed due to SagePay handshake error. Order status set to Payment Rejected.',
                        'order_id' => $payment->order_id,
                    ]);
            }

            /** The status indicates a failure of one state or another, so send the customer to orderFailed instead * */
            $strRedirectPage = "/checkout/order_error.php?VendorTxCode=" . $strSagePayVendorTxCode;
        } else {
            /** The status indicates a failure of one state or another, so send the customer to orderFailed instead * */
            $strRedirectPage = "/server/order-failed.php";
        }

        /** Only use the Internal FQDN value during development.  In LIVE systems, always use the actual FQDN * */
        echo "RedirectURL=" . $strYourSiteFQDN . $strVirtualDir . $strRedirectPage . $eoln;
        $url = "RedirectURL=" . $strYourSiteFQDN . $strVirtualDir . $strRedirectPage . $eoln;
    }
}

$output = ob_get_clean();


if (defined(SAGEPAY_DEBUG_EMAIL) && SAGEPAY_DEBUG_EMAIL!='') {

    mail(SAGEPAY_DEBUG_EMAIL, config('app.name') . ' sagepay server callback', print_r($_REQUEST, true) . PHP_EOL . PHP_EOL . $output);
}

header("Content-type: text/plain");
echo $output;
