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

Cron.php 77.4 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\Model;

26
use Magento\Framework\Api\SearchCriteriaBuilder;
27
use Magento\Framework\Webapi\Exception;
28
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
29
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
30
use Magento\Framework\App\Area;
31 32 33
use Magento\Framework\App\AreaList;
use Magento\Framework\Phrase\Renderer\Placeholder;
use Magento\Framework\Phrase;
34
use Magento\Sales\Model\OrderRepository;
35 36 37 38 39 40

class Cron
{

    /**
     * Logging instance
41
     *
42 43 44 45
     * @var \Adyen\Payment\Logger\AdyenLogger
     */
    protected $_logger;

46
    /**
47
     * @var ResourceModel\Notification\CollectionFactory
48
     */
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    protected $_notificationFactory;

    /**
     * @var \Magento\Sales\Model\OrderFactory
     */
    protected $_orderFactory;

    /**
     * @var \Magento\Sales\Model\Order
     */
    protected $_order;

    /**
     * Core store config
     *
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $_scopeConfig;

68 69 70
    /**
     * @var \Adyen\Payment\Helper\Data
     */
71 72 73 74 75 76 77
    protected $_adyenHelper;

    /**
     * @var OrderSender
     */
    protected $_orderSender;

78 79 80 81 82
    /**
     * @var InvoiceSender
     */
    protected $_invoiceSender;

rikterbeek's avatar
rikterbeek committed
83 84 85 86 87
    /**
     * @var \Magento\Framework\DB\TransactionFactory
     */
    protected $_transactionFactory;

88 89 90 91 92 93
    /**
     * @var \Adyen\Payment\Model\Billing\AgreementFactory
     */
    protected $_billingAgreementFactory;

    /**
94
     * @var ResourceModel\Billing\Agreement\CollectionFactory
95 96 97 98 99 100 101 102
     */
    protected $_billingAgreementCollectionFactory;

    /**
     * @var Api\PaymentRequest
     */
    protected $_adyenPaymentRequest;

103 104 105
    /**
     * notification attributes
     */
106
    protected $_pspReference;
107

108 109 110 111 112
    /**
     * @var
     */
    protected $_originalReference;

113 114 115
    /**
     * @var
     */
116
    protected $_merchantReference;
117

118 119 120 121 122
    /**
     * @var
     */
    protected $_acquirerReference;

123 124 125 126 127
    /**
     * @var
     */
    protected $ratepayDescriptor;

128 129 130
    /**
     * @var
     */
131
    protected $_eventCode;
132 133 134 135

    /**
     * @var
     */
136
    protected $_success;
137 138 139 140

    /**
     * @var
     */
141
    protected $_paymentMethod;
142 143 144 145

    /**
     * @var
     */
146
    protected $_reason;
147 148 149 150

    /**
     * @var
     */
151
    protected $_value;
152 153 154 155

    /**
     * @var
     */
156
    protected $_boletoOriginalAmount;
157 158 159 160

    /**
     * @var
     */
161
    protected $_boletoPaidAmount;
162 163 164 165

    /**
     * @var
     */
166
    protected $_modificationResult;
167 168 169 170

    /**
     * @var
     */
171
    protected $_klarnaReservationNumber;
172 173 174 175

    /**
     * @var
     */
176 177
    protected $_fraudManualReview;

178 179 180 181 182 183
    /**
     * @var Order\PaymentFactory
     */
    protected $_adyenOrderPaymentFactory;

    /**
184
     * @var ResourceModel\Order\Payment\CollectionFactory
185 186 187
     */
    protected $_adyenOrderPaymentCollectionFactory;

188 189 190 191 192
    /**
     * @var ResourceModel\InvoiceFactory
     */
    protected $_adyenInvoiceFactory;

193
    /**
194
     * @var AreaList
195
     */
196
    protected $_areaList;
197

Bas Maassen's avatar
Bas Maassen committed
198 199 200 201 202
    /**
     * @var \Magento\Sales\Model\ResourceModel\Order\Status\CollectionFactory
     */
    protected $_orderStatusCollection;

203 204 205 206 207 208 209 210 211 212
    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * @var OrderRepository
     */
    private $orderRepository;

213 214 215 216 217
    /**
     * @var ResourceModel\Billing\Agreement
     */
    private $agreementResourceModel;

218 219 220 221 222
    /**
     * @var \Magento\Sales\Model\Order\Payment\Transaction\Builder
     */
    private $transactionBuilder;

223 224 225 226 227
    /**
     * @var \Magento\Framework\Serialize\SerializerInterface
     */
    private $serializer;

228 229 230 231 232
    /**
     * @var \Magento\Framework\Notification\NotifierInterface
     */
    private $notifierPool;

233 234 235 236 237
    /**
     * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
     */
    private $timezone;

238
    /**
239
     * Cron constructor.
240
     *
241 242
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
243
     * @param ResourceModel\Notification\CollectionFactory $notificationFactory
244 245 246
     * @param \Magento\Sales\Model\OrderFactory $orderFactory
     * @param \Adyen\Payment\Helper\Data $adyenHelper
     * @param OrderSender $orderSender
247
     * @param InvoiceSender $invoiceSender
248
     * @param \Magento\Framework\DB\TransactionFactory $transactionFactory
249
     * @param Billing\AgreementFactory $billingAgreementFactory
250
     * @param ResourceModel\Billing\Agreement\CollectionFactory $billingAgreementCollectionFactory
251
     * @param Api\PaymentRequest $paymentRequest
252
     * @param Order\PaymentFactory $adyenOrderPaymentFactory
253
     * @param ResourceModel\Order\Payment\CollectionFactory $adyenOrderPaymentCollectionFactory
Bas Maassen's avatar
Bas Maassen committed
254
     * @param InvoiceFactory $adyenInvoiceFactory
255
     * @param AreaList $areaList
Bas Maassen's avatar
Bas Maassen committed
256
     * @param \Magento\Sales\Model\ResourceModel\Order\Status\CollectionFactory $orderStatusCollection
257 258
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param OrderRepository $orderRepository
259
     * @param ResourceModel\Billing\Agreement $agreementResourceModel
260
     * @param \Magento\Sales\Model\Order\Payment\Transaction\Builder $transactionBuilder
261 262 263 264
     */
    public function __construct(
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Adyen\Payment\Logger\AdyenLogger $adyenLogger,
265
        \Adyen\Payment\Model\ResourceModel\Notification\CollectionFactory $notificationFactory,
266 267
        \Magento\Sales\Model\OrderFactory $orderFactory,
        \Adyen\Payment\Helper\Data $adyenHelper,
rikterbeek's avatar
rikterbeek committed
268
        OrderSender $orderSender,
269
        InvoiceSender $invoiceSender,
270 271
        \Magento\Framework\DB\TransactionFactory $transactionFactory,
        \Adyen\Payment\Model\Billing\AgreementFactory $billingAgreementFactory,
272
        \Adyen\Payment\Model\ResourceModel\Billing\Agreement\CollectionFactory $billingAgreementCollectionFactory,
273 274
        \Adyen\Payment\Model\Api\PaymentRequest $paymentRequest,
        \Adyen\Payment\Model\Order\PaymentFactory $adyenOrderPaymentFactory,
275
        \Adyen\Payment\Model\ResourceModel\Order\Payment\CollectionFactory $adyenOrderPaymentCollectionFactory,
276
        \Adyen\Payment\Model\InvoiceFactory $adyenInvoiceFactory,
Bas Maassen's avatar
Bas Maassen committed
277
        AreaList $areaList,
278 279
        \Magento\Sales\Model\ResourceModel\Order\Status\CollectionFactory $orderStatusCollection,
        SearchCriteriaBuilder $searchCriteriaBuilder,
280
        OrderRepository $orderRepository,
281
        \Adyen\Payment\Model\ResourceModel\Billing\Agreement $agreementResourceModel,
282
        \Magento\Sales\Model\Order\Payment\Transaction\Builder $transactionBuilder,
283
        \Magento\Framework\Serialize\SerializerInterface $serializer,
284 285
        \Magento\Framework\Notification\NotifierInterface $notifierPool,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone
286
    ) {
287
        $this->_scopeConfig = $scopeConfig;
288
        $this->_adyenLogger = $adyenLogger;
289 290 291 292
        $this->_notificationFactory = $notificationFactory;
        $this->_orderFactory = $orderFactory;
        $this->_adyenHelper = $adyenHelper;
        $this->_orderSender = $orderSender;
293
        $this->_invoiceSender = $invoiceSender;
rikterbeek's avatar
rikterbeek committed
294
        $this->_transactionFactory = $transactionFactory;
295 296 297
        $this->_billingAgreementFactory = $billingAgreementFactory;
        $this->_billingAgreementCollectionFactory = $billingAgreementCollectionFactory;
        $this->_adyenPaymentRequest = $paymentRequest;
298 299
        $this->_adyenOrderPaymentFactory = $adyenOrderPaymentFactory;
        $this->_adyenOrderPaymentCollectionFactory = $adyenOrderPaymentCollectionFactory;
300
        $this->_adyenInvoiceFactory = $adyenInvoiceFactory;
301
        $this->_areaList = $areaList;
Bas Maassen's avatar
Bas Maassen committed
302
        $this->_orderStatusCollection = $orderStatusCollection;
303 304
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->orderRepository = $orderRepository;
305
        $this->agreementResourceModel = $agreementResourceModel;
306
        $this->transactionBuilder = $transactionBuilder;
307
        $this->serializer = $serializer;
308
        $this->notifierPool = $notifierPool;
309
        $this->timezone = $timezone;
310 311
    }

312 313
    /**
     * Process the notification
314
     *
315 316
     * @return void
     */
317
    public function processNotification()
318 319 320
    {
        try {
            $this->execute();
Rik ter Beek's avatar
Rik ter Beek committed
321
        } catch (\Exception $e) {
322 323 324 325 326 327
            $this->_adyenLogger->addAdyenNotificationCronjob($e->getMessage() . "\n" . $e->getTraceAsString());
            throw $e;
        }
    }

