Skip to content

Commit

Permalink
Merge pull request #87 from bencromwell/feature/shipping-costs-callback
Browse files Browse the repository at this point in the history
Add support for Instant Update API Callback
  • Loading branch information
delatbabel committed Jan 13, 2016
2 parents dc788fc + 7e2e440 commit b546d24
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 0 deletions.
97 changes: 97 additions & 0 deletions src/Message/ExpressAuthorizeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,86 @@

namespace Omnipay\PayPal\Message;

use Omnipay\Common\Exception\InvalidRequestException;
use Omnipay\PayPal\Support\InstantUpdateApi\ShippingOption;

/**
* PayPal Express Authorize Request
*/
class ExpressAuthorizeRequest extends AbstractRequest
{

const DEFAULT_CALLBACK_TIMEOUT = 5;

public function setCallback($callback)
{
return $this->setParameter('callback', $callback);
}

public function getCallback()
{
return $this->getParameter('callback');
}

public function setCallbackTimeout($callbackTimeout)
{
return $this->setParameter('callbackTimeout', $callbackTimeout);
}

public function getCallbackTimeout()
{
return $this->getParameter('callbackTimeout');
}

/**
* @param ShippingOption[] $data
*/
public function setShippingOptions($data)
{
$this->setParameter('shippingOptions', $data);
}

/**
* @return ShippingOption[]
*/
public function getShippingOptions()
{
return $this->getParameter('shippingOptions');
}

protected function validateCallback()
{
$callback = $this->getCallback();

if (!empty($callback)) {
$shippingOptions = $this->getShippingOptions();

if (empty($shippingOptions)) {
throw new InvalidRequestException(
'When setting a callback for the Instant Update API you must set shipping options'
);
} else {
$hasDefault = false;
foreach ($shippingOptions as $shippingOption) {
if ($shippingOption->isDefault()) {
$hasDefault = true;
continue;
}
}

if (!$hasDefault) {
throw new InvalidRequestException(
'One of the supplied shipping options must be set as default'
);
}
}
}
}

public function getData()
{
$this->validate('amount', 'returnUrl', 'cancelUrl');
$this->validateCallback();

$data = $this->getBaseData();
$data['METHOD'] = 'SetExpressCheckout';
Expand All @@ -34,6 +106,31 @@ public function getData()
$data['LOCALECODE'] = $this->getLocaleCode();
$data['CUSTOMERSERVICENUMBER'] = $this->getCustomerServiceNumber();

$callback = $this->getCallback();

if (!empty($callback)) {
$data['CALLBACK'] = $callback;
// callback timeout MUST be included and > 0
$timeout = $this->getCallbackTimeout();

$data['CALLBACKTIMEOUT'] = $timeout > 0 ? $timeout : self::DEFAULT_CALLBACK_TIMEOUT;

// if you're using a callback you MUST set shipping option(s)
$shippingOptions = $this->getShippingOptions();

if (!empty($shippingOptions)) {
foreach ($shippingOptions as $index => $shipping) {
$data['L_SHIPPINGOPTIONNAME' . $index] = $shipping->getName();
$data['L_SHIPPINGOPTIONAMOUNT' . $index] = number_format($shipping->getAmount(), 2);
$data['L_SHIPPINGOPTIONISDEFAULT' . $index] = $shipping->isDefault() ? '1' : '0';

if ($shipping->hasLabel()) {
$data['L_SHIPPINGOPTIONLABEL' . $index] = $shipping->getLabel();
}
}
}
}

$data['MAXAMT'] = $this->getMaxAmount();
$data['PAYMENTREQUEST_0_TAXAMT'] = $this->getTaxAmount();
$data['PAYMENTREQUEST_0_SHIPPINGAMT'] = $this->getShippingAmount();
Expand Down
72 changes: 72 additions & 0 deletions src/Support/InstantUpdateApi/ShippingOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace Omnipay\PayPal\Support\InstantUpdateApi;

class ShippingOption
{
/** @var string */
private $name;

/** @var float */
private $amount;

/** @var bool */
private $isDefault;

/** @var string */
private $label;

/**
* @param string $name L_SHIPPINGOPTIONNAME0
* @param float $amount L_SHIPPINGOPTIONAMOUNT0
* @param bool $isDefault L_SHIPPINGOPTIONISDEFAULT0
* @param string $label L_SHIPPINGOPTIONLABEL0
*/
public function __construct($name, $amount, $isDefault = false, $label = null)
{
$this->name = $name;
$this->amount = $amount;
$this->isDefault = $isDefault;
$this->label = $label;
}

/**
* @return bool
*/
public function hasLabel()
{
return !is_null($this->label);
}

/**
* @return string
*/
public function getName()
{
return $this->name;
}

/**
* @return float
*/
public function getAmount()
{
return $this->amount;
}

/**
* @return boolean
*/
public function isDefault()
{
return $this->isDefault;
}

/**
* @return string
*/
public function getLabel()
{
return $this->label;
}
}
155 changes: 155 additions & 0 deletions tests/Message/ExpressAuthorizeRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Omnipay\PayPal\Message;

use Omnipay\Common\CreditCard;
use Omnipay\PayPal\Support\InstantUpdateApi\ShippingOption;
use Omnipay\Tests\TestCase;

class ExpressAuthorizeRequestTest extends TestCase
Expand Down Expand Up @@ -215,4 +216,158 @@ public function testMaxAmount()

$this->assertSame(321.54, $data['MAXAMT']);
}

public function testDataWithCallback()
{
$baseData = array(
'amount' => '10.00',
'currency' => 'AUD',
'transactionId' => '111',
'description' => 'Order Description',
'returnUrl' => 'https://www.example.com/return',
'cancelUrl' => 'https://www.example.com/cancel',
'subject' => '[email protected]',
'headerImageUrl' => 'https://www.example.com/header.jpg',
'allowNote' => 0,
'addressOverride' => 0,
'brandName' => 'Dunder Mifflin Paper Company, Incy.',
);

$shippingOptions = array(
new ShippingOption('First Class', 1.20, true, '1-2 days'),
new ShippingOption('Second Class', 0.70, false, '3-5 days'),
new ShippingOption('International', 3.50),
);

// with a default callback timeout
$this->request->initialize(array_merge($baseData, array(
'callback' => 'https://www.example.com/calculate-shipping',
'shippingOptions' => $shippingOptions,
)));

$data = $this->request->getData();
$this->assertSame('https://www.example.com/calculate-shipping', $data['CALLBACK']);
$this->assertSame(ExpressAuthorizeRequest::DEFAULT_CALLBACK_TIMEOUT, $data['CALLBACKTIMEOUT']);

$this->assertSame('First Class', $data['L_SHIPPINGOPTIONNAME0']);
$this->assertSame('1.20', $data['L_SHIPPINGOPTIONAMOUNT0']);
$this->assertSame('1', $data['L_SHIPPINGOPTIONISDEFAULT0']);
$this->assertSame('1-2 days', $data['L_SHIPPINGOPTIONLABEL0']);

$this->assertSame('Second Class', $data['L_SHIPPINGOPTIONNAME1']);
$this->assertSame('0.70', $data['L_SHIPPINGOPTIONAMOUNT1']);
$this->assertSame('0', $data['L_SHIPPINGOPTIONISDEFAULT1']);
$this->assertSame('3-5 days', $data['L_SHIPPINGOPTIONLABEL1']);

$this->assertSame('International', $data['L_SHIPPINGOPTIONNAME2']);
$this->assertSame('3.50', $data['L_SHIPPINGOPTIONAMOUNT2']);
$this->assertSame('0', $data['L_SHIPPINGOPTIONISDEFAULT2']);

// with a defined callback timeout
$this->request->initialize(array_merge($baseData, array(
'callback' => 'https://www.example.com/calculate-shipping',
'callbackTimeout' => 10,
'shippingOptions' => $shippingOptions,
)));

$data = $this->request->getData();
$this->assertSame('https://www.example.com/calculate-shipping', $data['CALLBACK']);
$this->assertSame(10, $data['CALLBACKTIMEOUT']);
}

public function testDataWithCallbackAndNoDefaultShippingOption()
{
$baseData = array(
'amount' => '10.00',
'currency' => 'AUD',
'transactionId' => '111',
'description' => 'Order Description',
'returnUrl' => 'https://www.example.com/return',
'cancelUrl' => 'https://www.example.com/cancel',
'subject' => '[email protected]',
'headerImageUrl' => 'https://www.example.com/header.jpg',
'allowNote' => 0,
'addressOverride' => 0,
'brandName' => 'Dunder Mifflin Paper Company, Incy.',
);

$shippingOptions = array(
new ShippingOption('First Class', 1.20, false, '1-2 days'),
new ShippingOption('Second Class', 0.70, false, '3-5 days'),
new ShippingOption('International', 3.50),
);

// with a default callback timeout
$this->request->initialize(array_merge($baseData, array(
'callback' => 'https://www.example.com/calculate-shipping',
'shippingOptions' => $shippingOptions,
)));

$this->setExpectedException(
'\Omnipay\Common\Exception\InvalidRequestException',
'One of the supplied shipping options must be set as default'
);

$this->request->getData();
}

public function testNoAmount()
{
$baseData = array(// nothing here - should cause a certain exception
);

$this->request->initialize($baseData);

$this->setExpectedException(
'\Omnipay\Common\Exception\InvalidRequestException',
'The amount parameter is required'
);

$this->request->getData();
}

public function testAmountButNoReturnUrl()
{
$baseData = array(
'amount' => 10.00,
);

$this->request->initialize($baseData);

$this->setExpectedException(
'\Omnipay\Common\Exception\InvalidRequestException',
'The returnUrl parameter is required'
);

$this->request->getData();
}

public function testBadCallbackConfiguration()
{
$baseData = array(
'amount' => '10.00',
'currency' => 'AUD',
'transactionId' => '111',
'description' => 'Order Description',
'returnUrl' => 'https://www.example.com/return',
'cancelUrl' => 'https://www.example.com/cancel',
'subject' => '[email protected]',
'headerImageUrl' => 'https://www.example.com/header.jpg',
'allowNote' => 0,
'addressOverride' => 0,
'brandName' => 'Dunder Mifflin Paper Company, Incy.',
);

$this->request->initialize(array_merge($baseData, array(
'callback' => 'https://www.example.com/calculate-shipping',
)));

// from the docblock on this exception -
// Thrown when a request is invalid or missing required fields.
// callback has been set but no shipping options so expect one of these:
$this->setExpectedException('\Omnipay\Common\Exception\InvalidRequestException');

$this->request->getData();
}

}

0 comments on commit b546d24

Please sign in to comment.