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/SBogers95/rentman.io/app/Komma/Shop/Tests/Unit/PSPTest.php
<?php

namespace App\Komma\Shop\Tests\Unit;

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\Models\Order;
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\Role;
use App\Komma\Users\Models\User;
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

    /**
     * Runs before each test
     */
    public function setup()
    {
        parent::setup();
    }

    /**
     * Runs after each test
     */
    public function teardown()
    {
        parent::tearDown();
    }

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

        $customer = User::where('role_id', '=', Role::Customer)->first();

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = \App::make(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = \App::make(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::make(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItemsForCustomer($shoppingCartService->getItems(), $customer);

        //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::make(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        $customer = User::where('role_id', '=', Role::Customer)->first();

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = \App::make(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = \App::make(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::make(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItemsForCustomer($shoppingCartService->getItems(), $customer);

        //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::make(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        $customer = User::where('role_id', '=', Role::Customer)->first();

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = \App::make(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = \App::make(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::make(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItemsForCustomer($shoppingCartService->getItems(), $customer);

        //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::make(SiteServiceInterface::class);
        $siteService->setCurrentSiteToDefault();

        $customer = User::where('role_id', '=', Role::Customer)->first();

        /** @var ShoppingCartServiceInterface $shoppingCartService */
        $shoppingCartService = \App::make(ShoppingCartServiceInterface::class);

        /** @var PaymentServiceInterface $pspService */
        $pspService = \App::make(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::make(CheckoutServiceInterface::class);
        $order = $checkoutService->createOrderFromShoppingCartItemsForCustomer($shoppingCartService->getItems(), $customer);

        //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, $transactionId, $body = [], $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);
    }
}