    public function execute()
328
    {
329
        // needed for Magento < 2.2.0 https://github.com/magento/magento2/pull/8413
330
        $renderer = Phrase::getRenderer();
331
        if ($renderer instanceof Placeholder) {
332
            $this->_areaList->getArea(Area::AREA_CRONTAB)->load(Area::PART_TRANSLATE);
333 334
        }

335 336
        $this->_order = null;

337
        // execute notifications from 2 minute or earlier because order could not yet been created by magento
338
        $dateStart = new \DateTime();
339
        $dateStart->modify('-5 day');
340
        $dateEnd = new \DateTime();
341
        $dateEnd->modify('-1 minute');
342 343
        $dateRange = ['from' => $dateStart, 'to' => $dateEnd, 'datetime' => true];

344
        // create collection
345 346
        $notifications = $this->_notificationFactory->create();
        $notifications->addFieldToFilter('done', 0);
347
        $notifications->addFieldToFilter('processing', 0);
348
        $notifications->addFieldToFilter('created_at', $dateRange);
349
        $notifications->addFieldToFilter('error_count', ['lt' => Notification::MAX_ERROR_COUNT]);
350

351 352
        foreach ($notifications as $notification) {
            // set Cron processing to true
353
            $this->_updateNotification($notification, true, false);
354 355
        }

356
        // loop over the notifications
357
        $count = 0;
358
        foreach ($notifications as $notification) {
359
            try {
360
                $this->_adyenLogger->addAdyenNotificationCronjob(
361
                    sprintf("Processing notification %s", $notification->getEntityId())
362
                );
363

364 365 366 367 368 369 370 371 372 373 374 375
                // ignore duplicate notification
                if ($this->_isDuplicate($notification)) {
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        "This is a duplicate notification and will be ignored"
                    );
                    $this->_updateNotification($notification, false, true);
                    ++$count;
                    continue;
                }

                // log the executed notification
                $this->_adyenLogger->addAdyenNotificationCronjob(print_r($notification->debug(), 1));
376

377 378
                // get order
                $incrementId = $notification->getMerchantReference();
379

380 381 382
                $searchCriteria = $this->searchCriteriaBuilder
                    ->addFilter('increment_id', $incrementId, 'eq')
                    ->create();
383

384
                $orderList = $this->orderRepository->getList($searchCriteria)->getItems();
385

386 387 388
                /** @var \Magento\Sales\Model\Order $order */
                $order = reset($orderList);
                $this->_order = $order;
389

390 391 392 393 394
                if (!$this->_order) {
                    // order does not exists remove from queue
                    $notification->delete();
                    continue;
                }
395

396 397
                // declare all variables that are needed
                $this->_declareVariables($notification);
398

399 400
                // add notification to comment history status is current status
                $this->_addStatusHistoryComment();
401

402
                $previousAdyenEventCode = $this->_order->getData('adyen_notification_event_code');
403

404 405
                // update order details
                $this->_updateAdyenAttributes($notification);
406

407 408 409 410 411 412 413 414 415 416 417
                // check if success is true of false
                if (strcmp($this->_success, 'false') == 0 || strcmp($this->_success, '0') == 0) {
                    /*
                     * Only cancel the order when it is in state pending, payment review or
                     * if the ORDER_CLOSED is failed (means split payment has not be successful)
                     */
                    if ($this->_order->getState() === \Magento\Sales\Model\Order::STATE_PENDING_PAYMENT ||
                        $this->_order->getState() === \Magento\Sales\Model\Order::STATE_PAYMENT_REVIEW ||
                        $this->_eventCode == Notification::ORDER_CLOSED
                    ) {
                        $this->_adyenLogger->addAdyenNotificationCronjob('Going to cancel the order');
418

419 420 421
                        // if payment is API check, check if API result pspreference is the same as reference
                        if ($this->_eventCode == NOTIFICATION::AUTHORISATION && $this->_getPaymentMethodType() == 'api') {
                            // don't cancel the order becasue order was successfull through api
422
                            $this->_adyenLogger->addAdyenNotificationCronjob(
423
                                'order is not cancelled because api result was succesfull'
424
                            );
425 426 427 428 429 430 431 432 433
                        } else {
                            /*
                             * don't cancel the order if previous state is authorisation with success=true
                             * Split payments can fail if the second payment has failed the first payment is
                             * refund/cancelled as well so if it is a split payment that failed cancel the order as well
                             */
                            if ($previousAdyenEventCode != "AUTHORISATION : TRUE" ||
                                $this->_eventCode == Notification::ORDER_CLOSED
                            ) {
434 435 436 437
                                // Move the order from PAYMENT_REVIEW to NEW, so that can be cancelled
                                if ($this->_order->getState() === \Magento\Sales\Model\Order::STATE_PAYMENT_REVIEW) {
                                    $this->_order->setState(\Magento\Sales\Model\Order::STATE_NEW);
                                }
438 439 440 441 442 443 444 445
                                $this->_holdCancelOrder(false);
                            } else {
                                $this->_order->setData('adyen_notification_event_code', $previousAdyenEventCode);
                                $this->_adyenLogger->addAdyenNotificationCronjob(
                                    'order is not cancelled because previous notification
                                    was an authorisation that succeeded'
                                );
                            }
446
                        }
447 448 449 450
                    } else {
                        $this->_adyenLogger->addAdyenNotificationCronjob(
                            'Order is already processed so ignore this notification state is:' . $this->_order->getState()
                        );
451
                    }
452 453 454 455
                    //Trigger admin notice for unsuccessful REFUND notifications
                    if ($this->_eventCode == Notification::REFUND){
                        $this->addRefundFailedNotice();
                    }
456
                } else {
457 458
                    // Notification is successful
                    $this->_processNotification();
459 460
                }

461 462 463 464 465 466 467 468 469 470 471 472
                try {
                    // set done to true
                    $this->_order->save();
                } catch (\Exception $e) {
                    $this->_adyenLogger->addAdyenNotificationCronjob($e->getMessage());
                }

                $this->_updateNotification($notification, false, true);
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    sprintf("Notification %s is processed", $notification->getEntityId())
                );
                ++$count;
473
            } catch (\Exception $e) {
474
                $this->_updateNotification($notification, false, false);
475
                $this->handleNotificationError($notification, $e->getMessage());
476
                $this->_adyenLogger->addAdyenNotificationCronjob(
477 478
                    sprintf("Notification %s had an error: %s \n %s", $notification->getEntityId(), $e->getMessage(),
                        $e->getTraceAsString())
479
                );
480
            }
481 482 483 484
        }

        if ($count > 0) {
            $this->_adyenLogger->addAdyenNotificationCronjob(sprintf("Cronjob updated %s notification(s)", $count));
485 486 487
        }
    }

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
    /**
     * @param $notification
     * @param $processing
     * @param $done
     */
    protected function _updateNotification($notification, $processing, $done)
    {
        if ($done) {
            $notification->setDone(true);
        }
        $notification->setProcessing($processing);
        $notification->setUpdatedAt(new \DateTime());
        $notification->save();
    }

    /**
     * Check if the notification is already executed if so this is a duplicate and ignore this one
     *
     * @param $notification
     * @return bool
     */
    protected function _isDuplicate($notification)
    {
        return $notification->isDuplicate(
512 513 514 515 516
            $notification->getPspreference(),
            $notification->getEventCode(),
            $notification->getSuccess(),
            $notification->getOriginalReference(),
            true
517 518 519
        );
    }

520 521 522 523 524 525
    /**
     * Declare private variables for processing notification
     *
     * @param Object $notification
     * @return void
     */
