diff --git a/.travis.yml b/.travis.yml index abb187d5..11ab3fd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ language: php php: - - 5.3 - - 5.4 - - 5.5 - 5.6 - 7.0 - 7.1 @@ -24,13 +21,14 @@ env: matrix: include: - - php: 5.3 - env: setup=lowest - - php: 5.5 + - php: 5.6 env: setup=lowest + - php: 5.6 + env: setup=stable install: - if [[ $setup = 'basic' ]]; then travis_retry composer install --prefer-dist --no-interaction; fi + - if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-stable; fi - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable; fi script: vendor/bin/phpcs --standard=PSR2 src && vendor/bin/phpunit --coverage-text diff --git a/README.md b/README.md index 5c6f4127..be5bf702 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,388 @@ -# Omnipay Common +# Omnipay -**Core components for the Omnipay PHP payment processing library** +**An easy to use, consistent payment processing library for PHP 5.6+** -[![Build Status](https://travis-ci.org/thephpleague/omnipay-common.png?branch=master)](https://travis-ci.org/thephpleague/omnipay-common) -[![Latest Stable Version](https://poser.pugx.org/omnipay/common/version.png)](https://packagist.org/packages/omnipay/common) -[![Total Downloads](https://poser.pugx.org/omnipay/common/d/total.png)](https://packagist.org/packages/omnipay/common) +[![Build Status](https://travis-ci.org/thephpleague/omnipay.png?branch=3.0)](https://travis-ci.org/thephpleague/omnipay) +[![Latest Stable Version](https://poser.pugx.org/league/omnipay/version)](https://packagist.org/packages/league/omnipay) +[![Total Downloads](https://poser.pugx.org/league/omnipay/d/total)](https://packagist.org/packages/league/omnipay) -[Omnipay](https://github.com/thephpleague/omnipay) is a framework agnostic, multi-gateway payment -processing library for PHP 5.3+. This package implements common classes required by Omnipay. + +** Omnipay 3.x is Work in Progress, please do not use in production yet ** + +Omnipay is a payment processing library for PHP. It has been designed based on +ideas from [Active Merchant](http://activemerchant.org/), plus experience implementing +dozens of gateways for [CI Merchant]. It has a clear and consistent API, +is fully unit tested, and even comes with an example application to get you started. + +**Why use Omnipay instead of a gateway's official PHP package/example code?** + +* Because you can learn one API and use it in multiple projects using different payment gateways +* Because if you need to change payment gateways you won't need to rewrite your code +* Because most official PHP payment gateway libraries are a mess +* Because most payment gateways have exceptionally poor documentation +* Because you are writing a shopping cart and need to support multiple gateways + +**Important Note: Upgrading from < 2.0** + +If you are upgrading from a pre-2.0 version of Omnipay, please note that the +project has now been split into multiple packages. There have also been some +changes to how gateway instances are created. See the +[full release notes](https://github.com/thephpleague/omnipay/releases/tag/v2.0.0) +for more details. + +## TL;DR + +Just want to see some code? + +```php +use Omnipay\Omnipay; + +$gateway = Omnipay::create('Stripe'); +$gateway->setApiKey('abc123'); + +$formData = array('number' => '4242424242424242', 'expiryMonth' => '6', 'expiryYear' => '2016', 'cvv' => '123'); +$response = $gateway->purchase(array('amount' => '10.00', 'currency' => 'USD', 'card' => $formData))->send(); + +if ($response->isSuccessful()) { + // payment was successful: update database + print_r($response); +} elseif ($response->isRedirect()) { + // redirect to offsite payment gateway + $response->redirect(); +} else { + // payment failed: display message to customer + echo $response->getMessage(); +} +``` + +As you can see, Omnipay has a consistent, well thought out API. We try to abstract as much +as possible the differences between the various payments gateways. + +## Package Layout + +Omnipay is a collection of packages which all depend on the +[league/omnipay](https://github.com/thephpleague/omnipay) package to provide +a consistent interface. There are no dependencies on official payment gateway PHP packages - +we prefer to work with the HTTP API directly. Under the hood, we use the popular and powerful +[Guzzle](http://guzzlephp.org/) library to make HTTP requests. + +New gateways can be created by cloning the layout of an existing package. When choosing a +name for your package, please don't use the `omnipay` vendor prefix, as this implies that +it is officially supported. You should use your own username as the vendor prefix, and prepend +`omnipay-` to the package name to make it clear that your package works with Omnipay. +For example, if your GitHub username was `santa`, and you were implementing the `giftpay` +payment library, a good name for your composer package would be `santa/omnipay-giftpay`. + +If you want to add your gateway to the list of officially supported gateways, please open a pull request on the +[league/omnipay](https://github.com/thephpleague/omnipay) package. Before new gateways will +be accepted, they must have 100% unit test code coverage, and follow the conventions +and code style used in other Omnipay gateways. + +## Installation + +Omnipay is installed via [Composer](https://getcomposer.org/). For most uses, you will need to require an individual gateway: + +``` +composer require omnipay/paypal:~2.0 +``` + + + +## Payment Gateways + +All payment gateways must implement [GatewayInterface](https://github.com/thephpleague/omnipay/blob/3.0/src/Common/GatewayInterface.php), and will usually +extend [AbstractGateway](https://github.com/thephpleague/omnipay/blob/3.0/src/Common/AbstractGateway.php) for basic functionality. + +The available gateways can be viewed at http://omnipay.thephpleague.com/gateways/official/ + +Gateways are created and initialized like so: + +```php +use Omnipay\Omnipay; + +$gateway = Omnipay::create('PayPal_Express'); +$gateway->setUsername('adrian'); +$gateway->setPassword('12345'); +``` + +Most settings are gateway specific. If you need to query a gateway to get a list +of available settings, you can call `getDefaultParameters()`: + +```php +$settings = $gateway->getDefaultParameters(); +// default settings array format: +array( + 'username' => '', // string variable + 'testMode' => false, // boolean variable + 'landingPage' => array('billing', 'login'), // enum variable, first item should be treated as default +); +``` + +Generally most payment gateways can be classified as one of two types: + +* Off-site gateways such as PayPal Express, where the customer is redirected to a third party site to enter payment details +* On-site (merchant-hosted) gateways such as PayPal Pro, where the customer enters their credit card details on your site + +However, there are some gateways such as Sage Pay Direct, where you take credit card details on site, then optionally redirect +if the customer's card supports 3D Secure authentication. Therefore, there is no point differentiating between the two types of +gateway (other than by the methods they support). + +## Credit Card / Payment Form Input + +User form input is directed to an [CreditCard](https://github.com/thephpleague/omnipay/blob/3.0/src/Common/CreditCard.php) +object. This provides a safe way to accept user input. + +The `CreditCard` object has the following fields: + +* firstName +* lastName +* number +* expiryMonth +* expiryYear +* startMonth +* startYear +* cvv +* issueNumber +* type +* billingAddress1 +* billingAddress2 +* billingCity +* billingPostcode +* billingState +* billingCountry +* billingPhone +* shippingAddress1 +* shippingAddress2 +* shippingCity +* shippingPostcode +* shippingState +* shippingCountry +* shippingPhone +* company +* email + +Even off-site gateways make use of the `CreditCard` object, because often you need to pass +customer billing or shipping details through to the gateway. + +The `CreditCard` object can be initialized with untrusted user input via the constructor. +Any fields passed to the constructor which are not recognized will be ignored. + +```php +$formInputData = array( + 'firstName' => 'Bobby', + 'lastName' => 'Tables', + 'number' => '4111111111111111', +); +$card = new CreditCard($formInputData); +``` + +You can also just pass the form data array directly to the gateway, and a `CreditCard` object +will be created for you. + +CreditCard fields can be accessed using getters and setters: + +```php +$number = $card->getNumber(); +$card->setFirstName('Adrian'); +``` + +If you submit credit card details which are obviously invalid (missing required fields, or a number +which fails the Luhn check), [InvalidCreditCardException](https://github.com/thephpleague/omnipay/blob/3.0/src/Common/Exception/InvalidCreditCardException.php) +will be thrown. You should validate the card details using your framework's validation library +before submitting the details to your gateway, to avoid unnecessary API calls. + +For on-site payment gateways, the following card fields are generally required: + +* firstName +* lastName +* number +* expiryMonth +* expiryYear +* cvv + +You can also verify the card number using the Luhn algorithm by calling `Helper::validateLuhn($number)`. + +## Gateway Methods + +The main methods implemented by gateways are: + +* `authorize($options)` - authorize an amount on the customer's card +* `completeAuthorize($options)` - handle return from off-site gateways after authorization +* `capture($options)` - capture an amount you have previously authorized +* `purchase($options)` - authorize and immediately capture an amount on the customer's card +* `completePurchase($options)` - handle return from off-site gateways after purchase +* `refund($options)` - refund an already processed transaction +* `void($options)` - generally can only be called up to 24 hours after submitting a transaction +* `acceptNotification()` - convert an incoming request from an off-site gateway to a generic notification object + for further processing + +On-site gateways do not need to implement the `completeAuthorize` and `completePurchase` methods. Gateways that don't +receive payment notifications don't need to implement `acceptNotification`. If any gateway does not support certain +features (such as refunds), it will throw `BadMethodCallException`. + +All gateway methods except `acceptNotification` take an `$options` array as an argument. The `acceptNotification` method +does not take any parameters and will access the HTTP URL variables or POST data implicitly. Each gateway differs in +which parameters are required, and the gateway will throw `InvalidRequestException` if you omit any required parameters. +All gateways will accept a subset of these options: + +* card +* token +* amount +* currency +* description +* transactionId +* clientIp +* returnUrl +* cancelUrl + +Pass the options through to the method like so: + +```php +$card = new CreditCard($formData); +$request = $gateway->authorize(array( + 'amount' => '10.00', // this represents $10.00 + 'card' => $card, + 'returnUrl' => 'https://www.example.com/return', +)); +``` + +When calling the `completeAuthorize` or `completePurchase` methods, the exact same arguments should be provided as +when you made the initial `authorize` or `purchase` call (some gateways will need to verify for example the actual +amount paid equals the amount requested). The only parameter you can omit is `card`. + +To summarize the various parameters you have available to you: + +* Gateway settings (e.g. username and password) are set directly on the gateway. These settings apply to all payments, and generally you will store these in a configuration file or in the database. +* Method options are used for any payment-specific options, which are not set by the customer. For example, the payment `amount`, `currency`, `transactionId` and `returnUrl`. +* CreditCard parameters are data which the user supplies. For example, you want the user to specify their `firstName` and `billingCountry`, but you don't want a user to specify the payment `currency` or `returnUrl`. + +## The Payment Response + +The payment response must implement [ResponseInterface](https://github.com/thephpleague/omnipay/blob/3.0/src/Common/Message/ResponseInterface.php). There are two main types of response: + +* Payment was successful (standard response) +* Website requires redirect to off-site payment form (redirect response) + +### Successful Response + +For a successful responses, a reference will normally be generated, which can be used to capture or refund the transaction +at a later date. The following methods are always available: + +```php +$response = $gateway->purchase(array('amount' => '10.00', 'card' => $card))->send(); + +$response->isSuccessful(); // is the response successful? +$response->isRedirect(); // is the response a redirect? +$response->getTransactionReference(); // a reference generated by the payment gateway +$response->getTransactionId(); // the reference set by the originating website if available. +$response->getMessage(); // a message generated by the payment gateway +``` + +In addition, most gateways will override the response object, and provide access to any extra fields returned by the gateway. + +### Redirect Response + +The redirect response is further broken down by whether the customer's browser must redirect using GET (RedirectResponse object), or +POST (FormRedirectResponse). These could potentially be combined into a single response class, with a `getRedirectMethod()`. + +After processing a payment, the cart should check whether the response requires a redirect, and if so, redirect accordingly: + +```php +$response = $gateway->purchase(array('amount' => '10.00', 'card' => $card))->send(); +if ($response->isSuccessful()) { + // payment is complete +} elseif ($response->isRedirect()) { + $response->redirect(); // this will automatically forward the customer +} else { + // not successful +} +``` + +The customer isn't automatically forwarded on, because often the cart or developer will want to customize the redirect method +(or if payment processing is happening inside an AJAX call they will want to return JS to the browser instead). + +To display your own redirect page, simply call `getRedirectUrl()` on the response, then display it accordingly: + +```php +$url = $response->getRedirectUrl(); +// for a form redirect, you can also call the following method: +$data = $response->getRedirectData(); // associative array of fields which must be posted to the redirectUrl +``` + +## Error Handling + +You can test for a successful response by calling `isSuccessful()` on the response object. If there +was an error communicating with the gateway, or your request was obviously invalid, an exception +will be thrown. In general, if the gateway does not throw an exception, but returns an unsuccessful +response, it is a message you should display to the customer. If an exception is thrown, it is +either a bug in your code (missing required fields), or a communication error with the gateway. + +You can handle both scenarios by wrapping the entire request in a try-catch block: + +```php +try { + $response = $gateway->purchase(array('amount' => '10.00', 'card' => $card))->send(); + if ($response->isSuccessful()) { + // mark order as complete + } elseif ($response->isRedirect()) { + $response->redirect(); + } else { + // display error to customer + exit($response->getMessage()); + } +} catch (\Exception $e) { + // internal error, log exception and display a generic message to the customer + exit('Sorry, there was an error processing your payment. Please try again later.'); +} +``` + +## Token Billing + +Token billing allows you to store a credit card with your gateway, and charge it at a later date. +Token billing is not supported by all gateways. For supported gateways, the following methods +are available: + +* `createCard($options)` - returns a response object which includes a `cardReference`, which can be used for future transactions +* `updateCard($options)` - update a stored card, not all gateways support this method +* `deleteCard($options)` - remove a stored card, not all gateways support this method + +Once you have a `cardReference`, you can use it instead of the `card` parameter when creating a charge: + + $gateway->purchase(array('amount' => '10.00', 'cardReference' => 'abc')); + +## Recurring Billing + +At this stage, automatic recurring payments functionality is out of scope for this library. +This is because there is likely far too many differences between how each gateway handles +recurring billing profiles. Also in most cases token billing will cover your needs, as you can +store a credit card then charge it on whatever schedule you like. Feel free to get in touch if +you really think this should be a core feature and worth the effort. + +## Incoming Notifications + +Some gateways (e.g. Cybersource, GoPay) offer HTTP notifications to inform the merchant about the completion (or, in +general, status) of the payment. To assist with handling such notifications, the `acceptNotification()` method will +extract the transaction reference and payment status from the HTTP request and return a generic `NotificationInterface`. + +```php +$notification = $gateway->acceptNotification(); + +$notification->getTransactionReference(); // A reference provided by the gateway to represent this transaction +$notification->getTransactionStatus(); // Current status of the transaction, one of NotificationInterface::STATUS_* +$notification->getMessage(); // Additional message, if any, provided by the gateway + +// update the status of the corresponding transaction in your database +``` + +## Example Application + +An example application is provided in the [omnipay/example](https://github.com/thephpleague/omnipay-example) repo. +You can run it using PHP's built in web server (PHP 5.4+): + + $ php composer.phar update --dev + $ php -S localhost:8000 + +For more information, see the [Omnipay example application](https://github.com/thephpleague/omnipay-example). ## Support @@ -19,5 +394,15 @@ If you want to keep up to date with release anouncements, discuss ideas for the or ask more detailed questions, there is also a [mailing list](https://groups.google.com/forum/#!forum/omnipay) which you can subscribe to. -If you believe you have found a bug, please report it using the [GitHub issue tracker](https://github.com/thephpleague/omnipay-common/issues), -or better yet, fork the library and submit a pull request. +If you believe you have found a bug, please report it using the GitHub issue tracker +for the appropriate package, or better yet, fork the library and submit a pull request. + +## Security +If you discover any security related issues, please email kayladnls@gmail.com instead of using the issue tracker. + + +## Feedback + +**Please provide feedback!** We want to make this library useful in as many projects as possible. +Please head on over to the [mailing list](https://groups.google.com/forum/#!forum/omnipay) +and point out what you do and don't like, or fork the project and make suggestions. **No issue is too small.** \ No newline at end of file diff --git a/composer.json b/composer.json index 320d2145..fbaf6d17 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,7 @@ "type": "library", "description": "Common components for Omnipay payment processing library", "keywords": [ + "league", "gateway", "merchant", "omnipay", @@ -17,62 +18,35 @@ "name": "Adrian Macneil", "email": "adrian@adrianmacneil.com" }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + }, { "name": "Omnipay Contributors", "homepage": "https://github.com/thephpleague/omnipay-common/contributors" } ], "autoload": { - "psr-0": { "Omnipay\\Common\\" : "src/" }, - "classmap": ["src/Omnipay/Omnipay.php"] + "psr-4": { + "League\\Omnipay\\Common\\" : "src/" + } + }, + "autoload-dev": { + "psr-4": { + "League\\Omnipay\\Common\\" : "tests/" + } }, "require": { - "php": ">=5.3.2", - "guzzle/guzzle": "~3.9", - "symfony/http-foundation": "~2.1|~3.0" + "php": ">=5.6", + "alcohol/iso4217": "^3.1", + "psr/http-message": "^1.0", + "guzzlehttp/psr7": "^1.3" }, "require-dev": { - "omnipay/tests": "~2.0", - "squizlabs/php_codesniffer": "~1.5" + "omnipay/tests": "^3.0", + "guzzlehttp/guzzle": "^6.2.1" }, - "extra": { - "branch-alias": { - "dev-master": "2.5.x-dev" - }, - "gateways": [ - "AuthorizeNet_AIM", - "AuthorizeNet_SIM", - "Buckaroo_CreditCard", - "Buckaroo_Ideal", - "Buckaroo_PayPal", - "CardSave", - "Coinbase", - "Dummy", - "Eway_Rapid", - "FirstData_Connect", - "GoCardless", - "Manual", - "Migs_ThreeParty", - "Migs_TwoParty", - "Mollie", - "MultiSafepay", - "Netaxept", - "NetBanx", - "PayFast", - "Payflow_Pro", - "PaymentExpress_PxPay", - "PaymentExpress_PxPost", - "PayPal_Express", - "PayPal_Pro", - "Pin", - "SagePay_Direct", - "SagePay_Server", - "SecurePay_DirectPost", - "Stripe", - "TargetPay_Directebanking", - "TargetPay_Ideal", - "TargetPay_Mrcash", - "WorldPay" - ] - } + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/phpunit.php b/phpunit.php new file mode 100644 index 00000000..b5947821 --- /dev/null +++ b/phpunit.php @@ -0,0 +1,7 @@ + httpClient = $httpClient ?: $this->getDefaultHttpClient(); - $this->httpRequest = $httpRequest ?: $this->getDefaultHttpRequest(); + $this->httpClient = $httpClient; + $this->httpRequest = $httpRequest; $this->initialize(); } @@ -101,7 +98,7 @@ public function initialize(array $parameters = array()) } } - Helper::initialize($this, $parameters); + Helper::initializeParameters($this, $parameters); return $this; } @@ -114,35 +111,6 @@ public function getDefaultParameters() return array(); } - /** - * @return array - */ - public function getParameters() - { - return $this->parameters->all(); - } - - /** - * @param string $key - * @return mixed - */ - public function getParameter($key) - { - return $this->parameters->get($key); - } - - /** - * @param string $key - * @param mixed $value - * @return $this - */ - public function setParameter($key, $value) - { - $this->parameters->set($key, $value); - - return $this; - } - /** * @return boolean */ @@ -291,15 +259,15 @@ public function supportsUpdateCard() * Create and initialize a request object * * This function is usually used to create objects of type - * Omnipay\Common\Message\AbstractRequest (or a non-abstract subclass of it) + * League\Omnipay\Common\Message\AbstractRequest (or a non-abstract subclass of it) * and initialise them with using existing parameters from this gateway. * * Example: * * - * class MyRequest extends \Omnipay\Common\Message\AbstractRequest {}; + * class MyRequest extends \League\Omnipay\Common\Message\AbstractRequest {}; * - * class MyGateway extends \Omnipay\Common\AbstractGateway { + * class MyGateway extends \League\Omnipay\Common\AbstractGateway { * function myRequest($parameters) { * $this->createRequest('MyRequest', $parameters); * } @@ -312,10 +280,10 @@ public function supportsUpdateCard() * $myRequest = $gw->myRequest($someParameters); * * - * @see \Omnipay\Common\Message\AbstractRequest + * @see \League\Omnipay\Common\Message\AbstractRequest * @param string $class The request class name * @param array $parameters - * @return \Omnipay\Common\Message\AbstractRequest + * @return \League\Omnipay\Common\Message\AbstractRequest */ protected function createRequest($class, array $parameters) { @@ -323,29 +291,4 @@ protected function createRequest($class, array $parameters) return $obj->initialize(array_replace($this->getParameters(), $parameters)); } - - /** - * Get the global default HTTP client. - * - * @return HttpClient - */ - protected function getDefaultHttpClient() - { - return new HttpClient( - '', - array( - 'curl.options' => array(CURLOPT_CONNECTTIMEOUT => 60), - ) - ); - } - - /** - * Get the global default HTTP request. - * - * @return HttpRequest - */ - protected function getDefaultHttpRequest() - { - return HttpRequest::createFromGlobals(); - } } diff --git a/src/Amount.php b/src/Amount.php new file mode 100644 index 00000000..28df4f20 --- /dev/null +++ b/src/Amount.php @@ -0,0 +1,130 @@ +amount = (string) $amount; + $this->currency = self::findCurrency($currency); + } + + /** + * Get the amount in smallest units (eg. cents) + * + * @return string + */ + public function getInteger() + { + return $this->amount; + } + + /** + * Get the amount as decimal string + * + * @return string + */ + public function getFormatted() + { + $decimals = $this->currency->getDecimals(); + $amount = $this->amount / pow(10, $decimals); + + return number_format( + $amount, + $decimals, + '.', + '' + ); + } + + /** + * Get the currency + * + * @return Currency + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @return bool + */ + public function isNegative() + { + return $this->amount < 0; + } + + /** + * @return bool + */ + public function isZero() + { + return $this->amount == 0; + } + + /** + * Get the amount, based on a decimal string + * + * @param string|float $amount + * @param string|Currency $currency + * @return static + */ + public static function fromDecimal($amount, $currency) + { + $amount = Helper::toFloat($amount); + + $currency = self::findCurrency($currency); + $factor = pow(10, $currency->getDecimals()); + $amount = (int) round($amount * $factor); + + return new self($amount, $currency); + } + + /** + * @param string|Currency $currencyCode + * @return Currency + */ + private static function findCurrency($currencyCode) + { + if ($currencyCode instanceof Currency) { + return $currencyCode; + } elseif (is_string($currencyCode) || is_integer($currencyCode)) { + $currency = Currency::find($currencyCode); + if (is_null($currency)) { + throw new InvalidArgumentException('Invalid currency'); + } + return $currency; + } + + throw new InvalidArgumentException('Currency must be a string or Currency object'); + } +} diff --git a/src/AmountInterface.php b/src/AmountInterface.php new file mode 100644 index 00000000..d2276365 --- /dev/null +++ b/src/AmountInterface.php @@ -0,0 +1,47 @@ + + * // Define credit card parameters, which should look like this + * $parameters = [ + * 'firstName' => 'Bobby', + * 'lastName' => 'Tables', + * 'number' => '4444333322221111', + * 'cvv' => '123', + * 'expiryMonth' => '12', + * 'expiryYear' => '2017', + * 'email' => 'testcard@gmail.com', + * ]; + * + * // Create a credit card object + * $card = new CreditCard($parameters); + * + * + * The full list of card attributes that may be set via the parameter to + * *new* is as follows: + * + * * title + * * firstName + * * lastName + * * name + * * company + * * address1 + * * address2 + * * city + * * postcode + * * state + * * country + * * phone + * * phoneExtension + * * fax + * * number + * * expiryMonth + * * expiryYear + * * startMonth + * * startYear + * * cvv + * * issueNumber + * * billingTitle + * * billingName + * * billingFirstName + * * billingLastName + * * billingCompany + * * billingAddress1 + * * billingAddress2 + * * billingCity + * * billingPostcode + * * billingState + * * billingCountry + * * billingPhone + * * billingFax + * * shippingTitle + * * shippingName + * * shippingFirstName + * * shippingLastName + * * shippingCompany + * * shippingAddress1 + * * shippingAddress2 + * * shippingCity + * * shippingPostcode + * * shippingState + * * shippingCountry + * * shippingPhone + * * shippingFax + * * email + * * birthday + * * gender + * + * If any unknown parameters are passed in, they will be ignored. No error is thrown. + */ +class CreditCard implements ParameterizedInterface, \JsonSerializable +{ + use HasParametersTrait; + + const BRAND_VISA = 'visa'; + const BRAND_MASTERCARD = 'mastercard'; + const BRAND_DISCOVER = 'discover'; + const BRAND_AMEX = 'amex'; + const BRAND_DINERS_CLUB = 'diners_club'; + const BRAND_JCB = 'jcb'; + const BRAND_SWITCH = 'switch'; + const BRAND_SOLO = 'solo'; + const BRAND_DANKORT = 'dankort'; + const BRAND_MAESTRO = 'maestro'; + const BRAND_FORBRUGSFORENINGEN = 'forbrugsforeningen'; + const BRAND_LASER = 'laser'; + + /** + * All known/supported card brands, and a regular expression to match them. + * + * The order of the card brands is important, as some of the regular expressions overlap. + * + * Note: The fact that a particular card brand has been added to this array does not imply + * that a selected gateway will support the card. + * + * @link https://github.com/Shopify/active_merchant/blob/master/lib/active_merchant/billing/credit_card_methods.rb + * @var array + */ + protected $supported_cards = [ + self::BRAND_VISA => '/^4\d{12}(\d{3})?$/', + self::BRAND_MASTERCARD => '/^(5[1-5]\d{4}|677189)\d{10}$/', + self::BRAND_DISCOVER => '/^(6011|65\d{2}|64[4-9]\d)\d{12}|(62\d{14})$/', + self::BRAND_AMEX => '/^3[47]\d{13}$/', + self::BRAND_DINERS_CLUB => '/^3(0[0-5]|[68]\d)\d{11}$/', + self::BRAND_JCB => '/^35(28|29|[3-8]\d)\d{12}$/', + self::BRAND_SWITCH => '/^6759\d{12}(\d{2,3})?$/', + self::BRAND_SOLO => '/^6767\d{12}(\d{2,3})?$/', + self::BRAND_DANKORT => '/^5019\d{12}$/', + self::BRAND_MAESTRO => '/^(5[06-8]|6\d)\d{10,17}$/', + self::BRAND_FORBRUGSFORENINGEN => '/^600722\d{10}$/', + self::BRAND_LASER => '/^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/', + ]; + + /** + * @var Customer + */ + private $shippingCustomer; + + /** + * @var Customer + */ + private $billingCustomer; + + /** + * Create a new CreditCard object using the specified parameters + * + * @param array $parameters An array of parameters to set on the new object + */ + public function __construct(array $parameters = []) + { + $this->initialize($parameters); + } + + /** + * All known/supported card brands, and a regular expression to match them. + * + * Note: The fact that this class knows about a particular card brand does not imply + * that your gateway supports it. + * + * @see self::$supported_cards + * @return array + */ + public function getSupportedBrands() + { + return $this->supported_cards; + } + + /** + * Set a custom supported card brand with a regular expression to match it. + * + * Note: The fact that a particular card is known does not imply that your + * gateway supports it. + * + * Set $add_to_front to true if the key should be added to the front of the array + * + * @param string $name The name of the new supported brand. + * @param string $expression The regular expression to check if a card is supported. + * @return boolean success + */ + public function addSupportedBrand($name, $expression) + { + $known_brands = array_keys($this->supported_cards); + + if (in_array($name, $known_brands)) { + return false; + } + + $this->supported_cards[$name] = $expression; + + return true; + } + + /** + * Set the credit card year. + * + * The input value is normalised to a 4 digit number. + * + * @param string $key Parameter key, e.g. 'expiryYear' + * @param mixed $value Parameter value + * @return CreditCard provides a fluent interface. + */ + protected function setYearParameter($key, $value) + { + // normalize year to four digits + if (null === $value || '' === $value) { + $value = null; + } else { + $value = (int) gmdate('Y', gmmktime(0, 0, 0, 1, 1, (int) $value)); + } + + return $this->setParameter($key, $value); + } + + /** + * Validate this credit card. If the card is invalid, InvalidCreditCardException is thrown. + * + * This method is called internally by gateways to avoid wasting time with an API call + * when the credit card is clearly invalid. + * + * Generally if you want to validate the credit card yourself with custom error + * messages, you should use your framework's validation library, not this method. + * + * @throws InvalidCreditCardException + * @return void + */ + public function validate() + { + foreach (['number', 'expiryMonth', 'expiryYear'] as $key) { + if (!$this->getParameter($key)) { + throw new InvalidCreditCardException("The $key parameter is required"); + } + } + + if ($this->getExpiryDate('Ym') < gmdate('Ym')) { + throw new InvalidCreditCardException('Card has expired'); + } + + if (!Helper::validateLuhn($this->getNumber())) { + throw new InvalidCreditCardException('Card number is invalid'); + } + + if (!is_null($this->getNumber()) && !preg_match('/^\d{12,19}$/i', $this->getNumber())) { + throw new InvalidCreditCardException('Card number should have 12 to 19 digits'); + } + } + + /** + * Get Card Number. + * + * @return string + */ + public function getNumber() + { + return $this->getParameter('number'); + } + + /** + * Set Card Number + * + * Non-numeric characters are stripped out of the card number, so + * it's safe to pass in strings such as "4444-3333 2222 1111" etc. + * + * @param string $value Parameter value + * @return CreditCard provides a fluent interface. + */ + public function setNumber($value) + { + // strip non-numeric characters + return $this->setParameter('number', preg_replace('/\D/', '', $value)); + } + + /** + * Get the last 4 digits of the card number. + * + * @return string + */ + public function getNumberLastFour() + { + return substr($this->getNumber(), -4, 4) ?: null; + } + + /** + * Returns a masked credit card number with only the last 4 chars visible + * + * @param string $mask Character to use in place of numbers + * @return string + */ + public function getNumberMasked($mask = 'X') + { + $maskLength = strlen($this->getNumber()) - 4; + + return str_repeat($mask, $maskLength) . $this->getNumberLastFour(); + } + + /** + * Credit Card Brand + * + * Iterates through known/supported card brands to determine the brand of this card + * + * @return string + */ + public function getBrand() + { + foreach ($this->getSupportedBrands() as $brand => $val) { + if (preg_match($val, $this->getNumber())) { + return $brand; + } + } + } + + /** + * Get the card expiry month. + * + * @return string + */ + public function getExpiryMonth() + { + return $this->getParameter('expiryMonth'); + } + + /** + * Sets the card expiry month. + * + * @param string $value + * @return CreditCard provides a fluent interface. + */ + public function setExpiryMonth($value) + { + return $this->setParameter('expiryMonth', (int) $value); + } + + /** + * Get the card expiry year. + * + * @return string + */ + public function getExpiryYear() + { + return $this->getParameter('expiryYear'); + } + + /** + * Sets the card expiry year. + * + * @param string $value + * @return CreditCard provides a fluent interface. + */ + public function setExpiryYear($value) + { + return $this->setYearParameter('expiryYear', $value); + } + + /** + * Get the card expiry date, using the specified date format string. + * + * @param string $format + * + * @return string + */ + public function getExpiryDate($format) + { + return gmdate($format, gmmktime(0, 0, 0, $this->getExpiryMonth(), 1, $this->getExpiryYear())); + } + + /** + * Get the card start month. + * + * @return string + */ + public function getStartMonth() + { + return $this->getParameter('startMonth'); + } + + /** + * Sets the card start month. + * + * @param string $value + * @return CreditCard provides a fluent interface. + */ + public function setStartMonth($value) + { + return $this->setParameter('startMonth', (int) $value); + } + + /** + * Get the card start year. + * + * @return string + */ + public function getStartYear() + { + return $this->getParameter('startYear'); + } + + /** + * Sets the card start year. + * + * @param string $value + * @return CreditCard provides a fluent interface. + */ + public function setStartYear($value) + { + return $this->setYearParameter('startYear', $value); + } + + /** + * Get the card start date, using the specified date format string + * + * @param string $format + * + * @return string + */ + public function getStartDate($format) + { + return gmdate($format, gmmktime(0, 0, 0, $this->getStartMonth(), 1, $this->getStartYear())); + } + + /** + * Get the card CVV. + * + * @return string + */ + public function getCvv() + { + return $this->getParameter('cvv'); + } + + /** + * Sets the card CVV. + * + * @param string $value + * @return CreditCard provides a fluent interface. + */ + public function setCvv($value) + { + return $this->setParameter('cvv', $value); + } + + /** + * Get the card issue number. + * + * @return string + */ + public function getIssueNumber() + { + return $this->getParameter('issueNumber'); + } + + /** + * Sets the card issue number. + * + * @param string $value + * @return CreditCard provides a fluent interface. + */ + public function setIssueNumber($value) + { + return $this->setParameter('issueNumber', $value); + } + + /** + * @param Customer $customer + */ + public function setCustomer(Customer $customer) + { + $this->setBillingCustomer($customer); + $this->setShippingCustomer($customer); + } + + /** + * @param Customer $customer + */ + private function setBillingCustomer(Customer $customer) + { + $this->billingCustomer = $customer; + } + + /** + * @param Customer $customer + */ + private function setShippingCustomer(Customer $customer) + { + $this->shippingCustomer = $customer; + } + + /** + * @return Customer + */ + public function getShippingCustomer() + { + return $this->shippingCustomer; + } + + /** + * @return Customer + */ + public function getBillingCustomer() + { + return $this->billingCustomer; + } + + /** + * @return Customer + */ + public function getCustomer() + { + return $this->getBillingCustomer(); + } + + /** + * Mask some parameters when debugging. + * + * @return array + */ + protected function toMaskedArray() + { + $params = $this->parameters->all(); + + if (isset($params['number'])) { + $params['number'] = $this->getNumberMasked(); + } + + $maskedKeys = ['expiryYear', 'expiryMonth', 'cvv', 'issueNumber']; + foreach ($maskedKeys as $key) { + if (isset($params[$key])) { + $params[$key] = str_pad('', strlen((string)$params[$key]), '*'); + } + } + + return $params; + } + + /** + * Serialize with sensitive data + * + * @return array + */ + public function jsonSerialize() + { + return $this->toMaskedArray(); + } + + /** + * var_dump or print_r without sensitive data + * + * @return array + */ + public function __debugInfo() + { + return $this->toMaskedArray(); + } +} diff --git a/src/Currency.php b/src/Currency.php new file mode 100644 index 00000000..f9be7c62 --- /dev/null +++ b/src/Currency.php @@ -0,0 +1,101 @@ +code = $code; + $this->numeric = $numeric; + $this->decimals = $decimals; + } + + /** + * Get the three letter code for the currency + * + * @return string + */ + public function getCode() + { + return $this->code; + } + + /** + * Get the numeric code for this currency + * + * @return string + */ + public function getNumeric() + { + return $this->numeric; + } + + /** + * Get the number of decimal places for this currency + * + * @return int + */ + public function getDecimals() + { + return $this->decimals; + } + + /** + * Find a specific currency + * + * @param string $code The three letter currency code + * @return mixed A Currency object, or null if no currency was found + */ + public static function find($code) + { + $iso4217 = new ISO4217(); + + try { + $currency = $iso4217->getByAlpha3($code); + } catch (\Exception $e) { + return null; + } + + return new static($currency['alpha3'], $currency['numeric'], $currency['exp']); + } + + /** + * Get an array of all supported currencies + * + * @return array + */ + public static function all() + { + $currencies = []; + + $iso4217 = new ISO4217(); + foreach ($iso4217->getAll() as $currency) { + $currencies[$currency['alpha3']] = [ + 'numeric' => $currency['numeric'], + 'decimals' => $currency['exp'], + ]; + } + + return $currencies; + } +} diff --git a/src/Customer.php b/src/Customer.php new file mode 100644 index 00000000..04c83ae2 --- /dev/null +++ b/src/Customer.php @@ -0,0 +1,414 @@ +initialize($parameters); + } + + /** + * Get Customer Title. + * + * @return string + */ + public function getTitle() + { + return $this->getParameter('title'); + } + + /** + * Set Customer Title. + * + * @param string $value Parameter value + * @return CreditCard provides a fluent interface. + */ + public function setTitle($value) + { + $this->setParameter('title', $value); + + return $this; + } + + /** + * Get Customer First Name. + * + * @return string + */ + public function getFirstName() + { + return $this->getParameter('firstName'); + } + + /** + * Set Customer First Name (Billing and Shipping). + * + * @param string $value Parameter value + * @return $this provides a fluent interface. + */ + public function setFirstName($value) + { + $this->setParameter('firstName', $value); + + return $this; + } + + /** + * Get Customer Last Name. + * + * @return string + */ + public function getLastName() + { + return $this->getParameter('lastName'); + } + + /** + * Set Customer Last Name (Billing and Shipping). + * + * @param string $value Parameter value + * @return $this provides a fluent interface. + */ + public function setLastName($value) + { + $this->setParameter('lastName', $value); + + return $this; + } + + /** + * Get Customer Name. + * + * @return string + */ + public function getName() + { + return trim($this->getFirstName() . ' ' . $this->getLastName()); + } + + /** + * Set Customer Name (Billing and Shipping). + * + * @param string $value Parameter value + * @return $this provides a fluent interface. + */ + public function setName($value) + { + $names = explode(' ', $value, 2); + $this->setFirstName($names[0]); + $this->setLastName(isset($names[1]) ? $names[1] : null); + + return $this; + } + + /** + * Get the address, line 1. + * + * @return string + */ + public function getAddress1() + { + return $this->getParameter('address1'); + } + + /** + * Sets the address, line 1. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setAddress1($value) + { + $this->setParameter('address1', $value); + + return $this; + } + + /** + * Get the address, line 2. + * + * @return string + */ + public function getAddress2() + { + return $this->getParameter('address2'); + } + + /** + * Sets the address, line 2. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setAddress2($value) + { + $this->setParameter('address2', $value); + + return $this; + } + + /** + * Get the city. + * + * @return string + */ + public function getCity() + { + return $this->getParameter('city'); + } + + /** + * Sets the city. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setCity($value) + { + $this->setParameter('city', $value); + + return $this; + } + + /** + * Get the postcode. + * + * @return string + */ + public function getPostcode() + { + return $this->getParameter('postcode'); + } + + /** + * Sets the postcode. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setPostcode($value) + { + $this->setParameter('postcode', $value); + + return $this; + } + + /** + * Get the state. + * + * @return string + */ + public function getState() + { + return $this->getParameter('state'); + } + + /** + * Sets the state. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setState($value) + { + $this->setParameter('state', $value); + + return $this; + } + + /** + * Get the country. + * + * @return string + */ + public function getCountry() + { + return $this->getParameter('country'); + } + + /** + * Sets the country. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setCountry($value) + { + $this->setParameter('country', $value); + + return $this; + } + + /** + * Get the phone number. + * + * @return string + */ + public function getPhone() + { + return $this->getParameter('phone'); + } + + /** + * Sets the phone number. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setPhone($value) + { + $this->setParameter('phone', $value); + + return $this; + } + + /** + * Get the phone number extension. + * + * @return string + */ + public function getPhoneExtension() + { + return $this->getParameter('phoneExtension'); + } + + /** + * Sets the phone number extension. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setPhoneExtension($value) + { + $this->setParameter('phoneExtension', $value); + + return $this; + } + + /** + * Get the fax number.. + * + * @return string + */ + public function getFax() + { + return $this->getParameter('fax'); + } + + /** + * Sets the fax number. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setFax($value) + { + $this->setParameter('fax', $value); + + return $this; + } + + /** + * Get the company name. + * + * @return string + */ + public function getCompany() + { + return $this->getParameter('company'); + } + + /** + * Sets the company name. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setCompany($value) + { + $this->setParameter('company', $value); + + return $this; + } + + /** + * Get the customer's email address. + * + * @return string + */ + public function getEmail() + { + return $this->getParameter('email'); + } + + /** + * Sets the customer's email address. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setEmail($value) + { + return $this->setParameter('email', $value); + } + + /** + * Get the customer's birthday. + * + * @return string + */ + public function getBirthday($format = 'Y-m-d') + { + $value = $this->getParameter('birthday'); + + return $value ? $value->format($format) : null; + } + + /** + * Sets the customer's birthday. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setBirthday($value) + { + if ($value) { + $value = new DateTime($value, new DateTimeZone('UTC')); + } else { + $value = null; + } + + return $this->setParameter('birthday', $value); + } + + /** + * Get the customer's gender. + * + * @return string + */ + public function getGender() + { + return $this->getParameter('gender'); + } + + /** + * Sets the customer's gender. + * + * @param string $value + * @return $this provides a fluent interface. + */ + public function setGender($value) + { + return $this->setParameter('gender', $value); + } +} diff --git a/src/Omnipay/Common/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php similarity index 76% rename from src/Omnipay/Common/Exception/BadMethodCallException.php rename to src/Exception/BadMethodCallException.php index 2396e697..f62bbd2e 100644 --- a/src/Omnipay/Common/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -1,6 +1,6 @@ parameters->set($key, $value); + + return $this; + } + + /** + * Get one parameter. + * + * @return mixed A single parameter value. + */ + public function getParameter($key) + { + return $this->parameters->get($key); + } + + /** + * Get all parameters. + * + * @return array An associative array of parameters. + */ + public function getParameters() + { + return $this->parameters->all(); + } + + /** + * Initialize the object with parameters. + * + * If any unknown parameters passed, they will be ignored. + * + * @param array $parameters An associative array of parameters + * @return $this. + */ + public function initialize(array $parameters = []) + { + $this->parameters = new ParameterBag; + + Helper::initializeParameters($this, $parameters); + + return $this; + } +} diff --git a/src/Omnipay/Common/Helper.php b/src/Helper.php similarity index 60% rename from src/Omnipay/Common/Helper.php rename to src/Helper.php index 13637378..7a37377f 100644 --- a/src/Omnipay/Common/Helper.php +++ b/src/Helper.php @@ -3,9 +3,10 @@ * Helper class */ -namespace Omnipay\Common; +namespace League\Omnipay\Common; use InvalidArgumentException; +use League\Omnipay\Common\Exception\RuntimeException; /** * Helper class @@ -75,16 +76,18 @@ public static function validateLuhn($number) * Parameters are automatically converted to camelCase. Any parameters which do * not match a setter on the target object are ignored. * - * @param mixed $target The object to set parameters on + * @param ParameterizedInterface $target The object to set parameters on * @param array $parameters An array of parameters to set */ - public static function initialize($target, $parameters) + public static function initializeParameters(ParameterizedInterface $target, array $parameters = []) { if (is_array($parameters)) { foreach ($parameters as $key => $value) { $method = 'set'.ucfirst(static::camelCase($key)); if (method_exists($target, $method)) { $target->$method($value); + } else { + $target->setParameter($key, $value); } } } @@ -102,8 +105,8 @@ public static function getGatewayShortName($className) $className = substr($className, 1); } - if (0 === strpos($className, 'Omnipay\\')) { - return trim(str_replace('\\', '_', substr($className, 8, -7)), '_'); + if (0 === strpos($className, 'League\\Omnipay\\')) { + return trim(str_replace('\\', '_', substr($className, 15, -7)), '_'); } return '\\'.$className; @@ -117,9 +120,9 @@ public static function getGatewayShortName($className) * * \Custom\Gateway => \Custom\Gateway * \Custom_Gateway => \Custom_Gateway - * Stripe => \Omnipay\Stripe\Gateway - * PayPal\Express => \Omnipay\PayPal\ExpressGateway - * PayPal_Express => \Omnipay\PayPal\ExpressGateway + * Stripe => \League\Omnipay\Stripe\Gateway + * PayPal\Express => \League\Omnipay\PayPal\ExpressGateway + * PayPal_Express => \League\Omnipay\PayPal\ExpressGateway * * @param string $shortName The short gateway name * @return string The fully namespaced gateway class name @@ -136,7 +139,7 @@ public static function getGatewayClassName($shortName) $shortName .= '\\'; } - return '\\Omnipay\\'.$shortName.'Gateway'; + return '\\League\\Omnipay\\'.$shortName.'Gateway'; } /** @@ -164,4 +167,62 @@ public static function toFloat($value) return (float)$value; } + + /** + * Parse the JSON response body and return an array + * + * Copied from Response->json() in Guzzle3 (copyright @mtdowling) + * @link https://github.com/guzzle/guzzle3/blob/v3.9.3/src/Guzzle/Http/Message/Response.php + * + * @param string $body + * @throws RuntimeException if the response body is not in JSON format + * @return array|string|int|bool|float + */ + public static function jsonDecode($body) + { + $data = json_decode((string) $body, true); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); + } + return $data === null ? [] : $data; + } + + /** + * Parse the XML response body and return a \SimpleXMLElement. + * + * In order to prevent XXE attacks, this method disables loading external + * entities. If you rely on external entities, then you must parse the + * XML response manually by accessing the response body directly. + * + * Copied from Response->xml() in Guzzle3 (copyright @mtdowling) + * @link https://github.com/guzzle/guzzle3/blob/v3.9.3/src/Guzzle/Http/Message/Response.php + * + * @param string $body + * @return \SimpleXMLElement + * @throws RuntimeException if the response body is not in XML format + * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + * + */ + public static function xmlDecode($body) + { + $errorMessage = null; + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + try { + $xml = new \SimpleXMLElement((string) $body ?: '', LIBXML_NONET); + if ($error = libxml_get_last_error()) { + $errorMessage = $error->message; + } + } catch (\Exception $e) { + $errorMessage = $e->getMessage(); + } + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + if ($errorMessage) { + throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage); + } + return $xml; + } } diff --git a/src/Http/AbstractClient.php b/src/Http/AbstractClient.php new file mode 100644 index 00000000..7b53a12a --- /dev/null +++ b/src/Http/AbstractClient.php @@ -0,0 +1,122 @@ +sendRequest($request); + } + + /** + * Send a POST request. + * + * @param UriInterface|string $uri + * @param array $headers + * @param string|null|resource|StreamInterface $body + * @return ResponseInterface + */ + public function post($uri, $headers = [], $body = null) + { + $request = Factory::createRequest('POST', $uri, $headers, $body); + + return $this->sendRequest($request); + } + + /** + * Send a PUT request. + * + * @param UriInterface|string $uri + * @param array $headers + * @param string|null|resource|StreamInterface $body + * @return ResponseInterface + */ + public function put($uri, $headers = [], $body = null) + { + $request = Factory::createRequest('PUT', $uri, $headers, $body); + + return $this->sendRequest($request); + } + + /** + * Send a PATCH request. + * + * @param UriInterface|string $uri + * @param array $headers + * @param string|null|resource|StreamInterface $body + * @return ResponseInterface + */ + public function patch($uri, $headers = [], $body = null) + { + $request = Factory::createRequest('PATCH', $uri, $headers, $body); + + return $this->sendRequest($request); + } + + /** + * Send a DELETE request. + * + * @param UriInterface|string $uri + * @param array $headers + * @param string|null|resource|StreamInterface $body + * @return ResponseInterface + */ + public function delete($uri, $headers = [], $body = null) + { + $request = Factory::createRequest('DELETE', $uri, $headers, $body); + + return $this->sendRequest($request); + } + + /** + * Send a HEAD request. + * + * @param UriInterface|string $uri + * @param array $headers + * @return ResponseInterface + */ + public function head($uri, $headers = []) + { + $request = Factory::createRequest('HEAD', $uri, $headers); + + return $this->sendRequest($request); + } + + /** + * Send a OPTIONS request. + * + * @param UriInterface|string $uri + * @return ResponseInterface + */ + public function options($uri) + { + $request = Factory::createRequest('OPTIONS', $uri); + + return $this->sendRequest($request); + } +} diff --git a/src/Http/ClientInterface.php b/src/Http/ClientInterface.php new file mode 100644 index 00000000..7686ea52 --- /dev/null +++ b/src/Http/ClientInterface.php @@ -0,0 +1,90 @@ +guzzle = $client ?: new Client(); + } + + public function getGuzzleClient() + { + return $this->guzzle; + } + + public function setGuzzleClient(Client $client) + { + $this->guzzle = $client; + } + + /** + * @param RequestInterface $request + * @return ResponseInterface + */ + public function sendRequest(RequestInterface $request) + { + return $this->guzzle->send($request); + } +} diff --git a/src/Omnipay/Common/Issuer.php b/src/Issuer.php similarity index 97% rename from src/Omnipay/Common/Issuer.php rename to src/Issuer.php index fcbfc327..5101cf99 100644 --- a/src/Omnipay/Common/Issuer.php +++ b/src/Issuer.php @@ -3,7 +3,7 @@ * Issuer */ -namespace Omnipay\Common; +namespace League\Omnipay\Common; /** * Issuer diff --git a/src/Omnipay/Common/Item.php b/src/Item.php similarity index 57% rename from src/Omnipay/Common/Item.php rename to src/Item.php index 02d0652d..1e32fec6 100644 --- a/src/Omnipay/Common/Item.php +++ b/src/Item.php @@ -3,9 +3,7 @@ * Cart Item */ -namespace Omnipay\Common; - -use Symfony\Component\HttpFoundation\ParameterBag; +namespace League\Omnipay\Common; /** * Cart Item @@ -14,55 +12,20 @@ * * @see ItemInterface */ -class Item implements ItemInterface +class Item implements ItemInterface, ParameterizedInterface { - /** - * @var \Symfony\Component\HttpFoundation\ParameterBag - */ - protected $parameters; + use HasParametersTrait; /** * Create a new item with the specified parameters * - * @param array|null $parameters An array of parameters to set on the new object + * @param array $parameters An array of parameters to set on the new object */ - public function __construct($parameters = null) + public function __construct(array $parameters = []) { $this->initialize($parameters); } - /** - * Initialize this item with the specified parameters - * - * @param array|null $parameters An array of parameters to set on this object - * @return $this Item - */ - public function initialize($parameters = null) - { - $this->parameters = new ParameterBag; - - Helper::initialize($this, $parameters); - - return $this; - } - - public function getParameters() - { - return $this->parameters->all(); - } - - protected function getParameter($key) - { - return $this->parameters->get($key); - } - - protected function setParameter($key, $value) - { - $this->parameters->set($key, $value); - - return $this; - } - /** * {@inheritDoc} */ diff --git a/src/Omnipay/Common/ItemBag.php b/src/ItemBag.php similarity index 98% rename from src/Omnipay/Common/ItemBag.php rename to src/ItemBag.php index f726c3b7..a0c77368 100644 --- a/src/Omnipay/Common/ItemBag.php +++ b/src/ItemBag.php @@ -3,7 +3,7 @@ * Cart Item Bag */ -namespace Omnipay\Common; +namespace League\Omnipay\Common; /** * Cart Item Bag diff --git a/src/Omnipay/Common/ItemInterface.php b/src/ItemInterface.php similarity index 93% rename from src/Omnipay/Common/ItemInterface.php rename to src/ItemInterface.php index 9380e65c..d295a7e8 100644 --- a/src/Omnipay/Common/ItemInterface.php +++ b/src/ItemInterface.php @@ -3,7 +3,7 @@ * Cart Item interface */ -namespace Omnipay\Common; +namespace League\Omnipay\Common; /** * Cart Item interface diff --git a/src/Locale.php b/src/Locale.php new file mode 100644 index 00000000..8741c704 --- /dev/null +++ b/src/Locale.php @@ -0,0 +1,89 @@ +primaryLanguage = strtolower($primaryLanguage); + $this->region = strtolower($region); + } + + /** + * Get the full locale + * + * @return string + */ + public function getLocale() + { + return $this->primaryLanguage . ($this->region ? '-' . $this->region : ''); + } + + /** + * Get the primary language + * + * @return string + */ + public function getPrimaryLanguage() + { + return $this->primaryLanguage; + } + + /** + * Get the region + * + * @return string + */ + public function getRegion() + { + return $this->region ?: null; + } + + /** + * Get the locale, based on a string + * + * @param string $locale + * @return static + */ + public static function parse($locale) + { + $primaryLanguage = $locale; + $region = null; + + $locale = str_replace('_', '-', $locale); + if (strpos($locale, '-') !== false) { + list($primaryLanguage, $region) = explode('-', $locale); + } + + return new static($primaryLanguage, $region); + } + + public function __toString() + { + return $this->getLocale(); + } +} diff --git a/src/Omnipay/Common/Message/AbstractRequest.php b/src/Message/AbstractRequest.php similarity index 74% rename from src/Omnipay/Common/Message/AbstractRequest.php rename to src/Message/AbstractRequest.php index 14d34ce1..b63cab65 100644 --- a/src/Omnipay/Common/Message/AbstractRequest.php +++ b/src/Message/AbstractRequest.php @@ -3,17 +3,23 @@ * 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; +namespace League\Omnipay\Common\Message; + +use League\Omnipay\Common\Amount; +use League\Omnipay\Common\AmountInterface; +use League\Omnipay\Common\HasParametersTrait; +use League\Omnipay\Common\Http\ClientInterface; +use League\Omnipay\Common\CreditCard; +use League\Omnipay\Common\Currency; +use League\Omnipay\Common\Customer; +use League\Omnipay\Common\Exception\InvalidRequestException; +use League\Omnipay\Common\Exception\RuntimeException; +use League\Omnipay\Common\Helper; +use League\Omnipay\Common\ItemBag; +use League\Omnipay\Common\Locale; +use League\Omnipay\Common\ParameterBag; +use League\Omnipay\Common\ParameterizedInterface; +use Psr\Http\Message\ServerRequestInterface; use InvalidArgumentException; /** @@ -29,9 +35,9 @@ * Example -- creating a request: * * - * class MyRequest extends \Omnipay\Common\Message\AbstractRequest {}; + * class MyRequest extends \League\Omnipay\Common\Message\AbstractRequest {}; * - * class MyGateway extends \Omnipay\Common\AbstractGateway { + * class MyGateway extends \League\Omnipay\Common\AbstractGateway { * function myRequest($parameters) { * $this->createRequest('MyRequest', $parameters); * } @@ -59,26 +65,26 @@ * @see RequestInterface * @see AbstractResponse */ -abstract class AbstractRequest implements RequestInterface +abstract class AbstractRequest implements RequestInterface, ParameterizedInterface { /** * The request parameters * - * @var \Symfony\Component\HttpFoundation\ParameterBag + * @var ParameterBag */ protected $parameters; /** * The request client. * - * @var \Guzzle\Http\ClientInterface + * @var ClientInterface */ protected $httpClient; /** * The HTTP request object. * - * @var \Symfony\Component\HttpFoundation\Request + * @var ServerRequestInterface */ protected $httpRequest; @@ -102,10 +108,10 @@ abstract class AbstractRequest implements RequestInterface /** * Create a new Request * - * @param ClientInterface $httpClient A Guzzle client to make API calls with - * @param HttpRequest $httpRequest A Symfony HTTP request object + * @param ClientInterface $httpClient A Http client to make API calls with + * @param ServerRequestInterface $httpRequest A HTTP request object */ - public function __construct(ClientInterface $httpClient, HttpRequest $httpRequest) + public function __construct(ClientInterface $httpClient, ServerRequestInterface $httpRequest) { $this->httpClient = $httpClient; $this->httpRequest = $httpRequest; @@ -122,7 +128,7 @@ public function __construct(ClientInterface $httpClient, HttpRequest $httpReques * @return $this * @throws RuntimeException */ - public function initialize(array $parameters = array()) + public function initialize(array $parameters = []) { if (null !== $this->response) { throw new RuntimeException('Request cannot be modified after it has been sent!'); @@ -130,7 +136,7 @@ public function initialize(array $parameters = array()) $this->parameters = new ParameterBag; - Helper::initialize($this, $parameters); + Helper::initializeParameters($this, $parameters); return $this; } @@ -151,7 +157,7 @@ public function getParameters() * @param string $key The parameter key * @return mixed */ - protected function getParameter($key) + public function getParameter($key) { return $this->parameters->get($key); } @@ -164,14 +170,12 @@ protected function getParameter($key) * @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) + public 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; } @@ -215,6 +219,31 @@ public function validate() } } + /** + * Get the customer. + * + * @return Customer + */ + public function getCustomer() + { + return $this->getParameter('customer'); + } + + /** + * Sets the customer. + * + * @param Customer $value + * @return AbstractRequest Provides a fluent interface + */ + public function setCustomer($value) + { + if ($value && !$value instanceof Customer) { + $value = new Customer($value); + } + + return $this->setParameter('customer', $value); + } + /** * Get the card. * @@ -283,72 +312,64 @@ public function setCardReference($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. + * @return string */ + protected function getCurrency() + { + return strtoupper($this->getParameter('currency')); + } - public function toFloat($value) + /** + * @param string $value + * @return $this + */ + public function setCurrency($value) { - try { - return Helper::toFloat($value); - } catch (InvalidArgumentException $e) { - // Throw old exception for legacy implementations. - throw new InvalidRequestException($e->getMessage(), $e->getCode(), $e); - } + return $this->setParameter('currency', $value); } /** - * Validates and returns the formated amount. + * Validates and returns amount as integer. * * @throws InvalidRequestException on any validation failure. - * @return string The amount formatted to the correct number of decimal places for the selected currency. + * @return Amount The amount as decimal */ 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).' - ); - }; - } + if (!$amount instanceof AmountInterface) { + // Default currency when none set + $currency = $this->getCurrency(); - $amount = $this->toFloat($amount); + if ($currency == null) { + throw new InvalidRequestException('A currency is required.'); + } + + $amount = Amount::fromDecimal($amount, $currency); + } // Check for a negative amount. - if (!$this->negativeAmountAllowed && $amount < 0) { + if (!$this->negativeAmountAllowed && $amount->isNegative()) { throw new InvalidRequestException('A negative amount is not allowed.'); } // Check for a zero amount. - if (!$this->zeroAmountAllowed && $amount === 0.0) { + if (!$this->zeroAmountAllowed && $amount->isZero()) { 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); + return $amount; } } /** * Sets the payment amount. * - * @param string $value + * @param string|AmountInterface $value * @return AbstractRequest Provides a fluent interface + * @throws InvalidRequestException */ public function setAmount($value) { @@ -356,83 +377,33 @@ public function setAmount($value) } /** - * Get the payment amount as an integer. + * Get the request locale. * - * @return integer + * @return Locale */ - public function getAmountInteger() + public function getLocale() { - return (int) round($this->getAmount() * $this->getCurrencyDecimalFactor()); + return $this->getParameter('locale'); } /** - * Get the payment currency code. + * Set the request Locale * - * @return string - */ - public function getCurrency() - { - return $this->getParameter('currency'); - } - - /** - * Sets the payment currency code. - * - * @param string $value + * @param string|Locale $value + * @throws InvalidRequestException * @return AbstractRequest Provides a fluent interface */ - public function setCurrency($value) + public function setLocale($value) { - if ($value !== null) { - $value = strtoupper($value); + if (is_string($value)) { + $value = Locale::parse($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(); + if (! $value instanceof Locale) { + throw new InvalidRequestException('A valid Locale is required'); } - } - /** - * 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(), - '.', - '' - ); + return $this->setParameter('locale', $value); } /** @@ -691,4 +662,17 @@ public function getResponse() return $this->response; } + + /** + * Get a query parameter + * + * @param $key + * @param null $default + * @return mixed + */ + public function query($key, $default = null) + { + $params = $this->httpRequest->getQueryParams(); + return isset($params[$key]) ? $params[$key] : $default; + } } diff --git a/src/Omnipay/Common/Message/AbstractResponse.php b/src/Message/AbstractResponse.php similarity index 78% rename from src/Omnipay/Common/Message/AbstractResponse.php rename to src/Message/AbstractResponse.php index 27b63be5..2ab71216 100644 --- a/src/Omnipay/Common/Message/AbstractResponse.php +++ b/src/Message/AbstractResponse.php @@ -3,11 +3,14 @@ * Abstract Response */ -namespace Omnipay\Common\Message; +namespace League\Omnipay\Common\Message; -use Omnipay\Common\Exception\RuntimeException; -use Symfony\Component\HttpFoundation\RedirectResponse as HttpRedirectResponse; -use Symfony\Component\HttpFoundation\Response as HttpResponse; +use League\Omnipay\Common\AmountInterface; +use League\Omnipay\Common\Exception\RuntimeException; +use League\Omnipay\Common\Http\Factory; +use Psr\Http\Message\ResponseInterface as HttpResponseInterface; +use Zend\Diactoros\Response\HtmlResponse; +use Zend\Diactoros\Response\RedirectResponse; /** * Abstract Response @@ -68,13 +71,30 @@ public function getRequest() } /** - * Is the response successful? + * Is the response completed? + * + * @return boolean + */ + abstract public function isCompleted(); + + /** + * Get the response status + * + * @return string + */ + public function getStatus() + { + return ResponseInterface::STATUS_UNDEFINED; + } + + /** + * Is the response pending? * * @return boolean */ public function isPending() { - return false; + return $this->getStatus() === ResponseInterface::STATUS_PENDING; } /** @@ -104,7 +124,17 @@ public function isTransparentRedirect() */ public function isCancelled() { - return false; + return $this->getStatus() === ResponseInterface::STATUS_CANCELLED; + } + + /** + * Get the amount of the transaction if returned by the Gateway + * + * @return null|AmountInterface + */ + public function getAmount() + { + return null; } /** @@ -174,7 +204,7 @@ public function redirect() } /** - * @return HttpRedirectResponse + * @return HttpResponseInterface */ public function getRedirectResponse() { @@ -182,8 +212,11 @@ public function getRedirectResponse() throw new RuntimeException('This response does not support redirection.'); } + /** @var $this RedirectResponseInterface */ if ('GET' === $this->getRedirectMethod()) { - return HttpRedirectResponse::create($this->getRedirectUrl()); + return Factory::createResponse(302, [ + 'location' => $this->getRedirectUrl(), + ]); } elseif ('POST' === $this->getRedirectMethod()) { $hiddenFields = ''; foreach ($this->getRedirectData() as $key => $value) { @@ -216,7 +249,9 @@ public function getRedirectResponse() $hiddenFields ); - return HttpResponse::create($output); + return Factory::createResponse(200, [ + 'content-type' => 'text/html', + ], $output); } throw new RuntimeException('Invalid redirect method "'.$this->getRedirectMethod().'".'); diff --git a/src/Omnipay/Common/Message/FetchIssuersResponseInterface.php b/src/Message/FetchIssuersResponseInterface.php similarity index 85% rename from src/Omnipay/Common/Message/FetchIssuersResponseInterface.php rename to src/Message/FetchIssuersResponseInterface.php index e86d6a4a..939b6f81 100644 --- a/src/Omnipay/Common/Message/FetchIssuersResponseInterface.php +++ b/src/Message/FetchIssuersResponseInterface.php @@ -3,7 +3,7 @@ * Fetch Issuers Response interface */ -namespace Omnipay\Common\Message; +namespace League\Omnipay\Common\Message; /** * Fetch Issuers Response interface @@ -16,7 +16,7 @@ * card issuer. * * @see ResponseInterface - * @see Omnipay\Common\Issuer + * @see League\Omnipay\Common\Issuer */ interface FetchIssuersResponseInterface extends ResponseInterface { @@ -25,7 +25,7 @@ interface FetchIssuersResponseInterface extends ResponseInterface * * These represent banks which the user must choose between. * - * @return \Omnipay\Common\Issuer[] + * @return \League\Omnipay\Common\Issuer[] */ public function getIssuers(); } diff --git a/src/Omnipay/Common/Message/FetchPaymentMethodsResponseInterface.php b/src/Message/FetchPaymentMethodsResponseInterface.php similarity index 84% rename from src/Omnipay/Common/Message/FetchPaymentMethodsResponseInterface.php rename to src/Message/FetchPaymentMethodsResponseInterface.php index 57ca8800..46c289cb 100644 --- a/src/Omnipay/Common/Message/FetchPaymentMethodsResponseInterface.php +++ b/src/Message/FetchPaymentMethodsResponseInterface.php @@ -3,7 +3,7 @@ * Fetch Payment Methods Response interface */ -namespace Omnipay\Common\Message; +namespace League\Omnipay\Common\Message; /** * Fetch Payment Methods Response interface @@ -16,7 +16,7 @@ * payment method. * * @see ResponseInterface - * @see Omnipay\Common\PaymentMethod + * @see League\Omnipay\Common\PaymentMethod */ interface FetchPaymentMethodsResponseInterface extends ResponseInterface { @@ -25,7 +25,7 @@ interface FetchPaymentMethodsResponseInterface extends ResponseInterface * * These represent separate payment methods which the user must choose between. * - * @return \Omnipay\Common\PaymentMethod[] + * @return \League\Omnipay\Common\PaymentMethod[] */ public function getPaymentMethods(); } diff --git a/src/Omnipay/Common/Message/MessageInterface.php b/src/Message/MessageInterface.php similarity index 83% rename from src/Omnipay/Common/Message/MessageInterface.php rename to src/Message/MessageInterface.php index f472e2d0..b5d33979 100644 --- a/src/Omnipay/Common/Message/MessageInterface.php +++ b/src/Message/MessageInterface.php @@ -3,13 +3,13 @@ * Message Interface */ -namespace Omnipay\Common\Message; +namespace League\Omnipay\Common\Message; /** * Message Interface * * This interface class defines the standard functions that any Omnipay message - * interface needs to be able to provide. + * interface needs to be able to provide. */ interface MessageInterface { diff --git a/src/Omnipay/Common/Message/NotificationInterface.php b/src/Message/NotificationInterface.php similarity index 95% rename from src/Omnipay/Common/Message/NotificationInterface.php rename to src/Message/NotificationInterface.php index 14d53a90..731976d4 100755 --- a/src/Omnipay/Common/Message/NotificationInterface.php +++ b/src/Message/NotificationInterface.php @@ -1,6 +1,6 @@ - * // Define credit card parameters, which should look like this - * $parameters = [ - * 'firstName' => 'Bobby', - * 'lastName' => 'Tables', - * 'number' => '4444333322221111', - * 'cvv' => '123', - * 'expiryMonth' => '12', - * 'expiryYear' => '2017', - * 'email' => 'testcard@gmail.com', - * ]; - * - * // Create a credit card object - * $card = new CreditCard($parameters); - * - * - * The full list of card attributes that may be set via the parameter to - * *new* is as follows: - * - * * title - * * firstName - * * lastName - * * name - * * company - * * address1 - * * address2 - * * city - * * postcode - * * state - * * country - * * phone - * * phoneExtension - * * fax - * * number - * * expiryMonth - * * expiryYear - * * startMonth - * * startYear - * * cvv - * * tracks - * * issueNumber - * * billingTitle - * * billingName - * * billingFirstName - * * billingLastName - * * billingCompany - * * billingAddress1 - * * billingAddress2 - * * billingCity - * * billingPostcode - * * billingState - * * billingCountry - * * billingPhone - * * billingFax - * * shippingTitle - * * shippingName - * * shippingFirstName - * * shippingLastName - * * shippingCompany - * * shippingAddress1 - * * shippingAddress2 - * * shippingCity - * * shippingPostcode - * * shippingState - * * shippingCountry - * * shippingPhone - * * shippingFax - * * email - * * birthday - * * gender - * - * If any unknown parameters are passed in, they will be ignored. No error is thrown. - */ -class CreditCard -{ - const BRAND_VISA = 'visa'; - const BRAND_MASTERCARD = 'mastercard'; - const BRAND_DISCOVER = 'discover'; - const BRAND_AMEX = 'amex'; - const BRAND_DINERS_CLUB = 'diners_club'; - const BRAND_JCB = 'jcb'; - const BRAND_SWITCH = 'switch'; - const BRAND_SOLO = 'solo'; - const BRAND_DANKORT = 'dankort'; - const BRAND_MAESTRO = 'maestro'; - const BRAND_FORBRUGSFORENINGEN = 'forbrugsforeningen'; - const BRAND_LASER = 'laser'; - - /** - * All known/supported card brands, and a regular expression to match them. - * - * The order of the card brands is important, as some of the regular expressions overlap. - * - * Note: The fact that a particular card brand has been added to this array does not imply - * that a selected gateway will support the card. - * - * @link https://github.com/Shopify/active_merchant/blob/master/lib/active_merchant/billing/credit_card_methods.rb - * @var array - */ - const REGEX_MASTERCARD = '/^(5[1-5]\d{4}|677189)\d{10}$|^2(?:2(?:2[1-9]|[3-9]\d)|[3-6]\d\d|7(?:[01]\d|20))\d{12}$/'; - protected $supported_cards = array( - self::BRAND_VISA => '/^4\d{12}(\d{3})?$/', - self::BRAND_MASTERCARD => self::REGEX_MASTERCARD, - self::BRAND_DISCOVER => '/^(6011|65\d{2}|64[4-9]\d)\d{12}|(62\d{14})$/', - self::BRAND_AMEX => '/^3[47]\d{13}$/', - self::BRAND_DINERS_CLUB => '/^3(0[0-5]|[68]\d)\d{11}$/', - self::BRAND_JCB => '/^35(28|29|[3-8]\d)\d{12}$/', - self::BRAND_SWITCH => '/^6759\d{12}(\d{2,3})?$/', - self::BRAND_SOLO => '/^6767\d{12}(\d{2,3})?$/', - self::BRAND_DANKORT => '/^5019\d{12}$/', - self::BRAND_MAESTRO => '/^(5[06-8]|6\d)\d{10,17}$/', - self::BRAND_FORBRUGSFORENINGEN => '/^600722\d{10}$/', - self::BRAND_LASER => '/^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/', - ); - - /** - * Internal storage of all of the card parameters. - * - * @var \Symfony\Component\HttpFoundation\ParameterBag - */ - protected $parameters; - - /** - * Create a new CreditCard object using the specified parameters - * - * @param array $parameters An array of parameters to set on the new object - */ - public function __construct($parameters = null) - { - $this->initialize($parameters); - } - - /** - * All known/supported card brands, and a regular expression to match them. - * - * Note: The fact that this class knows about a particular card brand does not imply - * that your gateway supports it. - * - * @see self::$supported_cards - * @return array - */ - public function getSupportedBrands() - { - return $this->supported_cards; - } - - /** - * Set a custom supported card brand with a regular expression to match it. - * - * Note: The fact that a particular card is known does not imply that your - * gateway supports it. - * - * Set $add_to_front to true if the key should be added to the front of the array - * - * @param string $name The name of the new supported brand. - * @param string $expression The regular expression to check if a card is supported. - * @return boolean success - */ - public function addSupportedBrand($name, $expression) - { - $known_brands = array_keys($this->supported_cards); - - if (in_array($name, $known_brands)) { - return false; - } - - $this->supported_cards[$name] = $expression; - return true; - } - - /** - * Initialize the object with parameters. - * - * If any unknown parameters passed, they will be ignored. - * - * @param array $parameters An associative array of parameters - * @return CreditCard provides a fluent interface. - */ - public function initialize($parameters = null) - { - $this->parameters = new ParameterBag; - - Helper::initialize($this, $parameters); - - return $this; - } - - /** - * Get all parameters. - * - * @return array An associative array of parameters. - */ - public function getParameters() - { - return $this->parameters->all(); - } - - /** - * Get one parameter. - * - * @return mixed A single parameter value. - */ - protected function getParameter($key) - { - return $this->parameters->get($key); - } - - /** - * Set one parameter. - * - * @param string $key Parameter key - * @param mixed $value Parameter value - * @return CreditCard provides a fluent interface. - */ - protected function setParameter($key, $value) - { - $this->parameters->set($key, $value); - - return $this; - } - - /** - * Set the credit card year. - * - * The input value is normalised to a 4 digit number. - * - * @param string $key Parameter key, e.g. 'expiryYear' - * @param mixed $value Parameter value - * @return CreditCard provides a fluent interface. - */ - protected function setYearParameter($key, $value) - { - // normalize year to four digits - if (null === $value || '' === $value) { - $value = null; - } else { - $value = (int) gmdate('Y', gmmktime(0, 0, 0, 1, 1, (int) $value)); - } - - return $this->setParameter($key, $value); - } - - /** - * Validate this credit card. If the card is invalid, InvalidCreditCardException is thrown. - * - * This method is called internally by gateways to avoid wasting time with an API call - * when the credit card is clearly invalid. - * - * Generally if you want to validate the credit card yourself with custom error - * messages, you should use your framework's validation library, not this method. - * - * @throws InvalidCreditCardException - * @return void - */ - public function validate() - { - foreach (array('number', 'expiryMonth', 'expiryYear') as $key) { - if (!$this->getParameter($key)) { - throw new InvalidCreditCardException("The $key parameter is required"); - } - } - - if ($this->getExpiryDate('Ym') < gmdate('Ym')) { - throw new InvalidCreditCardException('Card has expired'); - } - - if (!Helper::validateLuhn($this->getNumber())) { - throw new InvalidCreditCardException('Card number is invalid'); - } - - if (!is_null($this->getNumber()) && !preg_match('/^\d{12,19}$/i', $this->getNumber())) { - throw new InvalidCreditCardException('Card number should have 12 to 19 digits'); - } - } - - /** - * Get Card Title. - * - * @return string - */ - public function getTitle() - { - return $this->getBillingTitle(); - } - - /** - * Set Card Title. - * - * @param string $value Parameter value - * @return CreditCard provides a fluent interface. - */ - public function setTitle($value) - { - $this->setBillingTitle($value); - $this->setShippingTitle($value); - - return $this; - } - - /** - * Get Card First Name. - * - * @return string - */ - public function getFirstName() - { - return $this->getBillingFirstName(); - } - - /** - * Set Card First Name (Billing and Shipping). - * - * @param string $value Parameter value - * @return CreditCard provides a fluent interface. - */ - public function setFirstName($value) - { - $this->setBillingFirstName($value); - $this->setShippingFirstName($value); - - return $this; - } - - /** - * Get Card Last Name. - * - * @return string - */ - public function getLastName() - { - return $this->getBillingLastName(); - } - - /** - * Set Card Last Name (Billing and Shipping). - * - * @param string $value Parameter value - * @return CreditCard provides a fluent interface. - */ - public function setLastName($value) - { - $this->setBillingLastName($value); - $this->setShippingLastName($value); - - return $this; - } - - /** - * Get Card Name. - * - * @return string - */ - public function getName() - { - return $this->getBillingName(); - } - - /** - * Set Card Name (Billing and Shipping). - * - * @param string $value Parameter value - * @return CreditCard provides a fluent interface. - */ - public function setName($value) - { - $this->setBillingName($value); - $this->setShippingName($value); - - return $this; - } - - /** - * Get Card Number. - * - * @return string - */ - public function getNumber() - { - return $this->getParameter('number'); - } - - /** - * Set Card Number - * - * Non-numeric characters are stripped out of the card number, so - * it's safe to pass in strings such as "4444-3333 2222 1111" etc. - * - * @param string $value Parameter value - * @return CreditCard provides a fluent interface. - */ - public function setNumber($value) - { - // strip non-numeric characters - return $this->setParameter('number', preg_replace('/\D/', '', $value)); - } - - /** - * Get the last 4 digits of the card number. - * - * @return string - */ - public function getNumberLastFour() - { - return substr($this->getNumber(), -4, 4) ?: null; - } - - /** - * Returns a masked credit card number with only the last 4 chars visible - * - * @param string $mask Character to use in place of numbers - * @return string - */ - public function getNumberMasked($mask = 'X') - { - $maskLength = strlen($this->getNumber()) - 4; - - return str_repeat($mask, $maskLength) . $this->getNumberLastFour(); - } - - /** - * Credit Card Brand - * - * Iterates through known/supported card brands to determine the brand of this card - * - * @return string - */ - public function getBrand() - { - foreach ($this->getSupportedBrands() as $brand => $val) { - if (preg_match($val, $this->getNumber())) { - return $brand; - } - } - } - - /** - * Get the card expiry month. - * - * @return string - */ - public function getExpiryMonth() - { - return $this->getParameter('expiryMonth'); - } - - /** - * Sets the card expiry month. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setExpiryMonth($value) - { - return $this->setParameter('expiryMonth', (int) $value); - } - - /** - * Get the card expiry year. - * - * @return string - */ - public function getExpiryYear() - { - return $this->getParameter('expiryYear'); - } - - /** - * Sets the card expiry year. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setExpiryYear($value) - { - return $this->setYearParameter('expiryYear', $value); - } - - /** - * Get the card expiry date, using the specified date format string. - * - * @param string $format - * - * @return string - */ - public function getExpiryDate($format) - { - return gmdate($format, gmmktime(0, 0, 0, $this->getExpiryMonth(), 1, $this->getExpiryYear())); - } - - /** - * Get the card start month. - * - * @return string - */ - public function getStartMonth() - { - return $this->getParameter('startMonth'); - } - - /** - * Sets the card start month. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setStartMonth($value) - { - return $this->setParameter('startMonth', (int) $value); - } - - /** - * Get the card start year. - * - * @return string - */ - public function getStartYear() - { - return $this->getParameter('startYear'); - } - - /** - * Sets the card start year. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setStartYear($value) - { - return $this->setYearParameter('startYear', $value); - } - - /** - * Get the card start date, using the specified date format string - * - * @param string $format - * - * @return string - */ - public function getStartDate($format) - { - return gmdate($format, gmmktime(0, 0, 0, $this->getStartMonth(), 1, $this->getStartYear())); - } - - /** - * Get the card CVV. - * - * @return string - */ - public function getCvv() - { - return $this->getParameter('cvv'); - } - - /** - * Sets the card CVV. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setCvv($value) - { - return $this->setParameter('cvv', $value); - } - - /** - * Get raw data for all tracks on the credit card magnetic strip. - * - * @return string - */ - public function getTracks() - { - return $this->getParameter('tracks'); - } - - /** - * Get raw data for track 1 on the credit card magnetic strip. - * - * @return string - */ - public function getTrack1() - { - $track1 = null; - if ($tracks = $this->getTracks()) { - $pattern = '/\%B\d{1,19}\^.{2,26}\^\d{4}\d*\?/'; - if (preg_match($pattern, $tracks, $matches) === 1) { - $track1 = $matches[0]; - } - } - return $track1; - } - - /** - * Get raw data for track 2 on the credit card magnetic strip. - * - * @return string - */ - public function getTrack2() - { - $track2 = null; - if ($tracks = $this->getTracks()) { - $pattern = '/;\d{1,19}=\d{4}\d*\?/'; - if (preg_match($pattern, $tracks, $matches) === 1) { - $track2 = $matches[0]; - } - } - return $track2; - } - - /** - * Sets raw data from all tracks on the credit card magnetic strip. Used by gateways that support card-present - * transactions. - * - * @param $value - * @return CreditCard - */ - public function setTracks($value) - { - return $this->setParameter('tracks', $value); - } - - /** - * Get the card issue number. - * - * @return string - */ - public function getIssueNumber() - { - return $this->getParameter('issueNumber'); - } - - /** - * Sets the card issue number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setIssueNumber($value) - { - return $this->setParameter('issueNumber', $value); - } - - /** - * Get the card billing title. - * - * @return string - */ - public function getBillingTitle() - { - return $this->getParameter('billingTitle'); - } - - /** - * Sets the card billing title. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingTitle($value) - { - return $this->setParameter('billingTitle', $value); - } - - /** - * Get the card billing name. - * - * @return string - */ - public function getBillingName() - { - return trim($this->getBillingFirstName() . ' ' . $this->getBillingLastName()); - } - - /** - * Sets the card billing name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingName($value) - { - $names = explode(' ', $value, 2); - $this->setBillingFirstName($names[0]); - $this->setBillingLastName(isset($names[1]) ? $names[1] : null); - - return $this; - } - - /** - * Get the first part of the card billing name. - * - * @return string - */ - public function getBillingFirstName() - { - return $this->getParameter('billingFirstName'); - } - - /** - * Sets the first part of the card billing name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingFirstName($value) - { - return $this->setParameter('billingFirstName', $value); - } - - /** - * Get the last part of the card billing name. - * - * @return string - */ - public function getBillingLastName() - { - return $this->getParameter('billingLastName'); - } - - /** - * Sets the last part of the card billing name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingLastName($value) - { - return $this->setParameter('billingLastName', $value); - } - - /** - * Get the billing company name. - * - * @return string - */ - public function getBillingCompany() - { - return $this->getParameter('billingCompany'); - } - - /** - * Sets the billing company name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingCompany($value) - { - return $this->setParameter('billingCompany', $value); - } - - /** - * Get the billing address, line 1. - * - * @return string - */ - public function getBillingAddress1() - { - return $this->getParameter('billingAddress1'); - } - - /** - * Sets the billing address, line 1. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingAddress1($value) - { - return $this->setParameter('billingAddress1', $value); - } - - /** - * Get the billing address, line 2. - * - * @return string - */ - public function getBillingAddress2() - { - return $this->getParameter('billingAddress2'); - } - - /** - * Sets the billing address, line 2. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingAddress2($value) - { - return $this->setParameter('billingAddress2', $value); - } - - /** - * Get the billing city. - * - * @return string - */ - public function getBillingCity() - { - return $this->getParameter('billingCity'); - } - - /** - * Sets billing city. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingCity($value) - { - return $this->setParameter('billingCity', $value); - } - - /** - * Get the billing postcode. - * - * @return string - */ - public function getBillingPostcode() - { - return $this->getParameter('billingPostcode'); - } - - /** - * Sets the billing postcode. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingPostcode($value) - { - return $this->setParameter('billingPostcode', $value); - } - - /** - * Get the billing state. - * - * @return string - */ - public function getBillingState() - { - return $this->getParameter('billingState'); - } - - /** - * Sets the billing state. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingState($value) - { - return $this->setParameter('billingState', $value); - } - - /** - * Get the billing country name. - * - * @return string - */ - public function getBillingCountry() - { - return $this->getParameter('billingCountry'); - } - - /** - * Sets the billing country name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingCountry($value) - { - return $this->setParameter('billingCountry', $value); - } - - /** - * Get the billing phone number. - * - * @return string - */ - public function getBillingPhone() - { - return $this->getParameter('billingPhone'); - } - - /** - * Sets the billing phone number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingPhone($value) - { - return $this->setParameter('billingPhone', $value); - } - - /** - * Get the billing phone number extension. - * - * @return string - */ - public function getBillingPhoneExtension() - { - return $this->getParameter('billingPhoneExtension'); - } - - /** - * Sets the billing phone number extension. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingPhoneExtension($value) - { - return $this->setParameter('billingPhoneExtension', $value); - } - - /** - * Get the billing fax number. - * - * @return string - */ - public function getBillingFax() - { - return $this->getParameter('billingFax'); - } - - /** - * Sets the billing fax number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBillingFax($value) - { - return $this->setParameter('billingFax', $value); - } - - /** - * Get the title of the card shipping name. - * - * @return string - */ - public function getShippingTitle() - { - return $this->getParameter('shippingTitle'); - } - - /** - * Sets the title of the card shipping name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingTitle($value) - { - return $this->setParameter('shippingTitle', $value); - } - - /** - * Get the card shipping name. - * - * @return string - */ - public function getShippingName() - { - return trim($this->getShippingFirstName() . ' ' . $this->getShippingLastName()); - } - - /** - * Sets the card shipping name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingName($value) - { - $names = explode(' ', $value, 2); - $this->setShippingFirstName($names[0]); - $this->setShippingLastName(isset($names[1]) ? $names[1] : null); - - return $this; - } - - /** - * Get the first part of the card shipping name. - * - * @return string - */ - public function getShippingFirstName() - { - return $this->getParameter('shippingFirstName'); - } - - /** - * Sets the first part of the card shipping name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingFirstName($value) - { - return $this->setParameter('shippingFirstName', $value); - } - - /** - * Get the last part of the card shipping name. - * - * @return string - */ - public function getShippingLastName() - { - return $this->getParameter('shippingLastName'); - } - - /** - * Sets the last part of the card shipping name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingLastName($value) - { - return $this->setParameter('shippingLastName', $value); - } - - /** - * Get the shipping company name. - * - * @return string - */ - public function getShippingCompany() - { - return $this->getParameter('shippingCompany'); - } - - /** - * Sets the shipping company name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingCompany($value) - { - return $this->setParameter('shippingCompany', $value); - } - - /** - * Get the shipping address, line 1. - * - * @return string - */ - public function getShippingAddress1() - { - return $this->getParameter('shippingAddress1'); - } - - /** - * Sets the shipping address, line 1. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingAddress1($value) - { - return $this->setParameter('shippingAddress1', $value); - } - - /** - * Get the shipping address, line 2. - * - * @return string - */ - public function getShippingAddress2() - { - return $this->getParameter('shippingAddress2'); - } - - /** - * Sets the shipping address, line 2. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingAddress2($value) - { - return $this->setParameter('shippingAddress2', $value); - } - - /** - * Get the shipping city. - * - * @return string - */ - public function getShippingCity() - { - return $this->getParameter('shippingCity'); - } - - /** - * Sets the shipping city. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingCity($value) - { - return $this->setParameter('shippingCity', $value); - } - - /** - * Get the shipping postcode. - * - * @return string - */ - public function getShippingPostcode() - { - return $this->getParameter('shippingPostcode'); - } - - /** - * Sets the shipping postcode. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingPostcode($value) - { - return $this->setParameter('shippingPostcode', $value); - } - - /** - * Get the shipping state. - * - * @return string - */ - public function getShippingState() - { - return $this->getParameter('shippingState'); - } - - /** - * Sets the shipping state. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingState($value) - { - return $this->setParameter('shippingState', $value); - } - - /** - * Get the shipping country. - * - * @return string - */ - public function getShippingCountry() - { - return $this->getParameter('shippingCountry'); - } - - /** - * Sets the shipping country. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingCountry($value) - { - return $this->setParameter('shippingCountry', $value); - } - - /** - * Get the shipping phone number. - * - * @return string - */ - public function getShippingPhone() - { - return $this->getParameter('shippingPhone'); - } - - /** - * Sets the shipping phone number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingPhone($value) - { - return $this->setParameter('shippingPhone', $value); - } - - /** - * Get the shipping phone number extension. - * - * @return string - */ - public function getShippingPhoneExtension() - { - return $this->getParameter('shippingPhoneExtension'); - } - - /** - * Sets the shipping phone number extension. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingPhoneExtension($value) - { - return $this->setParameter('shippingPhoneExtension', $value); - } - - /** - * Get the shipping fax number. - * - * @return string - */ - public function getShippingFax() - { - return $this->getParameter('shippingFax'); - } - - /** - * Sets the shipping fax number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setShippingFax($value) - { - return $this->setParameter('shippingFax', $value); - } - - /** - * Get the billing address, line 1. - * - * @return string - */ - public function getAddress1() - { - return $this->getParameter('billingAddress1'); - } - - /** - * Sets the billing and shipping address, line 1. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setAddress1($value) - { - $this->setParameter('billingAddress1', $value); - $this->setParameter('shippingAddress1', $value); - - return $this; - } - - /** - * Get the billing address, line 2. - * - * @return string - */ - public function getAddress2() - { - return $this->getParameter('billingAddress2'); - } - - /** - * Sets the billing and shipping address, line 2. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setAddress2($value) - { - $this->setParameter('billingAddress2', $value); - $this->setParameter('shippingAddress2', $value); - - return $this; - } - - /** - * Get the billing city. - * - * @return string - */ - public function getCity() - { - return $this->getParameter('billingCity'); - } - - /** - * Sets the billing and shipping city. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setCity($value) - { - $this->setParameter('billingCity', $value); - $this->setParameter('shippingCity', $value); - - return $this; - } - - /** - * Get the billing postcode. - * - * @return string - */ - public function getPostcode() - { - return $this->getParameter('billingPostcode'); - } - - /** - * Sets the billing and shipping postcode. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setPostcode($value) - { - $this->setParameter('billingPostcode', $value); - $this->setParameter('shippingPostcode', $value); - - return $this; - } - - /** - * Get the billing state. - * - * @return string - */ - public function getState() - { - return $this->getParameter('billingState'); - } - - /** - * Sets the billing and shipping state. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setState($value) - { - $this->setParameter('billingState', $value); - $this->setParameter('shippingState', $value); - - return $this; - } - - /** - * Get the billing country. - * - * @return string - */ - public function getCountry() - { - return $this->getParameter('billingCountry'); - } - - /** - * Sets the billing and shipping country. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setCountry($value) - { - $this->setParameter('billingCountry', $value); - $this->setParameter('shippingCountry', $value); - - return $this; - } - - /** - * Get the billing phone number. - * - * @return string - */ - public function getPhone() - { - return $this->getParameter('billingPhone'); - } - - /** - * Sets the billing and shipping phone number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setPhone($value) - { - $this->setParameter('billingPhone', $value); - $this->setParameter('shippingPhone', $value); - - return $this; - } - - /** - * Get the billing phone number extension. - * - * @return string - */ - public function getPhoneExtension() - { - return $this->getParameter('billingPhoneExtension'); - } - - /** - * Sets the billing and shipping phone number extension. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setPhoneExtension($value) - { - $this->setParameter('billingPhoneExtension', $value); - $this->setParameter('shippingPhoneExtension', $value); - - return $this; - } - - /** - * Get the billing fax number.. - * - * @return string - */ - public function getFax() - { - return $this->getParameter('billingFax'); - } - - /** - * Sets the billing and shipping fax number. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setFax($value) - { - $this->setParameter('billingFax', $value); - $this->setParameter('shippingFax', $value); - - return $this; - } - - /** - * Get the card billing company name. - * - * @return string - */ - public function getCompany() - { - return $this->getParameter('billingCompany'); - } - - /** - * Sets the billing and shipping company name. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setCompany($value) - { - $this->setParameter('billingCompany', $value); - $this->setParameter('shippingCompany', $value); - - return $this; - } - - /** - * Get the cardholder's email address. - * - * @return string - */ - public function getEmail() - { - return $this->getParameter('email'); - } - - /** - * Sets the cardholder's email address. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setEmail($value) - { - return $this->setParameter('email', $value); - } - - /** - * Get the cardholder's birthday. - * - * @return string - */ - public function getBirthday($format = 'Y-m-d') - { - $value = $this->getParameter('birthday'); - - return $value ? $value->format($format) : null; - } - - /** - * Sets the cardholder's birthday. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setBirthday($value) - { - if ($value) { - $value = new DateTime($value, new DateTimeZone('UTC')); - } else { - $value = null; - } - - return $this->setParameter('birthday', $value); - } - - /** - * Get the cardholder's gender. - * - * @return string - */ - public function getGender() - { - return $this->getParameter('gender'); - } - - /** - * Sets the cardholder's gender. - * - * @param string $value - * @return CreditCard provides a fluent interface. - */ - public function setGender($value) - { - return $this->setParameter('gender', $value); - } -} diff --git a/src/Omnipay/Common/Currency.php b/src/Omnipay/Common/Currency.php deleted file mode 100644 index 8b2a1a90..00000000 --- a/src/Omnipay/Common/Currency.php +++ /dev/null @@ -1,145 +0,0 @@ -code = $code; - $this->numeric = $numeric; - $this->decimals = $decimals; - } - - /** - * Get the three letter code for the currency - * - * @return string - */ - public function getCode() - { - return $this->code; - } - - /** - * Get the numeric code for this currency - * - * @return string - */ - public function getNumeric() - { - return $this->numeric; - } - - /** - * Get the number of decimal places for this currency - * - * @return int - */ - public function getDecimals() - { - return $this->decimals; - } - - /** - * Find a specific currency - * - * @param string $code The three letter currency code - * @return mixed A Currency object, or null if no currency was found - */ - public static function find($code) - { - $code = strtoupper($code); - $currencies = static::all(); - - if (isset($currencies[$code])) { - return new static($code, $currencies[$code]['numeric'], $currencies[$code]['decimals']); - } - } - - /** - * Get an array of all supported currencies - * - * @return array - */ - public static function all() - { - return array( - 'AED' => array('numeric' => '784', 'decimals' => 2), - 'ARS' => array('numeric' => '032', 'decimals' => 2), - 'AUD' => array('numeric' => '036', 'decimals' => 2), - 'BHD' => array('numeric' => '048', 'decimals' => 4), - 'BND' => array('numeric' => '096', 'decimals' => 2), - 'BOB' => array('numeric' => '068', 'decimals' => 2), - 'BRL' => array('numeric' => '986', 'decimals' => 2), - 'BTC' => array('numeric' => null, 'decimals' => 8), - 'CAD' => array('numeric' => '124', 'decimals' => 2), - 'CHF' => array('numeric' => '756', 'decimals' => 2), - 'CLP' => array('numeric' => '152', 'decimals' => 0), - 'CNY' => array('numeric' => '156', 'decimals' => 2), - 'COP' => array('numeric' => '170', 'decimals' => 2), - 'CRC' => array('numeric' => '188', 'decimals' => 2), - 'CZK' => array('numeric' => '203', 'decimals' => 2), - 'DKK' => array('numeric' => '208', 'decimals' => 2), - 'DOP' => array('numeric' => '214', 'decimals' => 2), - 'EUR' => array('numeric' => '978', 'decimals' => 2), - 'FJD' => array('numeric' => '242', 'decimals' => 2), - 'GBP' => array('numeric' => '826', 'decimals' => 2), - 'GTQ' => array('numeric' => '320', 'decimals' => 2), - 'HKD' => array('numeric' => '344', 'decimals' => 2), - 'HUF' => array('numeric' => '348', 'decimals' => 2), - 'ILS' => array('numeric' => '376', 'decimals' => 2), - 'INR' => array('numeric' => '356', 'decimals' => 2), - 'JPY' => array('numeric' => '392', 'decimals' => 0), - 'KES' => array('numeric' => '404', 'decimals' => 2), - 'KRW' => array('numeric' => '410', 'decimals' => 0), - 'LAK' => array('numeric' => '418', 'decimals' => 0), - 'LBP' => array('numeric' => '422', 'decimals' => 2), - 'MXN' => array('numeric' => '484', 'decimals' => 2), - 'MYR' => array('numeric' => '458', 'decimals' => 2), - 'NGN' => array('numeric' => '566', 'decimals' => 2), - 'NOK' => array('numeric' => '578', 'decimals' => 2), - 'NZD' => array('numeric' => '554', 'decimals' => 2), - 'OMR' => array('numeric' => '512', 'decimals' => 3), - 'PEN' => array('numeric' => '604', 'decimals' => 2), - 'PGK' => array('numeric' => '598', 'decimals' => 2), - 'PHP' => array('numeric' => '608', 'decimals' => 2), - 'PLN' => array('numeric' => '985', 'decimals' => 2), - 'PYG' => array('numeric' => '600', 'decimals' => 0), - 'QAR' => array('numeric' => '634', 'decimals' => 2), - 'RUB' => array('numeric' => '643', 'decimals' => 2), - 'SAR' => array('numeric' => '682', 'decimals' => 2), - 'SBD' => array('numeric' => '090', 'decimals' => 2), - 'SEK' => array('numeric' => '752', 'decimals' => 2), - 'SGD' => array('numeric' => '702', 'decimals' => 2), - 'THB' => array('numeric' => '764', 'decimals' => 2), - 'TOP' => array('numeric' => '776', 'decimals' => 2), - 'TRY' => array('numeric' => '949', 'decimals' => 2), - 'TWD' => array('numeric' => '901', 'decimals' => 2), - 'USD' => array('numeric' => '840', 'decimals' => 2), - 'UYU' => array('numeric' => '858', 'decimals' => 2), - 'VEF' => array('numeric' => '937', 'decimals' => 2), - 'VND' => array('numeric' => '704', 'decimals' => 0), - 'VUV' => array('numeric' => '548', 'decimals' => 0), - 'WST' => array('numeric' => '882', 'decimals' => 2), - 'ZAR' => array('numeric' => '710', 'decimals' => 2), - ); - } -} diff --git a/src/Omnipay/Common/GatewayFactory.php b/src/Omnipay/Common/GatewayFactory.php deleted file mode 100644 index f81ba667..00000000 --- a/src/Omnipay/Common/GatewayFactory.php +++ /dev/null @@ -1,122 +0,0 @@ - - * // Create a gateway for the PayPal ExpressGateway - * // (routes to GatewayFactory::create) - * $gateway = Omnipay::create('ExpressGateway'); - * - * - * @see Omnipay\Omnipay - */ -class GatewayFactory -{ - /** - * Internal storage for all available gateways - * - * @var array - */ - private $gateways = array(); - - /** - * All available gateways - * - * @return array An array of gateway names - */ - public function all() - { - return $this->gateways; - } - - /** - * Replace the list of available gateways - * - * @param array $gateways An array of gateway names - */ - public function replace(array $gateways) - { - $this->gateways = $gateways; - } - - /** - * Register a new gateway - * - * @param string $className Gateway name - */ - public function register($className) - { - if (!in_array($className, $this->gateways)) { - $this->gateways[] = $className; - } - } - - /** - * Automatically find and register all officially supported gateways - * - * @return array An array of gateway names - */ - public function find() - { - foreach ($this->getSupportedGateways() as $gateway) { - $class = Helper::getGatewayClassName($gateway); - if (class_exists($class)) { - $this->register($gateway); - } - } - - ksort($this->gateways); - - return $this->all(); - } - - /** - * Create a new gateway instance - * - * @param string $class Gateway name - * @param ClientInterface|null $httpClient A Guzzle HTTP Client implementation - * @param HttpRequest|null $httpRequest A Symfony HTTP Request implementation - * @throws RuntimeException If no such gateway is found - * @return GatewayInterface An object of class $class is created and returned - */ - public function create($class, ClientInterface $httpClient = null, HttpRequest $httpRequest = null) - { - $class = Helper::getGatewayClassName($class); - - if (!class_exists($class)) { - throw new RuntimeException("Class '$class' not found"); - } - - return new $class($httpClient, $httpRequest); - } - - /** - * Get a list of supported gateways which may be available - * - * @return array - */ - public function getSupportedGateways() - { - $package = json_decode(file_get_contents(__DIR__.'/../../../composer.json'), true); - - return $package['extra']['gateways']; - } -} diff --git a/src/Omnipay/Omnipay.php b/src/Omnipay/Omnipay.php deleted file mode 100644 index fb064e7e..00000000 --- a/src/Omnipay/Omnipay.php +++ /dev/null @@ -1,117 +0,0 @@ - - * // Create a gateway for the PayPal ExpressGateway - * // (routes to GatewayFactory::create) - * $gateway = Omnipay::create('ExpressGateway'); - * - * // Initialise the gateway - * $gateway->initialize(...); - * - * // Get the gateway parameters. - * $parameters = $gateway->getParameters(); - * - * // Create a credit card object - * $card = new CreditCard(...); - * - * // Do an authorisation transaction on the gateway - * if ($gateway->supportsAuthorize()) { - * $gateway->authorize(...); - * } else { - * throw new \Exception('Gateway does not support authorize()'); - * } - * - * - * For further code examples see the *omnipay-example* repository on github. - * - * @method static array all() - * @method static array replace(array $gateways) - * @method static string register(string $className) - * @method static array find() - * @method static array getSupportedGateways() - * @codingStandardsIgnoreStart - * @method static \Omnipay\Common\GatewayInterface create(string $class, \Guzzle\Http\ClientInterface $httpClient = null, \Symfony\Component\HttpFoundation\Request $httpRequest = null) - * @codingStandardsIgnoreEnd - * - * @see Omnipay\Common\GatewayFactory - */ -class Omnipay -{ - - /** - * Internal factory storage - * - * @var GatewayFactory - */ - private static $factory; - - /** - * Get the gateway factory - * - * Creates a new empty GatewayFactory if none has been set previously. - * - * @return GatewayFactory A GatewayFactory instance - */ - public static function getFactory() - { - if (is_null(static::$factory)) { - static::$factory = new GatewayFactory; - } - - return static::$factory; - } - - /** - * Set the gateway factory - * - * @param GatewayFactory $factory A GatewayFactory instance - */ - public static function setFactory(GatewayFactory $factory = null) - { - static::$factory = $factory; - } - - /** - * Static function call router. - * - * All other function calls to the Omnipay class are routed to the - * factory. e.g. Omnipay::getSupportedGateways(1, 2, 3, 4) is routed to the - * factory's getSupportedGateways method and passed the parameters 1, 2, 3, 4. - * - * Example: - * - * - * // Create a gateway for the PayPal ExpressGateway - * $gateway = Omnipay::create('ExpressGateway'); - * - * - * @see GatewayFactory - * - * @param string $method The factory method to invoke. - * @param array $parameters Parameters passed to the factory method. - * - * @return mixed - */ - public static function __callStatic($method, $parameters) - { - $factory = static::getFactory(); - - return call_user_func_array(array($factory, $method), $parameters); - } -} diff --git a/src/ParameterBag.php b/src/ParameterBag.php new file mode 100644 index 00000000..45d61919 --- /dev/null +++ b/src/ParameterBag.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace League\Omnipay\Common; + +/** + * ParameterBag is a container for key/value pairs. + * + * @author Fabien Potencier + */ +class ParameterBag implements \IteratorAggregate, \Countable +{ + /** + * Parameter storage. + * + * @var array + */ + protected $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters An array of parameters + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $key The key + * @param mixed $default The default value if the parameter key does not exist + * + * @return mixed + * + * @throws \InvalidArgumentException + */ + public function get($key, $default = null) + { + return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; + } + + /** + * Sets a parameter by name. + * + * @param string $key The key + * @param mixed $value The value + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key The key + * + * @return bool true if the parameter exists, false otherwise + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key The key + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * Returns the alphabetic characters of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getAlpha($key, $default = '') + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default)); + } + + /** + * Returns the alphabetic characters and digits of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getAlnum($key, $default = '') + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default)); + } + + /** + * Returns the digits of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getDigits($key, $default = '') + { + // we need to remove - and + because they're allowed in the filter + return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT)); + } + + /** + * Returns the parameter value converted to integer. + * + * @param string $key The parameter key + * @param int $default The default value if the parameter key does not exist + * + * @return int The filtered value + */ + public function getInt($key, $default = 0) + { + return (int) $this->get($key, $default); + } + + /** + * Returns the parameter value converted to boolean. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * + * @return bool The filtered value + */ + public function getBoolean($key, $default = false) + { + return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN); + } + + /** + * Filter key. + * + * @param string $key Key. + * @param mixed $default Default = null. + * @param int $filter FILTER_* constant. + * @param mixed $options Filter options. + * + * @see http://php.net/manual/en/function.filter-var.php + * + * @return mixed + */ + public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array()) + { + $value = $this->get($key, $default); + + // Always turn $options into an array - this allows filter_var option shortcuts. + if (!is_array($options) && $options) { + $options = array('flags' => $options); + } + + // Add a convenience check for arrays. + if (is_array($value) && !isset($options['flags'])) { + $options['flags'] = FILTER_REQUIRE_ARRAY; + } + + return filter_var($value, $filter, $options); + } + + /** + * Returns an iterator for parameters. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->parameters); + } + + /** + * Returns the number of parameters. + * + * @return int The number of parameters + */ + public function count() + { + return count($this->parameters); + } +} diff --git a/src/ParameterizedInterface.php b/src/ParameterizedInterface.php new file mode 100644 index 00000000..5c10e0c1 --- /dev/null +++ b/src/ParameterizedInterface.php @@ -0,0 +1,20 @@ +gateway = m::mock('\Omnipay\Common\AbstractGateway')->makePartial(); + $this->gateway = m::mock('\League\Omnipay\Common\AbstractGateway')->makePartial(); $this->gateway->initialize(); } public function testConstruct() { - $this->gateway = new AbstractGatewayTest_MockAbstractGateway; - $this->assertInstanceOf('\Guzzle\Http\Client', $this->gateway->getProtectedHttpClient()); - $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Request', $this->gateway->getProtectedHttpRequest()); + $httpClient = new GuzzleClient(new Client()); + $httpRequest = Factory::createServerRequestFromGlobals(); + $this->gateway = new AbstractGatewayTest_MockAbstractGateway($httpClient, $httpRequest); + $this->assertInstanceOf('\League\Omnipay\Common\Http\ClientInterface', $this->gateway->getProtectedHttpClient()); + $this->assertInstanceOf('\Psr\Http\Message\ServerRequestInterface', $this->gateway->getProtectedHttpRequest()); $this->assertSame(array(), $this->gateway->getParameters()); } @@ -56,7 +60,7 @@ public function testInitializeParameters() 'unknown' => '42', )); - $this->assertSame(array('currency' => 'USD'), $this->gateway->getParameters()); + $this->assertSame(array('currency' => 'USD', 'unknown' => '42'), $this->gateway->getParameters()); } public function testGetDefaultParameters() @@ -140,9 +144,11 @@ public function testSupportsAcceptNotification() public function testCreateRequest() { - $this->gateway = new AbstractGatewayTest_MockAbstractGateway; + $httpClient = new GuzzleClient(new Client()); + $httpRequest = Factory::createServerRequestFromGlobals(); + $this->gateway = new AbstractGatewayTest_MockAbstractGateway($httpClient, $httpRequest); $request = $this->gateway->callCreateRequest( - '\Omnipay\Common\AbstractGatewayTest_MockAbstractRequest', + '\League\Omnipay\Common\AbstractGatewayTest_MockAbstractRequest', array('currency' => 'THB') ); diff --git a/tests/AmountTest.php b/tests/AmountTest.php new file mode 100644 index 00000000..d2b59d14 --- /dev/null +++ b/tests/AmountTest.php @@ -0,0 +1,182 @@ +assertSame('1000', $amount->getInteger()); + } + + public function testConstructInteger() + { + $amount = new Amount(1000, 'USD'); + $this->assertSame('1000', $amount->getInteger()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructFloat() + { + new Amount(10.00, 'USD'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructDecimalString() + { + new Amount('10.00', 'USD'); + } + + public function testFromDecimal() + { + $amount = Amount::fromDecimal('10.00', 'USD'); + $this->assertSame('1000', $amount->getInteger()); + } + + public function testFromDecimalRounded() + { + $amount = Amount::fromDecimal('10', 'USD'); + $this->assertSame('1000', $amount->getInteger()); + + $amount = Amount::fromDecimal(10, 'USD'); + $this->assertSame('1000', $amount->getInteger()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDecimalInvalid() + { + Amount::fromDecimal('1,234.00', 'USD'); + } + + public function testCurrencyString() + { + $amount = new Amount(1000, 'EUR'); + $this->assertSame('EUR', $amount->getCurrency()->getCode()); + } + + public function testCurrencyObject() + { + $currency = Currency::find('EUR'); + $amount = new Amount(1000, $currency); + + $this->assertSame('EUR', $amount->getCurrency()->getCode()); + } + + public function testFormatted() + { + $amount = new Amount(1000, 'USD'); + + $this->assertSame('10.00', $amount->getFormatted()); + } + + public function testGetAmountNoDecimals() + { + $amount = new Amount(1366, 'JPY'); + + $this->assertSame('JPY', $amount->getCurrency()->getCode()); + $this->assertSame('1366', $amount->getInteger()); + $this->assertSame('1366', $amount->getFormatted()); + } + + public function testFromDecimalNoDecimals() + { + $amount = Amount::fromDecimal('10', 'JPY'); + $this->assertSame('10', $amount->getInteger()); + } + + public function testIsNegative() + { + $amount = new Amount(-1, 'USD'); + $this->assertTrue($amount->isNegative()); + + $amount = new Amount(0, 'USD'); + $this->assertFalse($amount->isNegative()); + + $amount = new Amount(1, 'USD'); + $this->assertFalse($amount->isNegative()); + } + + public function testIsZero() + { + $amount = new Amount(-1, 'USD'); + $this->assertFalse($amount->isZero()); + + $amount = new Amount(0, 'USD'); + $this->assertTrue($amount->isZero()); + + $amount = new Amount('0', 'USD'); + $this->assertTrue($amount->isZero()); + + $amount = Amount::fromDecimal('0.00', 'USD'); + $this->assertTrue($amount->isZero()); + + $amount = new Amount(1, 'USD'); + $this->assertFalse($amount->isZero()); + } + + public function testAmountNegativeDecimalString() + { + $amount = Amount::fromDecimal('-123.00', 'USD'); + + $this->assertEquals('-12300', $amount->getInteger()); + $this->assertTrue($amount->isNegative()); + } + + public function testAmountNegativeDecimalFloat() + { + $amount = Amount::fromDecimal(-123.00, 'USD'); + + $this->assertEquals('-12300', $amount->getInteger()); + $this->assertTrue($amount->isNegative()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testAmountThousandsSepThrowsException() + { + Amount::fromDecimal('1,234', 'USD'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testAmountInvalidFormatThrowsException() + { + new Amount('1.234', 'USD'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testAmountInvalidTypeThrowsException() + { + new Amount(true, 'USD'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testAmountNegativeStringThrowsException() + { + new Amount('-123.00', 'USD'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testAmountNegativeFloatThrowsException() + { + new Amount(-123.00, 'USD'); + } +} diff --git a/tests/CreditCardTest.php b/tests/CreditCardTest.php new file mode 100644 index 00000000..854fca24 --- /dev/null +++ b/tests/CreditCardTest.php @@ -0,0 +1,327 @@ +card = new CreditCard; + $this->card->setNumber('4111111111111111'); + $this->card->setExpiryMonth('4'); + $this->card->setExpiryYear(gmdate('Y')+2); + $this->card->setCvv('123'); + } + + public function testConstructWithParams() + { + $card = new CreditCard(array('number' => '4111111111111111')); + $this->assertSame('4111111111111111', $card->getNumber()); + } + + public function testInitializeWithParams() + { + $card = new CreditCard; + $card->initialize(array('number' => '4111111111111111')); + $this->assertSame('4111111111111111', $card->getNumber()); + } + + public function testGetParamters() + { + $card = new CreditCard(array( + 'number' => '1234', + 'expiryMonth' => 6, + 'expiryYear' => 2016, + )); + + $parameters = $card->getParameters(); + $this->assertSame('1234', $parameters['number']); + $this->assertSame(6, $parameters['expiryMonth']); + $this->assertSame(2016, $parameters['expiryYear']); + } + + public function testValidateFixture() + { + $this->card->validate(); + } + + /** + * @expectedException \League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage The number parameter is required + */ + public function testValidateNumberRequired() + { + $this->card->setNumber(null); + $this->card->validate(); + } + + /** + * @expectedException \League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage The expiryMonth parameter is required + */ + public function testValidateExpiryMonthRequired() + { + $this->card->setExpiryMonth(null); + $this->card->validate(); + } + + /** + * @expectedException \League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage The expiryYear parameter is required + */ + public function testValidateExpiryYearRequired() + { + $this->card->setExpiryYear(null); + $this->card->validate(); + } + + /** + * @expectedException \League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage Card has expired + */ + public function testValidateExpiryDate() + { + $this->card->setExpiryYear(gmdate('Y')-1); + $this->card->validate(); + } + + /** + * @expectedException \League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage Card number is invalid + */ + public function testValidateNumber() + { + $this->card->setNumber('4111111111111110'); + $this->card->validate(); + } + + public function testGetSupportedBrands() + { + $brands = $this->card->getSupportedBrands(); + $this->assertInternalType('array', $brands); + $this->assertArrayHasKey(CreditCard::BRAND_VISA, $brands); + } + + public function testCustomSupportedBrand() + { + $this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'); + $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands()); + } + + public function testCustomBrandWorks() + { + $this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'); + $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands()); + $this->card->setNumber('9111111111111110'); + $this->card->validate(); + $this->assertEquals('omniexpress', $this->card->getBrand()); + } + + public function testNumber() + { + $this->card->setNumber('4000000000000000'); + $this->assertEquals('4000000000000000', $this->card->getNumber()); + } + + public function testSetNumberStripsNonDigits() + { + $this->card->setNumber('4000 0000 00b00 0000'); + $this->assertEquals('4000000000000000', $this->card->getNumber()); + } + + public function testGetNumberLastFourNull() + { + $this->card->setNumber(null); + $this->assertNull($this->card->getNumberLastFour()); + } + + public function testGetNumberLastFour() + { + $this->card->setNumber('4000000000001234'); + $this->assertSame('1234', $this->card->getNumberLastFour()); + } + + public function testGetNumberLastFourNonDigits() + { + $this->card->setNumber('4000 0000 0000 12x34'); + $this->assertSame('1234', $this->card->getNumberLastFour()); + } + + public function testGetNumberMasked() + { + $this->card->setNumber('4000000000001234'); + + $this->assertSame('XXXXXXXXXXXX1234', $this->card->getNumberMasked()); + } + + public function testGetNumberMaskedNonDigits() + { + $this->card->setNumber('4000 0000 0000 12x34'); + + $this->assertSame('XXXXXXXXXXXX1234', $this->card->getNumberMasked()); + } + + public function testGetBrandDefault() + { + $card = new CreditCard; + $this->assertNull($card->getBrand()); + } + + public function testGetBrandVisa() + { + $card = new CreditCard(array('number' => '4242424242424242')); + $this->assertSame(CreditCard::BRAND_VISA, $card->getBrand()); + } + + public function testGetBrandMasterCard() + { + $card = new CreditCard(array('number' => '5555555555554444')); + $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand()); + } + + public function testGetBrandAmex() + { + $card = new CreditCard(array('number' => '378282246310005')); + $this->assertSame(CreditCard::BRAND_AMEX, $card->getBrand()); + } + + public function testGetBrandDiscover() + { + $card = new CreditCard(array('number' => '6011111111111117')); + $this->assertSame(CreditCard::BRAND_DISCOVER, $card->getBrand()); + } + + public function testGetBrandDinersClub() + { + $card = new CreditCard(array('number' => '30569309025904')); + $this->assertSame(CreditCard::BRAND_DINERS_CLUB, $card->getBrand()); + } + + public function testGetBrandJcb() + { + $card = new CreditCard(array('number' => '3530111333300000')); + $this->assertSame(CreditCard::BRAND_JCB, $card->getBrand()); + } + + public function testExpiryMonth() + { + $this->card->setExpiryMonth(9); + $this->assertSame(9, $this->card->getExpiryMonth()); + } + + public function testExpiryMonthLeadingZeros() + { + $this->card->setExpiryMonth('09'); + $this->assertSame(9, $this->card->getExpiryMonth()); + } + + public function testExpiryYear() + { + $this->card->setExpiryYear(2012); + $this->assertSame(2012, $this->card->getExpiryYear()); + } + + public function testExpiryYearTwoDigits() + { + $this->card->setExpiryYear('12'); + $this->assertSame(2012, $this->card->getExpiryYear()); + } + + public function testExpiryDate() + { + $this->assertSame($this->card, $this->card->setExpiryMonth('09')); + $this->assertSame($this->card, $this->card->setExpiryYear('2012')); + $this->assertSame('092012', $this->card->getExpiryDate('mY')); + } + + public function testStartMonth() + { + $this->card->setStartMonth(9); + $this->assertSame(9, $this->card->getStartMonth()); + } + + public function testStartMonthLeadingZeros() + { + $this->card->setStartMonth('09'); + $this->assertSame(9, $this->card->getStartMonth()); + } + + public function testStartYear() + { + $this->card->setStartYear(2012); + $this->assertSame(2012, $this->card->getStartYear()); + } + + public function testStartYearTwoDigits() + { + $this->card->setStartYear('12'); + $this->assertSame(2012, $this->card->getStartYear()); + } + + public function testStartDate() + { + $this->card->setStartMonth('11'); + $this->card->setStartYear('2012'); + $this->assertEquals('112012', $this->card->getStartDate('mY')); + } + + public function testCvv() + { + $this->card->setCvv('456'); + $this->assertEquals('456', $this->card->getCvv()); + } + + public function testIssueNumber() + { + $this->card->setIssueNumber('12'); + $this->assertSame('12', $this->card->getIssueNumber()); + } + + /** + * @expectedException League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage Card number is invalid + */ + public function testInvalidLuhn() + { + $this->card->setNumber('43'); + $this->card->validate(); + } + + /** + * @expectedException League\Omnipay\Common\Exception\InvalidCreditCardException + * @expectedExceptionMessage Card number should have 12 to 19 digits + */ + public function testInvalidShortCard() + { + $this->card->setNumber('4440'); + $this->card->validate(); + } + + public function testMaskedJson() + { + $card = json_decode(json_encode($this->card), true); + + $this->assertEquals('XXXXXXXXXXXX1111', $card['number']); + $this->assertEquals('****', $card['expiryYear']); + $this->assertEquals('*', $card['expiryMonth']); + $this->assertEquals('***', $card['cvv']); + } + + + public function testMaskedDebug() + { + if (version_compare(PHP_VERSION, '5.6.0', '<')) { + $this->markTestSkipped( + '__debugInfo is not available in PHP 5.6' + ); + } + + $output = print_r($this->card, true); + + $this->assertContains('XXXXXXXXXXXX1111', $output); + $this->assertNotContains('4111111111111111', $output); + $this->assertContains('****', $output); + } +} diff --git a/tests/Omnipay/Common/CurrencyTest.php b/tests/CurrencyTest.php similarity index 78% rename from tests/Omnipay/Common/CurrencyTest.php rename to tests/CurrencyTest.php index 6eadeffc..576bf7e6 100644 --- a/tests/Omnipay/Common/CurrencyTest.php +++ b/tests/CurrencyTest.php @@ -1,9 +1,9 @@ assertSame(2, $currency->getDecimals()); } + public function testNumericCodeReturnsNull() + { + $currency = Currency::find('840'); + + $this->assertNull($currency); + } + public function testUnknownCurrencyReturnsNull() { $currency = Currency::find('XYZ'); diff --git a/tests/CustomerTest.php b/tests/CustomerTest.php new file mode 100644 index 00000000..be02b569 --- /dev/null +++ b/tests/CustomerTest.php @@ -0,0 +1,161 @@ +setTitle('Mr.'); + $this->assertEquals('Mr.', $customer->getTitle()); + } + + public function testFirstName() + { + $customer = new Customer(); + $customer->setFirstName('Bob'); + $this->assertEquals('Bob', $customer->getFirstName()); + } + + public function testLastName() + { + $customer = new Customer(); + $customer->setLastName('Smith'); + $this->assertEquals('Smith', $customer->getLastName()); + } + + public function testGetName() + { + $customer = new Customer(); + $customer->setFirstName('Bob'); + $customer->setLastName('Smith'); + $this->assertEquals('Bob Smith', $customer->getName()); + } + + public function testSetName() + { + $customer = new Customer(); + $customer->setName('Bob Smith'); + $this->assertEquals('Bob', $customer->getFirstName()); + $this->assertEquals('Smith', $customer->getLastName()); + } + + public function testSetNameWithOneName() + { + $customer = new Customer(); + $customer->setName('Bob'); + $this->assertEquals('Bob', $customer->getFirstName()); + $this->assertEquals('', $customer->getLastName()); + } + + public function testSetNameWithMultipleNames() + { + $customer = new Customer(); + $customer->setName('Bob John Smith'); + $this->assertEquals('Bob', $customer->getFirstName()); + $this->assertEquals('John Smith', $customer->getLastName()); + } + + public function testCompany() + { + $customer = new Customer(); + $customer->setCompany('FooBar'); + $this->assertEquals('FooBar', $customer->getCompany()); + } + + public function testAddress1() + { + $customer = new Customer(); + $customer->setAddress1('31 Spooner St'); + $this->assertEquals('31 Spooner St', $customer->getAddress1()); + } + + public function testAddress2() + { + $customer = new Customer(); + $customer->setAddress2('Suburb'); + $this->assertEquals('Suburb', $customer->getAddress2()); + } + + public function testCity() + { + $customer = new Customer(); + $customer->setCity('Quahog'); + $this->assertEquals('Quahog', $customer->getCity()); + } + + public function testPostcode() + { + $customer = new Customer(); + $customer->setPostcode('12345'); + $this->assertEquals('12345', $customer->getPostcode()); + } + + public function testState() + { + $customer = new Customer(); + $customer->setState('RI'); + $this->assertEquals('RI', $customer->getState()); + } + + public function testCountry() + { + $customer = new Customer(); + $customer->setCountry('US'); + $this->assertEquals('US', $customer->getCountry()); + } + + public function testPhone() + { + $customer = new Customer(); + $customer->setPhone('12345'); + $this->assertEquals('12345', $customer->getPhone()); + } + + public function testPhoneExtension() + { + $customer = new Customer(); + $customer->setPhoneExtension('001'); + $this->assertEquals('001', $customer->getPhoneExtension()); + } + + public function testFax() + { + $customer = new Customer(); + $customer->setFax('54321'); + $this->assertEquals('54321', $customer->getFax()); + } + + public function testEmail() + { + $customer = new Customer(); + $customer->setEmail('adrian@example.com'); + $this->assertEquals('adrian@example.com', $customer->getEmail()); + } + + public function testBirthday() + { + $customer = new Customer(); + $customer->setBirthday('01-02-2000'); + $this->assertEquals('2000-02-01', $customer->getBirthday()); + $this->assertEquals('01/02/2000', $customer->getBirthday('d/m/Y')); + } + + public function testBirthdayEmpty() + { + $customer = new Customer(); + $customer->setBirthday(''); + $this->assertNull($customer->getBirthday()); + } + + public function testGender() + { + $customer = new Customer(); + $customer->setGender('female'); + $this->assertEquals('female', $customer->getGender()); + } +} diff --git a/tests/Omnipay/Common/Exception/BadMethodCallExceptionTest.php b/tests/Exception/BadMethodCallExceptionTest.php similarity index 74% rename from tests/Omnipay/Common/Exception/BadMethodCallExceptionTest.php rename to tests/Exception/BadMethodCallExceptionTest.php index 7795cf24..332942f7 100644 --- a/tests/Omnipay/Common/Exception/BadMethodCallExceptionTest.php +++ b/tests/Exception/BadMethodCallExceptionTest.php @@ -1,8 +1,8 @@ assertTrue($result); } - public function testInitializeIgnoresNull() - { - $target = m::mock(); - Helper::initialize($target, null); - } - - public function testInitializeIgnoresString() - { - $target = m::mock(); - Helper::initialize($target, 'invalid'); - } - public function testInitializeCallsSetters() { - $target = m::mock('\Omnipay\Common\CreditCard'); - $target->shouldReceive('setName')->once()->with('adrian'); + $target = m::mock('\League\Omnipay\Common\CreditCard'); $target->shouldReceive('setNumber')->once()->with('1234'); - Helper::initialize($target, array('name' => 'adrian', 'number' => '1234')); - } - - public function testInitializeIgnoresInvalidParameters() - { - $target = m::mock('\Omnipay\Common\CreditCard'); - $target->shouldReceive('setName')->once()->with('adrian'); - - Helper::initialize($target, array('name' => 'adrian', 'extra' => 'invalid')); + Helper::initializeParameters($target, ['number' => '1234']); } public function testGetGatewayShortNameSimple() { - $shortName = Helper::getGatewayShortName('Omnipay\\Stripe\\Gateway'); + $shortName = Helper::getGatewayShortName('League\\Omnipay\\Stripe\\Gateway'); $this->assertSame('Stripe', $shortName); } public function testGetGatewayShortNameSimpleLeadingSlash() { - $shortName = Helper::getGatewayShortName('\\Omnipay\\Stripe\\Gateway'); + $shortName = Helper::getGatewayShortName('\\League\\Omnipay\\Stripe\\Gateway'); $this->assertSame('Stripe', $shortName); } public function testGetGatewayShortNameUnderscore() { - $shortName = Helper::getGatewayShortName('Omnipay\\PayPal\\ExpressGateway'); + $shortName = Helper::getGatewayShortName('League\\Omnipay\\PayPal\\ExpressGateway'); $this->assertSame('PayPal_Express', $shortName); } public function testGetGatewayShortNameUnderscoreLeadingSlash() { - $shortName = Helper::getGatewayShortName('\\Omnipay\\PayPal\\ExpressGateway'); + $shortName = Helper::getGatewayShortName('\\League\\Omnipay\\PayPal\\ExpressGateway'); $this->assertSame('PayPal_Express', $shortName); } @@ -123,13 +102,13 @@ public function testGetGatewayClassNameExistingNamespaceUnderscore() public function testGetGatewayClassNameSimple() { $class = Helper::getGatewayClassName('Stripe'); - $this->assertEquals('\\Omnipay\\Stripe\\Gateway', $class); + $this->assertEquals('\\League\\Omnipay\\Stripe\\Gateway', $class); } public function testGetGatewayClassNamePartialNamespace() { $class = Helper::getGatewayClassName('PayPal\\Express'); - $this->assertEquals('\\Omnipay\\PayPal\\ExpressGateway', $class); + $this->assertEquals('\\League\\Omnipay\\PayPal\\ExpressGateway', $class); } /** @@ -138,7 +117,7 @@ public function testGetGatewayClassNamePartialNamespace() public function testGetGatewayClassNameUnderscoreNamespace() { $class = Helper::getGatewayClassName('PayPal_Express'); - $this->assertEquals('\\Omnipay\\PayPal\\ExpressGateway', $class); + $this->assertEquals('\\League\\Omnipay\\PayPal\\ExpressGateway', $class); } /** diff --git a/tests/Http/AbstractClientTest.php b/tests/Http/AbstractClientTest.php new file mode 100644 index 00000000..178ea21d --- /dev/null +++ b/tests/Http/AbstractClientTest.php @@ -0,0 +1,82 @@ +client = m::mock(AbstractClient::class)->makePartial(); + } + + public function testGet() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->get('https://thephpleague.com/')); + } + + public function testPost() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->post('https://thephpleague.com/', [], 'my-body')); + } + + public function testPut() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->put('https://thephpleague.com/', [], 'my-body')); + } + + public function testPatch() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->patch('https://thephpleague.com/', [], 'my-body')); + } + + public function testDelete() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->patch('https://thephpleague.com/', [], 'my-body')); + } + + public function testHead() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->head('https://thephpleague.com/', [])); + } + + public function testOptions() + { + $response = m::mock(ResponseInterface::class); + + $this->client->shouldReceive('sendRequest')->once()->andReturn($response); + + $this->assertSame($response, $this->client->options('https://thephpleague.com/')); + } +} diff --git a/tests/Http/FactoryTest.php b/tests/Http/FactoryTest.php new file mode 100644 index 00000000..1c303fd1 --- /dev/null +++ b/tests/Http/FactoryTest.php @@ -0,0 +1,41 @@ + 'value'], 'my-body'); + + $this->assertInstanceOf(RequestInterface::class, $request); + $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('https://thephpleague.com/', $request->getUri()); + $this->assertEquals('value', $request->getHeaderLine('key')); + $this->assertEquals('my-body', $request->getBody()); + } + + public function testCreateUri() + { + $uri = Factory::createUri('https://thephpleague.com/'); + + $this->assertInstanceOf(UriInterface::class, $uri); + $this->assertEquals('https://thephpleague.com/', (string) $uri); + } + + public function testCreateStream() + { + $stream = Factory::createStream('my-body'); + + $this->assertInstanceOf(StreamInterface::class, $stream); + $this->assertEquals('my-body', (string) $stream); + } +} diff --git a/tests/Http/GuzzleClientTest.php b/tests/Http/GuzzleClientTest.php new file mode 100644 index 00000000..a7aeb85e --- /dev/null +++ b/tests/Http/GuzzleClientTest.php @@ -0,0 +1,54 @@ +guzzle = m::mock(Guzzle::class)->makePartial(); + $this->client = new GuzzleClient($this->guzzle); + } + + public function testEmptyConstruct() + { + $client = new GuzzleClientTest_MockGuzzleClient(); + $this->assertInstanceOf(ClientInterface::class, $client); + $this->assertInstanceOf(Guzzle::class, $client->guzzle); + $this->assertNotEquals($this->guzzle, $client->guzzle); + } + + public function testGuzzleConstruct() + { + $client = new GuzzleClientTest_MockGuzzleClient($this->guzzle); + $this->assertInstanceOf(ClientInterface::class, $client); + $this->assertInstanceOf(Guzzle::class, $client->guzzle); + $this->assertEquals($this->guzzle, $client->guzzle); + } + + public function testSendRequest() + { + $request = m::mock(RequestInterface::class); + $response = m::mock(ResponseInterface::class); + + $this->guzzle->shouldReceive('send')->once()->with($request)->andReturn($response); + + $this->assertSame($response, $this->client->sendRequest($request)); + } +} + +class GuzzleClientTest_MockGuzzleClient extends GuzzleClient +{ + public $guzzle; +} \ No newline at end of file diff --git a/tests/Omnipay/Common/IssuerTest.php b/tests/IssuerTest.php similarity index 90% rename from tests/Omnipay/Common/IssuerTest.php rename to tests/IssuerTest.php index ca7c40d7..1afd5eca 100644 --- a/tests/Omnipay/Common/IssuerTest.php +++ b/tests/IssuerTest.php @@ -1,8 +1,8 @@ bag->add($item); $contents = $this->bag->all(); - $this->assertInstanceOf('\Omnipay\Common\Item', $contents[0]); + $this->assertInstanceOf('\League\Omnipay\Common\Item', $contents[0]); $this->assertSame('CD-ROM', $contents[0]->getName()); } diff --git a/tests/Omnipay/Common/ItemTest.php b/tests/ItemTest.php similarity index 94% rename from tests/Omnipay/Common/ItemTest.php rename to tests/ItemTest.php index 8c008b51..6647713f 100644 --- a/tests/Omnipay/Common/ItemTest.php +++ b/tests/ItemTest.php @@ -1,8 +1,8 @@ assertSame('en-us', $locale->getLocale()); + $this->assertSame('en', $locale->getPrimaryLanguage()); + $this->assertSame('us', $locale->getRegion()); + $this->assertSame('en-us', (string) $locale); + } + + public function testConstructWithoutRegion() + { + $locale = new Locale('en'); + $this->assertSame('en', $locale->getPrimaryLanguage()); + $this->assertNull($locale->getRegion()); + } + + public function testParse() + { + $locale = Locale::parse('en-us'); + $this->assertSame('en', $locale->getPrimaryLanguage()); + $this->assertSame('us', $locale->getRegion()); + } + + public function testParseUnderscore() + { + $locale = Locale::parse('en_us'); + $this->assertSame('en', $locale->getPrimaryLanguage()); + $this->assertSame('us', $locale->getRegion()); + } + + public function testParseWithoutRegion() + { + $locale = Locale::parse('en'); + $this->assertSame('en', $locale->getPrimaryLanguage()); + $this->assertNull($locale->getRegion()); + } + + public function testLowercase() + { + $locale = Locale::parse('En-US'); + $this->assertSame('en-us', $locale->getLocale()); + $this->assertSame('en', $locale->getPrimaryLanguage()); + $this->assertSame('us', $locale->getRegion()); + } +} diff --git a/tests/Omnipay/Common/Message/AbstractRequestTest.php b/tests/Message/AbstractRequestTest.php similarity index 53% rename from tests/Omnipay/Common/Message/AbstractRequestTest.php rename to tests/Message/AbstractRequestTest.php index 0e07a984..2b9437a4 100644 --- a/tests/Omnipay/Common/Message/AbstractRequestTest.php +++ b/tests/Message/AbstractRequestTest.php @@ -1,18 +1,21 @@ request = m::mock('\Omnipay\Common\Message\AbstractRequest')->makePartial(); - $this->request->initialize(); + $this->request = m::mock('\League\Omnipay\Common\Message\AbstractRequest')->makePartial(); + $this->request->initialize([ + 'currency' => 'USD', + ]); } /** @@ -39,12 +42,12 @@ public function testConstruct() public function testInitializeWithParams() { - $this->assertSame($this->request, $this->request->initialize(array('amount' => '1.23'))); - $this->assertSame('1.23', $this->request->getAmount()); + $this->assertSame($this->request, $this->request->initialize(array('amount' => '1.23', 'currency' => 'USD'))); + $this->assertSame('123', $this->request->getAmount()->getInteger()); } /** - * @expectedException \Omnipay\Common\Exception\RuntimeException + * @expectedException \League\Omnipay\Common\Exception\RuntimeException * @expectedExceptionMessage Request cannot be modified after it has been sent! */ public function testInitializeAfterRequestSent() @@ -69,7 +72,7 @@ public function testSetCardWithArray() $this->assertSame($this->request, $this->request->setCard(array('number' => '1234'))); $card = $this->request->getCard(); - $this->assertInstanceOf('\Omnipay\Common\CreditCard', $card); + $this->assertInstanceOf('\League\Omnipay\Common\CreditCard', $card); $this->assertSame('1234', $card->getNumber()); } @@ -87,14 +90,14 @@ public function testCardReference() public function testAmount() { - $this->assertSame($this->request, $this->request->setAmount('2.00')); - $this->assertSame('2.00', $this->request->getAmount()); + $this->assertSame($this->request, $this->request->setAmount('2')); + $this->assertSame('200', $this->request->getAmount()->getInteger()); } - public function testAmountWithFloat() + public function testAmountWithInt() { - $this->assertSame($this->request, $this->request->setAmount(2.0)); - $this->assertSame('2.00', $this->request->getAmount()); + $this->assertSame($this->request, $this->request->setAmount(2)); + $this->assertSame('200', $this->request->getAmount()->getInteger()); } public function testAmountWithEmpty() @@ -105,198 +108,52 @@ public function testAmountWithEmpty() public function testAmountZeroFloat() { - $this->assertSame($this->request, $this->request->setAmount(0.0)); - $this->assertSame('0.00', $this->request->getAmount()); + $this->assertSame($this->request, $this->request->setAmount(0)); + $this->assertSame('0', $this->request->getAmount()->getInteger()); } public function testAmountZeroString() { - $this->assertSame($this->request, $this->request->setAmount('0.000000')); - $this->assertSame('0.00', $this->request->getAmount()); + $this->assertSame($this->request, $this->request->setAmount('0')); + $this->assertSame('0', $this->request->getAmount()->getInteger()); } /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException + * @expectedException League\Omnipay\Common\Exception\InvalidRequestException * @expectedExceptionMessage A zero amount is not allowed. */ public function testAmountZeroNotAllowed() { $this->changeProtectedProperty('zeroAmountAllowed', false); - $this->request->setAmount('0.00'); - $this->request->getAmount(); - } - - // See https://github.com/thephpleague/omnipay-common/issues/69 - public function testAmountPrecision() - { - // The default precision for PHP is 6 decimal places. - ini_set('precision', 6); - $this->assertSame($this->request, $this->request->setAmount('67.10')); - $this->assertSame('67.10', $this->request->getAmount()); - - // At 17 decimal places, 67.10 will echo as 67.09999... - // This is *why* PHP sets the default precision at 6. - ini_set('precision', 17); - $this->assertSame('67.10', $this->request->getAmount()); - - $this->assertSame($this->request, $this->request->setAmount('67.01')); - $this->assertSame('67.01', $this->request->getAmount()); - - $this->assertSame($this->request, $this->request->setAmount('0.10')); - $this->assertSame('0.10', $this->request->getAmount()); - - $this->assertSame($this->request, $this->request->setAmount('0.01')); - $this->assertSame('0.01', $this->request->getAmount()); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - * - * We still want to catch obvious fractions of the minor units that are - * not precision errors at a much lower level. - */ - public function testAmountPrecisionTooHigh() - { - $this->assertSame($this->request, $this->request->setAmount('123.005')); - $this->assertSame('123.005', $this->request->getAmount()); - } - - public function testGetAmountNoDecimals() - { - $this->assertSame($this->request, $this->request->setCurrency('JPY')); - $this->assertSame($this->request, $this->request->setAmount('1366')); - $this->assertSame('1366', $this->request->getAmount()); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testGetAmountNoDecimalsRounding() - { - // There will not be any rounding; the amount is sent as requested or not at all. - $this->assertSame($this->request, $this->request->setAmount('136.5')); - $this->assertSame($this->request, $this->request->setCurrency('JPY')); - $this->request->getAmount(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountWithIntThrowsException() - { - // ambiguous value, avoid errors upgrading from v0.9 - $this->assertSame($this->request, $this->request->setAmount(10)); - $this->request->getAmount(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountWithIntStringThrowsException() - { - // ambiguous value, avoid errors upgrading from v0.9 - // Some currencies only take integers, so an integer (in a string) should be valid. - $this->assertSame($this->request, $this->request->setAmount('10')); + $this->request->setAmount('0'); $this->request->getAmount(); } - public function testGetAmountInteger() + public function testGetAmountFormatted() { - $this->assertSame($this->request, $this->request->setAmount('13.66')); - $this->assertSame(1366, $this->request->getAmountInteger()); - } - - public function testGetAmountIntegerNoDecimals() - { - $this->assertSame($this->request, $this->request->setCurrency('JPY')); - $this->assertSame($this->request, $this->request->setAmount('1366')); - $this->assertSame(1366, $this->request->getAmountInteger()); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountThousandsSepThrowsException() - { - $this->assertSame($this->request, $this->request->setAmount('1,234.00')); - $this->request->getAmount(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountInvalidFormatThrowsException() - { - $this->assertSame($this->request, $this->request->setAmount('1.234.00')); - $this->request->getAmount(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountInvalidTypeThrowsException() - { - $this->assertSame($this->request, $this->request->setAmount(true)); - $this->request->getAmount(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountNegativeStringThrowsException() - { - $this->assertSame($this->request, $this->request->setAmount('-123.00')); - $this->request->getAmount(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidRequestException - */ - public function testAmountNegativeFloatThrowsException() - { - $this->assertSame($this->request, $this->request->setAmount(-123.00)); - $this->request->getAmount(); + $this->assertSame($this->request, $this->request->setAmount(13.66)); + $this->assertSame('13.66', $this->request->getAmount()->getFormatted()); } public function testCurrency() { - $this->assertSame($this->request, $this->request->setCurrency('USD')); - $this->assertSame('USD', $this->request->getCurrency()); + $this->assertSame($this->request, $this->request->setCurrency('EUR')->setAmount(1)); + $this->assertSame('EUR', $this->request->getAmount()->getCurrency()->getCode()); } - public function testCurrencyLowercase() + public function testAmountWithCurrency() { - $this->assertSame($this->request, $this->request->setCurrency('usd')); - $this->assertSame('USD', $this->request->getCurrency()); + $this->assertSame($this->request, $this->request->setCurrency('USD')->setAmount(new Amount(1, 'EUR'))); + $this->assertSame('EUR', $this->request->getAmount()->getCurrency()->getCode()); } - public function testCurrencyNull() + public function testLocale() { - $this->assertSame($this->request, $this->request->setCurrency(null)); - $this->assertNull($this->request->getCurrency()); - } + $this->assertSame($this->request, $this->request->setLocale('en-us')); - public function testCurrencyNumeric() - { - $this->assertSame($this->request, $this->request->setCurrency('USD')); - $this->assertSame('840', $this->request->getCurrencyNumeric()); - } - - public function testCurrencyDecimals() - { - $this->assertSame($this->request, $this->request->setCurrency('JPY')); - $this->assertSame(0, $this->request->getCurrencyDecimalPlaces()); - } - - public function testFormatCurrency() - { - $this->assertSame('1234.00', $this->request->formatCurrency(1234)); - } - - public function testFormatCurrencyNoDecimals() - { - $this->request->setCurrency('JPY'); - $this->assertSame('1234', $this->request->formatCurrency(1234)); + $locale = $this->request->getLocale(); + $this->assertInstanceOf('\League\Omnipay\Common\Locale', $locale); + $this->assertSame('en-us', $locale->getLocale()); } public function testDescription() @@ -325,7 +182,7 @@ public function testItemsArray() ))); $itemBag = $this->request->getItems(); - $this->assertInstanceOf('\Omnipay\Common\ItemBag', $itemBag); + $this->assertInstanceOf('\League\Omnipay\Common\ItemBag', $itemBag); $items = $itemBag->all(); $this->assertSame('Floppy Disk', $items[0]->getName()); @@ -394,12 +251,13 @@ public function testGetParameters() $expected = array( 'testMode' => true, 'token' => 'asdf', + 'currency' => 'USD', ); $this->assertEquals($expected, $this->request->getParameters()); } /** - * @expectedException \Omnipay\Common\Exception\RuntimeException + * @expectedException \League\Omnipay\Common\Exception\RuntimeException * @expectedExceptionMessage Request cannot be modified after it has been sent! */ public function testSetParameterAfterRequestSent() @@ -407,7 +265,7 @@ public function testSetParameterAfterRequestSent() $this->request = new AbstractRequestTest_MockAbstractRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->send(); - $this->request->setCurrency('PHP'); + $this->request->setCurrency('USD'); } public function testCanValidateExistingParameters() @@ -419,7 +277,7 @@ public function testCanValidateExistingParameters() } /** - * @expectedException \Omnipay\Common\Exception\InvalidRequestException + * @expectedException \League\Omnipay\Common\Exception\InvalidRequestException */ public function testInvalidParametersThrowsException() { @@ -428,14 +286,14 @@ public function testInvalidParametersThrowsException() $this->request->validate('testMode', 'token'); } - public function testNoCurrencyReturnedIfCurrencyNotSet() + public function testNoAmountReturnedIfAmountNotSet() { - $this->assertNull($this->request->getCurrencyNumeric()); + $this->assertNull($this->request->getAmount()); } public function testSend() { - $response = m::mock('\Omnipay\Common\Message\ResponseInterface'); + $response = m::mock('\League\Omnipay\Common\Message\ResponseInterface'); $data = array('request data'); $this->request->shouldReceive('getData')->once()->andReturn($data); @@ -445,7 +303,7 @@ public function testSend() } /** - * @expectedException \Omnipay\Common\Exception\RuntimeException + * @expectedException \League\Omnipay\Common\Exception\RuntimeException * @expectedExceptionMessage You must call send() before accessing the Response! */ public function testGetResponseBeforeRequestSent() @@ -460,7 +318,7 @@ public function testGetResponseAfterRequestSent() $this->request->send(); $response = $this->request->getResponse(); - $this->assertInstanceOf('\Omnipay\Common\Message\ResponseInterface', $response); + $this->assertInstanceOf('\League\Omnipay\Common\Message\ResponseInterface', $response); } } @@ -470,6 +328,6 @@ public function getData() {} public function sendData($data) { - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponse'); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponse'); } } diff --git a/tests/Omnipay/Common/Message/AbstractResponseTest.php b/tests/Message/AbstractResponseTest.php similarity index 69% rename from tests/Omnipay/Common/Message/AbstractResponseTest.php rename to tests/Message/AbstractResponseTest.php index 1657073d..4dbd68f8 100644 --- a/tests/Omnipay/Common/Message/AbstractResponseTest.php +++ b/tests/Message/AbstractResponseTest.php @@ -1,22 +1,22 @@ response = m::mock('\Omnipay\Common\Message\AbstractResponse')->makePartial(); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponse')->makePartial(); } public function testConstruct() { $data = array('foo' => 'bar'); $request = $this->getMockRequest(); - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponse', array($request, $data))->makePartial(); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponse', array($request, $data))->makePartial(); $this->assertSame($request, $this->response->getRequest()); $this->assertSame($data, $this->response->getData()); @@ -30,12 +30,13 @@ public function testDefaultMethods() $this->assertFalse($this->response->isCancelled()); $this->assertNull($this->response->getData()); $this->assertNull($this->response->getTransactionReference()); + $this->assertNull($this->response->getTransactionId()); $this->assertNull($this->response->getMessage()); $this->assertNull($this->response->getCode()); } /** - * @expectedException \Omnipay\Common\Exception\RuntimeException + * @expectedException \League\Omnipay\Common\Exception\RuntimeException * @expectedExceptionMessage This response does not support redirection. */ public function testGetRedirectResponseNotImplemented() @@ -44,12 +45,12 @@ public function testGetRedirectResponseNotImplemented() } /** - * @expectedException \Omnipay\Common\Exception\RuntimeException + * @expectedException \League\Omnipay\Common\Exception\RuntimeException * @expectedExceptionMessage This response does not support redirection. */ public function testGetRedirectResponseNotSupported() { - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); $this->response->shouldReceive('isRedirect')->once()->andReturn(false); $this->response->getRedirectResponse(); @@ -57,45 +58,39 @@ public function testGetRedirectResponseNotSupported() public function testGetRedirectResponseGet() { - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); $this->response->shouldReceive('getRedirectMethod')->andReturn('GET'); $httpResponse = $this->response->getRedirectResponse(); $this->assertSame(302, $httpResponse->getStatusCode()); - $this->assertSame('https://example.com/redirect?a=1&b=2', $httpResponse->getTargetUrl()); + $this->assertSame('https://example.com/redirect?a=1&b=2', $httpResponse->getHeaderLine('Location')); } public function testGetRedirectResponsePost() { $data = array('foo' => 'bar', 'key&"' => ''); - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); $this->response->shouldReceive('getRedirectMethod')->andReturn('POST'); $this->response->shouldReceive('getRedirectData')->andReturn($data); $httpResponse = $this->response->getRedirectResponse(); $this->assertSame(200, $httpResponse->getStatusCode()); - $this->assertContains('
', $httpResponse->getContent()); - $this->assertContains('', $httpResponse->getContent()); - $this->assertContains('', $httpResponse->getContent()); + $this->assertContains('', (string) $httpResponse->getBody()); + $this->assertContains('', (string) $httpResponse->getBody()); + $this->assertContains('', (string) $httpResponse->getBody()); } /** - * @expectedException \Omnipay\Common\Exception\RuntimeException + * @expectedException \League\Omnipay\Common\Exception\RuntimeException * @expectedExceptionMessage Invalid redirect method "DELETE". */ public function testGetRedirectResponseInvalidMethod() { - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); + $this->response = m::mock('\League\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); $this->response->shouldReceive('getRedirectMethod')->andReturn('DELETE'); $this->response->getRedirectResponse(); } - - public function testGetTransactionIdNull() - { - $this->response = m::mock('\Omnipay\Common\Message\AbstractResponseTest_MockRedirectResponse')->makePartial(); - $this->assertNull($this->response->getTransactionId()); - } } class AbstractResponseTest_MockRedirectResponse extends AbstractResponse implements RedirectResponseInterface @@ -105,7 +100,7 @@ public function isPending() return false; } - public function isSuccessful() + public function isCompleted() { return false; } diff --git a/tests/Omnipay/Common/CreditCardTest.php b/tests/Omnipay/Common/CreditCardTest.php deleted file mode 100644 index b829369a..00000000 --- a/tests/Omnipay/Common/CreditCardTest.php +++ /dev/null @@ -1,682 +0,0 @@ -card = new CreditCard; - $this->card->setNumber('4111111111111111'); - $this->card->setFirstName('Example'); - $this->card->setLastName('Customer'); - $this->card->setExpiryMonth('4'); - $this->card->setExpiryYear(gmdate('Y')+2); - $this->card->setCvv('123'); - } - - public function testConstructWithParams() - { - $card = new CreditCard(array('name' => 'Test Customer')); - $this->assertSame('Test Customer', $card->getName()); - } - - public function testInitializeWithParams() - { - $card = new CreditCard; - $card->initialize(array('name' => 'Test Customer')); - $this->assertSame('Test Customer', $card->getName()); - } - - public function testGetParamters() - { - $card = new CreditCard(array( - 'name' => 'Example Customer', - 'number' => '1234', - 'expiryMonth' => 6, - 'expiryYear' => 2016, - )); - - $parameters = $card->getParameters(); - $this->assertSame('Example', $parameters['billingFirstName']); - $this->assertSame('Customer', $parameters['billingLastName']); - $this->assertSame('1234', $parameters['number']); - $this->assertSame(6, $parameters['expiryMonth']); - $this->assertSame(2016, $parameters['expiryYear']); - } - - public function testValidateFixture() - { - $this->card->validate(); - } - - /** - * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage The number parameter is required - */ - public function testValidateNumberRequired() - { - $this->card->setNumber(null); - $this->card->validate(); - } - - /** - * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage The expiryMonth parameter is required - */ - public function testValidateExpiryMonthRequired() - { - $this->card->setExpiryMonth(null); - $this->card->validate(); - } - - /** - * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage The expiryYear parameter is required - */ - public function testValidateExpiryYearRequired() - { - $this->card->setExpiryYear(null); - $this->card->validate(); - } - - /** - * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage Card has expired - */ - public function testValidateExpiryDate() - { - $this->card->setExpiryYear(gmdate('Y')-1); - $this->card->validate(); - } - - /** - * @expectedException \Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage Card number is invalid - */ - public function testValidateNumber() - { - $this->card->setNumber('4111111111111110'); - $this->card->validate(); - } - - public function testGetSupportedBrands() - { - $brands = $this->card->getSupportedBrands(); - $this->assertInternalType('array', $brands); - $this->assertArrayHasKey(CreditCard::BRAND_VISA, $brands); - } - - public function testCustomSupportedBrand() - { - $this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'); - $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands()); - } - - public function testCustomBrandWorks() - { - $this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/'); - $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands()); - $this->card->setNumber('9111111111111110'); - $this->card->validate(); - $this->assertEquals('omniexpress', $this->card->getBrand()); - } - - public function testCustomBrandAddedTwiceReturnsFalse() - { - $this->assertTrue($this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/')); - $this->assertArrayHasKey('omniexpress', $this->card->getSupportedBrands()); - $this->assertFalse($this->card->addSupportedBrand('omniexpress', '/^9\d{12}(\d{3})?$/')); - } - - public function testTitle() - { - $this->card->setTitle('Mr.'); - $this->assertEquals('Mr.', $this->card->getTitle()); - } - - public function testFirstName() - { - $this->card->setFirstName('Bob'); - $this->assertEquals('Bob', $this->card->getFirstName()); - } - - public function testLastName() - { - $this->card->setLastName('Smith'); - $this->assertEquals('Smith', $this->card->getLastName()); - } - - public function testGetName() - { - $this->card->setFirstName('Bob'); - $this->card->setLastName('Smith'); - $this->assertEquals('Bob Smith', $this->card->getName()); - } - - public function testSetName() - { - $this->card->setName('Bob Smith'); - $this->assertEquals('Bob', $this->card->getFirstName()); - $this->assertEquals('Smith', $this->card->getLastName()); - } - - public function testSetNameWithOneName() - { - $this->card->setName('Bob'); - $this->assertEquals('Bob', $this->card->getFirstName()); - $this->assertEquals('', $this->card->getLastName()); - } - - public function testSetNameWithMultipleNames() - { - $this->card->setName('Bob John Smith'); - $this->assertEquals('Bob', $this->card->getFirstName()); - $this->assertEquals('John Smith', $this->card->getLastName()); - } - - public function testNumber() - { - $this->card->setNumber('4000000000000000'); - $this->assertEquals('4000000000000000', $this->card->getNumber()); - } - - public function testSetNumberStripsNonDigits() - { - $this->card->setNumber('4000 0000 00b00 0000'); - $this->assertEquals('4000000000000000', $this->card->getNumber()); - } - - public function testGetNumberLastFourNull() - { - $this->card->setNumber(null); - $this->assertNull($this->card->getNumberLastFour()); - } - - public function testGetNumberLastFour() - { - $this->card->setNumber('4000000000001234'); - $this->assertSame('1234', $this->card->getNumberLastFour()); - } - - public function testGetNumberLastFourNonDigits() - { - $this->card->setNumber('4000 0000 0000 12x34'); - $this->assertSame('1234', $this->card->getNumberLastFour()); - } - - public function testGetNumberMasked() - { - $this->card->setNumber('4000000000001234'); - - $this->assertSame('XXXXXXXXXXXX1234', $this->card->getNumberMasked()); - } - - public function testGetNumberMaskedNonDigits() - { - $this->card->setNumber('4000 0000 0000 12x34'); - - $this->assertSame('XXXXXXXXXXXX1234', $this->card->getNumberMasked()); - } - - public function testGetBrandDefault() - { - $card = new CreditCard; - $this->assertNull($card->getBrand()); - } - - public function testGetBrandVisa() - { - $card = new CreditCard(array('number' => '4242424242424242')); - $this->assertSame(CreditCard::BRAND_VISA, $card->getBrand()); - } - - public function testGetBrandMasterCard() - { - $card = new CreditCard(array('number' => '5555555555554444')); - $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand()); - $card = new CreditCard(array('number' => '2230000010000006')); - $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand()); - $card = new CreditCard(array('number' => '6771890000000008')); - $this->assertSame(CreditCard::BRAND_MASTERCARD, $card->getBrand()); - } - - public function testGetBrandAmex() - { - $card = new CreditCard(array('number' => '378282246310005')); - $this->assertSame(CreditCard::BRAND_AMEX, $card->getBrand()); - } - - public function testGetBrandDiscover() - { - $card = new CreditCard(array('number' => '6011111111111117')); - $this->assertSame(CreditCard::BRAND_DISCOVER, $card->getBrand()); - } - - public function testGetBrandDinersClub() - { - $card = new CreditCard(array('number' => '30569309025904')); - $this->assertSame(CreditCard::BRAND_DINERS_CLUB, $card->getBrand()); - } - - public function testGetBrandJcb() - { - $card = new CreditCard(array('number' => '3530111333300000')); - $this->assertSame(CreditCard::BRAND_JCB, $card->getBrand()); - } - - public function testExpiryMonth() - { - $this->card->setExpiryMonth(9); - $this->assertSame(9, $this->card->getExpiryMonth()); - } - - public function testExpiryMonthLeadingZeros() - { - $this->card->setExpiryMonth('09'); - $this->assertSame(9, $this->card->getExpiryMonth()); - } - - public function testExpiryYear() - { - $this->card->setExpiryYear(2012); - $this->assertSame(2012, $this->card->getExpiryYear()); - } - - public function testExpiryYearTwoDigits() - { - $this->card->setExpiryYear('12'); - $this->assertSame(2012, $this->card->getExpiryYear()); - } - - public function testExpiryDate() - { - $this->assertSame($this->card, $this->card->setExpiryMonth('09')); - $this->assertSame($this->card, $this->card->setExpiryYear('2012')); - $this->assertSame('092012', $this->card->getExpiryDate('mY')); - } - - public function testStartMonth() - { - $this->card->setStartMonth(9); - $this->assertSame(9, $this->card->getStartMonth()); - } - - public function testStartMonthLeadingZeros() - { - $this->card->setStartMonth('09'); - $this->assertSame(9, $this->card->getStartMonth()); - } - - public function testStartYear() - { - $this->card->setStartYear(2012); - $this->assertSame(2012, $this->card->getStartYear()); - } - - public function testStartYearTwoDigits() - { - $this->card->setStartYear('12'); - $this->assertSame(2012, $this->card->getStartYear()); - } - - public function testStartDate() - { - $this->card->setStartMonth('11'); - $this->card->setStartYear('2012'); - $this->assertEquals('112012', $this->card->getStartDate('mY')); - } - - public function testCvv() - { - $this->card->setCvv('456'); - $this->assertEquals('456', $this->card->getCvv()); - } - - public function testTracks() - { - $this->card->setTracks('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?'); - $this->assertSame('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?', $this->card->getTracks()); - } - - public function testShouldReturnTrack1() - { - $this->card->setTracks('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?'); - $actual = $this->card->getTrack1(); - $this->assertEquals('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?', $actual); - } - - public function testShouldReturnTrack2() - { - $this->card->setTracks('%B4242424242424242^SMITH/JOHN ^1520126100000000000000444000000?;4242424242424242=15201269999944401?'); - $actual = $this->card->getTrack2(); - $this->assertEquals(';4242424242424242=15201269999944401?', $actual); - } - - public function testIssueNumber() - { - $this->card->setIssueNumber('12'); - $this->assertSame('12', $this->card->getIssueNumber()); - } - - public function testBillingTitle() - { - $this->card->setBillingTitle('Mrs.'); - $this->assertEquals('Mrs.', $this->card->getBillingTitle()); - $this->assertEquals('Mrs.', $this->card->getTitle()); - } - - public function testBillingFirstName() - { - $this->card->setBillingFirstName('Bob'); - $this->assertEquals('Bob', $this->card->getBillingFirstName()); - $this->assertEquals('Bob', $this->card->getFirstName()); - } - - public function testBillingLastName() - { - $this->card->setBillingLastName('Smith'); - $this->assertEquals('Smith', $this->card->getBillingLastName()); - $this->assertEquals('Smith', $this->card->getLastName()); - } - - public function testBillingName() - { - $this->card->setBillingFirstName('Bob'); - $this->card->setBillingLastName('Smith'); - $this->assertEquals('Bob Smith', $this->card->getBillingName()); - - $this->card->setBillingName('John Foo'); - $this->assertEquals('John', $this->card->getBillingFirstName()); - $this->assertEquals('Foo', $this->card->getBillingLastName()); - } - - public function testBillingCompany() - { - $this->card->setBillingCompany('SuperSoft'); - $this->assertEquals('SuperSoft', $this->card->getBillingCompany()); - $this->assertEquals('SuperSoft', $this->card->getCompany()); - } - - public function testBillingAddress1() - { - $this->card->setBillingAddress1('31 Spooner St'); - $this->assertEquals('31 Spooner St', $this->card->getBillingAddress1()); - $this->assertEquals('31 Spooner St', $this->card->getAddress1()); - } - - public function testBillingAddress2() - { - $this->card->setBillingAddress2('Suburb'); - $this->assertEquals('Suburb', $this->card->getBillingAddress2()); - $this->assertEquals('Suburb', $this->card->getAddress2()); - } - - public function testBillingCity() - { - $this->card->setBillingCity('Quahog'); - $this->assertEquals('Quahog', $this->card->getBillingCity()); - $this->assertEquals('Quahog', $this->card->getCity()); - } - - public function testBillingPostcode() - { - $this->card->setBillingPostcode('12345'); - $this->assertEquals('12345', $this->card->getBillingPostcode()); - $this->assertEquals('12345', $this->card->getPostcode()); - } - - public function testBillingState() - { - $this->card->setBillingState('RI'); - $this->assertEquals('RI', $this->card->getBillingState()); - $this->assertEquals('RI', $this->card->getState()); - } - - public function testBillingCountry() - { - $this->card->setBillingCountry('US'); - $this->assertEquals('US', $this->card->getBillingCountry()); - $this->assertEquals('US', $this->card->getCountry()); - } - - public function testBillingPhone() - { - $this->card->setBillingPhone('12345'); - $this->assertSame('12345', $this->card->getBillingPhone()); - $this->assertSame('12345', $this->card->getPhone()); - } - - public function testBillingPhoneExtension() - { - $this->card->setBillingPhoneExtension('001'); - $this->assertSame('001', $this->card->getBillingPhoneExtension()); - $this->assertSame('001', $this->card->getPhoneExtension()); - } - - public function testBillingFax() - { - $this->card->setBillingFax('54321'); - $this->assertSame('54321', $this->card->getBillingFax()); - $this->assertSame('54321', $this->card->getFax()); - } - - public function testShippingTitle() - { - $this->card->setShippingTitle('Dr.'); - $this->assertEquals('Dr.', $this->card->getShippingTitle()); - } - - public function testShippingFirstName() - { - $this->card->setShippingFirstName('James'); - $this->assertEquals('James', $this->card->getShippingFirstName()); - } - - public function testShippingLastName() - { - $this->card->setShippingLastName('Doctor'); - $this->assertEquals('Doctor', $this->card->getShippingLastName()); - } - - public function testShippingName() - { - $this->card->setShippingFirstName('Bob'); - $this->card->setShippingLastName('Smith'); - $this->assertEquals('Bob Smith', $this->card->getShippingName()); - - $this->card->setShippingName('John Foo'); - $this->assertEquals('John', $this->card->getShippingFirstName()); - $this->assertEquals('Foo', $this->card->getShippingLastName()); - } - - public function testShippingCompany() - { - $this->card->setShippingCompany('SuperSoft'); - $this->assertEquals('SuperSoft', $this->card->getShippingCompany()); - } - - public function testShippingAddress1() - { - $this->card->setShippingAddress1('31 Spooner St'); - $this->assertEquals('31 Spooner St', $this->card->getShippingAddress1()); - } - - public function testShippingAddress2() - { - $this->card->setShippingAddress2('Suburb'); - $this->assertEquals('Suburb', $this->card->getShippingAddress2()); - } - - public function testShippingCity() - { - $this->card->setShippingCity('Quahog'); - $this->assertEquals('Quahog', $this->card->getShippingCity()); - } - - public function testShippingPostcode() - { - $this->card->setShippingPostcode('12345'); - $this->assertEquals('12345', $this->card->getShippingPostcode()); - } - - public function testShippingState() - { - $this->card->setShippingState('RI'); - $this->assertEquals('RI', $this->card->getShippingState()); - } - - public function testShippingCountry() - { - $this->card->setShippingCountry('US'); - $this->assertEquals('US', $this->card->getShippingCountry()); - } - - public function testShippingPhone() - { - $this->card->setShippingPhone('12345'); - $this->assertEquals('12345', $this->card->getShippingPhone()); - } - - public function testShippingPhoneExtension() - { - $this->card->setShippingPhoneExtension('001'); - $this->assertEquals('001', $this->card->getShippingPhoneExtension()); - } - - public function testShippingFax() - { - $this->card->setShippingFax('54321'); - $this->assertEquals('54321', $this->card->getShippingFax()); - } - - public function testCompany() - { - $this->card->setCompany('FooBar'); - $this->assertEquals('FooBar', $this->card->getCompany()); - $this->assertEquals('FooBar', $this->card->getBillingCompany()); - $this->assertEquals('FooBar', $this->card->getShippingCompany()); - } - - public function testAddress1() - { - $this->card->setAddress1('31 Spooner St'); - $this->assertEquals('31 Spooner St', $this->card->getAddress1()); - $this->assertEquals('31 Spooner St', $this->card->getBillingAddress1()); - $this->assertEquals('31 Spooner St', $this->card->getShippingAddress1()); - } - - public function testAddress2() - { - $this->card->setAddress2('Suburb'); - $this->assertEquals('Suburb', $this->card->getAddress2()); - $this->assertEquals('Suburb', $this->card->getBillingAddress2()); - $this->assertEquals('Suburb', $this->card->getShippingAddress2()); - } - - public function testCity() - { - $this->card->setCity('Quahog'); - $this->assertEquals('Quahog', $this->card->getCity()); - $this->assertEquals('Quahog', $this->card->getBillingCity()); - $this->assertEquals('Quahog', $this->card->getShippingCity()); - } - - public function testPostcode() - { - $this->card->setPostcode('12345'); - $this->assertEquals('12345', $this->card->getPostcode()); - $this->assertEquals('12345', $this->card->getBillingPostcode()); - $this->assertEquals('12345', $this->card->getShippingPostcode()); - } - - public function testState() - { - $this->card->setState('RI'); - $this->assertEquals('RI', $this->card->getState()); - $this->assertEquals('RI', $this->card->getBillingState()); - $this->assertEquals('RI', $this->card->getShippingState()); - } - - public function testCountry() - { - $this->card->setCountry('US'); - $this->assertEquals('US', $this->card->getCountry()); - $this->assertEquals('US', $this->card->getBillingCountry()); - $this->assertEquals('US', $this->card->getShippingCountry()); - } - - public function testPhone() - { - $this->card->setPhone('12345'); - $this->assertEquals('12345', $this->card->getPhone()); - $this->assertEquals('12345', $this->card->getBillingPhone()); - $this->assertEquals('12345', $this->card->getShippingPhone()); - } - - public function testPhoneExtension() - { - $this->card->setPhoneExtension('001'); - $this->assertEquals('001', $this->card->getPhoneExtension()); - $this->assertEquals('001', $this->card->getBillingPhoneExtension()); - $this->assertEquals('001', $this->card->getShippingPhoneExtension()); - } - - public function testFax() - { - $this->card->setFax('54321'); - $this->assertEquals('54321', $this->card->getFax()); - $this->assertEquals('54321', $this->card->getBillingFax()); - $this->assertEquals('54321', $this->card->getShippingFax()); - } - - public function testEmail() - { - $this->card->setEmail('adrian@example.com'); - $this->assertEquals('adrian@example.com', $this->card->getEmail()); - } - - public function testBirthday() - { - $this->card->setBirthday('01-02-2000'); - $this->assertEquals('2000-02-01', $this->card->getBirthday()); - $this->assertEquals('01/02/2000', $this->card->getBirthday('d/m/Y')); - } - - public function testBirthdayEmpty() - { - $this->card->setBirthday(''); - $this->assertNull($this->card->getBirthday()); - } - - public function testGender() - { - $this->card->setGender('female'); - $this->assertEquals('female', $this->card->getGender()); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage Card number is invalid - */ - public function testInvalidLuhn() - { - $this->card->setNumber('43'); - $this->card->validate(); - } - - /** - * @expectedException Omnipay\Common\Exception\InvalidCreditCardException - * @expectedExceptionMessage Card number should have 12 to 19 digits - */ - public function testInvalidShortCard() - { - $this->card->setNumber('4440'); - $this->card->validate(); - } -} diff --git a/tests/Omnipay/Common/GatewayFactoryTest.php b/tests/Omnipay/Common/GatewayFactoryTest.php deleted file mode 100644 index 22556c86..00000000 --- a/tests/Omnipay/Common/GatewayFactoryTest.php +++ /dev/null @@ -1,95 +0,0 @@ -factory = new GatewayFactory; - } - - public function testReplace() - { - $gateways = array('Foo'); - $this->factory->replace($gateways); - - $this->assertSame($gateways, $this->factory->all()); - } - - public function testRegister() - { - $this->factory->register('Bar'); - - $this->assertSame(array('Bar'), $this->factory->all()); - } - - public function testRegisterExistingGateway() - { - $this->factory->register('Milky'); - $this->factory->register('Bar'); - $this->factory->register('Bar'); - - $this->assertSame(array('Milky', 'Bar'), $this->factory->all()); - } - - public function testFindRegistersAvailableGateways() - { - $this->factory = m::mock('Omnipay\Common\GatewayFactory[getSupportedGateways]'); - $this->factory->shouldReceive('getSupportedGateways')->once() - ->andReturn(array('SpareChange_Test')); - - $gateways = $this->factory->find(); - - $this->assertContains('SpareChange_Test', $gateways); - $this->assertContains('SpareChange_Test', $this->factory->all()); - } - - public function testFindIgnoresUnavailableGateways() - { - $this->factory = m::mock('Omnipay\Common\GatewayFactory[getSupportedGateways]'); - $this->factory->shouldReceive('getSupportedGateways')->once() - ->andReturn(array('SpareChange_Gone')); - - $gateways = $this->factory->find(); - - $this->assertEmpty($gateways); - $this->assertEmpty($this->factory->all()); - } - - public function testCreateShortName() - { - $gateway = $this->factory->create('SpareChange_Test'); - $this->assertInstanceOf('\\Omnipay\\SpareChange\\TestGateway', $gateway); - } - - public function testCreateFullyQualified() - { - $gateway = $this->factory->create('\\Omnipay\\SpareChange\\TestGateway'); - $this->assertInstanceOf('\\Omnipay\\SpareChange\\TestGateway', $gateway); - } - - /** - * @expectedException \Omnipay\Common\Exception\RuntimeException - * @expectedExceptionMessage Class '\Omnipay\Invalid\Gateway' not found - */ - public function testCreateInvalid() - { - $gateway = $this->factory->create('Invalid'); - } - - public function testGetSupportedGateways() - { - $gateways = $this->factory->getSupportedGateways(); - - $this->assertContains('Stripe', $gateways); - } -} diff --git a/tests/Omnipay/OmnipayTest.php b/tests/Omnipay/OmnipayTest.php deleted file mode 100644 index 2ad08124..00000000 --- a/tests/Omnipay/OmnipayTest.php +++ /dev/null @@ -1,42 +0,0 @@ -assertInstanceOf('Omnipay\Common\GatewayFactory', $factory); - } - - public function testSetFactory() - { - $factory = m::mock('Omnipay\Common\GatewayFactory'); - - Omnipay::setFactory($factory); - - $this->assertSame($factory, Omnipay::getFactory()); - } - - public function testCallStatic() - { - $factory = m::mock('Omnipay\Common\GatewayFactory'); - $factory->shouldReceive('testMethod')->with('some-argument')->once()->andReturn('some-result'); - - Omnipay::setFactory($factory); - - $result = Omnipay::testMethod('some-argument'); - $this->assertSame('some-result', $result); - } -} diff --git a/tests/Omnipay/Common/PaymentMethodTest.php b/tests/PaymentMethodTest.php similarity index 79% rename from tests/Omnipay/Common/PaymentMethodTest.php rename to tests/PaymentMethodTest.php index 3d29aec6..de57cd77 100644 --- a/tests/Omnipay/Common/PaymentMethodTest.php +++ b/tests/PaymentMethodTest.php @@ -1,8 +1,8 @@ add('Omnipay', __DIR__);