From 339bce9659c17bd3c4b09a480cd6f5c442628d02 Mon Sep 17 00:00:00 2001 From: Myles Derham Date: Thu, 7 Feb 2019 23:30:48 +1300 Subject: [PATCH] inital commit --- .editorconfig | 15 +++ .gitignore | 5 + CHANGELOG.md | 6 ++ CONTRIBUTING.md | 13 +++ LICENSE | 21 ++++ README.md | 2 + bootstrap.php | 4 + composer.json | 37 +++++++ phpunit.xml.dist | 25 +++++ src/Gateway.php | 95 +++++++++++++++++ src/Message/AuthorizeRequest.php | 127 +++++++++++++++++++++++ src/Message/CompletePurchaseRequest.php | 27 +++++ src/Message/PurchaseRequest.php | 131 ++++++++++++++++++++++++ src/Message/PurchaseResponse.php | 53 ++++++++++ src/Message/Response.php | 39 +++++++ tests/GatewayTest.php | 94 +++++++++++++++++ tests/Mock/OrderSuccess.txt | 10 ++ 17 files changed, 704 insertions(+) create mode 100755 .editorconfig create mode 100755 .gitignore create mode 100755 CHANGELOG.md create mode 100755 CONTRIBUTING.md create mode 100755 LICENSE create mode 100755 README.md create mode 100644 bootstrap.php create mode 100755 composer.json create mode 100755 phpunit.xml.dist create mode 100755 src/Gateway.php create mode 100755 src/Message/AuthorizeRequest.php create mode 100755 src/Message/CompletePurchaseRequest.php create mode 100755 src/Message/PurchaseRequest.php create mode 100755 src/Message/PurchaseResponse.php create mode 100755 src/Message/Response.php create mode 100755 tests/GatewayTest.php create mode 100755 tests/Mock/OrderSuccess.txt diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..cd8eb86 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..cbfb80a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/vendor +composer.lock +composer.phar +phpunit.xml +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100755 index 0000000..56697fe --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Release Notes for Laybuy gateway for Omnipay + +## 0.9.0 - 2019-02-03 +### Added +- Initial release which includes basic tests and updated dependencies. +- Built for Omnipay 2. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100755 index 0000000..dfb8f23 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +Submit a pull request on Github to get started. + +## Testing +Test test test. If you're adding functionality ensure you also create a test to ensure we don't introduce breaking changes or bugs. + +## Running Tests + +``` bash +$ composer install +$ composer test +``` diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..ad38fbb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Myles Derham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..82d23ba --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# omnipay-laybuy +Laybuy gateway for Omnipay payment processing library diff --git a/bootstrap.php b/bootstrap.php new file mode 100644 index 0000000..573fcda --- /dev/null +++ b/bootstrap.php @@ -0,0 +1,4 @@ + + + + + ./tests/ + + + + + + + + ./src + + + diff --git a/src/Gateway.php b/src/Gateway.php new file mode 100755 index 0000000..4a74943 --- /dev/null +++ b/src/Gateway.php @@ -0,0 +1,95 @@ + '', + 'merchantSecret' => '', + 'testMode' => false, + ); + } + + /** + * @return mixed + */ + public function getMerchantId() + { + return $this->getParameter('merchantId'); + } + + /** + * @param mixed $value + * @return $this + */ + public function setMerchantId($value) + { + return $this->setParameter('merchantId', $value); + } + + /** + * @return mixed + */ + public function getMerchantSecret() + { + return $this->getParameter('merchantSecret'); + } + + /** + * @param mixed $value + * @return $this + */ + public function setMerchantSecret($value) + { + return $this->setParameter('merchantSecret', $value); + } + + /** + * Configuration Request. + * + * @param array $options + * @return \Omnipay\Laybuy\Message\AuthorizeRequest + */ + public function authorize(array $options = array()) + { + return $this->createRequest('\Omnipay\Laybuy\Message\AuthorizeRequest', $options); + } + + /** + * Authorize and immediately capture an amount on the customers card + * + * @param array $options + * @return \Omnipay\Common\Message\ResponseInterface + */ + public function purchase(array $options = array()) + { + return $this->createRequest('\Omnipay\Laybuy\Message\PurchaseRequest', $options); + } + + /** + * Handle return from off-site gateways after purchase + * + * @param array $options + * @return \Omnipay\Common\Message\ResponseInterface + */ + public function completePurchase(array $options = array()) + { + return $this->createRequest('\Omnipay\Laybuy\Message\CompletePurchaseRequest', $options); + } +} diff --git a/src/Message/AuthorizeRequest.php b/src/Message/AuthorizeRequest.php new file mode 100755 index 0000000..b6b9a74 --- /dev/null +++ b/src/Message/AuthorizeRequest.php @@ -0,0 +1,127 @@ +getParameter('merchantId'); + } + + /** + * @param mixed $value + * @return $this + * @throws \Omnipay\Common\Exception\RuntimeException + */ + public function setMerchantId($value) + { + return $this->setParameter('merchantId', $value); + } + + /** + * @return mixed + */ + public function getMerchantSecret() + { + return $this->getParameter('merchantSecret'); + } + + /** + * @param mixed $value + * @return $this + * @throws \Omnipay\Common\Exception\RuntimeException + */ + public function setMerchantSecret($value) + { + return $this->setParameter('merchantSecret', $value); + } + + /** + * @return array + */ + public function getHeaders() + { + $headers = []; + return $headers; + } + + public function getData() + { + $this->validate('merchantId'); + } + + /** + * @param mixed $data + * @return \Omnipay\Laybuy\Message\Response + * @throws \Guzzle\Http\Exception\RequestException + */ + public function sendData($data) + { + $endpoint = $this->getEndpoint(); + $httpMethod = $this->getHttpMethod(); + $httpRequest = $this->httpClient->createRequest($httpMethod, $endpoint); + $httpRequest->getCurlOptions()->set(CURLOPT_SSLVERSION, 6); // CURL_SSLVERSION_TLSv1_2 + $httpRequest->addHeader('Authorization', $this->buildAuthorizationHeader()); + $httpRequest->addHeader('Content-type', 'application/json'); + $httpRequest->addHeader('Accept', 'application/json'); + $httpRequest->setBody(json_encode($data)); + $httpResponse = $httpRequest->send(); + return $this->createResponse($httpResponse); + } + + /** + * @return string + */ + public function getHttpMethod() + { + return 'POST'; + } + + /** + * @return string + */ + protected function getEndpoint() + { + return $this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint; + } + + /** + * @param \Guzzle\Http\Message\Response $httpResponse + * @return array + */ + // protected function parseResponseData(Guzzle\Http\Message\Response $httpResponse) + // { + // return $httpResponse->json(); + // } + + /** + * @param mixed $data + * @return \Omnipay\Laybuy\Message\Response + */ + protected function createResponse($data) + { + return new Response($this, $data); + } + + /** + * @return string + */ + protected function buildAuthorizationHeader() + { + $merchantId = $this->getMerchantId(); + $merchantSecret = $this->getMerchantSecret(); + + return 'Basic ' . base64_encode($merchantId . ':' . $merchantSecret); + } +} diff --git a/src/Message/CompletePurchaseRequest.php b/src/Message/CompletePurchaseRequest.php new file mode 100755 index 0000000..0bbebc9 --- /dev/null +++ b/src/Message/CompletePurchaseRequest.php @@ -0,0 +1,27 @@ + $this->getToken() + ); + } + + /** + * @return string + */ + public function getEndpoint() + { + return parent::getEndpoint() . '/order/confirm'; + } +} diff --git a/src/Message/PurchaseRequest.php b/src/Message/PurchaseRequest.php new file mode 100755 index 0000000..5421034 --- /dev/null +++ b/src/Message/PurchaseRequest.php @@ -0,0 +1,131 @@ +getCard(); + + $givenNames = $card->getFirstName(); + $surname = $card->getLastName(); + + if (empty($surname) && false !== $pos = strrpos($givenNames, ' ')) { + $surname = substr($givenNames, $pos + 1); + $givenNames = substr($givenNames, 0, $pos); + } + + $returnUrl = $this->getReturnUrl(); + $cancelUrl = $this->getCancelUrl(); + + $data = array( + 'amount' => $this->getAmount(), + 'tax' => $this->getTaxAmount(), + 'currency' => $this->getCurrency(), + 'customer' => array( + 'firstName' => $givenNames, + 'lastName' => $surname, + 'email' => $card->getEmail(), + 'phone' => $card->getPhone(), + ), + 'billingAddress' => array( + 'name' => $card->getBillingName(), + 'address1' => $card->getBillingAddress1(), + 'address2' => $card->getBillingAddress2(), + 'postcode' => $card->getBillingPostcode(), + 'country' => $card->getBillingCountry(), + 'phone' => $card->getBillingPhone(), + ), + 'shippingAddress' => array( + 'name' => $card->getShippingName(), + 'address1' => $card->getShippingAddress1(), + 'address1' => $card->getShippingAddress2(), + 'postcode' => $card->getShippingPostcode(), + 'country' => $card->getShippingCountry(), + 'phone' => $card->getShippingPhone(), + ), + 'items' => $this->getItemData(), + 'returnUrl' => $returnUrl, + 'merchantReference' => $this->getTransactionReference(), + ); + + return $data; + } + + + /** + * @return array + * @throws \Omnipay\Common\Exception\InvalidRequestException + */ + public function getItemData() + { + $items = $this->getItems(); + $itemArray = array(); + + if ($items !== null) { + /** @var \Omnipay\Common\ItemInterface $item */ + foreach ($items as $item) { + $itemArray[] = array( + 'id' => $item->getName(), // TODO: create getSku() setter and getter + 'description' => $item->getName(), + 'quantity' => $item->getQuantity(), + 'price' => $this->formatPrice($item->getPrice()) + ); + } + } + + return $itemArray; + } + + /** + * @return string + */ + public function getEndpoint() + { + return parent::getEndpoint() . '/order/create'; + } + + /** + * @param mixed $data + * @return \Omnipay\Laybuy\Message\Response + */ + protected function createResponse($data) + { + return new PurchaseResponse($this, $data); + } + + /** + * @param string|float|int $amount + * @return null|string + * @throws \Omnipay\Common\Exception\InvalidRequestException + */ + protected function formatPrice($amount) + { + if ($amount) { + if (!is_float($amount) && + $this->getCurrencyDecimalPlaces() > 0 && + 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).' + ); + } + + return $this->formatCurrency($amount); + } + + return null; + } +} diff --git a/src/Message/PurchaseResponse.php b/src/Message/PurchaseResponse.php new file mode 100755 index 0000000..604c123 --- /dev/null +++ b/src/Message/PurchaseResponse.php @@ -0,0 +1,53 @@ +data['token']) && !empty($this->data->paymentUrl)); + } + + public function getRedirectUrl() + { + if ($this->isRedirect()) { + return (string) $this->data->paymentUrl; + } + } + + /** + * @return string|null + */ + public function getToken() + { + return isset($this->data->token) ? $this->data->token : null; + } + + /** + * @return string + */ + public function getTransactionReference() + { + return $this->getToken(); + } + + /** + * @return string + */ + public function getPaymentUrl() + { + return isset($this->data->paymentUrl) ? $this->data->paymentUrl : null; + } +} diff --git a/src/Message/Response.php b/src/Message/Response.php new file mode 100755 index 0000000..97a69c2 --- /dev/null +++ b/src/Message/Response.php @@ -0,0 +1,39 @@ +data) && $this->data->result == 'SUCCESS') { + return true; + } + + return false; + } + + public function isRedirect() + { + return true; + } + +} diff --git a/tests/GatewayTest.php b/tests/GatewayTest.php new file mode 100755 index 0000000..d55ad4e --- /dev/null +++ b/tests/GatewayTest.php @@ -0,0 +1,94 @@ +gateway = new Gateway($this->getHttpClient(), $this->getHttpRequest()); + + $this->options = array( + 'amount' => '10.00', + 'returnUrl' => 'https://www.example.com/return', + ); + + } + + public function testAuthorizeSuccess() + { + $this->setMockHttpResponse('OrderSuccess.txt'); + + $response = $this->gateway->authorize($this->options)->send(); + + $this->_testSuccessfulPurchase($response); + } + + private function _testSuccessfulPurchase($response) + { + $this->assertFalse($response->isSuccessful()); + $this->assertTrue($response->isRedirect()); + $this->assertNull($response->getTransactionReference()); + $this->assertNull($response->getMessage()); + } + + /** @test */ + // public function authorize() + // { + // $request = $this->gateway->authorize(); + // $this->assertInstanceOf('Omnipay\Laybuy\Message\AuthorizeRequest', $request); + // } + + /** @test */ + // public function authorizeRequest() + // { + // $this->setMockHttpResponse('OrderSuccess.txt'); + + // $response = $this->gateway->authorize()->send(); + // $contents = (string) $response->getData(); + + // $expected = [ + // "result" => "SUCCESS", + // "token" => "frOilqUU0DboUCiyRtnzH1VdBXnrj7kD39NWhgsD", + // "paymentUrl" => "https://payment.laybuy.com/pay/frOilqUU0DboUCiyRtnzH1VdBXnrj7kD39NWhgsD" + // ]; + + // $this->assertTrue($response->isSuccessful()); + // $this->assertEquals($expected, $contents[0]); + // } + + public function testPurchase() + { + $request = $this->gateway->purchase(array('amount' => '10.00')); + + $this->assertInstanceOf('Omnipay\Laybuy\Message\PurchaseRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + + public function testPurchaseReturn() + { + $request = $this->gateway->completePurchase(array('amount' => '10.00')); + + $this->assertInstanceOf('Omnipay\Laybuy\Message\CompletePurchaseRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + +} diff --git a/tests/Mock/OrderSuccess.txt b/tests/Mock/OrderSuccess.txt new file mode 100755 index 0000000..cf9c851 --- /dev/null +++ b/tests/Mock/OrderSuccess.txt @@ -0,0 +1,10 @@ +HTTP/1.1 200 OK +Date: Fri, 15 Feb 2013 19:19:21 GMT +Server: Apache +Content-Length: 136 +Connection: close +Content-Type: application/json; charset=utf-8 + +result=SUCCESS +token=frOilqUU0DboUCiyRtnzH1VdBXnrj7kD39NWhgsD +paymentUrl=https://payment.laybuy.com/pay/frOilqUU0DboUCiyRtnzH1VdBXnrj7kD39NWhgsD