526 527 528 529
    protected function _declareVariables($notification)
    {
        //  declare the common parameters
        $this->_pspReference = $notification->getPspreference();
530
        $this->_originalReference = $notification->getOriginalReference();
531 532 533 534
        $this->_merchantReference = $notification->getMerchantReference();
        $this->_eventCode = $notification->getEventCode();
        $this->_success = $notification->getSuccess();
        $this->_paymentMethod = $notification->getPaymentMethod();
535
        $this->_reason = $notification->getReason();
536
        $this->_value = $notification->getAmountValue();
537
        $this->_live = $notification->getLive();
538

539
        $additionalData = !empty($notification->getAdditionalData()) ? $this->serializer->unserialize($notification->getAdditionalData()) : "";
540 541

        // boleto data
542 543
        if ($this->_paymentMethodCode() == "adyen_boleto") {
            if ($additionalData && is_array($additionalData)) {
544
                $boletobancario = isset($additionalData['boletobancario']) ? $additionalData['boletobancario'] : null;
545 546 547 548 549
                if ($boletobancario && is_array($boletobancario)) {
                    $this->_boletoOriginalAmount =
                        isset($boletobancario['originalAmount']) ? trim($boletobancario['originalAmount']) : "";
                    $this->_boletoPaidAmount =
                        isset($boletobancario['paidAmount']) ? trim($boletobancario['paidAmount']) : "";
550 551 552 553
                }
            }
        }

554
        if ($additionalData && is_array($additionalData)) {
555
            // check if the payment is in status manual review
556 557 558
            $fraudManualReview = isset($additionalData['fraudManualReview']) ?
                $additionalData['fraudManualReview'] : "";
            if ($fraudManualReview == "true") {
559 560 561 562 563
                $this->_fraudManualReview = true;
            } else {
                $this->_fraudManualReview = false;
            }

564
            // modification.action is it for JSON
565 566 567
            $modificationActionJson = isset($additionalData['modification.action']) ?
                $additionalData['modification.action'] : null;
            if ($modificationActionJson != "") {
568 569 570
                $this->_modificationResult = $modificationActionJson;
            }

571
            $modification = isset($additionalData['modification']) ? $additionalData['modification'] : null;
572
            if ($modification && is_array($modification)) {
573
                $this->_modificationResult = isset($modification['action']) ? trim($modification['action']) : "";
574 575
            }
            $additionalData2 = isset($additionalData['additionalData']) ? $additionalData['additionalData'] : null;
576
            if ($additionalData2 && is_array($additionalData2)) {
577 578
                $this->_klarnaReservationNumber = isset($additionalData2['acquirerReference']) ? trim($additionalData2['acquirerReference']) : "";
            }
579 580 581 582
            $acquirerReference = isset($additionalData['acquirerReference']) ? $additionalData['acquirerReference'] : null;
            if ($acquirerReference != "") {
                $this->_acquirerReference = $acquirerReference;
            }
583 584 585 586
            $ratepayDescriptor = isset($additionalData['openinvoicedata.descriptor']) ? $additionalData['openinvoicedata.descriptor'] : "";
            if ($ratepayDescriptor !== "") {
                $this->ratepayDescriptor = $ratepayDescriptor;
            }
587 588 589 590 591 592 593 594 595 596 597
        }
    }

    /**
     * @return mixed
     */
    protected function _paymentMethodCode()
    {
        return $this->_order->getPayment()->getMethod();
    }

598 599 600 601 602
    /**
     * @return mixed
     */
    protected function _getPaymentMethodType()
    {
rikterbeek's avatar
rikterbeek committed
603 604 605
        return $this->_order->getPayment()->getPaymentMethodType();
    }

606 607 608 609 610 611
    /**
     * @desc order comments or history
     * @param type $order
     */
    protected function _addStatusHistoryComment()
    {
612 613 614
        $successResult = (strcmp($this->_success, 'true') == 0 ||
            strcmp($this->_success, '1') == 0) ? 'true' : 'false';
        $success = (!empty($this->_reason)) ? "$successResult <br />reason:$this->_reason" : $successResult;
615

616
        if ($this->_eventCode == Notification::REFUND || $this->_eventCode == Notification::CAPTURE) {
617 618 619 620
            $currency = $this->_order->getOrderCurrencyCode();

            // check if it is a full or partial refund
            $amount = $this->_value;
621
            $orderAmount = (int)$this->_adyenHelper->formatAmount($this->_order->getGrandTotal(), $currency);
622

623
            $this->_adyenLogger->addAdyenNotificationCronjob(
624
                'amount notification:' . $amount . ' amount order:' . $orderAmount
625
            );
626

627 628
            if ($amount == $orderAmount) {
                $this->_order->setData(
629 630
                    'adyen_notification_event_code',
                    $this->_eventCode . " : " . strtoupper($successResult)
631
                );
632
            } else {
633
                $this->_order->setData(
634 635
                    'adyen_notification_event_code',
                    "(PARTIAL) " .
636 637
                    $this->_eventCode . " : " . strtoupper($successResult)
                );
638 639
            }
        } else {
640
            $this->_order->setData(
641 642
                'adyen_notification_event_code',
                $this->_eventCode . " : " . strtoupper($successResult)
643
            );
644 645
        }

Rik ter Beek's avatar
Rik ter Beek committed
646
        // if payment method is klarna, ratepay or openinvoice/afterpay show the reservartion number
Rik ter Beek's avatar
Rik ter Beek committed
647
        if ($this->_adyenHelper->isPaymentMethodOpenInvoiceMethod($this->_paymentMethod) && !empty($this->_klarnaReservationNumber)) {
648 649 650 651 652
            $klarnaReservationNumberText = "<br /> reservationNumber: " . $this->_klarnaReservationNumber;
        } else {
            $klarnaReservationNumberText = "";
        }

653
        if ($this->_boletoPaidAmount != null && $this->_boletoPaidAmount != "") {
654 655 656 657 658 659
            $boletoPaidAmountText = "<br /> Paid amount: " . $this->_boletoPaidAmount;
        } else {
            $boletoPaidAmountText = "";
        }

        $type = 'Adyen HTTP Notification(s):';
660 661 662 663 664 665 666 667 668 669 670
        $comment = __(
            '%1 <br /> eventCode: %2 <br /> pspReference: %3 <br /> paymentMethod: %4 <br />' .
            ' success: %5 %6 %7',
            $type,
            $this->_eventCode,
            $this->_pspReference,
            $this->_paymentMethod,
            $success,
            $klarnaReservationNumberText,
            $boletoPaidAmountText
        );
671 672

        // If notification is pending status and pending status is set add the status change to the comment history
673
        if ($this->_eventCode == Notification::PENDING) {
674
            $pendingStatus = $this->_getConfigData(
675 676 677
                'pending_status',
                'adyen_abstract',
                $this->_order->getStoreId()
678
            );
679
            if ($pendingStatus != "") {
680
                $this->_order->addStatusHistoryComment($comment, $pendingStatus);
681 682 683
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'Created comment history for this notification with status change to: ' . $pendingStatus
                );
684 685 686 687 688
                return;
            }
        }

        // if manual review is accepted and a status is selected. Change the status through this comment history item
689
        if ($this->_eventCode == Notification::MANUAL_REVIEW_ACCEPT
690 691
            && $this->_getFraudManualReviewAcceptStatus() != ""
        ) {
692 693
            $manualReviewAcceptStatus = $this->_getFraudManualReviewAcceptStatus();
            $this->_order->addStatusHistoryComment($comment, $manualReviewAcceptStatus);
694
            $this->_adyenLogger->addAdyenNotificationCronjob('Created comment history for this notification with status change to: ' . $manualReviewAcceptStatus);
695 696 697 698
            return;
        }

        $this->_order->addStatusHistoryComment($comment);
699
        $this->_adyenLogger->addAdyenNotificationCronjob('Created comment history for this notification');
700 701
    }

702 703 704
    /**
     * @param $notification
     */
705 706
    protected function _updateAdyenAttributes($notification)
    {
707
        $this->_adyenLogger->addAdyenNotificationCronjob('Updating the Adyen attributes of the order');
708

709 710
        $additionalData = !empty($notification->getAdditionalData()) ? $this->serializer->unserialize($notification->getAdditionalData()) : "";

711 712 713 714
        $_paymentCode = $this->_paymentMethodCode();

        if ($this->_eventCode == Notification::AUTHORISATION
            || $this->_eventCode == Notification::HANDLED_EXTERNALLY
715
        ) {
716 717 718 719 720 721
            /*
             * if current notification is authorisation : false and
             * the  previous notification was authorisation : true do not update pspreference
             */
            if (strcmp($this->_success, 'false') == 0 ||
                strcmp($this->_success, '0') == 0 ||
722 723
                strcmp($this->_success, '') == 0
            ) {
724 725 726 727 728 729 730 731 732 733
                $previousAdyenEventCode = $this->_order->getData('adyen_notification_event_code');
                if ($previousAdyenEventCode != "AUTHORISATION : TRUE") {
                    $this->_updateOrderPaymentWithAdyenAttributes($additionalData);
                }
            } else {
                $this->_updateOrderPaymentWithAdyenAttributes($additionalData);
            }
        }
    }

734 735 736
    /**
     * @param $additionalData
     */
