File: D:/HostingSpaces/pietvanmierlo/stempelbv.nl/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);
}
}