We will work on Apr 26th (Saturday) and will be off from Apr 30th (Wednesday) until May 2nd (Friday) for public holiday in our country

Result.php 11.3 KB
Newer Older
1
<?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/**
 *                       ######
 *                       ######
 * ############    ####( ######  #####. ######  ############   ############
 * #############  #####( ######  #####. ######  #############  #############
 *        ######  #####( ######  #####. ######  #####  ######  #####  ######
 * ###### ######  #####( ######  #####. ######  #####  #####   #####  ######
 * ###### ######  #####( ######  #####. ######  #####          #####  ######
 * #############  #############  #############  #############  #####  ######
 *  ############   ############  #############   ############  #####  ######
 *                                      ######
 *                               #############
 *                               ############
 *
 * Adyen Payment module (https://www.adyen.com/)
 *
 * Copyright (c) 2015 Adyen BV (https://www.adyen.com/)
 * See LICENSE.txt for license details.
 *
 * Author: Adyen <magento@adyen.com>
 */
23 24 25

namespace Adyen\Payment\Controller\Process;

26
use \Adyen\Payment\Model\Notification;
27 28 29

class Result extends \Magento\Framework\App\Action\Action
{
30 31 32
    /**
     * @var \Adyen\Payment\Helper\Data
     */
33 34
    protected $_adyenHelper;

35 36 37
    /**
     * @var \Magento\Sales\Model\OrderFactory
     */
38 39
    protected $_orderFactory;

40 41 42
    /**
     * @var \Magento\Sales\Model\Order
     */
43 44 45 46 47 48 49
    protected $_order;