737 738 739 740 741 742 743 744
    protected function _updateOrderPaymentWithAdyenAttributes($additionalData)
    {
        if ($additionalData && is_array($additionalData)) {
            $avsResult = (isset($additionalData['avsResult'])) ? $additionalData['avsResult'] : "";
            $cvcResult = (isset($additionalData['cvcResult'])) ? $additionalData['cvcResult'] : "";
            $totalFraudScore = (isset($additionalData['totalFraudScore'])) ? $additionalData['totalFraudScore'] : "";
            $ccLast4 = (isset($additionalData['cardSummary'])) ? $additionalData['cardSummary'] : "";
            $refusalReasonRaw = (isset($additionalData['refusalReasonRaw'])) ? $additionalData['refusalReasonRaw'] : "";
745 746
            $acquirerReference = (isset($additionalData['acquirerReference'])) ?
                $additionalData['acquirerReference'] : "";
747
            $authCode = (isset($additionalData['authCode'])) ? $additionalData['authCode'] : "";
748 749
            $cardBin = (isset($additionalData['cardBin'])) ? $additionalData['cardBin'] : "";
            $expiryDate = (isset($additionalData['expiryDate'])) ? $additionalData['expiryDate'] : "";
750 751 752 753 754 755 756 757 758 759 760
        }

        // if there is no server communication setup try to get last4 digits from reason field
        if (!isset($ccLast4) || $ccLast4 == "") {
            $ccLast4 = $this->_retrieveLast4DigitsFromReason($this->_reason);
        }

        $this->_order->getPayment()->setAdyenPspReference($this->_pspReference);
        $this->_order->getPayment()->setAdditionalInformation('pspReference', $this->_pspReference);

        if ($this->_klarnaReservationNumber != "") {
761
            $this->_order->getPayment()->setAdditionalInformation(
762 763
                'adyen_klarna_number',
                $this->_klarnaReservationNumber
764
            );
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
        }
        if (isset($ccLast4) && $ccLast4 != "") {
            // this field is column in db by core
            $this->_order->getPayment()->setccLast4($ccLast4);
        }
        if (isset($avsResult) && $avsResult != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_avs_result', $avsResult);
        }
        if (isset($cvcResult) && $cvcResult != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_cvc_result', $cvcResult);
        }
        if ($this->_boletoPaidAmount != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_boleto_paid_amount', $this->_boletoPaidAmount);
        }
        if (isset($totalFraudScore) && $totalFraudScore != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_total_fraud_score', $totalFraudScore);
        }
        if (isset($refusalReasonRaw) && $refusalReasonRaw != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_refusal_reason_raw', $refusalReasonRaw);
        }
        if (isset($acquirerReference) && $acquirerReference != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_acquirer_reference', $acquirerReference);
        }
        if (isset($authCode) && $authCode != "") {
            $this->_order->getPayment()->setAdditionalInformation('adyen_auth_code', $authCode);
        }
791 792 793 794 795 796
        if (!empty($cardBin)) {
            $this->_order->getPayment()->setAdditionalInformation('adyen_card_bin', $cardBin);
        }
        if (!empty($expiryDate)) {
            $this->_order->getPayment()->setAdditionalInformation('adyen_expiry_date', $expiryDate);
        }
797 798
        if ($this->ratepayDescriptor !== "") {
            $this->_order->getPayment()->setAdditionalInformation(
799 800
                'adyen_ratepay_descriptor',
                $this->ratepayDescriptor
801 802
            );
        }
803 804 805 806
    }

    /**
     * retrieve last 4 digits of card from the reason field
807
     *
808 809 810 811 812 813 814
     * @param $reason
     * @return string
     */
    protected function _retrieveLast4DigitsFromReason($reason)
    {
        $result = "";

815
        if ($reason != "") {
816
            $reasonArray = explode(":", $reason);
817 818
            if ($reasonArray != null && is_array($reasonArray)) {
                if (isset($reasonArray[1])) {
819 820 821 822 823 824 825
                    $result = $reasonArray[1];
                }
            }
        }
        return $result;
    }

826
    /**
827 828
     * @param $ignoreHasInvoice
     * @throws \Magento\Framework\Exception\LocalizedException
829 830 831
     */
    protected function _holdCancelOrder($ignoreHasInvoice)
    {
832
        $orderStatus = $this->_getConfigData(
833 834 835
            'payment_cancelled',
            'adyen_abstract',
            $this->_order->getStoreId()
836
        );
837 838 839

        // check if order has in invoice only cancel/hold if this is not the case
        if ($ignoreHasInvoice || !$this->_order->hasInvoices()) {
840
            if ($orderStatus == \Magento\Sales\Model\Order::STATE_HOLDED) {
841 842 843
                // Allow magento to hold order
                $this->_order->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_HOLD, true);

844 845 846
                if ($this->_order->canHold()) {
                    $this->_order->hold();
                } else {
847
                    $this->_adyenLogger->addAdyenNotificationCronjob('Order can not hold or is already on Hold');
848 849 850
                    return;
                }
            } else {
851 852 853
                // Allow magento to cancel order
                $this->_order->setActionFlag(\Magento\Sales\Model\Order::ACTION_FLAG_CANCEL, true);

854 855 856
                if ($this->_order->canCancel()) {
                    $this->_order->cancel();
                } else {
857
                    $this->_adyenLogger->addAdyenNotificationCronjob('Order can not be canceled');
858 859 860 861
                    return;
                }
            }
        } else {
862
            $this->_adyenLogger->addAdyenNotificationCronjob('Order has already an invoice so cannot be canceled');
863 864 865 866
        }
    }

    /**
867
     * Process the Notification
868 869 870
     */
    protected function _processNotification()
    {
871

872
        $this->_adyenLogger->addAdyenNotificationCronjob('Processing the notification');
873 874 875 876
        $_paymentCode = $this->_paymentMethodCode();

        switch ($this->_eventCode) {
            case Notification::REFUND_FAILED:
877 878
                //Trigger admin notice for REFUND_FAILED notifications
                $this->addRefundFailedNotice();
879 880
                break;
            case Notification::REFUND:
881
                $ignoreRefundNotification = $this->_getConfigData(
882 883 884
                    'ignore_refund_notification',
                    'adyen_abstract',
                    $this->_order->getStoreId()
885 886
                );
                if ($ignoreRefundNotification != true) {
rikterbeek's avatar
rikterbeek committed
887
                    $this->_refundOrder();
888
                    //refund completed
rikterbeek's avatar
rikterbeek committed
889
                    $this->_setRefundAuthorized();
890
                } else {
891 892 893
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        'Setting to ignore refund notification is enabled so ignore this notification'
                    );
894 895 896
                }
                break;
            case Notification::PENDING:
897
                if ($this->_getConfigData(
898 899 900 901
                    'send_email_bank_sepa_on_pending',
                    'adyen_abstract',
                    $this->_order->getStoreId()
                )
902
                ) {
903
                    // Check if payment is banktransfer or sepa if true then send out order confirmation email
rikterbeek's avatar
rikterbeek committed
904
                    $isBankTransfer = $this->_isBankTransfer();
905 906
                    if ($isBankTransfer || $this->_paymentMethod == 'sepadirectdebit') {
                        if (!$this->_order->getEmailSent()) {
907
                            $this->_sendOrderMail();
908
                        }
909 910 911 912 913
                    }
                }
                break;
            case Notification::HANDLED_EXTERNALLY:
            case Notification::AUTHORISATION:
914
                $this->_authorizePayment();
915 916 917 918 919
                break;
            case Notification::MANUAL_REVIEW_REJECT:
                // don't do anything it will send a CANCEL_OR_REFUND notification when this payment is captured
                break;
            case Notification::MANUAL_REVIEW_ACCEPT:
920 921 922 923
                /*
                 * only process this if you are on auto capture.
                 * On manual capture you will always get Capture or CancelOrRefund notification
                 */
924
                if ($this->_isAutoCapture()) {
925
                    $this->_setPaymentAuthorized(false);
926 927 928
                }
                break;
            case Notification::CAPTURE:
929 930 931 932 933 934 935
                /*
                 * ignore capture if you are on auto capture
                 * this could be called if manual review is enabled and you have a capture delay
                 */
                if (!$this->_isAutoCapture()) {
                    $this->_setPaymentAuthorized(false, true);

936
                    /*
937
                     * Add invoice in the adyen_invoice table
938
                     */
939 940 941 942 943 944 945 946 947 948
                    $invoiceCollection = $this->_order->getInvoiceCollection();
                    foreach ($invoiceCollection as $invoice) {
                        if ($invoice->getTransactionId() == $this->_pspReference) {
                            $this->_adyenInvoiceFactory->create()
                                ->setInvoiceId($invoice->getEntityId())
                                ->setPspreference($this->_pspReference)
                                ->setOriginalReference($this->_originalReference)
                                ->setAcquirerReference($this->_acquirerReference)
                                ->save();
                            $this->_adyenLogger->addAdyenNotificationCronjob('Created invoice entry in the Adyen table');
949
                        }
950 951 952
                    }
                }
                break;
953
            case Notification::OFFER_CLOSED:
954 955 956 957 958 959 960 961
                $previousSuccess = $this->_order->getData('adyen_notification_event_code_success');

                // Order is already Authorised
                if (!empty($previousSuccess)) {
                    $this->_adyenLogger->addAdyenNotificationCronjob("Order is already authorised, skipping OFFER_CLOSED");
                    break;
                }

962
                // Order is already Cancelled
963
                if ($this->_order->isCanceled()) {
964 965 966 967
                    $this->_adyenLogger->addAdyenNotificationCronjob("Order is already cancelled, skipping OFFER_CLOSED");
                    break;
                }

968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
                /*
                 * For cards, it can be 'visa', 'maestro',...
                 * For alternatives, it can be 'ideal', 'directEbanking',...
                 */
                $notificationPaymentMethod = $this->_paymentMethod;

                /*
                * For cards, it can be 'VI', 'MI',...
                * For alternatives, it can be 'ideal', 'directEbanking',...
                */
                $orderPaymentMethod = $this->_order->getPayment()->getCcType();

                $isOrderCc = strcmp($this->_paymentMethodCode(),
                        'adyen_cc') == 0 || strcmp($this->_paymentMethodCode(), 'adyen_oneclick') == 0;

                /*
                 * If the order was made with an Alternative payment method,
                 * continue with the cancellation only if the payment method of
                 * the notification matches the payment method of the order.
                 */
                if (!$isOrderCc && strcmp($notificationPaymentMethod, $orderPaymentMethod) !== 0) {
                    $this->_adyenLogger->addAdyenNotificationCronjob("Order is not a credit card, 
                    or the payment method in the notification does not match the payment method of the order, 
                    skipping OFFER_CLOSED");
                    break;
                }

995
                if (!$this->_order->canCancel()) {
996 997 998 999 1000
                    // Move the order from PAYMENT_REVIEW to NEW, so that can be cancelled
                    $this->_order->setState(\Magento\Sales\Model\Order::STATE_NEW);
                }
                $this->_holdCancelOrder(true);
                break;
1001 1002 1003 1004 1005 1006
            case Notification::CAPTURE_FAILED:
            case Notification::CANCELLATION:
            case Notification::CANCELLED:
                $this->_holdCancelOrder(true);
                break;
            case Notification::CANCEL_OR_REFUND:
1007 1008
                if (isset($this->_modificationResult) && $this->_modificationResult != "") {
                    if ($this->_modificationResult == "cancel") {
1009
                        $this->_holdCancelOrder(true);
1010
                    } elseif ($this->_modificationResult == "refund") {
rikterbeek's avatar
rikterbeek committed
1011
                        $this->_refundOrder();
1012
                        //refund completed
rikterbeek's avatar
rikterbeek committed
1013
                        $this->_setRefundAuthorized();
1014 1015
                    }
                } else {
1016
                    if ($this->_order->isCanceled() ||
1017 1018
                        $this->_order->getState() === \Magento\Sales\Model\Order::STATE_HOLDED
                    ) {
1019 1020 1021
                        $this->_adyenLogger->addAdyenNotificationCronjob(
                            'Order is already cancelled or holded so do nothing'
                        );
1022
                    } else {
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
                        if ($this->_order->canCancel() || $this->_order->canHold()) {
                            $this->_adyenLogger->addAdyenNotificationCronjob('try to cancel the order');
                            $this->_holdCancelOrder(true);
                        } else {
                            $this->_adyenLogger->addAdyenNotificationCronjob('try to refund the order');
                            // refund
                            $this->_refundOrder();
                            //refund completed
                            $this->_setRefundAuthorized();
                        }
1033 1034 1035
                    }
                }
                break;
1036

1037
            case Notification::RECURRING_CONTRACT:
1038 1039


1040 1041 1042 1043
                // only store billing agreements if Vault is disabled
                if (!$this->_adyenHelper->isCreditCardVaultEnabled()) {
                    // storedReferenceCode
                    $recurringDetailReference = $this->_pspReference;
1044

1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
                    $storeId = $this->_order->getStoreId();
                    $customerReference = $this->_order->getCustomerId();
                    $listRecurringContracts = null;
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        __(
                            'CustomerReference is: %1 and storeId is %2 and RecurringDetailsReference is %3',
                            $customerReference,
                            $storeId,
                            $recurringDetailReference
                        )
                    );
                    try {
                        $listRecurringContracts = $this->_adyenPaymentRequest->getRecurringContractsForShopper(
                            $customerReference,
                            $storeId
1060
                        );
1061 1062 1063
                        $contractDetail = null;
                        // get current Contract details and get list of all current ones
                        $recurringReferencesList = [];
1064

1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
                        if (!$listRecurringContracts) {
                            throw new \Exception("Empty list recurring contracts");
                        }
                        // Find the reference on the list
                        foreach ($listRecurringContracts as $rc) {
                            $recurringReferencesList[] = $rc['recurringDetailReference'];
                            if (isset($rc['recurringDetailReference']) &&
                                $rc['recurringDetailReference'] == $recurringDetailReference
                            ) {
                                $contractDetail = $rc;
                            }
                        }
1077

1078 1079 1080 1081 1082
                        if ($contractDetail == null) {
                            $this->_adyenLogger->addAdyenNotificationCronjob(print_r($listRecurringContracts, 1));
                            $message = __(
                                'Failed to create billing agreement for this order ' .
                                '(listRecurringCall did not contain contract)'
1083
                            );
1084
                            throw new \Exception($message);
1085 1086
                        }

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
                        $billingAgreements = $this->_billingAgreementCollectionFactory->create();
                        $billingAgreements->addFieldToFilter('customer_id', $customerReference);

                        // Get collection and update existing agreements

                        foreach ($billingAgreements as $updateBillingAgreement) {
                            if (!in_array($updateBillingAgreement->getReferenceId(), $recurringReferencesList)) {
                                $updateBillingAgreement->setStatus(
                                    \Adyen\Payment\Model\Billing\Agreement::STATUS_CANCELED
                                );
                            } else {
                                $updateBillingAgreement->setStatus(
                                    \Adyen\Payment\Model\Billing\Agreement::STATUS_ACTIVE
                                );
                            }
                            $updateBillingAgreement->save();
                        }
1104

1105
                        // Get or create billing agreement
1106
                        $billingAgreement = $this->_billingAgreementFactory->create();
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
                        $billingAgreement->load($recurringDetailReference, 'reference_id');
                        // check if BA exists
                        if (!($billingAgreement && $billingAgreement->getAgreementId() > 0 && $billingAgreement->isValid())) {
                            // create new
                            $this->_adyenLogger->addAdyenNotificationCronjob("Creating new Billing Agreement");
                            $this->_order->getPayment()->setBillingAgreementData(
                                [
                                    'billing_agreement_id' => $recurringDetailReference,
                                    'method_code' => $this->_order->getPayment()->getMethodCode(),
                                ]
                            );

                            $billingAgreement = $this->_billingAgreementFactory->create();
                            $billingAgreement->setStoreId($this->_order->getStoreId());
                            $billingAgreement->importOrderPayment($this->_order->getPayment());
                            $message = __('Created billing agreement #%1.', $recurringDetailReference);
                        } else {
                            $this->_adyenLogger->addAdyenNotificationCronjob("Using existing Billing Agreement");
                            $billingAgreement->setIsObjectChanged(true);
                            $message = __('Updated billing agreement #%1.', $recurringDetailReference);
                        }
1128

1129 1130 1131
                        // Populate billing agreement data
                        $billingAgreement->parseRecurringContractData($contractDetail);
                        if ($billingAgreement->isValid()) {
1132

1133 1134
                            if (!$this->agreementResourceModel->getOrderRelation($billingAgreement->getAgreementId(),
                                $this->_order->getId())) {
1135

1136 1137
                                // save into sales_billing_agreement_order
                                $billingAgreement->addOrderRelation($this->_order);
1138

1139 1140 1141 1142 1143 1144
                                // add to order to save agreement
                                $this->_order->addRelatedObject($billingAgreement);
                            }
                        } else {
                            $message = __('Failed to create billing agreement for this order.');
                            throw new \Exception($message);
1145
                        }
1146 1147
                    } catch (\Exception $exception) {
                        $message = $exception->getMessage();
1148
                    }
1149

1150 1151 1152 1153 1154 1155
                    $this->_adyenLogger->addAdyenNotificationCronjob($message);
                    $comment = $this->_order->addStatusHistoryComment($message);
                    $this->_order->addRelatedObject($comment);
                } else {
                    $this->_adyenLogger->addAdyenNotificationCronjob('Ignore recurring_contract notification because Vault feature is enabled');
                }
1156
                break;
1157
            default:
1158 1159 1160
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    sprintf('This notification event: %s is not supported so will be ignored', $this->_eventCode)
                );
