Skip to content

Commit

Permalink
Use events instead of model callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
hakito committed Feb 29, 2020
1 parent 9387ff5 commit bead2f4
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 45 deletions.
46 changes: 24 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ CREATE TABLE IF NOT EXISTS `PayPalPayments` (
Configuration
-------------

You can find a sample configuration in Config/bootstrap.php. Just override the settings in your own bootstrap.php.
You can find a sample configuration in tests/config/PayPal.php. Just override the settings in your own bootstrap.php.

Usage
-----
Expand Down Expand Up @@ -85,32 +85,34 @@ class OrdersController extends AppController {
}
```

To receive the payment notifications in your app the Plugin needs 3 functions available in your AppModel.php
To receive the payment notifications in your app the Plugin expects 3 event handlers

```php

public function beforePayPalPaymentExecution($orderId)
{
// Will be called just after PayPal redirects the customer
// back to your site. (You could begin a transaction here)
// True is always expected as return value, otherwise the plugin
// will throw an exception
return true;
}

public function cancelPayPalPaymentExecution($orderId)
{
// Will be called when the REST api call fails or
// the saleState != 'completed' or paymentState != 'approved'
// (You could rollback a transaction here)
}
$eventManager = TableRegistry::getTableLocator()->get('PayPal.PayPalPayments')->getEventManager();
$eventManager->setEventList(new EventList());

public function afterPayPalPaymentExecution($orderId)
// Will be called just after PayPal redirects the customer
// back to your site. (You could start a transaction here)
$eventManager->on('PayPal.Model.PayPalPayments.BeforePaymentExecution',
function($event, $remittanceIdentifier, &$handled)
{
// Will be called after the REST api call
// and only if the saleState == 'completed' and paymentState == 'approved'
// (You could commit a transaction here)
}
// Handled is expected to be set to TRUE, otherwise the plugin
// will throw an exception
$handled = true;
});

// Will be called when the REST api call fails or
// the saleState != 'completed' or paymentState != 'approved'
// (You could rollback a transaction here)
$eventManager->on('PayPal.Model.PayPalPayments.CancelPaymentExecution',
function($event, $remittanceIdentifier) {});

// Will be called after the REST api call
// and only if the saleState == 'completed' and paymentState == 'approved'
// (You could commit a transaction here)
$eventManager->on('PayPal.Model.PayPalPayments.AfterPaymentExecution',
function($event, $remittanceIdentifier) {});

