Source of file BaseNotificationServiceTest.php
Size: 26,499 Bytes - Last Modified: 2021-12-24T06:34:53+00:00
/var/www/docs.ssmods.com/process/src/tests/BaseNotificationServiceTest.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 | <?php namespace SilverStripe\Omnipay\Tests; use Omnipay\Common\Message\NotificationInterface; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Dev\SapphireTest; use SilverStripe\Omnipay\GatewayInfo; use SilverStripe\Omnipay\Model\Payment; use SilverStripe\Omnipay\Tests\Extensions\PaymentTestPaymentExtensionHooks; use SilverStripe\Omnipay\Tests\Extensions\PaymentTestServiceExtensionHooks; /** * Base class with common tests for Void, Capture and Refund Services */ abstract class BaseNotificationServiceTest extends PaymentTest { /** @var string the gateway method to call */ protected $gatewayMethod; /** @var string fixture identifier */ protected $fixtureIdentifier; /** @var string the receipt (transaction reference) that is stored in the fixture */ protected $fixtureReceipt; /** @var string the desired start status */ protected $startStatus; /** @var string the pending status */ protected $pendingStatus; /** @var string the end status */ protected $endStatus; /** @var array the messages generated by a successful attempt with the payment loaded from the fixture */ protected $successFromFixtureMessages; /** @var array the messages generated by a successful attempt with a fresh payment */ protected $successMessages; /** @var array failure messages */ protected $failureMessages; /** @var array messages when a failure notification comes in */ protected $notificationFailureMessages; /** @var string class name of the error message generated by this service */ protected $errorMessageClass; /** @var array expected payment hooks that will be called with a successful payment */ protected $successPaymentExtensionHooks; /** @var array expected service hooks that will be called when initiate method finishes */ protected $initiateServiceExtensionHooks; /** @var array expected service hooks that will be called when initiate method was interrupted by gateway error */ protected $initiateFailedServiceExtensionHooks; /** * Get the service to use for these tests * @param Payment $payment * @return \SilverStripe\Omnipay\Service\PaymentService */ abstract protected function getService(Payment $payment); public function testSuccess() { // load an authorized payment from fixture $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); $stubGateway = $this->buildPaymentGatewayStub(true, $this->fixtureReceipt); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($payment); $serviceResponse = $service->initiate(); // the service should not respond with an error $this->assertFalse($serviceResponse->isError()); // we should get a successful Omnipay response $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertTrue($serviceResponse->getOmnipayResponse()->isSuccessful()); // check payment status $this->assertEquals($payment->Status, $this->endStatus, 'Payment status should be set to ' . $this->endStatus); // check existence of messages and existence of references SapphireTest::assertListContains($this->successFromFixtureMessages, $payment->Messages()); // ensure payment hooks were called $this->assertEquals( $this->successPaymentExtensionHooks, $payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( $this->initiateServiceExtensionHooks, $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testManualSuccess() { // Use a manual payment (this payment doesn't have any previous messages to grab transaction reference from) $payment = $this->payment->setGateway('Manual'); $payment->Status = $this->startStatus; $stubGateway = $this->buildPaymentGatewayStub(true, 'testThisRecipe123'); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($payment); // Manual payments should succeed, even when there's no transaction reference given! $serviceResponse = $service->initiate(); // the service should not respond with an error $this->assertFalse($serviceResponse->isError()); // we should get a successful Omnipay response $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertTrue($serviceResponse->getOmnipayResponse()->isSuccessful()); // check payment status $this->assertEquals($payment->Status, $this->endStatus, 'Payment status should be set to ' . $this->endStatus); // check existance of messages and existence of references SapphireTest::assertListContains($this->successMessages, $payment->Messages()); // ensure payment hooks were called $this->assertEquals( $this->successPaymentExtensionHooks, $payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( $this->initiateServiceExtensionHooks, $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testSuccessWithTransactionParameter() { // set the payment status to the desired start status $this->payment->Status = $this->startStatus; $stubGateway = $this->buildPaymentGatewayStub(true, 'testThisRecipe123'); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($this->payment); // pass transaction reference as parameter $serviceResponse = $service->initiate(array('transactionReference' => 'testThisRecipe123')); // the service should not respond with an error $this->assertFalse($serviceResponse->isError()); // We should get a successful Omnipay response $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertTrue($serviceResponse->getOmnipayResponse()->isSuccessful()); // check payment status $this->assertEquals($this->payment->Status, $this->endStatus, 'Payment status should be set to ' . $this->endStatus); // check existance of messages and existence of references SapphireTest::assertListContains($this->successMessages, $this->payment->Messages()); // ensure payment hooks were called $this->assertEquals( $this->successPaymentExtensionHooks, $this->payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( $this->initiateServiceExtensionHooks, $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testSuccessWithLegacyTransactionParameter() { // set the payment status to the desired start status $this->payment->Status = $this->startStatus; $stubGateway = $this->buildPaymentGatewayStub(true, 'testThisRecipe123'); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($this->payment); // pass transaction reference as parameter $serviceResponse = $service->initiate(array('receipt' => 'testThisRecipe123')); // the service should not respond with an error $this->assertFalse($serviceResponse->isError()); // We should get a successful Omnipay response $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertTrue($serviceResponse->getOmnipayResponse()->isSuccessful()); // check payment status $this->assertEquals($this->payment->Status, $this->endStatus, 'Payment status should be set to ' . $this->endStatus); // check existance of messages and existence of references SapphireTest::assertListContains($this->successMessages, $this->payment->Messages()); // ensure payment hooks were called $this->assertEquals( $this->successPaymentExtensionHooks, $this->payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( $this->initiateServiceExtensionHooks, $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testSuccessViaNotification() { // load a payment from fixture $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); // use notification on the gateway Config::modify()->merge(GatewayInfo::class, $payment->Gateway, array( 'use_async_notification' => true )); $stubGateway = $this->buildPaymentGatewayStub(false, $this->fixtureReceipt); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($payment); $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->Reset(); // pass transaction reference as parameter $serviceResponse = $service->initiate(); // the service should not respond with an error $this->assertFalse($serviceResponse->isError()); // When waiting for a notification, request won't be successful from Omnipays point of view $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertFalse($serviceResponse->getOmnipayResponse()->isSuccessful()); // response should have the "AwaitingNotification" flag $this->assertTrue($serviceResponse->isAwaitingNotification()); // check payment status $this->assertEquals( $payment->Status, $this->pendingStatus, 'Payment status should be set to ' . $this->pendingStatus ); // check existance of messages and existence of references. // Since operation isn't complete, we shave off the latest message from the exptected messages! SapphireTest::assertListContains(array_slice($this->successFromFixtureMessages, 0, -1), $payment->Messages()); // Now a notification comes in $response = $this->get('paymentendpoint/'. $payment->Identifier .'/notify'); $this->assertEquals($response->getStatusCode(), 200); $this->assertEquals($response->getBody(), "OK"); // ensure payment hooks were called $this->assertEquals( $this->successPaymentExtensionHooks, PaymentTestPaymentExtensionHooks::findExtensionForID($payment->ID)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( array_merge($this->initiateServiceExtensionHooks, ['updateServiceResponse']), $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); // we'll have to "reload" the payment from the DB now $payment = Payment::get()->byID($payment->ID); $this->assertEquals($payment->Status, $this->endStatus, 'Payment status should be set to ' . $this->endStatus); // check existance of messages SapphireTest::assertListContains($this->successFromFixtureMessages, $payment->Messages()); $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->Reset(); // try to complete a second time $service = $this->getService($payment); $serviceResponse = $service->complete(); // the service should not respond with an error $this->assertFalse($serviceResponse->isError()); // since the payment is already completed, we should not touch omnipay again. $this->assertNull($serviceResponse->getOmnipayResponse()); // should not be waiting for notification $this->assertFalse($serviceResponse->isAwaitingNotification()); // must always be true $this->assertTrue($serviceResponse->isNotification()); // only a service response will be generated, as omnipay is no longer involved at this stage $this->assertEquals( array('updateServiceResponse'), $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testFailure() { // load an authorized payment from fixture $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); $stubGateway = $this->buildPaymentGatewayStub(false, $this->fixtureReceipt); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($payment); $serviceResponse = $service->initiate(); // the service should respond with an error $this->assertTrue($serviceResponse->isError()); // Omnipay response should be unsuccessful $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertFalse($serviceResponse->getOmnipayResponse()->isSuccessful()); // payment status should be unchanged $this->assertEquals($payment->Status, $this->startStatus, 'Payment status should be unchanged'); // check existance of messages and existence of references SapphireTest::assertListContains($this->failureMessages, $payment->Messages()); // ensure payment hooks were called $this->assertEquals( [], $payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( $this->initiateServiceExtensionHooks, $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testGatewayFailure() { // load an authorized payment from fixture /** @var Payment $payment */ $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); $stubGateway = $this->buildPaymentGatewayStub( false, $this->fixtureReceipt, NotificationInterface::STATUS_COMPLETED, true ); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($payment); $serviceResponse = $service->initiate(); // the service should respond with an error $this->assertTrue($serviceResponse->isError()); // There should be no omnipay response, as the gateway threw an exception $this->assertNull($serviceResponse->getOmnipayResponse()); // payment status should be unchanged $this->assertEquals($payment->Status, $this->startStatus, 'Payment status should be unchanged'); $msg = $payment->getLatestMessageOfType($this->errorMessageClass); $this->assertNotNull($msg, 'An error message should have been generated'); $this->assertEquals($msg->Message, 'Mock Send Exception'); // ensure payment hooks were called $this->assertEquals( [], $payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // ensure the correct service hooks were called $this->assertEquals( $this->initiateFailedServiceExtensionHooks, $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } /** * @expectedException \SilverStripe\Omnipay\Exception\InvalidConfigurationException */ public function testUnsupportedGatewayMethod() { // Build the dummy gateway that doesn't contain the requested method (eg. void, capture or refund) $stubGateway = $this->getMockBuilder('Omnipay\Common\AbstractGateway') ->setMethods(array('getName')) ->getMock(); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $this->payment->Status = $this->startStatus; $service = $this->getService($this->payment); // this should throw an exception, because the gateway doesn't support the method $service->initiate(array('receipt' => 'testThisRecipe123')); } public function testFailureViaNotification() { // load a payment from fixture $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); // use notification on the gateway Config::modify()->merge(GatewayInfo::class, $payment->Gateway, array( 'use_async_notification' => true )); $stubGateway = $this->buildPaymentGatewayStub( false, $this->fixtureReceipt, NotificationInterface::STATUS_FAILED ); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $service = $this->getService($payment); $service->initiate(); // Now a notification comes in (will fail) $this->get('paymentendpoint/'. $payment->Identifier .'/notify'); // we'll have to "reload" the payment from the DB now $payment = Payment::get()->byID($payment->ID); // Status should be reset $this->assertEquals($this->startStatus, $payment->Status); // check existance of messages SapphireTest::assertListContains($this->notificationFailureMessages, $payment->Messages()); } public function testGatewayNotificationFailure() { $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); $stubGateway = $this->buildPaymentGatewayStub( true, $this->fixtureReceipt, NotificationInterface::STATUS_COMPLETED, true ); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $payment->Status = $this->pendingStatus; $service = $this->getService($payment); $serviceResponse = $service->complete(); // the service should respond with an error $this->assertTrue($serviceResponse->isError()); // There should be no omnipay notification, as the gateway threw an exception $this->assertNull($serviceResponse->getOmnipayResponse()); // payment status should be unchanged $this->assertEquals($payment->Status, $this->pendingStatus, 'Payment status should be unchanged'); // ensure payment hooks were called $this->assertEquals( [], $payment->getExtensionInstance(PaymentTestPaymentExtensionHooks::class)->getCalledMethods() ); // only a service response will be generated with the notification $this->assertEquals( array('updateServiceResponse'), $service->getExtensionInstance(PaymentTestServiceExtensionHooks::class)->getCalledMethods() ); } public function testNotificationTransactionReferenceMismatch() { $payment = $this->objFromFixture(Payment::class, $this->fixtureIdentifier); // create gateway but use a different transaction reference $stubGateway = $this->buildPaymentGatewayStub(true, 'DifferentReference'); // register our mock gateway factory as injection Injector::inst()->registerService($this->stubGatewayFactory($stubGateway), 'Omnipay\Common\GatewayFactory'); $payment->Status = $this->pendingStatus; $service = $this->getService($payment); $serviceResponse = $service->complete(); // the service should respond with an error $this->assertTrue($serviceResponse->isError()); // There should be an omnipay notification $this->assertNotNull($serviceResponse->getOmnipayResponse()); $this->assertInstanceOf( '\Omnipay\Common\Message\NotificationInterface', $serviceResponse->getOmnipayResponse() ); // payment status should be unchanged $this->assertEquals($payment->Status, $this->pendingStatus, 'Payment status should be unchanged'); } /** * @expectedException \SilverStripe\Omnipay\Exception\InvalidConfigurationException */ public function testInvalidStatus() { $this->payment->Status = 'Created'; // create a service with a payment that is created $service = $this->getService($this->payment); // this should throw an exception $service->initiate(); } /** * @expectedException \SilverStripe\Omnipay\Exception\InvalidStateException */ public function testInvalidCompleteStatus() { $this->payment->Status = 'Created'; // create a service with a payment that is created $service = $this->getService($this->payment); // this should throw an exception $service->complete(); } /** * @expectedException \SilverStripe\Omnipay\Exception\MissingParameterException */ public function testMissingTransactionReference() { $this->payment->Status = $this->startStatus; // create a service with a payment that has the correct status // but doesn't have any transaction references in messages $service = $this->getService($this->payment); // this should throw an exception $service->initiate(); } /** * @expectedException \SilverStripe\Omnipay\Exception\InvalidConfigurationException */ public function testMethodDisabled() { // disallow the service via config $method = 'allow_' . $this->gatewayMethod; Config::modify()->merge(GatewayInfo::class, 'Dummy', array( $method => false )); $this->payment->setGateway('Dummy')->Status = 'Created'; // create a service with a payment that is created $service = $this->getService($this->payment); // this should throw an exception $service->initiate(); } protected function buildPaymentGatewayStub( $successValue, $transactionReference, $returnState = NotificationInterface::STATUS_COMPLETED, $throwGatewayException = false ) { //-------------------------------------------------------------------------------------------------------------- // void request and response $mockResponse = $this->getMockBuilder('Omnipay\Common\Message\AbstractResponse') ->disableOriginalConstructor()->getMock(); $mockResponse->expects($this->any()) ->method('isSuccessful')->will($this->returnValue($successValue)); $mockResponse->expects($this->any()) ->method('getTransactionReference')->will($this->returnValue($transactionReference)); $mockRequest = $this->getMockBuilder('Omnipay\Common\Message\AbstractRequest') ->disableOriginalConstructor()->getMock(); if ($throwGatewayException) { $mockRequest->expects($this->any())->method('send')->will($this->throwException( new \Omnipay\Common\Exception\RuntimeException('Mock Send Exception') )); } else { $mockRequest->expects($this->any()) ->method('send')->will($this->returnValue($mockResponse)); } $mockRequest->expects($this->any()) ->method('getTransactionReference')->will($this->returnValue($transactionReference)); //-------------------------------------------------------------------------------------------------------------- // Notification $notificationResponse = $this->getMockBuilder('Omnipay\Common\Message\NotificationInterface') ->disableOriginalConstructor()->getMock(); $notificationResponse->expects($this->any()) ->method('getTransactionStatus')->will($this->returnValue($returnState)); $notificationResponse->expects($this->any()) ->method('getTransactionReference')->will($this->returnValue($transactionReference)); //-------------------------------------------------------------------------------------------------------------- // Build the gateway $stubGateway = $this->getMockBuilder('Omnipay\Common\AbstractGateway') ->setMethods(array($this->gatewayMethod, 'acceptNotification', 'getName')) ->getMock(); $stubGateway->expects($this->any()) ->method($this->gatewayMethod) ->will($this->returnValue($mockRequest)); $stubGateway->expects($this->any()) ->method('acceptNotification') ->will( $throwGatewayException ? $this->throwException(new \Omnipay\Common\Exception\RuntimeException('Mock Notification Exception')) : $this->returnValue($notificationResponse) ); return $stubGateway; } } |