    /**
     * @var \Magento\Sales\Model\Order\Status\HistoryFactory
     */
    protected $_orderHistoryFactory;

50 51 52
    /**
     * @var \Magento\Checkout\Model\Session
     */
53 54
    protected $_session;

rikterbeek's avatar
rikterbeek committed
55 56 57 58 59
    /**
     * @var \Adyen\Payment\Logger\AdyenLogger
     */
    protected $_adyenLogger;

60
    /**
61 62
     * Result constructor.
     *
63
     * @param \Magento\Framework\App\Action\Context $context
64 65 66 67
     * @param \Adyen\Payment\Helper\Data $adyenHelper
     * @param \Magento\Sales\Model\OrderFactory $orderFactory
     * @param \Magento\Sales\Model\Order\Status\HistoryFactory $orderHistoryFactory
     * @param \Magento\Checkout\Model\Session $session
rikterbeek's avatar
rikterbeek committed
68
     * @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
69 70 71 72 73 74
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Adyen\Payment\Helper\Data $adyenHelper,
        \Magento\Sales\Model\OrderFactory $orderFactory,
        \Magento\Sales\Model\Order\Status\HistoryFactory $orderHistoryFactory,
rikterbeek's avatar
rikterbeek committed
75 76
        \Magento\Checkout\Model\Session $session,
        \Adyen\Payment\Logger\AdyenLogger $adyenLogger
77 78 79 80 81
    ) {
        $this->_adyenHelper = $adyenHelper;
        $this->_orderFactory = $orderFactory;
        $this->_orderHistoryFactory = $orderHistoryFactory;
        $this->_session = $session;
rikterbeek's avatar
rikterbeek committed
82
        $this->_adyenLogger = $adyenLogger;
83 84 85
        parent::__construct($context);
    }

86 87 88
    /**
     * @throws \Magento\Framework\Exception\LocalizedException
     */
89 90 91
    public function execute()
    {
        $response = $this->getRequest()->getParams();
92
        $this->_adyenLogger->addAdyenResult(print_r($response, true));
rikterbeek's avatar
rikterbeek committed
93

94
        if ($response) {
rikterbeek's avatar
rikterbeek committed
95 96 97 98 99
            $result = $this->validateResponse($response);

            if ($result) {
                $session = $this->_session;
                $session->getQuote()->setIsActive(false)->save();
100
                $this->_redirect('checkout/onepage/success', ['_query' => ['utm_nooverride' => '1']]);
rikterbeek's avatar
rikterbeek committed
101 102 103 104
            } else {
                $this->_cancel($response);
                $this->_redirect('checkout/cart');
            }
105
        } else {
rikterbeek's avatar
rikterbeek committed
106
            // redirect to checkout page
107 108 109 110
            $this->_redirect('checkout/cart');
        }
    }

111 112 113
    /**
     * @param $response
     */
114 115 116 117 118 119 120 121 122
    protected function _cancel($response)
    {
        $session = $this->_session;

        // restore the quote
        $session->restoreQuote();

        $order = $this->_order;

123
        $this->_adyenHelper->cancelOrder($order);
124

125
        if (isset($response['authResult']) && $response['authResult'] == \Adyen\Payment\Model\Notification::CANCELLED) {
126 127 128 129 130 131
            $this->messageManager->addError(__('You have cancelled the order. Please try again'));
        } else {
            $this->messageManager->addError(__('Your payment failed, Please try again later'));
        }
    }

132 133 134 135 136
    /**
     * @param $response
     * @return bool
     * @throws \Magento\Framework\Exception\LocalizedException
     */
137 138 139 140
    protected function validateResponse($response)
    {
        $result = true;

141
        $this->_adyenLogger->addAdyenResult('Processing ResultUrl');
142 143

        if (empty($response)) {
144 145 146 147 148 149 150
            $this->_adyenLogger->addAdyenResult(
                'Response is empty, please check your webserver that the result url accepts parameters'
            );

            throw new \Magento\Framework\Exception\LocalizedException(
                __('Response is empty, please check your webserver that the result url accepts parameters')
            );
151 152 153 154 155 156 157 158 159 160
        }

        // authenticate result url
        $authStatus = $this->_authenticate($response);
        if (!$authStatus) {
            throw new \Magento\Framework\Exception\LocalizedException(__('ResultUrl authentification failure'));
        }

        $incrementId = $response['merchantReference'];

161
        if ($incrementId) {
162 163 164 165 166 167 168 169
            $order = $this->_getOrder($incrementId);
            if ($order->getId()) {

                $this->_eventManager->dispatch('adyen_payment_process_resulturl_before', [
                    'order' => $order,
                    'adyen_response' => $response
                ]);
                if (isset($response['handled'])) {
rikterbeek's avatar
rikterbeek committed
170
                    return $response['handled_response'];
171 172 173 174 175 176 177 178 179 180 181
                }

                // update the order
                $result = $this->_validateUpdateOrder($order, $response);

                $this->_eventManager->dispatch('adyen_payment_process_resulturl_after', [
                    'order' => $order,
                    'adyen_response' => $response
                ]);

            } else {
182 183 184
                throw new \Magento\Framework\Exception\LocalizedException(
                    __('Order does not exists with increment_id: %1', $incrementId)
                );
185 186
            }
        } else {
187 188 189
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Empty merchantReference')
            );
190 191 192 193 194 195
        }
        return $result;
    }

    /**
     * @param $order
196 197
     * @param $response
     * @return bool
198 199 200 201 202
     */
    protected function _validateUpdateOrder($order, $response)
    {
        $result = false;

203
        $this->_adyenLogger->addAdyenResult('Updating the order');
204 205 206 207 208

        $authResult = $response['authResult'];
        $paymentMethod = isset($response['paymentMethod']) ? trim($response['paymentMethod']) : '';
        $pspReference = isset($response['pspReference']) ? trim($response['pspReference']) : '';

209
        $type = 'Adyen Result URL response:';
210 211 212
        $comment = __('%1 <br /> authResult: %2 <br /> pspReference: %3 <br /> paymentMethod: %4',
            $type, $authResult, $pspReference, $paymentMethod
        );
213 214


215
        // needed because then we need to save $order objects
216 217
        $order->setAdyenResulturlEventCode($authResult);

218
        switch ($authResult) {
219
            case Notification::AUTHORISED:
220 221 222
                $result = true;
                $this->_adyenLogger->addAdyenResult('Do nothing wait for the notification');
                break;
223
            case Notification::PENDING:
224 225
                // do nothing wait for the notification
                $result = true;
226 227 228 229 230 231 232 233 234 235 236 237
                if (strpos($paymentMethod,"bankTransfer") !== false){
                    $comment .= "<br /><br />Waiting for the customer to transfer the money.";
                }
                elseif($paymentMethod == "sepadirectdebit"){
                    $comment .= "<br /><br />This request will be send to the bank at the end of the day.";
                }
                else {
                    $comment .= "<br /><br />The payment result is not confirmed (yet).
                                 <br />Once the payment is authorised, the order status will be updated accordingly. 
                                 <br />If the order is stuck on this status, the payment can be seen as unsuccessful. 
                                 <br />The order can be automatically cancelled based on the OFFER_CLOSED notification. Please contact Adyen Support to enable this.";
                }
238
                $this->_adyenLogger->addAdyenResult('Do nothing wait for the notification');
239
                break;
240
            case Notification::CANCELLED:
241
                $this->_adyenLogger->addAdyenResult('Cancel or Hold the order');
242 243
                $result = false;
                break;
244
            case Notification::REFUSED:
245 246
                // if refused there will be a AUTHORIZATION : FALSE notification send only exception is idea
                $this->_adyenLogger->addAdyenResult('Cancel or Hold the order');
247 248
                $result = false;
                break;
249
            case Notification::ERROR:
250
                //attempt to hold/cancel
251
                $this->_adyenLogger->addAdyenResult('Cancel or Hold the order');
252 253 254
                $result = false;
                break;
            default:
255
                $this->_adyenLogger->addAdyenResult('This event is not supported: ' . $authResult);
256 257 258 259
                $result = false;
                break;
        }

260 261 262 263 264 265 266 267 268
        $history = $this->_orderHistoryFactory->create()
            //->setStatus($status)
            ->setComment($comment)
            ->setEntityName('order')
            ->setOrder($order)
        ;

        $history->save();

269 270
        return $result;
    }