```

Expand Down
25 changes: 16 additions & 9 deletions src/Model/Table/PayPalPaymentsTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PayPal\Model\Table;

use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\Table;
use Cake\Routing\Router;

Expand Down Expand Up @@ -117,9 +118,14 @@ public function execute($id)

$execution = new PaymentExecution();
$execution->setPayerId($_GET['PayerID']);
if (!$this->beforePayPalPaymentExecution($remittanceIdentifier))
$handled = false;
$event = new Event('PayPal.Model.PayPalPayments.BeforePaymentExecution',
$this, ['RemittanceIdentifier' => $remittanceIdentifier, 'Handled' => &$handled] );
$this->getEventManager()->dispatch($event);
if (!$handled)
throw new PayPalCallbackException('beforePayPalPaymentExecution did not return true');

$event = null;
try
{
$ppRes = $ppReq->execute($execution);
Expand All @@ -128,17 +134,18 @@ public function execute($id)
$relatedResources = $this->getRelatedResources($transactions);
$sale = $relatedResources->getSale();
$saleState = $sale->getState();
if ($saleState == 'completed' && $paymentState == 'approved')
$event = new Event('PayPal.Model.PayPalPayments.AfterPaymentExecution',
$this, ['RemittanceIdentifier' => $remittanceIdentifier]);
}
catch (\Exception $e)
finally
{
$this->cancelPayPalPaymentExecution($remittanceIdentifier);
throw $e;
}
if ($event == null)
$event = new Event('PayPal.Model.PayPalPayments.CancelPaymentExecution',
$this, ['RemittanceIdentifier' => $remittanceIdentifier]);

if ($saleState == 'completed' && $paymentState == 'approved')
$this->afterPayPalPaymentExecution($remittanceIdentifier);
else
$this->cancelPayPalPaymentExecution($remittanceIdentifier);
$this->getEventManager()->dispatch($event);
}

$this->savePayment($ppRes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PayPal\Test\TestCase\Model\Table;

use Cake\Core\Configure;
use Cake\Event\EventList;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;

Expand All @@ -14,14 +15,15 @@
/**
* @var \PayPal\Model\Table\PayPalPaymentTable PayPalPayment
*/
class PayPalPaymentTableTest extends TestCase
class PayPalPaymentsTableTest extends TestCase
{
public $fixtures = ['plugin.PayPal.PayPalPayments'];

public function setUp()
{
parent::setUp();
$this->PayPalPayments = TableRegistry::getTableLocator()->get('PayPal.PayPalPayments');
$this->PayPalPayments->getEventManager()->setEventList(new EventList());
}

public function testGetApiContext()
Expand Down Expand Up @@ -169,27 +171,27 @@ function() use ($p) { $p->setId('foo'); return $p; }));
$this->assertTextContains('0/cancel', $redirectUrls->getCancelUrl());
}

public function testExecuteCompletedAndApproved()
private function prepareExecuteTest($saleState = 'completed', $paymentState = 'approved')
{
$model = $this->getMockForModel('PayPal.PayPalPayments',
[
'ApiGet',
'savePayment',
'getRelatedResources',
'beforePayPalPaymentExecution',
'afterPayPalPaymentExecution'
]);

$model->getEventManager()->setEventList(new EventList());

$p = $this->getMockBuilder(Payment::class)
->setMethods(['execute'])
->getMock();

$p->setState('approved');
$p->setState($paymentState);

$rr = new RelatedResources();
$s = new Sale();
$rr->setSale($s);
$s->setState('completed');
$s->setState($saleState);

$model->expects($this->once())
->method('ApiGet')
Expand All @@ -200,20 +202,80 @@ public function testExecuteCompletedAndApproved()
->method('getRelatedResources')
->willReturn($rr);

$model->expects($this->once())
->method('beforePayPalPaymentExecution')
->with('ri')
->willReturn(true);

$model->expects($this->once())
->method('afterPayPalPaymentExecution')
->with('ri');
$model->getEventManager()->on('PayPal.Model.PayPalPayments.BeforePaymentExecution',
function($event, $remittanceIdentifier, &$handled)
{
$handled = true;
});

$p->expects($this->once())
->method('execute')
->willReturn($p);

return $model;
}

public function testExecuteCompletedAndApproved()
{
$model = $this->prepareExecuteTest();

$model->execute(1);
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.BeforePaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.AfterPaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
}

public function testExecuteCancelOnInvalidSaleState()
{
$model = $this->prepareExecuteTest('invalid');

$model->execute(1);
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.BeforePaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.CancelPaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
}

public function testExecuteCancelOnInvalidPaymentState()
{
$model = $this->prepareExecuteTest('completed', 'invalid');

$model->execute(1);
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.BeforePaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.CancelPaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
}

public function testExecuteCancelOnException()
{
$model = $this->getMockForModel('PayPal.PayPalPayments',
[
'ApiGet'
]);

$model->getEventManager()->setEventList(new EventList());

$p = $this->getMockBuilder(Payment::class)
->setMethods(['execute'])
->getMock();

$p->expects($this->once())
->method('execute')
->will($this->throwException(new \Exception('dummy')));

$model->expects($this->once())
->method('ApiGet')
->with('PayPalId')
->willReturn($p);
$model->getEventManager()->on('PayPal.Model.PayPalPayments.BeforePaymentExecution',
function($event, $remittanceIdentifier, &$handled)
{
$handled = true;
});

try {
$model->execute(1);
} catch (\Exception $th) {
$this->assertEquals('dummy', $th->getMessage());
}
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.BeforePaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
$this->assertEventFiredWith('PayPal.Model.PayPalPayments.CancelPaymentExecution', 'RemittanceIdentifier', 'ri', $model->getEventManager());
}

public function testGetRelatedResources()
Expand Down

0 comments on commit bead2f4

Please sign in to comment.