1161 1162 1163 1164
                break;
        }
    }

rikterbeek's avatar
rikterbeek committed
1165 1166
    /**
     * Not implemented
1167
     *
rikterbeek's avatar
rikterbeek committed
1168 1169 1170 1171
     * @return bool
     */
    protected function _refundOrder()
    {
1172 1173
        $this->_adyenLogger->addAdyenNotificationCronjob('Refunding the order');

1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
        // check if it is a split payment if so save the refunded data
        if ($this->_originalReference != "") {
            $this->_adyenLogger->addAdyenNotificationCronjob('Going to update the refund to split payments table');

            $orderPayment = $this->_adyenOrderPaymentCollectionFactory
                ->create()
                ->addFieldToFilter(\Adyen\Payment\Model\Notification::PSPREFRENCE, $this->_originalReference)
                ->getFirstItem();

            if ($orderPayment->getId() > 0) {
                $currency = $this->_order->getOrderCurrencyCode();
1185
                $amountRefunded = $amountRefunded = $orderPayment->getTotalRefunded() +
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
                    $this->_adyenHelper->originalAmount($this->_value, $currency);
                $orderPayment->setUpdatedAt(new \DateTime());
                $orderPayment->setTotalRefunded($amountRefunded);
                $orderPayment->save();
                $this->_adyenLogger->addAdyenNotificationCronjob('Update the refund in the split payments table');
            } else {
                $this->_adyenLogger->addAdyenNotificationCronjob('Payment not found in split payment table');
            }
        }

1196 1197 1198 1199
        /*
         * Don't create a credit memo if refund is initialize in Magento
         * because in this case the credit memo already exists
         */
1200
        $lastTransactionId = $this->_order->getPayment()->getLastTransId();
1201
        if ($lastTransactionId != $this->_pspReference) {
1202
            // refund is done through adyen backoffice so create a credit memo
1203 1204
            $order = $this->_order;
            if ($order->canCreditmemo()) {
1205 1206 1207
                $currency = $this->_order->getOrderCurrencyCode();
                $amount = $this->_adyenHelper->originalAmount($this->_value, $currency);
                $order->getPayment()->registerRefundNotification($amount);
1208

1209
                $this->_adyenLogger->addAdyenNotificationCronjob('Created credit memo for order');
1210 1211 1212 1213
            } else {
                $this->_adyenLogger->addAdyenNotificationCronjob('Could not create a credit memo for order');
            }
        } else {
1214
            $this->_adyenLogger->addAdyenNotificationCronjob(
1215
                'Did not create a credit memo for this order because refund is done through Magento'
1216
            );
1217
        }
rikterbeek's avatar
rikterbeek committed
1218 1219 1220 1221 1222 1223 1224
    }

    /**
     * @param $order
     */
    protected function _setRefundAuthorized()
    {
1225 1226 1227
        $this->_adyenLogger->addAdyenNotificationCronjob(
            'Status update to default status or refund_authorized status if this is set'
        );
rikterbeek's avatar
rikterbeek committed
1228 1229 1230
        $this->_order->addStatusHistoryComment(__('Adyen Refund Successfully completed'));
    }

