1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695:
<?php
/**
* Abstract Request
*/
namespace Omnipay\Common\Message;
use Guzzle\Http\ClientInterface;
use Omnipay\Common\CreditCard;
use Omnipay\Common\Currency;
use Omnipay\Common\Exception\InvalidRequestException;
use Omnipay\Common\Exception\RuntimeException;
use Omnipay\Common\Helper;
use Omnipay\Common\ItemBag;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
use InvalidArgumentException;
/**
* Abstract Request
*
* This abstract class implements RequestInterface and defines a basic
* set of functions that all Omnipay Requests are intended to include.
*
* Requests of this class are usually created using the createRequest
* function of the gateway and then actioned using methods within this
* class or a class that extends this class.
*
* Example -- creating a request:
*
* <code>
* class MyRequest extends \Omnipay\Common\Message\AbstractRequest {};
*
* class MyGateway extends \Omnipay\Common\AbstractGateway {
* function myRequest($parameters) {
* $this->createRequest('MyRequest', $parameters);
* }
* }
*
* // Create the gateway object
* $gw = Omnipay::create('MyGateway');
*
* // Create the request object
* $myRequest = $gw->myRequest($someParameters);
* </code>
*
* Example -- validating and sending a request:
*
* <code>
* try {
* $myRequest->validate();
* $myResponse = $myRequest->send();
* } catch (InvalidRequestException $e) {
* print "Something went wrong: " . $e->getMessage() . "\n";
* }
* // now do something with the $myResponse object, test for success, etc.
* </code>
*
* @see RequestInterface
* @see AbstractResponse
*/
abstract class AbstractRequest implements RequestInterface
{
/**
* The request parameters
*
* @var \Symfony\Component\HttpFoundation\ParameterBag
*/
protected $parameters;
/**
* The request client.
*
* @var \Guzzle\Http\ClientInterface
*/
protected $httpClient;
/**
* The HTTP request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $httpRequest;
/**
* An associated ResponseInterface.
*
* @var ResponseInterface
*/
protected $response;
/**
* @var bool
*/
protected $zeroAmountAllowed = true;
/**
* @var bool
*/
protected $negativeAmountAllowed = false;
/**
* Create a new Request
*
* @param ClientInterface $httpClient A Guzzle client to make API calls with
* @param HttpRequest $httpRequest A Symfony HTTP request object
*/
public function __construct(ClientInterface $httpClient, HttpRequest $httpRequest)
{
$this->httpClient = $httpClient;
$this->httpRequest = $httpRequest;
$this->initialize();
}
/**
* Initialize the object with parameters.
*
* If any unknown parameters passed, they will be ignored.
*
* @param array $parameters An associative array of parameters
*
* @return $this
* @throws RuntimeException
*/
public function initialize(array $parameters = array())
{
if (null !== $this->response) {
throw new RuntimeException('Request cannot be modified after it has been sent!');
}
$this->parameters = new ParameterBag;
Helper::initialize($this, $parameters);
return $this;
}
/**
* Get all parameters as an associative array.
*
* @return array
*/
public function getParameters()
{
return $this->parameters->all();
}
/**
* Get a single parameter.
*
* @param string $key The parameter key
* @return mixed
*/
protected function getParameter($key)
{
return $this->parameters->get($key);
}
/**
* Set a single parameter
*
* @param string $key The parameter key
* @param mixed $value The value to set
* @return AbstractRequest Provides a fluent interface
* @throws RuntimeException if a request parameter is modified after the request has been sent.
*/
protected function setParameter($key, $value)
{
if (null !== $this->response) {
throw new RuntimeException('Request cannot be modified after it has been sent!');
}
$this->parameters->set($key, $value);
return $this;
}
/**
* Gets the test mode of the request from the gateway.
*
* @return boolean
*/
public function getTestMode()
{
return $this->getParameter('testMode');
}
/**
* Sets the test mode of the request.
*
* @param boolean $value True for test mode on.
* @return AbstractRequest
*/
public function setTestMode($value)
{
return $this->setParameter('testMode', $value);
}
/**
* Validate the request.
*
* This method is called internally by gateways to avoid wasting time with an API call
* when the request is clearly invalid.
*
* @param string ... a variable length list of required parameters
* @throws InvalidRequestException
*/
public function validate()
{
foreach (func_get_args() as $key) {
$value = $this->parameters->get($key);
if (! isset($value)) {
throw new InvalidRequestException("The $key parameter is required");
}
}
}
/**
* Get the card.
*
* @return CreditCard
*/
public function getCard()
{
return $this->getParameter('card');
}
/**
* Sets the card.
*
* @param CreditCard $value
* @return AbstractRequest Provides a fluent interface
*/
public function setCard($value)
{
if ($value && !$value instanceof CreditCard) {
$value = new CreditCard($value);
}
return $this->setParameter('card', $value);
}
/**
* Get the card token.
*
* @return string
*/
public function getToken()
{
return $this->getParameter('token');
}
/**
* Sets the card token.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setToken($value)
{
return $this->setParameter('token', $value);
}
/**
* Get the card reference.
*
* @return string
*/
public function getCardReference()
{
return $this->getParameter('cardReference');
}
/**
* Sets the card reference.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setCardReference($value)
{
return $this->setParameter('cardReference', $value);
}
/**
* Convert an amount into a float.
*
* @var string|int|float $value The value to convert.
* @throws InvalidRequestException on any validation failure.
* @return float The amount converted to a float.
*/
public function toFloat($value)
{
try {
return Helper::toFloat($value);
} catch (InvalidArgumentException $e) {
// Throw old exception for legacy implementations.
throw new InvalidRequestException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* Validates and returns the formated amount.
*
* @throws InvalidRequestException on any validation failure.
* @return string The amount formatted to the correct number of decimal places for the selected currency.
*/
public function getAmount()
{
$amount = $this->getParameter('amount');
if ($amount !== null) {
// Don't allow integers for currencies that support decimals.
// This is for legacy reasons - upgrades from v0.9
if ($this->getCurrencyDecimalPlaces() > 0) {
if (is_int($amount) || (is_string($amount) && false === strpos((string) $amount, '.'))) {
throw new InvalidRequestException(
'Please specify amount as a string or float, '
. 'with decimal places (e.g. \'10.00\' to represent $10.00).'
);
};
}
$amount = $this->toFloat($amount);
// Check for a negative amount.
if (!$this->negativeAmountAllowed && $amount < 0) {
throw new InvalidRequestException('A negative amount is not allowed.');
}
// Check for a zero amount.
if (!$this->zeroAmountAllowed && $amount === 0.0) {
throw new InvalidRequestException('A zero amount is not allowed.');
}
// Check for rounding that may occur if too many significant decimal digits are supplied.
$decimal_count = strlen(substr(strrchr(sprintf('%.8g', $amount), '.'), 1));
if ($decimal_count > $this->getCurrencyDecimalPlaces()) {
throw new InvalidRequestException('Amount precision is too high for currency.');
}
return $this->formatCurrency($amount);
}
}
/**
* Sets the payment amount.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setAmount($value)
{
return $this->setParameter('amount', $value);
}
/**
* Get the payment amount as an integer.
*
* @return integer
*/
public function getAmountInteger()
{
return (int) round($this->getAmount() * $this->getCurrencyDecimalFactor());
}
/**
* Get the payment currency code.
*
* @return string
*/
public function getCurrency()
{
return $this->getParameter('currency');
}
/**
* Sets the payment currency code.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setCurrency($value)
{
if ($value !== null) {
$value = strtoupper($value);
}
return $this->setParameter('currency', $value);
}
/**
* Get the payment currency number.
*
* @return integer
*/
public function getCurrencyNumeric()
{
if ($currency = Currency::find($this->getCurrency())) {
return $currency->getNumeric();
}
}
/**
* Get the number of decimal places in the payment currency.
*
* @return integer
*/
public function getCurrencyDecimalPlaces()
{
if ($currency = Currency::find($this->getCurrency())) {
return $currency->getDecimals();
}
return 2;
}
private function getCurrencyDecimalFactor()
{
return pow(10, $this->getCurrencyDecimalPlaces());
}
/**
* Format an amount for the payment currency.
*
* @return string
*/
public function formatCurrency($amount)
{
return number_format(
$amount,
$this->getCurrencyDecimalPlaces(),
'.',
''
);
}
/**
* Get the request description.
*
* @return string
*/
public function getDescription()
{
return $this->getParameter('description');
}
/**
* Sets the request description.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setDescription($value)
{
return $this->setParameter('description', $value);
}
/**
* Get the transaction ID.
*
* The transaction ID is the identifier generated by the merchant website.
*
* @return string
*/
public function getTransactionId()
{
return $this->getParameter('transactionId');
}
/**
* Sets the transaction ID.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setTransactionId($value)
{
return $this->setParameter('transactionId', $value);
}
/**
* Get the transaction reference.
*
* The transaction reference is the identifier generated by the remote
* payment gateway.
*
* @return string
*/
public function getTransactionReference()
{
return $this->getParameter('transactionReference');
}
/**
* Sets the transaction reference.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setTransactionReference($value)
{
return $this->setParameter('transactionReference', $value);
}
/**
* A list of items in this order
*
* @return ItemBag|null A bag containing items in this order
*/
public function getItems()
{
return $this->getParameter('items');
}
/**
* Set the items in this order
*
* @param ItemBag|array $items An array of items in this order
* @return AbstractRequest
*/
public function setItems($items)
{
if ($items && !$items instanceof ItemBag) {
$items = new ItemBag($items);
}
return $this->setParameter('items', $items);
}
/**
* Get the client IP address.
*
* @return string
*/
public function getClientIp()
{
return $this->getParameter('clientIp');
}
/**
* Sets the client IP address.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setClientIp($value)
{
return $this->setParameter('clientIp', $value);
}
/**
* Get the request return URL.
*
* @return string
*/
public function getReturnUrl()
{
return $this->getParameter('returnUrl');
}
/**
* Sets the request return URL.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setReturnUrl($value)
{
return $this->setParameter('returnUrl', $value);
}
/**
* Get the request cancel URL.
*
* @return string
*/
public function getCancelUrl()
{
return $this->getParameter('cancelUrl');
}
/**
* Sets the request cancel URL.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setCancelUrl($value)
{
return $this->setParameter('cancelUrl', $value);
}
/**
* Get the request notify URL.
*
* @return string
*/
public function getNotifyUrl()
{
return $this->getParameter('notifyUrl');
}
/**
* Sets the request notify URL.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setNotifyUrl($value)
{
return $this->setParameter('notifyUrl', $value);
}
/**
* Get the payment issuer.
*
* This field is used by some European gateways, and normally represents
* the bank where an account is held (separate from the card brand).
*
* @return string
*/
public function getIssuer()
{
return $this->getParameter('issuer');
}
/**
* Set the payment issuer.
*
* This field is used by some European gateways, and normally represents
* the bank where an account is held (separate from the card brand).
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setIssuer($value)
{
return $this->setParameter('issuer', $value);
}
/**
* Get the payment issuer.
*
* This field is used by some European gateways, which support
* multiple payment providers with a single API.
*
* @return string
*/
public function getPaymentMethod()
{
return $this->getParameter('paymentMethod');
}
/**
* Set the payment method.
*
* This field is used by some European gateways, which support
* multiple payment providers with a single API.
*
* @param string $value
* @return AbstractRequest Provides a fluent interface
*/
public function setPaymentMethod($value)
{
return $this->setParameter('paymentMethod', $value);
}
/**
* Send the request
*
* @return ResponseInterface
*/
public function send()
{
$data = $this->getData();
return $this->sendData($data);
}
/**
* Get the associated Response.
*
* @return ResponseInterface
*/
public function getResponse()
{
if (null === $this->response) {
throw new RuntimeException('You must call send() before accessing the Response!');
}
return $this->response;
}
}