HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/slenders.komma.pro/app/Komma/Shop/Tests/Unit/PSPTest.php
<?php

namespace App\Komma\Shop\Tests\Unit;

use App\Komma\Addresses\Models\Address;
use App\Komma\Globalization\RegionInfo;
use App\Komma\Shop\Cart\ShoppingCartServiceInterface;
use App\Komma\Shop\Checkout\CheckoutServiceInterface;
use App\Komma\Shop\Payment\Clients\MultiSafepay\Client;
use App\Komma\Shop\Payment\Clients\MultiSafepay\Enums\OrderStatus;
use App\Komma\Shop\Payment\Clients\MultiSafepay\Object\Orders;
use App\Komma\Shop\Payment\PaymentServiceInterface;
use App\Komma\Shop\Payment\PSPAdapters\Mollie;
use App\Komma\Shop\Payment\PSPAdapters\MultiSafepay;
use App\Komma\Shop\Payment\Transaction;
use App\Komma\Shop\Payment\TransactionStatus;
use App\Komma\Shop\Products\Product\Product;
use App\Komma\Shop\Tests\TestCase;
use App\Komma\Sites\SiteServiceInterface;
use App\Komma\Users\Models\SiteUser;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Http\RedirectResponse;
use Mollie\Api\Endpoints\PaymentEndpoint;
use Mollie\Api\MollieApiClient;
use Mollie\Api\Resources\Payment;
use Mollie\Api\Types\PaymentStatus;
use PHPUnit\Framework\MockObject\MockObject;

class PSPTest extends TestCase
{
    use DatabaseTransactions; //Automatically rolls back database actions after tests