1231
    /**
1232
     * authorize payment
1233 1234 1235
     */
    protected function _authorizePayment()
    {
1236
        $this->_adyenLogger->addAdyenNotificationCronjob('Authorisation of the order');
1237 1238 1239 1240 1241

        // Set adyen_notification_event_code_success to true so that we ignore a possible OFFER_CLOSED
        if (strcmp($this->_success, 'true') == 0) {
            $this->_order->setData('adyen_notification_event_code_success', 1);
        }
1242 1243
        $fraudManualReviewStatus = $this->_getFraudManualReviewStatus();

1244
        // If manual review is active and a separate status is used then ignore the pre authorized status
1245
        if ($this->_fraudManualReview != true || $fraudManualReviewStatus == "") {
1246 1247
            $this->_setPrePaymentAuthorized();
        } else {
1248 1249 1250 1251
            $this->_adyenLogger->addAdyenNotificationCronjob(
                'Ignore the pre authorized status because the order is ' .
                'under manual review and use the Manual review status'
            );
1252 1253 1254 1255 1256 1257
        }

        $this->_prepareInvoice();
        $_paymentCode = $this->_paymentMethodCode();

        // for boleto confirmation mail is send on order creation
1258
        if ($this->_paymentMethod != "adyen_boleto") {
1259
            // send order confirmation mail after invoice creation so merchant can add invoicePDF to this mail
1260
            if (!$this->_order->getEmailSent()) {
1261
                $this->_sendOrderMail();
1262
            }
1263 1264
        }

1265 1266
        if ($this->_paymentMethod == "c_cash" &&
            $this->_getConfigData('create_shipment', 'adyen_cash', $this->_order->getStoreId())
1267
        ) {
rikterbeek's avatar
rikterbeek committed
1268
            $this->_createShipment();
1269 1270 1271
        }
    }

1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
    /**
     * Send order Mail
     *
     * @return void
     */
    private function _sendOrderMail()
    {
        try {
            $this->_orderSender->send($this->_order);
            $this->_adyenLogger->addAdyenNotificationCronjob('Send orderconfirmation email to shopper');
1282
        } catch (\Exception $exception) {
1283 1284 1285 1286 1287 1288 1289
            $this->_adyenLogger->addAdyenNotificationCronjob(
                "Exception in Send Mail in Magento. This is an issue in the the core of Magento" .
                $exception->getMessage()
            );
        }
    }

1290 1291
    /**
     * Set status on authorisation
1292 1293
     *
     * @return void
1294
     */
1295 1296
    private function _setPrePaymentAuthorized()
    {
1297
        $status = $this->_getConfigData(
1298 1299 1300
            'payment_pre_authorized',
            'adyen_abstract',
            $this->_order->getStoreId()
1301
        );
1302 1303

        // only do this if status in configuration is set
1304
        if (!empty($status)) {
rikterbeek's avatar
rikterbeek committed
1305
            $this->_order->addStatusHistoryComment(__('Payment is authorised waiting for capture'), $status);
Bas Maassen's avatar
Bas Maassen committed
1306 1307
            $this->_setState($status);

1308 1309 1310
            $this->_adyenLogger->addAdyenNotificationCronjob(
                'Order status is changed to Pre-authorised status, status is ' . $status
            );
1311
        } else {
1312
            $this->_adyenLogger->addAdyenNotificationCronjob('No pre-authorised status is used so ignore');
1313 1314 1315 1316
        }
    }

    /**
1317
     * @return void
1318
     * @throws Exception
1319 1320 1321
     */
    protected function _prepareInvoice()
    {
1322
        $this->_adyenLogger->addAdyenNotificationCronjob('Prepare invoice for order');
1323 1324 1325 1326 1327

        //Set order state to new because with order state payment_review it is not possible to create an invoice
        if (strcmp($this->_order->getState(), \Magento\Sales\Model\Order::STATE_PAYMENT_REVIEW) == 0) {
            $this->_order->setState(\Magento\Sales\Model\Order::STATE_NEW);
        }
1328

1329 1330 1331 1332 1333 1334 1335 1336 1337
        $paymentObj = $this->_order->getPayment();

        // set pspReference as transactionId
        $paymentObj->setCcTransId($this->_pspReference);
        $paymentObj->setLastTransId($this->_pspReference);

        // set transaction
        $paymentObj->setTransactionId($this->_pspReference);

1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
        // Prepare transaction
        $transaction = $this->transactionBuilder->setPayment($paymentObj)
            ->setOrder($this->_order)
            ->setTransactionId($this->_pspReference)
            ->build(\Magento\Sales\Api\Data\TransactionInterface::TYPE_AUTH);

        $transaction->setIsClosed(false);

        $transaction->save();

1348 1349 1350
        //capture mode
        if (!$this->_isAutoCapture()) {
            $this->_order->addStatusHistoryComment(__('Capture Mode set to Manual'));
1351
            $this->_adyenLogger->addAdyenNotificationCronjob('Capture mode is set to Manual');
1352 1353

            // show message if order is in manual review
1354
            if ($this->_fraudManualReview) {
1355 1356
                // check if different status is selected
                $fraudManualReviewStatus = $this->_getFraudManualReviewStatus();
1357
                if ($fraudManualReviewStatus != "") {
1358 1359 1360 1361 1362 1363
                    $status = $fraudManualReviewStatus;
                    $comment = "Adyen Payment is in Manual Review check the Adyen platform";
                    $this->_order->addStatusHistoryComment(__($comment), $status);
                }
            }

1364
            $createPendingInvoice = (bool)$this->_getConfigData(
1365 1366 1367
                'create_pending_invoice',
                'adyen_abstract',
                $this->_order->getStoreId()
1368 1369 1370 1371 1372 1373
            );

            if (!$createPendingInvoice) {
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'Setting pending invoice is off so don\'t create an invoice wait for the capture notification'
                );
1374 1375 1376 1377 1378 1379
                return;
            }
        }

        // validate if amount is total amount
        $orderCurrencyCode = $this->_order->getOrderCurrencyCode();
1380 1381
        $amount = $this->_adyenHelper->originalAmount($this->_value, $orderCurrencyCode);

1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402
        try {
            // add to order payment
            $date = new \DateTime();
            $this->_adyenOrderPaymentFactory->create()
                ->setPspreference($this->_pspReference)
                ->setMerchantReference($this->_merchantReference)
                ->setPaymentId($paymentObj->getId())
                ->setPaymentMethod($this->_paymentMethod)
                ->setAmount($amount)
                ->setTotalRefunded(0)
                ->setCreatedAt($date)
                ->setUpdatedAt($date)
                ->save();
        } catch (\Exception $e) {
            $this->_adyenLogger->addError(
                'While processing a notification an exception occured. The payment has already been saved in the ' .
                'adyen_order_payment table but something went wrong later. Please check your logs for potential ' .
                'error messages regarding the merchant reference (order id): "' . $this->_merchantReference .
                '" and PSP reference: "' . $this->_pspReference . '"'
            );
        }
1403 1404

        if ($this->_isTotalAmount($paymentObj->getEntityId(), $orderCurrencyCode)) {
1405
            $this->_createInvoice();
1406
        } else {
1407 1408 1409
            $this->_adyenLogger->addAdyenNotificationCronjob(
                'This is a partial AUTHORISATION and the full amount is not reached'
            );
1410 1411 1412 1413 1414 1415 1416 1417
        }
    }

    /**
     * @return bool
     */
    protected function _isAutoCapture()
    {
Alessio Zampatti's avatar
Alessio Zampatti committed
1418
        // validate if payment methods allows manual capture
1419
        if ($this->_manualCaptureAllowed()) {
1420
            $captureMode = trim($this->_getConfigData(
1421 1422 1423 1424
                'capture_mode',
                'adyen_abstract',
                $this->_order->getStoreId()
            ));
1425
            $sepaFlow = trim($this->_getConfigData(
1426 1427 1428 1429
                'sepa_flow',
                'adyen_abstract',
                $this->_order->getStoreId()
            ));
1430
            $_paymentCode = $this->_paymentMethodCode();
1431
            $captureModeOpenInvoice = $this->_getConfigData(
1432 1433 1434
                'auto_capture_openinvoice',
                'adyen_abstract',
                $this->_order->getStoreId()
1435
            );
1436
            $manualCapturePayPal = trim($this->_getConfigData(
1437 1438 1439 1440
                'paypal_capture_mode',
                'adyen_abstract',
                $this->_order->getStoreId()
            ));
1441 1442 1443 1444 1445

            /*
             * if you are using authcap the payment method is manual.
             * There will be a capture send to indicate if payment is successful
             */
1446
            if ($this->_paymentMethod == "sepadirectdebit" && $sepaFlow == "authcap") {
1447 1448 1449
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'Manual Capture is applied for sepa because it is in authcap flow'
                );
1450 1451
                return false;
            }
1452

1453
            // payment method ideal, cash adyen_boleto has direct capture
1454
            if ($this->_paymentMethod == "sepadirectdebit" && $sepaFlow != "authcap") {
1455 1456
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'This payment method does not allow manual capture.(2) paymentCode:' .
1457
                    $_paymentCode . ' paymentMethod:' . $this->_paymentMethod . ' sepaFLow:' . $sepaFlow
1458
                );
1459 1460
                return true;
            }
1461