271
    
272
    /**
273 274 275 276
     * Authenticate using sha1 Merchant signature
     *
     * @param $response
     * @return bool
277 278 279 280
     */
    protected function _authenticate($response) {

        $hmacKey = $this->_adyenHelper->getHmac();
281 282 283 284 285 286 287 288 289 290 291 292 293
        $merchantSigNotification = $response['merchantSig'];

        // do it like this because $_GET is converting dot to underscore
        $queryString = $_SERVER['QUERY_STRING'];
        $result = [];
        $pairs = explode("&", $queryString);

        foreach ($pairs as $pair) {
            $nv = explode("=", $pair);
            $name = urldecode($nv[0]);
            $value = urldecode($nv[1]);
            $result[$name] = $value;
        }
294 295

        // do not include the merchantSig in the merchantSig calculation
296
        unset($result['merchantSig']);
297 298

        // Sort the array by key using SORT_STRING order
299
        ksort($result, SORT_STRING);
300 301

        // Generate the signing data string
302
        $signData = implode(":", array_map([$this, 'escapeString'],
303
            array_merge(array_keys($result), array_values($result))));
304

305
        $merchantSig = base64_encode(hash_hmac('sha256', $signData, pack("H*", $hmacKey), true));
306 307 308 309 310 311 312

        if (strcmp($merchantSig, $merchantSigNotification) === 0) {
            return true;
        }
        return false;
    }

313 314 315 316 317 318
    /**
     * The character escape function is called from the array_map function in _signRequestParams
     *
     * @param $val
     * @return mixed
     */
319 320 321 322 323
    protected function escapeString($val)
    {
        return str_replace(':','\\:',str_replace('\\','\\\\',$val));
    }

324 325 326 327 328 329
    /**
     * Get order based on increment_id
     *
     * @param $incrementId
     * @return \Magento\Sales\Model\Order
     */
330 331 332 333 334 335 336 337
    protected function _getOrder($incrementId)
    {
        if (!$this->_order) {
            $this->_order = $this->_orderFactory->create()->loadByIncrementId($incrementId);
        }
        return $this->_order;
    }
}