    /**
     * @group PSP
     * @test
     */
    public function testMolliePSPPaymentCreation()
    {
        //Initialize service interfaces
        /** @var SiteServiceInterface $siteService */
        $siteService = app(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        /** @var SiteUser $customer */
        $customer = factory(SiteUser::class)->create();

        /** @var Address $address */
        $address = factory(Address::class)->create();
        $customer->addresses()->save($address);

        $address = $customer->addresses()->first();
        $this->assertInstanceOf(Address::class, $address);

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = app(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = app(PaymentServiceInterface::class);

        //Add a product into the shoppingcart
        $product = Product::inRandomOrder()->first();
        $shoppingCartService->addProductable($product);

        //User the checkoutService to create an order
        /** @var CheckoutServiceInterface $checkoutService */
        $checkoutService = app(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItems($shoppingCartService->getItems(), $customer, $address, $address);

        //Check that the order has the same price as the product. Then we know we can trust it to the payment service related code
        $this->assertEquals($order->total_price, $product->getTotal());


        //Define fake paymentData
        $fakePaymentPspId = 'tr_8mkgj9TQRAphpunit';
        $checkoutUrl = 'https://www.mollie.com/payscreen/select-method/'.explode('_', $fakePaymentPspId)[1];

        //Mock / fake the MollieApi client, used by the Mollie adapter, to make it return fake, but real like api call results, like a mollie payment.
        \App::bind(MollieApiClient::class, function() use($order, $checkoutUrl, $fakePaymentPspId) {
            $fakeMollieApiClient = $this->createMock(MollieApiClient::class);

            //Fake the payment endpoint that is in the fakeMollie api client
            $paymentsEndpoint = $this->createMock(PaymentEndpoint::class);
            $fakeMollieApiClient->payments = $paymentsEndpoint;

            //Create a faked mollie Payment
            $fakedPayment = $this->createMock(\Mollie\Api\Resources\Payment::class);
            $fakedPayment->id = $fakePaymentPspId;

            //returnCallback returns a callback that is executed when calling the "create" method on a payment.
            //The paymentData variable will contain data that is passed to that call of the create method.
            $paymentsEndpoint->method('create')->will($this->returnCallback(function($paymentData) use($fakedPayment, $checkoutUrl, $fakePaymentPspId) {
                $fakedPayment->amount = (object) $paymentData['amount'];
                $fakedPayment->description = $paymentData['description'];
                $fakedPayment->redirectUrl = $paymentData['redirectUrl'];
                $fakedPayment->metadata = $paymentData['metadata'];
                $fakedPayment->locale = $paymentData['locale'];
                $fakedPayment->createdAt = '2019-01-21T11:23:33+00:00';
                $fakedPayment->expiresAt = '2019-01-21T11:28:33+00:00';
                $fakedPayment->method = 'ideal';

                return $fakedPayment;
            }));
            $fakedPayment->method('getCheckoutUrl')->willReturn($checkoutUrl);
            $paymentsEndpoint->method('get')->willReturn($fakedPayment);

            return $fakeMollieApiClient;
        });

        //Make sure the configured psp is mollie
        config()->set('payment.payment_service_provider', 'mollie');

        //Get an adapter for the currently configured psp (must be mollie)
        $pspAdapter = $pspService->getAdapter();
        $this->assertInstanceOf(Mollie::class, $pspAdapter);

        //Get a transaction for the order (normally done in the checkoutService)
        $transaction = $pspAdapter->createTransaction($order);
        $this->assertInstanceOf(Transaction::class, $transaction);

        //Validate the transaction
        $shopRegionInfo = new RegionInfo('NL'); //Shop was build with euros in mind and the Dutch language as the primary one
        $this->assertTrue($transaction->exists); //Check that it exists in the database
        $this->assertEquals($order->total_price, $transaction->amount);
        $this->assertEquals($order, $transaction->order);
        $this->assertEquals(TransactionStatus::OPEN, $transaction->status);
        $this->assertEquals('tr_8mkgj9TQRAphpunit', $transaction->psp_id);
        $this->assertEquals($shopRegionInfo->getISOCurrencySymbol(), $transaction->currency_iso_4217_code);
        $this->assertEquals(request()->ip(), $transaction->ip);
        $this->assertEquals('ideal', $transaction->payment_method);
        $this->assertEquals('open', $transaction->status);
        $this->assertEquals(Carbon::parse('2019-01-21T11:28:33+01:00'), $transaction->expire_date);


        $redirectResponse = $pspAdapter->redirectForPayment($transaction);

        $this->assertInstanceOf(RedirectResponse::class, $redirectResponse);
        $this->assertEquals('https://www.mollie.com/payscreen/select-method/'.explode('_', $fakePaymentPspId)[1], $redirectResponse->getTargetUrl());

        return $fakePaymentPspId;
    }

    /**
     * @group PSP
     * @test
     * @throws \Throwable
     */
    public function testMolliePSPPaymentRetrieval()
    {
        //Initialize service interfaces
        /** @var SiteServiceInterface $siteService */
        $siteService = app(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        /** @var SiteUser $customer */
        $customer = factory(SiteUser::class)->create();

        /** @var Address $address */
        $address = factory(Address::class)->create();
        $customer->addresses()->save($address);

        $address = $customer->addresses()->first();
        $this->assertInstanceOf(Address::class, $address);

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = app(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = app(PaymentServiceInterface::class);

        //Add a product into the shoppingcart
        $product = Product::inRandomOrder()->first();
        $shoppingCartService->addProductable($product);

        //User the checkoutService to create an order
        /** @var CheckoutServiceInterface $checkoutService */
        $checkoutService = app(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItems($shoppingCartService->getItems(), $customer, $address, $address);

        //Define fake paymentData
        $fakePaymentPspId = 'tr_8mkgj9TQRAphpunit';
        $checkoutUrl = 'https://www.mollie.com/payscreen/select-method/'.explode('_', $fakePaymentPspId)[1];

        //Create a mocked / faked mollie Payment
        $fakedPayment = $this->createMock(\Mollie\Api\Resources\Payment::class);
        $fakedPayment->id = $fakePaymentPspId;

        //Mock / fake the MollieApi client, used by the Mollie adapter, to make it return fake, but real like api call results, like a mollie payment.
        /** @var Payment|MockObject|null $fakedPayment */
        \App::bind(MollieApiClient::class, function() use($order, $checkoutUrl, $fakePaymentPspId, &$fakedPayment) {
            $fakeMollieApiClient = $this->createMock(MollieApiClient::class);

            //Fake the payment endpoint that is in the fakeMollie api client
            $paymentsEndpoint = $this->createMock(PaymentEndpoint::class);
            $fakeMollieApiClient->payments = $paymentsEndpoint;

            //returnCallback returns a callback that is executed when calling the "create" method on a payment.
            //The paymentData variable will contain data that is passed to that call of the create method.
            $paymentsEndpoint->method('create')->will($this->returnCallback(function($paymentData) use(&$fakedPayment, $checkoutUrl, $fakePaymentPspId) {
                $fakedPayment->amount = (object) $paymentData['amount'];
                $fakedPayment->description = $paymentData['description'];
                $fakedPayment->redirectUrl = $paymentData['redirectUrl'];
                $fakedPayment->metadata = $paymentData['metadata'];
                $fakedPayment->locale = $paymentData['locale'];
                $fakedPayment->createdAt = '2019-01-21T11:23:33+00:00';
                $fakedPayment->expiresAt = '2019-01-21T11:28:33+00:00';
                $fakedPayment->method = 'ideal';

                return $fakedPayment;
            }));
            $fakedPayment->method('getCheckoutUrl')->willReturn($checkoutUrl);
            $paymentsEndpoint->method('get')->will($this->returnCallback(function($psp_id) use (&$fakedPayment) {
                return $fakedPayment;
            }));

            return $fakeMollieApiClient;
        });

        //Make sure the configured psp is mollie
        config()->set('payment.payment_service_provider', 'mollie');

        $pspAdapter = $pspService->getAdapter();
        $createdTransaction = $pspAdapter->createTransaction($order);

        //Simulate mollie calling our api telling we have an update for a transaction without an id
        $this->post(route('transaction.statusupdate', ['order' => $order]))
            ->assertSee('Please provide a transaction id using the key \"id\"')
            ->assertStatus(400);

        //Simulate mollie calling our api telling we have an update for a transaction with an incorrect id
        $route = route('transaction.statusupdate', ['order' => $order]).'?id='.$fakePaymentPspId.'_oops';
        $this->post($route)
            ->assertSee('The transaction \/ payment could not be found')
            ->assertStatus(404);

        //Simulate mollie calling our api telling we have an update for transaction with the given id
        $route = route('transaction.statusupdate', ['order' => $order]).'?id='.$fakePaymentPspId;
        $this->post($route)
            ->assertSee('Payment updated successfully!')
            ->assertStatus(200);

        //The payment still must be open at this point since we did not change it (e.g.) The user did not pay for it yet.
        $createdTransaction->refresh(); //Reload from db
        $this->assertEquals(TransactionStatus::OPEN, $createdTransaction->status);

        //Simulate that mollie received a payment. E.g. Transaction was paid at mollie side
        //Simulate the payment after verifying we have the correct object.
        $this->assertInstanceOf(Payment::class, $fakedPayment);
        $this->assertInstanceOf(MockObject::class, $fakedPayment);
        $fakedPayment->status = PaymentStatus::STATUS_PAID;

        //Simulate the call by mollie to let our api (what mollie calls webhook) know that they processed a payment. This hits the processPSPResponse method from the mollie class via de PSPResponseController
        $route = route('transaction.statusupdate', ['order' => $order]).'?id='.$fakePaymentPspId;
        $this->post($route)
            ->assertSee('Payment updated successfully!')
            ->assertStatus(200);

        //Now the transaction should have the status of paid.
        $createdTransaction->refresh(); //Reload from db
        $this->assertEquals(TransactionStatus::PAYMENT_PAID, $createdTransaction->status);
    }

    /**
     * @group PSP
     * @test
     */
    public function testMultiSafePayPSPPaymentCreation()
    {
        //Initialize service interfaces
        /** @var SiteServiceInterface $siteService */
        $siteService = app(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        /** @var SiteUser $customer */
        $customer = factory(SiteUser::class)->create();

        /** @var Address $address */
        $address = factory(Address::class)->create();
        $customer->addresses()->save($address);

        $address = $customer->addresses()->first();
        $this->assertInstanceOf(Address::class, $address);

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = app(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = app(PaymentServiceInterface::class);

        //Add a product into the shoppingcart
        $product = Product::inRandomOrder()->first();
        $shoppingCartService->addProductable($product);

        //User the checkoutService to create an order
        /** @var CheckoutServiceInterface $checkoutService */
        $checkoutService = app(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItems($shoppingCartService->getItems(), $customer, $address, $address);

        //Check that the order has the same price as the product. Then we know we can trust it to the payment service related code
        $this->assertEquals($order->total_price, $product->getTotal());


        //Define fake paymentData
        $fakePaymentPspId = '3233377';
        $checkoutUrl = 'https://testpayv2.multisafepay.com/simulator/ideal?trxid=12364692762434462&ideal=prob&issuerid=4371&merchantReturnURL=https%3A%2F%2Ftestpayv2%2Emultisafepay%2Ecom%2Fconnect%2Fredirect%3Flang%3Dnl%5FNL%26mspid%3D'.$fakePaymentPspId;

        //Mock / fake the MollieApi client, used by the Mollie adapter, to make it return fake, but real like api call results, like a mollie payment.
        \App::bind(Client::class, function() use($order, $checkoutUrl, $fakePaymentPspId) {
            $fakeMultiSafepayApiClient = $this->createMock(Client::class);

            //Fake the payment endpoint that is in the fakeMollie api client
            $fakeOrdersEndpoint = $this->createMock(Orders::class);
            $fakeMultiSafepayApiClient->orders = $fakeOrdersEndpoint;

            //Create a faked multisafepay Order
            $fakedOrder = [
              "transaction_id" => $fakePaymentPspId,
              "order_id" => 26,
              "created" => "2019-01-25T12:17:52",
              "currency" => "EUR",
              "amount" => 799,
              "description" => "order #26",
              "var1" => null,
              "var2" => null,
              "var3" => null,
              "items" => null,
              "amount_refunded" => 0,
              "status" => "initialized",
              "financial_status" => "initialized",
              "reason" => "",
              "reason_code" => "",
              "fastcheckout" => "NO",
              "modified" => "2019-01-25T12:18:01",
              "payment_url" => $checkoutUrl,
              "customer" => [
                "locale" => "nl_NL",
                "first_name" => "Komma",
                "last_name" => "Super Admin",
                "address1" => null,
                "address2" => null,
                "house_number" => 0,
                "zip_code" => null,
                "city" => null,
                "state" => null,
                "country" => "NL",
                "country_name" => null,
                "phone1" => null,
                "phone2" => "",
                "email" => "jules@komma.pro"
              ],
              "payment_details" => [
                "recurring_id" => null,
                "type" => "IDEAL",
                "account_id" => null,
                "account_holder_name" => null,
                "external_transaction_id" => 12364692762434462,
                  //Yeah i know....Multisafepay really did put a checkout url in the account iban..... i leave it here because it is simulates real life situation
                "account_iban" => $checkoutUrl
              ],
              "costs" => [
                [
                  "transaction_id" => 1451590,
                  "description" => "0.49 For iDEAL Transactions",
                  "type" => "SYSTEM",
                  "amount" => 0.48999999999999999
                ]
              ],
              "related_transactions" => null,
              "payment_methods" => [
                [
                  "amount" => 799,
                  "currency" => "EUR",
                  "description" => "order #26",
                  "external_transaction_id" => 12364692762434462,
                  "payment_description" => "iDEAL",
                  "status" => "initialized",
                  "type" => "IDEAL"
                ]
              ]
            ];

            //returnCallback returns a callback that is executed when calling the "create" method on a payment.
            //The paymentData variable will contain data that is passed to that call of the create method.
            $fakeOrdersEndpoint->method('post')->will($this->returnCallback(function($paymentData) use($fakedOrder, $checkoutUrl, $fakePaymentPspId) {
                $modifiedOrder = $fakedOrder; //Copies array
                unset($modifiedOrder['payment_details']['type']); //MultiSafepay does not give you this at this stage

                return (object) $modifiedOrder;
            }));

            return $fakeMultiSafepayApiClient;
        });

        //Make sure the configured psp is multisafepay
        config()->set('payment.payment_service_provider', 'multisafepay');

        //Get an adapter for the currently configured psp (must be the multisafepay adapter)
        $pspAdapter = $pspService->getAdapter();
        $this->assertInstanceOf(MultiSafepay::class, $pspAdapter);

        //Get a transaction for the order (normally done in the checkoutService)
        $transaction = $pspAdapter->createTransaction($order);
        $this->assertInstanceOf(Transaction::class, $transaction);

        //Validate the transaction
        $shopRegionInfo = new RegionInfo('NL'); //Shop was build with euros in mind and the Dutch language as the primary one
        $this->assertTrue($transaction->exists); //Check that it exists in the database
        $this->assertEquals($order->total_price, $transaction->amount);
        $this->assertEquals($order, $transaction->order);
        $this->assertEquals(TransactionStatus::OPEN, $transaction->status);
        $this->assertEquals($fakePaymentPspId, $transaction->psp_id); //They don't give you a psp_id at this stage
        $this->assertEquals($shopRegionInfo->getISOCurrencySymbol(), $transaction->currency_iso_4217_code);
        $this->assertEquals(request()->ip(), $transaction->ip);
        $this->assertEquals(null, $transaction->payment_method); //They don't give you a payment method at this stage
        $this->assertEquals('open', $transaction->status);
        $this->assertEquals(null, $transaction->expire_date); //They don't give you an expiration date

        $redirectResponse = $pspAdapter->redirectForPayment($transaction);

        $this->assertInstanceOf(RedirectResponse::class, $redirectResponse);
        $this->assertEquals("https://testpayv2.multisafepay.com/simulator/ideal?trxid=12364692762434462&ideal=prob&issuerid=4371&merchantReturnURL=https%3A%2F%2Ftestpayv2%2Emultisafepay%2Ecom%2Fconnect%2Fredirect%3Flang%3Dnl%5FNL%26mspid%3D".$fakePaymentPspId, $redirectResponse->getTargetUrl());

        return $fakePaymentPspId;
    }

    /**
     * @group PSP
     * @test
     * @throws \Throwable
     */
    public function testMultiSafepayPaymentRetrieval()
    {
        //Initialize service interfaces
        /** @var SiteServiceInterface $siteService */
        $siteService = app(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        /** @var SiteUser $customer */
        $customer = factory(SiteUser::class)->create();

        /** @var Address $address */
        $address = factory(Address::class)->create();
        $customer->addresses()->save($address);

        $address = $customer->addresses()->first();
        $this->assertInstanceOf(Address::class, $address);

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = app(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = app(PaymentServiceInterface::class);

        //Add a product into the shoppingcart
        $product = Product::inRandomOrder()->first();
        $shoppingCartService->addProductable($product);

        //User the checkoutService to create an order
        /** @var CheckoutServiceInterface $checkoutService */
        $checkoutService = app(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItems($shoppingCartService->getItems(), $customer, $address, $address);

        //Define fake paymentData
        $fakePaymentPspId = '3233377';
        $checkoutUrl = 'https://testpayv2.multisafepay.com/simulator/ideal?trxid=12364692762434462&ideal=prob&issuerid=4371&merchantReturnURL=https%3A%2F%2Ftestpayv2%2Emultisafepay%2Ecom%2Fconnect%2Fredirect%3Flang%3Dnl%5FNL%26mspid%3D'.$fakePaymentPspId;

        //Create fake multisafepay order data
        /** @var array $multiSafepayOrderData */
        $multiSafepayOrderData = [
            "transaction_id" => $fakePaymentPspId,
            "order_id" => $order->id,
            "created" => "2019-01-25T12:17:52",
            "currency" => "EUR",
            "amount" => 799,
            "description" => "order #".$order->id,
            "var1" => null,
            "var2" => null,
            "var3" => null,
            "items" => null,
            "amount_refunded" => 0,
            "status" => OrderStatus::INITIALIZED,
            "financial_status" => OrderStatus::INITIALIZED,
            "reason" => "",
            "reason_code" => "",
            "fastcheckout" => "NO",
            "modified" => "2019-01-25T12:18:01",
            "payment_url" => $checkoutUrl,
            "customer" => [
                "locale" => "nl_NL",
                "first_name" => "Komma",
                "last_name" => "Super Admin",
                "address1" => null,
                "address2" => null,
                "house_number" => 0,
                "zip_code" => null,
                "city" => null,
                "state" => null,
                "country" => "NL",
                "country_name" => null,
                "phone1" => null,
                "phone2" => "",
                "email" => "jules@komma.pro"
            ],
            "payment_details" => [
                "recurring_id" => null,
                "type" => "IDEAL",
                "account_id" => null,
                "account_holder_name" => null,
                "external_transaction_id" => 12364692762434462,
                //Yeah i know....Multisafepay really did put a checkout url in the account iban..... i leave it here because it is simulates real life situation
                "account_iban" => $checkoutUrl
            ],
            "costs" => [
                [
                    "transaction_id" => 1451590,
                    "description" => "0.49 For iDEAL Transactions",
                    "type" => "SYSTEM",
                    "amount" => 0.48999999999999999
                ]
            ],
            "related_transactions" => null,
            "payment_methods" => [
                [
                    "amount" => 799,
                    "currency" => "EUR",
                    "description" => "order #".$order->id,
                    "external_transaction_id" => 12364692762434462,
                    "payment_description" => "iDEAL",
                    "status" => OrderStatus::INITIALIZED,
                    "type" => "IDEAL"
                ]
            ]
        ];

        //Mock / fake the MollieApi client, used by the Mollie adapter, to make it return fake, but real like api call results, like a mollie payment.
        /** @var Payment|MockObject|null $fakedPayment */
        \App::bind(Client::class, function() use($order, $checkoutUrl, $fakePaymentPspId, &$multiSafepayOrderData) {
            $fakeMultiSafepayApiClient = $this->createMock(Client::class);

            //Fake the payment endpoint that is in the fakeMollie api client
            $fakeOrdersEndpoint = $this->createMock(Orders::class);
            $fakeMultiSafepayApiClient->orders = $fakeOrdersEndpoint;


            //returnCallback returns a callback that is executed when calling the "create" method on a payment.
            //The paymentData variable will contain data that is passed to that call of the create method.
            $fakeOrdersEndpoint->method('post')->will($this->returnCallback(function($paymentData) use(&$multiSafepayOrderData, $checkoutUrl, $fakePaymentPspId) {
                $modifiedOrder = $multiSafepayOrderData; //Copies array
                unset($modifiedOrder['payment_details']['type']); //MultiSafepay does not give you this at this stage

                return (object) $modifiedOrder;
            }));

            //returnCallback returns a callback that is executed when calling the "create" method on a payment.
            //The paymentData variable will contain data that is passed to that call of the create method.
            $fakeOrdersEndpoint->method('get')->will($this->returnCallback(function($endpoint = 'orders', $transactionId, $body = array(), $query_string = false) use(&$multiSafepayOrderData, $checkoutUrl, $fakePaymentPspId) {
                return (object) $multiSafepayOrderData;
            }));

            return $fakeMultiSafepayApiClient;
        });

        //Make sure the configured psp is multisafepay
        config()->set('payment.payment_service_provider', 'multisafepay');

        $pspAdapter = $pspService->getAdapter();
        $createdTransaction = $pspAdapter->createTransaction($order);

        //Let it follow redirects since the statusupdate endpoint will redirect users to the correct page
        $this->followingRedirects();
        //Simulate MultiSafepay calling our api telling we have an update for a transaction without an transaction id
        $this->post(route('transaction.statusupdate', ['order' => $order->id]))
            ->assertSee('Something went wrong, and because of that we cannot tell you about your payment. Please contact us.')
            ->assertStatus(400);

        //Let it follow redirects since the statusupdate endpoint will redirect users to the correct page
        $this->followingRedirects();
        //Simulate MultiSafepay calling our api telling we have an update for a transaction with an incorrect id
        $route = route('transaction.statusupdate', ['order' => $order->id]).'?transactionid=-1'; //This is what multiSafepay really does
        $this->post($route)
            ->assertSee('Something went wrong, and because of that we cannot tell you about your payment. Please contact us.')
            ->assertStatus(400);

        //The payment still must be open at this point since we did not change it (e.g.) The user did not pay for it yet.
        $createdTransaction->refresh(); //Reload from db
        $this->assertEquals(TransactionStatus::OPEN, $createdTransaction->status);

        //Let it follow redirects since the statusupdate endpoint will redirect users to the correct page
        $this->followingRedirects();

        //Simulate MultiSafepay calling our api telling we have an update for transaction with the given id
        $multiSafepayOrderData['status'] = OrderStatus::COMPLETED;
        $multiSafepayOrderData['financial_status'] = OrderStatus::COMPLETED;
        $multiSafepayOrderData['payment_methods'][0]['status'] = OrderStatus::COMPLETED;

        $route = route('transaction.statusupdate', ['order' => $order->id]).'?transactionid='.$createdTransaction->psp_id; //This is what multiSafepay really does
        $this->post($route)
            ->assertSee('We have received your payment. This is what we are going to do next:')
            ->assertStatus(200);

        //The payment still must be paid at this moment. The multisafepay adapter requested an update and multisafepay client must return the order data.
        $createdTransaction->refresh(); //Reload from db
        $this->assertEquals(TransactionStatus::PAYMENT_PAID, $createdTransaction->status);
    }
}