1462
            if ($_paymentCode == "adyen_pos_cloud") {
1463 1464
                $captureModePos = $this->_adyenHelper->getAdyenPosCloudConfigData('capture_mode_pos',
                    $this->_order->getStoreId());
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
                if (strcmp($captureModePos, 'auto') === 0) {
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        'This payment method is POS Cloud and configured to be working as auto capture '
                    );
                    return true;
                } elseif (strcmp($captureModePos, 'manual') === 0) {
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        'This payment method is POS Cloud and configured to be working as manual capture '
                    );
                    return false;
                }
            }

1478
            // if auto capture mode for openinvoice is turned on then use auto capture
1479
            if ($captureModeOpenInvoice == true &&
1480 1481
                $this->_adyenHelper->isPaymentMethodOpenInvoiceMethod($this->_paymentMethod)
            ) {
1482 1483 1484
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'This payment method is configured to be working as auto capture '
                );
1485
                return true;
1486
            }
1487

1488
            // if PayPal capture modues is different from the default use this one
1489 1490
            if (strcmp($this->_paymentMethod, 'paypal') === 0) {
                if ($manualCapturePayPal) {
1491 1492 1493
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        'This payment method is paypal and configured to work as manual capture'
                    );
1494
                    return false;
1495 1496 1497 1498 1499
                } else {
                    $this->_adyenLogger->addAdyenNotificationCronjob(
                        'This payment method is paypal and configured to work as auto capture'
                    );
                    return true;
1500 1501 1502
                }
            }
            if (strcmp($captureMode, 'manual') === 0) {
1503
                $this->_adyenLogger->addAdyenNotificationCronjob('Capture mode for this payment is set to manual');
1504 1505
                return false;
            }
1506 1507 1508 1509 1510

            /*
             * online capture after delivery, use Magento backend to online invoice
             * (if the option auto capture mode for openinvoice is not set)
             */
1511
            if ($this->_adyenHelper->isPaymentMethodOpenInvoiceMethod($this->_paymentMethod)) {
1512
                $this->_adyenLogger->addAdyenNotificationCronjob('Capture mode for klarna is by default set to manual');
1513 1514
                return false;
            }
1515 1516

            $this->_adyenLogger->addAdyenNotificationCronjob('Capture mode is set to auto capture');
1517 1518 1519
            return true;
        } else {
            // does not allow manual capture so is always immediate capture
1520
            $this->_adyenLogger->addAdyenNotificationCronjob('This payment method does not allow manual capture');
1521
            return true;
1522
        }
1523 1524 1525 1526 1527
    }

    /**
     * Validate if this payment methods allows manual capture
     * This is a default can be forced differently to overrule on acquirer level
1528 1529
     *
     * @return bool|null
1530 1531 1532 1533 1534 1535
     */
    protected function _manualCaptureAllowed()
    {
        $manualCaptureAllowed = null;
        $paymentMethod = $this->_paymentMethod;

Rik ter Beek's avatar
Rik ter Beek committed
1536 1537 1538 1539 1540
        // For all openinvoice methods manual capture is the default
        if ($this->_adyenHelper->isPaymentMethodOpenInvoiceMethod($paymentMethod)) {
            return true;
        }

1541
        switch ($paymentMethod) {
1542 1543 1544
            case 'cup':
            case 'cartebancaire':
            case 'visa':
1545
            case 'visadankort':
1546 1547 1548 1549
            case 'mc':
            case 'uatp':
            case 'amex':
            case 'maestro':
rikterbeek's avatar
rikterbeek committed
1550
            case 'maestrouk':
1551 1552 1553 1554 1555 1556
            case 'diners':
            case 'discover':
            case 'jcb':
            case 'laser':
            case 'paypal':
            case 'sepadirectdebit':
1557 1558 1559
            case 'dankort':
            case 'elo':
            case 'hipercard':
1560 1561 1562 1563 1564 1565
            case 'mc_applepay':
            case 'visa_applepay':
            case 'amex_applepay':
            case 'discover_applepay':
            case 'maestro_applepay':
            case 'paywithgoogle':
1566 1567 1568 1569
                $manualCaptureAllowed = true;
                break;
            default:
                $manualCaptureAllowed = false;
1570
        }
1571 1572

        return $manualCaptureAllowed;
1573 1574 1575 1576 1577
    }

    /**
     * @return bool
     */
1578 1579
    protected function _isBankTransfer()
    {
1580
        if (strlen($this->_paymentMethod) >= 12 && substr($this->_paymentMethod, 0, 12) == "bankTransfer") {
1581 1582 1583 1584 1585 1586 1587
            $isBankTransfer = true;
        } else {
            $isBankTransfer = false;
        }
        return $isBankTransfer;
    }

1588 1589 1590
    /**
     * @return mixed
     */
1591 1592
    protected function _getFraudManualReviewStatus()
    {
1593
        return $this->_getConfigData(
1594 1595 1596
            'fraud_manual_review_status',
            'adyen_abstract',
            $this->_order->getStoreId()
1597
        );
1598 1599
    }

1600 1601 1602
    /**
     * @return mixed
     */
1603 1604
    protected function _getFraudManualReviewAcceptStatus()
    {
1605
        return $this->_getConfigData(
1606 1607 1608
            'fraud_manual_review_accept_status',
            'adyen_abstract',
            $this->_order->getStoreId()
1609
        );
1610 1611
    }

1612
    /**
1613 1614
     * @param int $paymentId
     * @param string $orderCurrencyCode
1615 1616
     * @return bool
     */
1617
    protected function _isTotalAmount($paymentId, $orderCurrencyCode)
1618 1619 1620 1621
    {
        $this->_adyenLogger->addAdyenNotificationCronjob(
            'Validate if AUTHORISATION notification has the total amount of the order'
        );
1622

1623
        // get total amount of the order
1624
        $grandTotal = (int)$this->_adyenHelper->formatAmount($this->_order->getGrandTotal(), $orderCurrencyCode);
1625 1626 1627 1628 1629

        // check if total amount of the order is authorised
        $res = $this->_adyenOrderPaymentCollectionFactory
            ->create()
            ->getTotalAmount($paymentId);
1630

1631
        if ($res && isset($res[0]) && is_array($res[0])) {
1632 1633
            $amount = $res[0]['total_amount'];
            $orderAmount = $this->_adyenHelper->formatAmount($amount, $orderCurrencyCode);
1634
            $this->_adyenLogger->addAdyenNotificationCronjob(
1635 1636
                sprintf(
                    'The grandtotal amount is %s and the total order amount that is authorised is: %s',
1637 1638 1639 1640
                    $grandTotal,
                    $orderAmount
                )
            );
1641 1642 1643 1644 1645 1646 1647 1648 1649 1650

            if ($grandTotal == $orderAmount) {
                $this->_adyenLogger->addAdyenNotificationCronjob('AUTHORISATION has the full amount');
                return true;
            } else {
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'This is a partial AUTHORISATION, the amount is ' . $this->_value
                );
                return false;
            }
1651
        }
1652
        return false;
1653 1654
    }

1655
    /**
1656
     * @return void
1657 1658
     * @throws \Magento\Framework\Exception\LocalizedException
     * @throws Exception
1659
     */
1660 1661
    protected function _createInvoice()
    {
1662
        $this->_adyenLogger->addAdyenNotificationCronjob('Creating invoice for order');
1663 1664

        if ($this->_order->canInvoice()) {
1665 1666
            /* We do not use this inside a transaction because order->save()
             * is always done on the end of the notification
1667 1668 1669 1670 1671 1672 1673
             * and it could result in a deadlock see https://github.com/Adyen/magento/issues/334
             */
            try {
                $invoice = $this->_order->prepareInvoice();
                $invoice->getOrder()->setIsInProcess(true);

                // set transaction id so you can do a online refund from credit memo
1674
                $invoice->setTransactionId($this->_pspReference);
1675

1676

1677
                $autoCapture = $this->_isAutoCapture();
1678
                $createPendingInvoice = (bool)$this->_getConfigData(
1679 1680 1681
                    'create_pending_invoice',
                    'adyen_abstract',
                    $this->_order->getStoreId()
1682
                );
1683

1684
                if ((!$autoCapture) && ($createPendingInvoice)) {
1685 1686
                    // if amount is zero create a offline invoice
                    $value = (int)$this->_value;
1687
                    if ($value == 0) {
rikterbeek's avatar
rikterbeek committed
1688
                        $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
1689
                    } else {
rikterbeek's avatar
rikterbeek committed
1690
                        $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::NOT_CAPTURE);
1691 1692 1693 1694 1695 1696 1697 1698
                    }

                    $invoice->register();
                } else {
                    $invoice->register()->pay();
                }

                $invoice->save();
1699
                $this->_adyenLogger->addAdyenNotificationCronjob('Created invoice');
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711

                /*
                 * Add invoice in the adyen_invoice table
                 */
                $this->_adyenInvoiceFactory->create()
                    ->setInvoiceId($invoice->getEntityId())
                    ->setPspreference($this->_pspReference)
                    ->setOriginalReference($this->_pspReference)
                    ->setAcquirerReference($this->_acquirerReference)
                    ->save();

                $this->_adyenLogger->addAdyenNotificationCronjob('Created invoice entry in the Adyen table');
1712
            } catch (Exception $e) {
1713 1714 1715
                $this->_adyenLogger->addAdyenNotificationCronjob(
                    'Error saving invoice. The error message is: ' . $e->getMessage()
                );
1716 1717 1718 1719 1720
                throw new Exception(sprintf('Error saving invoice. The error message is:', $e->getMessage()));
            }

            $this->_setPaymentAuthorized();

1721 1722 1723 1724
            $invoiceAutoMail = (bool)$this->_scopeConfig->isSetFlag(
                \Magento\Sales\Model\Order\Email\Container\InvoiceIdentity::XML_PATH_EMAIL_ENABLED,
                \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
                $this->_order->getStoreId()
1725 1726
            );

1727
            if ($invoiceAutoMail) {
1728
                $this->_invoiceSender->send($invoice);
1729 1730
            }
        } else {
1731
            $this->_adyenLogger->addAdyenNotificationCronjob('It is not possible to create invoice for this order');
1732 1733 1734 1735
        }
    }

    /**
1736 1737 1738
     * @param bool $manualReviewComment
     * @param bool $createInvoice
     * @throws Exception
1739 1740 1741
     */
    protected function _setPaymentAuthorized($manualReviewComment = true, $createInvoice = false)
    {
1742
        $this->_adyenLogger->addAdyenNotificationCronjob('Set order to authorised');
1743 1744 1745 1746

        // if full amount is captured create invoice
        $currency = $this->_order->getOrderCurrencyCode();
        $amount = $this->_value;
1747
        $orderAmount = (int)$this->_adyenHelper->formatAmount($this->_order->getGrandTotal(), $currency);
1748 1749

        // create invoice for the capture notification if you are on manual capture
1750 1751
        if ($createInvoice == true && $amount == $orderAmount) {
            $this->_adyenLogger->addAdyenNotificationCronjob(
1752
                'amount notification:' . $amount . ' amount order:' . $orderAmount
1753
            );
rikterbeek's avatar
rikterbeek committed
1754
            $this->_createInvoice();
1755
        }
1756

1757
        $status = $this->_getConfigData(
1758 1759 1760
            'payment_authorized',
            'adyen_abstract',
            $this->_order->getStoreId()
1761
        );
1762 1763

        // virtual order can have different status
1764
        if ($this->_order->getIsVirtual()) {
1765
            $this->_adyenLogger->addAdyenNotificationCronjob('Product is a virtual product');
1766
            $virtualStatus = $this->_getConfigData(
1767 1768 1769
                'payment_authorized_virtual',
                'adyen_abstract',
                $this->_order->getStoreId()
1770
            );
1771 1772
            if ($virtualStatus != "") {
                $status = $virtualStatus;
1773 1774 1775 1776
            }
        }

        // check for boleto if payment is totally paid
1777
        if ($this->_paymentMethodCode() == "adyen_boleto") {
1778 1779 1780 1781
            // check if paid amount is the same as orginal amount
            $orginalAmount = $this->_boletoOriginalAmount;
            $paidAmount = $this->_boletoPaidAmount;

1782
            if ($orginalAmount != $paidAmount) {
1783 1784
                // not the full amount is paid. Check if it is underpaid or overpaid
                // strip the  BRL of the string
1785
                $orginalAmount = str_replace("BRL", "", $orginalAmount);
1786 1787
                $orginalAmount = floatval(trim($orginalAmount));

1788
                $paidAmount = str_replace("BRL", "", $paidAmount);
1789 1790
                $paidAmount = floatval(trim($paidAmount));

1791
                if ($paidAmount > $orginalAmount) {
1792
                    $overpaidStatus = $this->_getConfigData(
1793 1794 1795
                        'order_overpaid_status',
                        'adyen_boleto',
                        $this->_order->getStoreId()
1796
                    );
1797 1798 1799
                    // check if there is selected a status if not fall back to the default
                    $status = (!empty($overpaidStatus)) ? $overpaidStatus : $status;
                } else {
1800
                    $underpaidStatus = $this->_getConfigData(
1801 1802 1803
                        'order_underpaid_status',
                        'adyen_boleto',
                        $this->_order->getStoreId()
1804
                    );
1805 1806 1807 1808 1809 1810 1811 1812 1813
                    // check if there is selected a status if not fall back to the default
                    $status = (!empty($underpaidStatus)) ? $underpaidStatus : $status;
                }
            }
        }

        $comment = "Adyen Payment Successfully completed";

        // if manual review is true use the manual review status if this is set
1814
        if ($manualReviewComment == true && $this->_fraudManualReview) {
1815 1816
            // check if different status is selected
            $fraudManualReviewStatus = $this->_getFraudManualReviewStatus();
1817
            if ($fraudManualReviewStatus != "") {
1818 1819 1820 1821 1822 1823
                $status = $fraudManualReviewStatus;
                $comment = "Adyen Payment is in Manual Review check the Adyen platform";
            }
        }
        $status = (!empty($status)) ? $status : $this->_order->getStatus();
        $this->_order->addStatusHistoryComment(__($comment), $status);
Bas Maassen's avatar
Bas Maassen committed
1824 1825
        $this->_setState($status);

1826 1827 1828
        $this->_adyenLogger->addAdyenNotificationCronjob(
            'Order status is changed to authorised status, status is ' . $status
        );
1829 1830
    }

Bas Maassen's avatar
Bas Maassen committed
1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845
    /**
     * Set State from Status
     */

    protected function _setState($status)
    {
        $statusObject = $this->_orderStatusCollection->create()
            ->addFieldToFilter('main_table.status', $status)
            ->addFieldToFilter('state_table.is_default', true)
            ->joinStates()
            ->getFirstItem();

        $this->_order->setState($statusObject->getState());
        $this->_adyenLogger->addAdyenNotificationCronjob('State is changed to  ' . $statusObject->getState());
    }
1846

rikterbeek's avatar
rikterbeek committed
1847
    /**
1848
     * Create shipment
rikterbeek's avatar
rikterbeek committed
1849
     *
1850
     * @throws bool
rikterbeek's avatar
rikterbeek committed
1851
     */
1852 1853
    protected function _createShipment()
    {
1854
        $this->_adyenLogger->addAdyenNotificationCronjob('Creating shipment for order');
rikterbeek's avatar
rikterbeek committed
1855 1856
        // create shipment for cash payment
        $payment = $this->_order->getPayment()->getMethodInstance();
1857 1858
        if ($this->_order->canShip()) {
            $itemQty = [];
rikterbeek's avatar
rikterbeek committed
1859
            $shipment = $this->_order->prepareShipment($itemQty);
1860
            if ($shipment) {
rikterbeek's avatar
rikterbeek committed
1861 1862 1863 1864
                $shipment->register();
                $shipment->getOrder()->setIsInProcess(true);
                $comment = __('Shipment created by Adyen');
                $shipment->addComment($comment);
rikterbeek's avatar
rikterbeek committed
1865 1866 1867 1868

                /** @var \Magento\Framework\DB\Transaction $transaction */
                $transaction = $this->_transactionFactory->create();
                $transaction->addObject($shipment)
rikterbeek's avatar
rikterbeek committed
1869 1870
                    ->addObject($shipment->getOrder())
                    ->save();
rikterbeek's avatar
rikterbeek committed
1871

1872
                $this->_adyenLogger->addAdyenNotificationCronjob('Order is shipped');
rikterbeek's avatar
rikterbeek committed
1873 1874
            }
        } else {
1875
            $this->_adyenLogger->addAdyenNotificationCronjob('Order can\'t be shipped');
rikterbeek's avatar
rikterbeek committed
1876 1877 1878
        }
    }

1879 1880 1881
    /**
     * Retrieve information from payment configuration
     *
1882 1883 1884
     * @param $field
     * @param string $paymentMethodCode
     * @param $storeId
1885 1886 1887 1888 1889 1890 1891
     * @return mixed
     */
    protected function _getConfigData($field, $paymentMethodCode = 'adyen_cc', $storeId)
    {
        $path = 'payment/' . $paymentMethodCode . '/' . $field;
        return $this->_scopeConfig->getValue($path, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeId);
    }
1892 1893 1894 1895 1896 1897

    /**
     * Add admin notice message for refund failed notification
     *
     * @return void
     */
1898 1899
    protected function addRefundFailedNotice()
    {
1900 1901
        $this->notifierPool->addNotice(
            __("Adyen: Refund for order #%1 has failed", $this->_merchantReference),
1902 1903 1904
            __("Reason: %1 | PSPReference: %2 | You can go to Adyen Customer Area and trigger this refund manually or contact our support.",
                $this->_reason, $this->_pspReference),
            $this->_adyenHelper->getPspReferenceSearchUrl($this->_pspReference, $this->_live)
1905 1906
        );
    }
1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956

    /**
     * Add/update info on notification processing errors
     *
     * @param \Adyen\Payment\Model\Notification $notification
     * @param string $errorMessage
     * @return void
     */
    private function handleNotificationError($notification, $errorMessage)
    {
        $this->setNotificationError($notification, $errorMessage);
        $this->addNotificationErrorComment($errorMessage);
    }

    /**
     * Increases error count and appends error message to notification
     *
     * @param \Adyen\Payment\Model\Notification $notification
     * @param string $errorMessage
     * @return void
     */
    private function setNotificationError($notification, $errorMessage)
    {
        $notification->setErrorCount($notification->getErrorCount() + 1);
        $oldMessage = $notification->getErrorMessage();
        $newMessage = sprintf(
            "[%s]: %s",
            $this->timezone->formatDateTime($notification->getUpdatedAt()),
            $errorMessage
        );
        if (empty($oldMessage)) {
            $notification->setErrorMessage($newMessage);
        } else {
            $notification->setErrorMessage($oldMessage . "\n" . $newMessage);
        }
        $notification->save();
    }

    /**
     * Adds a comment to the order history with the notification processing error
     *
     * @param string $errorMessage
     * @return void
     */
    private function addNotificationErrorComment($errorMessage)
    {
        $comment = __('The order failed to update: %1', $errorMessage);
        $this->_order->addStatusHistoryComment($comment);
        $this->_order->save();
    }
1957
}