diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..abc8bed
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+payabbhi.tar.gz
+.DS_Store
+dist/*
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dbcd938
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Paypermint
+
+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/Makefile b/Makefile
new file mode 100644
index 0000000..2b41fbc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+VERSION_FILE=VERSION
+VER=`cat $(VERSION_FILE)`
+
+release: init prepare archive cleanup
+
+init:
+ mkdir dist
+
+prepare:
+ cp VERSION payabbhi
+ markdown-pdf README.md
+ zip -r payabbhi.zip payabbhi
+
+
+archive:
+ zip -r payabbhi-prestashop-$(VER).zip payabbhi.zip README.pdf
+ tar -cvzf payabbhi-prestashop-$(VER).tar.gz payabbhi.zip README.pdf
+
+
+cleanup:
+ mv payabbhi-prestashop-$(VER).zip dist
+ mv payabbhi-prestashop-$(VER).tar.gz dist
+ rm payabbhi/VERSION
+ rm README.pdf
+ rm payabbhi.zip
+
+
+clean:
+ rm -rf dist
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d0ed6fc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+## Payabbhi Payments - Prestashop Integration
+
+This extension is built on Payabbhi PHP Library to provide seamless integration of [Payabbhi Checkout ](https://payabbhi.com/docs/checkout) with PrestaShop 1.6
+
+
+### Installation
+
+Make sure you have signed up for your [Payabbhi Account](https://payabbhi.com/docs/account) and downloaded the [API keys](https://payabbhi.com/docs/account/#api-keys) from the [Portal](https://payabbhi.com/portal).
+
+1. Unzip [payabbhi-prestashop-VERSION.zip](https://github.com/payabbhi/payabbhi-prestashop/releases).
+
+2. Navigate to `PrestaShop Back Office` -> `Modules and Services` and click on `Add a new module`.
+
+3. Browse for `payabbhi.zip` to add Payabbhi Payment Extension to PrestaShop.
+
+4. On successful upload, `payabbhi` folder should get added to PrestaShop installation directory as follows:
+
+```
+PrestaShop/
+ modules/
+ payabbhi/
+ config.xml
+ controllers/
+ index.php
+ logo.png
+ payabbhi-php/
+ payabbhi.php
+ VERSION
+ views/
+```
+
+4. After successful upload, navigate to `Modules List` ->`Payments and Gateways` and install `Payabbhi Checkout` as per on-screen instructions. If you do not find Payabbhi on the list, please use the search option to find it.
+
+5. Configure `Payabbhi` and Save the settings:
+ - [Access ID](https://payabbhi.com/docs/account/#api-keys)
+ - [Secret Key](https://payabbhi.com/docs/account/#api-keys)
+ - [payment_auto_capture](https://payabbhi.com/docs/api/#create-an-order)
+
+
+[Payabbhi Checkout](https://payabbhi.com/docs/checkout) is now enabled in PrestaShop.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..3eefcb9
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+1.0.0
diff --git a/payabbhi/config.xml b/payabbhi/config.xml
new file mode 100644
index 0000000..1045060
--- /dev/null
+++ b/payabbhi/config.xml
@@ -0,0 +1,13 @@
+
+
+ payabbhi
+
+
+
+
+
+
+ 1
+ 1
+
+
diff --git a/payabbhi/controllers/front/index.php b/payabbhi/controllers/front/index.php
new file mode 100644
index 0000000..01f7806
--- /dev/null
+++ b/payabbhi/controllers/front/index.php
@@ -0,0 +1,10 @@
+context->cart;
+ $payabbhi = new payabbhi();
+ $payabbhi->execPayment($cart);
+
+ $this->context->smarty->assign(array(
+ 'nbProducts' => $cart->nbProducts(),
+ 'total' => $cart->getOrderTotal(true, Cart::BOTH),
+ 'this_path' => $this->module->getPathUri(),
+ 'this_path_bw' => $this->module->getPathUri(),
+ 'this_path_ssl' => Tools::getShopDomainSsl(true, true).__PS_BASE_URI__.'modules/'.$this->module->name.'/'
+ )
+ );
+
+ $this->setTemplate('payment_execution.tpl');
+ }
+}
diff --git a/payabbhi/controllers/front/validation.php b/payabbhi/controllers/front/validation.php
new file mode 100644
index 0000000..60c43d3
--- /dev/null
+++ b/payabbhi/controllers/front/validation.php
@@ -0,0 +1,70 @@
+ $payment_id,
+ 'order_id' => $_REQUEST['order_id'],
+ 'payment_signature' => $_REQUEST['payment_signature'],
+ );
+
+ $cart = $this->context->cart;
+ $cart_id = $cart->id;
+
+ $payabbhi = new Payabbhi();
+ $success = true;
+
+ $accessID = Configuration::get('PAYABBHI_ACCESS_ID'); //TODO fetch from $this if possible
+ $secretKey = Configuration::get('PAYABBHI_SECRET_KEY');
+ $client = new \Payabbhi\Client($accessID, $secretKey);
+
+ try {
+ $client->utility->verifyPaymentSignature($attributes);
+ $payment = $client->payment->retrieve($payment_id);
+ } catch (\Payabbhi\Error $e) {
+ $success = false;
+ $error = 'Prestashop Error: Payment failed because signature verification error';
+ }
+
+ if ($success == true)
+ {
+ $customer = new Customer($cart->id_customer);
+ $total = (float) $cart->getOrderTotal(true, Cart::BOTH);
+ $payabbhi->validateOrder($cart_id, _PS_OS_PAYMENT_, $total, $payabbhi->displayName . ' (' . $payment->method . ')', '', array(), NULL, false, $customer->secure_key);
+
+ Logger::addLog("Payment Successful for Order#" . $cart_id . ". Payabbhi payment ID: " . $payment_id . ". Payabbhi order ID: " . $_REQUEST['order_id'], 1);
+
+ $order = new Order((int)$payabbhi->currentOrder);
+ $payments = $order->getOrderPayments();
+
+ if (!empty($payments)) {
+ $payments[0]->transaction_id = $payment_id;
+ $payments[0]->update();
+ }
+
+ $query = http_build_query(array(
+ 'controller' => 'order-confirmation',
+ 'id_cart' => (int) $cart->id,
+ 'id_module' => (int) $this->module->id,
+ 'id_order' => $payabbhi->currentOrder
+ ), '', '&');
+
+ $url = 'index.php?'. $query;
+ Tools::redirect($url);
+ }
+ else
+ {
+ Logger::addLog("Payment Failed for Order# ". $cart_id . "Error: ". $error, 4);
+ echo 'Error! Please contact the seller directly for assistance.';
+ echo 'Order Id: '.$cart_id.'';
+ echo 'Error: '.str_replace(' ', ' ', ucwords(str_replace('_', ' ', $response_code))).'';
+ }
+
+ }
+}
diff --git a/payabbhi/index.php b/payabbhi/index.php
new file mode 100644
index 0000000..01f7806
--- /dev/null
+++ b/payabbhi/index.php
@@ -0,0 +1,10 @@
+setAppInfo('app_name','app_version','app_url');
+```
+
+### Orders
+
+```php
+
+// Create order
+$order = $client->order->create(array('merchant_order_id' => $merchantOrderID,
+ 'amount' => $amount,
+ 'currency' => $currency));
+
+// Retrieve a particular order object
+$order = $client->order->retrieve($orderId);
+
+// Retrieve a set of order objects based on given filter params
+$orders = $client->order->all(array('count' => 2));
+
+// Retrieve a set of payments for a given order
+$payments = $client->order->retrieve($orderId)->payments();
+```
+
+### Payments
+
+```php
+// Retrieve all payments
+$payments = $client->payment->all();
+
+// Retrieve a particular payment
+$payment = $client->payment->retrieve($id);
+
+// Capture a payment
+$payment->capture();
+
+// Fully Refund a payment
+$refund = $payment->refund();
+
+// Retrieve a set of refund objects for a given payment with optional filter params
+$refunds = $payment->refunds();
+```
+
+
+### Refunds
+
+```php
+// Create a refund
+$fullRefund = $client->refund->create($paymentID);
+
+// Create a partial refund
+$partialRefund = $client->refund->create($paymentID, array('amount'=>$refundAmount));
+
+// Retrieve a set of orders with the given filter params
+$refunds = $client->refund->all(array('count' => 2));
+
+// Retrieve a particular refund object
+$refund = $client->refund->retrieve($refundId);
+```
+
+### Verifying payment signature
+
+```php
+$attributes = array(
+ 'payment_id' => $payment_id,
+ 'order_id' => $order_id,
+ 'payment_signature' => $payment_signature
+ );
+$client->utility->verifyPaymentSignature($attributes);
+
+```
+
+## Development
+
+Install dependencies:
+
+``` bash
+$ composer install
+```
+
+## Tests
+
+Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), set ACCESS_ID and SECRET_KEY as environment variable then you can run the test suite:
+
+```bash
+$ export ACCESS_ID=""
+$ export SECRET_KEY=""
+$ ./vendor/bin/phpunit
+```
+
+Or to run an individual test file:
+
+```bash
+$ export ACCESS_ID=""
+$ export SECRET_KEY=""
+$ ./vendor/bin/phpunit tests/PaymentTest.php
+```
diff --git a/payabbhi/payabbhi-php/VERSION b/payabbhi/payabbhi-php/VERSION
new file mode 100644
index 0000000..3eefcb9
--- /dev/null
+++ b/payabbhi/payabbhi-php/VERSION
@@ -0,0 +1 @@
+1.0.0
diff --git a/payabbhi/payabbhi-php/composer.json b/payabbhi/payabbhi-php/composer.json
new file mode 100644
index 0000000..f1c8680
--- /dev/null
+++ b/payabbhi/payabbhi-php/composer.json
@@ -0,0 +1,35 @@
+{
+ "name": "payabbhi/payabbhi-php",
+ "description": "Payabbhi PHP package provides client library for Payabbhi API's",
+ "keywords": [
+ "payabbhi",
+ "payment processing",
+ "api"
+ ],
+ "homepage": "https://payabbhi.com/",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Payabbhi Team",
+ "homepage": "http://github.com/payabbhi"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "ext-curl": "*",
+ "ext-json": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0",
+ "satooshi/php-coveralls": "~0.6.1"
+ },
+ "config": {
+ "bin-dir": "bin"
+ },
+ "autoload": {
+ "psr-0": {
+ "Payabbhi": "src/"
+ }
+ }
+}
diff --git a/payabbhi/payabbhi-php/init.php b/payabbhi/payabbhi-php/init.php
new file mode 100644
index 0000000..951e43d
--- /dev/null
+++ b/payabbhi/payabbhi-php/init.php
@@ -0,0 +1,33 @@
+
+
+
+ tests
+
+
+
+
+ src
+
+
+
+
+
+
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/ApiResource.php b/payabbhi/payabbhi-php/src/Payabbhi/ApiResource.php
new file mode 100644
index 0000000..1f51977
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/ApiResource.php
@@ -0,0 +1,262 @@
+id)) {
+ throw new Error\InvalidRequest("Object Identifier not set");
+ }
+ return $this->id;
+ }
+
+ protected function _retrieve($id)
+ {
+ return $this->_request(static::instanceUrl($id), "GET", null);
+ }
+
+ protected function _all($params = null)
+ {
+ $this->_validateParams($params);
+ return $this->_request(static::classUrl(), "GET", $params);
+ }
+
+ /**
+ * @param array $params
+ * @throws Error\InvalidRequest
+ */
+ public static function _validateParams($params = null)
+ {
+ if ($params && !is_array($params)) {
+ $message = "You must pass an array as the first argument to Payabbhi API method calls.";
+ throw new Error\InvalidRequest($message);
+ }
+ }
+
+
+ protected function _request($url, $method, $params)
+ {
+ $response = CurlClient::instance()->request($url, $method, $params);
+ if ((isset($response->object)) and
+ ($response->object == $this->getObject()))
+ {
+ $this->update($response);
+
+ return $this;
+ }
+ else
+ {
+ return static::buildObject($response);
+ }
+ }
+
+ public static function convertObjectToArray($values)
+ {
+ $results = array();
+ foreach ($values as $k => $v) {
+ if ($v instanceof Collection || $v instanceof Order || $v instanceof Payment || $v instanceof Refund) {
+ $results[$k] = $v->__toArray(true);
+ } elseif (is_array($v)) {
+ $results[$k] = self::convertObjectToArray($v);
+ } else {
+ $results[$k] = $v;
+ }
+ }
+ return $results;
+ }
+
+
+ protected static function buildObject($data)
+ {
+ $objects = static::getObjectsArray();
+ if (isset($data->object))
+ {
+
+ if (in_array($data->object, $objects))
+ {
+ $class = static::getObjectClass($data->object);
+ if ($class == "Payabbhi\List") {
+ $object = new Collection;
+ } else {
+ $object = new $class;
+ }
+
+ }
+ else
+ {
+ $object = new static;
+ }
+ }
+ else
+ {
+ $object = new static;
+ }
+
+ $object->update($data);
+
+ return $object;
+ }
+
+ protected static function getObjectsArray()
+ {
+ return array(
+ 'list',
+ 'payment',
+ 'refund',
+ 'order');
+ }
+
+ protected static function getObjectClass($name)
+ {
+ return __NAMESPACE__.'\\'.ucfirst($name);
+ }
+
+ protected function getObject()
+ {
+ $class = get_class($this);
+ $pos = strrpos($class, '\\');
+ $object = strtolower(substr($class, $pos + 1));
+ if ($object == 'list') {
+ $object = 'collection';
+ }
+
+ return $object;
+ }
+ public function update($data)
+ {
+ $attributes = array();
+
+ foreach ($data as $key => $value)
+ {
+ if (is_array($value))
+ {
+ if (static::isAssocArray($value) === false)
+ {
+ $collection = array();
+
+ foreach ($value as $v)
+ {
+ if (is_array($v))
+ {
+ $object = static::buildObject($v);
+ array_push($collection, $object);
+ }
+ else
+ {
+ array_push($collection, static::buildObject($v));
+ }
+ }
+
+ $value = $collection;
+ }
+ else
+ {
+ $value = static::buildObject($value);
+ }
+ }
+ else if(is_object($value))
+ {
+ $value = static::buildObject($value);
+ }
+
+ $attributes[$key] = $value;
+ }
+
+ $this->attributes = $attributes;
+ }
+
+ public static function isAssocArray($arr)
+ {
+ return array_keys($arr) !== range(0, count($arr) - 1);
+ }
+
+ public function toArray()
+ {
+ return $this->convertToArray($this->attributes);
+ }
+
+ protected function convertToArray($attributes)
+ {
+ $array = $attributes;
+
+ foreach ($attributes as $key => $value)
+ {
+ if (is_object($value))
+ {
+ $array[$key] = $value->toArray();
+ }
+ else if (is_array($value) and self::isAssocArray($value) == false)
+ {
+ $array[$key] = $this->convertToArray($value);
+ }
+ }
+
+ return $array;
+ }
+
+ public function __toJSON()
+ {
+ if (defined('JSON_PRETTY_PRINT')) {
+ return json_encode($this->__toArray(true), JSON_PRETTY_PRINT| JSON_UNESCAPED_SLASHES);
+ } else {
+ return json_encode($this->__toArray(true));
+ }
+ }
+
+
+ public function __toString()
+ {
+ return $this->__toJSON();
+ }
+
+ public function __toArray($recursive = false)
+ {
+ if ($recursive) {
+ return self::convertObjectToArray($this->attributes);
+ } else {
+ return $this->attributes;
+ }
+ }
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/ArrayableInterface.php b/payabbhi/payabbhi-php/src/Payabbhi/ArrayableInterface.php
new file mode 100644
index 0000000..17ca530
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/ArrayableInterface.php
@@ -0,0 +1,13 @@
+attributes['total_count']))
+ {
+ return $this->attributes['total_count'];
+ }
+
+ return $totalCount;
+ }
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/Error/Api.php b/payabbhi/payabbhi-php/src/Payabbhi/Error/Api.php
new file mode 100644
index 0000000..a353d0b
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/Error/Api.php
@@ -0,0 +1,8 @@
+httpStatus = $httpStatus;
+ $this->description = $description;
+ $this->field = $field;
+ $this->message = $this->errorMessage();
+ }
+
+ function __toString()
+ {
+ return $this->message;
+ }
+
+ public function getHttpStatus()
+ {
+ return $this->httpStatus;
+ }
+
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ public function getField()
+ {
+ return $this->field;
+ }
+
+ public function getHttpHeaders()
+ {
+ return $this->httpHeaders;
+ }
+
+ private function errorMessage()
+ {
+ $msg = "message: " . $this->description;
+ $msg = empty($this->httpStatus) ? $msg : ($msg . ", http_code: " . $this->httpStatus);
+ $msg = empty($this->field) ? $msg : ($msg . ", field: " . $this->field);
+
+ return $msg . "\n";
+ }
+
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/Error/Gateway.php b/payabbhi/payabbhi-php/src/Payabbhi/Error/Gateway.php
new file mode 100644
index 0000000..2bcb642
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/Error/Gateway.php
@@ -0,0 +1,8 @@
+timeout = (int) max($seconds, 0);
+ return $this;
+ }
+
+ /**
+ * @return int timeout
+ */
+ public function getTimeout()
+ {
+ return $this->timeout;
+ }
+ public function setConnectTimeout($seconds)
+ {
+ $this->connectTimeout = (int) max($seconds, 0);
+ return $this;
+ }
+
+ /**
+ * @return int connectTimeout
+ */
+ public function getConnectTimeout()
+ {
+ return $this->connectTimeout;
+ }
+
+ /**
+ * @return mixed curl handler
+ * @throws Error\Api
+ */
+ private function getCurlHandler()
+ {
+ $ch = curl_init();
+ if (!$ch) {
+ throw new Error\Api("Could not initialize curl handler");
+ }
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ return $ch;
+ }
+
+ /**
+ * @param mixed $ch
+ * @param string $path
+ * @param null|array $params
+ */
+ public static function buildGETRequest($ch, $path, $params)
+ {
+ $url = Client::baseUrl() . $path;
+ if ($params != null) {
+ foreach ($params as $key => $value){
+ if (gettype($value) == "boolean"){
+ $params[$key] = ($value) ? 'true' : 'false';
+ }
+ }
+ $query = http_build_query($params);
+ $url = $url . '?' . $query;
+ }
+ curl_setopt($ch, CURLOPT_URL, $url);
+ }
+
+ /**
+ * @param mixed $ch
+ * @param string $path
+ * @param null|array $params
+ * @return mixed json encoded body
+ * @throws Error\InvalidRequest
+ */
+ public static function buildPOSTRequest($ch, $path, $params)
+ {
+ $url = Client::baseUrl() . $path;
+ $body = json_encode($params);
+ switch (json_last_error()) {
+ case JSON_ERROR_NONE:
+ break;
+ default:
+ curl_close($ch);
+ throw new Error\InvalidRequest("Error in request payload formation");
+ }
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ curl_setopt($ch, CURLOPT_URL, $url);
+ }
+
+ /**
+ * @param mixed $ch
+ * @return string $url
+ */
+ public static function getPath($ch)
+ {
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ $parsed_url = parse_url($url);
+ $path = $parsed_url['path'];
+ $url = isset($parsed_url['query']) ? $path . '?' . $parsed_url['query'] : $path;
+ return $url;
+ }
+
+
+ /**
+ * @param mixed $ch
+ * @param string $method
+ * @param null|array $body
+ * @throws Error\Authentication
+ */
+ public static function signRequest($ch)
+ {
+ $access_id = Client::getAccessID();
+ $secret_key = Client::getSecretKey();
+ curl_setopt($ch, CURLOPT_USERPWD, $access_id . ":" . $secret_key);
+ }
+
+
+ /**
+ * @param mixed $ch
+ * @param string $path
+ * @param string $method
+ * @param null|array $params
+ * @throws Error\Api
+ */
+ private static function handleHTTPMethod($ch, $path, $method, $params)
+ {
+ switch ($method) {
+ case "GET":
+ self::buildGETRequest($ch, $path, $params);
+ self::signRequest($ch);
+ break;
+ case "POST":
+ self::buildPOSTRequest($ch, $path, $params);
+ self::signRequest($ch);
+ break;
+ default:
+ curl_close($ch);
+ $msg = 'Unexpected Method: ' . $method;
+ throw new Error\Api($msg, "method");
+ }
+ }
+
+ /**
+ * @param mixed $ch
+ * @throws Error\ApiConnection
+ */
+ public static function handleCurlError($ch)
+ {
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ $errno = curl_errno($ch);
+ $message = curl_error($ch);
+ curl_close($ch);
+ switch ($errno) {
+ case CURLE_COULDNT_CONNECT:
+ case CURLE_COULDNT_RESOLVE_HOST:
+ case CURLE_OPERATION_TIMEOUTED:
+ $msg = "Could not connect to Payabbhi ($url). Please check your " . "internet connection and try again, or";
+ break;
+ case CURLE_SSL_CACERT:
+ case CURLE_SSL_PEER_CERTIFICATE:
+ $msg = "Could not verify Payabbhi's SSL certificate. Please make sure " . "that your network is not intercepting certificates. " . "(Try going to $url in your browser.) " . "If this problem persists,";
+ break;
+ default:
+ $msg = "Unexpected error communicating with Payabbhi. " . "If this problem persists,";
+ }
+ $msg .= " let us know at support@payabbhi.com.";
+ $msg .= "\n\n(Network error [errno $errno]: $message)";
+ throw new Error\ApiConnection($msg);
+
+ }
+
+ /**
+ * @param mixed $ch
+ * @param mixed $result
+ * @return array
+ */
+ public static function parseRawResponse($ch, $result)
+ {
+ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+ $header = substr($result, 0, $header_size);
+ $body = substr($result, $header_size);
+ return array(
+ $http_code,
+ $header,
+ $body
+ );
+ }
+
+ /**
+ * @param mixed $ch
+ * @param mixed $result
+ * @throws Error\Api
+ */
+ public static function parseResult($ch, $result)
+ {
+ $decodedResult = json_decode($result);
+ switch (json_last_error()) {
+ case JSON_ERROR_NONE:
+ return $decodedResult;
+ default:
+ curl_close($ch);
+ throw new Error\Api("Something did not work as expected on our side", null, 500);
+ }
+ }
+
+ /**
+ * @param mixed $ch.
+ * @param array $decodedResult
+ *
+ * @throws Error\InvalidRequest if the error is caused by the invalid request parameters.
+ * @throws Error\Authentication if the error is caused by invalid keys.
+ * @throws Error\Api otherwise.
+ */
+ private static function checkHTTPStatusCode($ch, $decodedResult)
+ {
+ if (!curl_errno($ch)) {
+ switch ($http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE)) {
+ case 200: # OK
+ break;
+ case 400:
+ case 404:
+ curl_close($ch);
+ $decodedError = $decodedResult->error;
+ throw new Error\InvalidRequest($decodedError->message, $decodedError->field, $http_code);
+ case 401:
+ curl_close($ch);
+ $decodedError = $decodedResult->error;
+ throw new Error\Authentication($decodedError->message, $decodedError->field, $http_code);
+ case 500:
+ curl_close($ch);
+ $decodedError = $decodedResult->error;
+ throw new Error\Api($decodedError->message, $decodedError->field, $http_code);
+ case 502:
+ curl_close($ch);
+ $decodedError = $decodedResult->error;
+ if (empty($decodedError->message)) {
+ throw new Error\Api("Something did not work as expected on our side", null, $http_code);
+ }
+ throw new Error\Gateway($decodedError->message, $decodedError->field, $http_code);
+ default:
+ curl_close($ch);
+ $msg = 'Unexpected HTTP code: ' . $http_code;
+ throw new Error\Api($msg, null, $http_code);
+ }
+ }
+ }
+
+ private static function _formatAppInfo($appInfo)
+ {
+ if ($appInfo !== null) {
+ $string = $appInfo['name'];
+ if ($appInfo['version'] !== null) {
+ $string .= '/' . $appInfo['version'];
+ }
+ if ($appInfo['url'] !== null) {
+ $string .= ' (' . $appInfo['url'] . ')';
+ }
+ return $string;
+ } else {
+ return null;
+ }
+ }
+
+ private static function setDefaultHeaders($ch)
+ {
+ $appInfo = \Payabbhi\Client::getAppInfo();
+
+ $uaString = 'Payabbhi/v1 PhpBindings/' . Client::VERSION;
+
+ $langVersion = phpversion();
+ $uname = php_uname();
+
+ $httplib = 'unknown';
+ $ssllib = 'unknown';
+
+ if (function_exists('curl_version')) {
+ $curlVersion = curl_version();
+ $httplib = 'curl ' . $curlVersion['version'];
+ $ssllib = $curlVersion['ssl_version'];
+ }
+
+ $ua = array(
+ 'bindings_version' => Client::VERSION,
+ 'lang' => 'php',
+ 'lang_version' => $langVersion,
+ 'publisher' => 'payabbhi',
+ 'uname' => $uname,
+ 'httplib' => $httplib,
+ 'ssllib' => $ssllib,
+ );
+ if ($appInfo !== null) {
+ $uaString .= ' ' . self::_formatAppInfo($appInfo);
+ $ua['application'] = $appInfo;
+ }
+
+ $defaultHeaders = array(
+ 'Content-Type: application/json',
+ 'X-Payabbhi-Client-User-Agent: ' . json_encode($ua, JSON_UNESCAPED_SLASHES),
+ 'User-Agent: '. $uaString
+ );
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $defaultHeaders);
+ }
+
+ /**
+ * @param string $path.
+ * @param string $method
+ * @param null|array $params
+ *
+ * @return $json
+ */
+ public function request($path, $method, $params)
+ {
+ $ch = $this->getCurlHandler();
+ self::setDefaultHeaders($ch);
+ self::handleHTTPMethod($ch, $path, $method, $params);
+ $result = curl_exec($ch);
+ if (!$result) {
+ self::handleCurlError($ch);
+ }
+ $raw_resp = self::parseRawResponse($ch, $result);
+ $json = self::parseResult($ch, $raw_resp[2]);
+ self::checkHTTPStatusCode($ch, $json);
+ curl_close($ch);
+ return $json;
+ }
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/Order.php b/payabbhi/payabbhi-php/src/Payabbhi/Order.php
new file mode 100644
index 0000000..84411cf
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/Order.php
@@ -0,0 +1,61 @@
+_retrieve($id);
+ }
+
+ /**
+ * @param array|null $params
+ *
+ * @return Collection of Orders
+ */
+ public function all($params = null)
+ {
+ return $this->_all($params);
+
+ }
+
+ /**
+ * @return Collection of Payments
+ */
+ public function payments()
+ {
+ return $this->_request(static::instanceUrl($this->getObjectIdentifier()) . "/payments", "GET",null);
+ }
+
+ /**
+ * @param array|null $params.
+ *
+ * @return Order
+ */
+ public function create($params)
+ {
+ return $this->_request(static::classUrl(), "POST", $params);
+ }
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/Payment.php b/payabbhi/payabbhi-php/src/Payabbhi/Payment.php
new file mode 100644
index 0000000..f246789
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/Payment.php
@@ -0,0 +1,92 @@
+getObjectIdentifier()) . "/refunds", "GET", $params);
+ }
+
+ /**
+ * @param string $id The ID of the payment to capture.
+ *
+ * @return Payment
+ */
+ public function capture($params = null)
+ {
+ return self::_request(static::instanceUrl($this->getObjectIdentifier()) . "/capture", "POST", $params);
+ }
+
+ /**
+ * @param array|null $params
+ *
+ * @return Refund
+ */
+ public function refund($params = null)
+ {
+ $refund = new Refund;
+ $refund->create($this->getObjectIdentifier(), $params);
+ self::retrieve($this->getObjectIdentifier());
+ return $refund;
+ }
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/Refund.php b/payabbhi/payabbhi-php/src/Payabbhi/Refund.php
new file mode 100644
index 0000000..7de33ae
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/Refund.php
@@ -0,0 +1,53 @@
+attributes);
+ }
+
+ public function offsetExists($offset)
+ {
+ return (isset($this->attributes[$offset]));
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ $this->attributes[$offset] = $value;
+ }
+
+ public function offsetGet($offset)
+ {
+ return $this->attributes[$offset];
+ }
+
+ public function offsetUnset($offset)
+ {
+ unset($this->attributes[$offset]);
+ }
+
+ public function __get($key)
+ {
+ return $this->attributes[$key];
+ }
+
+ public function __set($key, $value)
+ {
+ return $this->attributes[$key] = $value;
+ }
+
+ public function __isset($key)
+ {
+ return (isset($this->attributes[$key]));
+ }
+
+ public function __unset($key)
+ {
+ unset($this->attributes[$key]);
+ }
+}
diff --git a/payabbhi/payabbhi-php/src/Payabbhi/Util/Util.php b/payabbhi/payabbhi-php/src/Payabbhi/Util/Util.php
new file mode 100644
index 0000000..915b0e0
--- /dev/null
+++ b/payabbhi/payabbhi-php/src/Payabbhi/Util/Util.php
@@ -0,0 +1,34 @@
+hashEquals($actualSignature, $expectedSignature)) === false)
+ {
+ throw new Error\SignatureVerification('Invalid signature passed');
+ }
+ return true;
+ }
+
+ private function hashEquals($actualSignature, $expectedSignature)
+ {
+ if (!is_string($actualSignature) || !is_string($expectedSignature)) {
+ return false;
+ }
+
+ if (strlen($actualSignature) !== strlen($expectedSignature)) {
+ return false;
+ }
+
+ $status = 0;
+ for ($i = 0; $i < strlen($actualSignature); $i++) {
+ $status |= ord($actualSignature[$i]) ^ ord($expectedSignature[$i]);
+ }
+ return $status === 0;
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/ApiConnectionErrorTest.php b/payabbhi/payabbhi-php/tests/ApiConnectionErrorTest.php
new file mode 100644
index 0000000..58460b4
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/ApiConnectionErrorTest.php
@@ -0,0 +1,50 @@
+assertEmpty($e->getHttpStatus());
+ $this->assertContains('errno 28',$e->getDescription());
+ $this->assertContains('errno 28',$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+
+ public function testInsecureCertificate()
+ {
+ try {
+ $ch = curl_init();
+ $url = "https://www.payabbhi.com/";
+ curl_setopt($ch, CURLOPT_URL, $url);
+ $result = curl_exec($ch);
+ } catch (\Payabbhi\Error\ApiConnection $e) {
+ $this->assertEmpty($e->getHttpStatus());
+ $message = "SSL certificate problem: Invalid certificate chain";
+ $msg = "Could not verify Payabbhi's SSL certificate. Please make sure " . "that your network is not intercepting certificates. " . "(Try going to $url in your browser.) " . "If this problem persists,";
+ $msg .= " let us know at support@payabbhi.com.";
+ $msg .= "\n\n(Network error [errno 60]: $message)";
+ $this->assertEquals($message,$e->getDescription());
+ $this->assertEquals($msg,$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/ApiResourceTest.php b/payabbhi/payabbhi-php/tests/ApiResourceTest.php
new file mode 100644
index 0000000..b0fe4be
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/ApiResourceTest.php
@@ -0,0 +1,42 @@
+assertEquals("apiresource", $result);
+ }
+
+ public function testClassUrl()
+ {
+ $result = ApiResource::classUrl();
+ $this->assertEquals("/api/v1/apiresources", $result);
+ }
+
+ public function testBadParams()
+ {
+ try {
+ ApiResource::_validateParams("123");
+ } catch (\Payabbhi\Error\InvalidRequest $e) {
+ $message = "You must pass an array as the first argument to Payabbhi API method calls.";
+ $this->assertNull($e->getHttpStatus());
+ $this->assertEquals($message,$e->getDescription());
+ $this->assertEquals("message: You must pass an array as the first argument to Payabbhi API method calls.\n",$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+
+ public function testArrayParams()
+ {
+ $result = ApiResource::_validateParams(Array("123"));
+ $this->assertNull($result);
+ }
+
+
+}
diff --git a/payabbhi/payabbhi-php/tests/AuthenticationErrorTest.php b/payabbhi/payabbhi-php/tests/AuthenticationErrorTest.php
new file mode 100644
index 0000000..2178f10
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/AuthenticationErrorTest.php
@@ -0,0 +1,22 @@
+payment->all();
+ $this->fail("Did not raise error");
+ } catch (\Payabbhi\Error\Authentication $e) {
+ $this->assertEquals(401, $e->getHttpStatus());
+ $this->assertEquals("Incorrect access_id or secret_key provided",$e->getDescription());
+ $this->assertEquals("message: Incorrect access_id or secret_key provided, http_code: 401\n",$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/ClientTest.php b/payabbhi/payabbhi-php/tests/ClientTest.php
new file mode 100644
index 0000000..2f59fab
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/ClientTest.php
@@ -0,0 +1,52 @@
+assertEquals("https://payabbhitest.com", $result);
+ \Payabbhi\Client::$apiBase = $result;
+ }
+
+ public function testAccessID()
+ {
+ $api = new \Payabbhi\Client("test_access_id","test_secret_key");
+ $this->assertEquals("test_access_id",\Payabbhi\Client::getAccessID());
+ }
+
+ public function testSecretKey()
+ {
+ $api = new \Payabbhi\Client("test_access_id","test_secret_key");
+ $this->assertEquals("test_secret_key",\Payabbhi\Client::getSecretKey());
+ }
+
+ /**
+ * @dataProvider AppInfoProvider
+ */
+ public function testAppInfo($name,$version,$url)
+ {
+ $api = new \Payabbhi\Client("test_access_id","test_secret_key");
+ $api->setAppInfo($name , $version, $url);
+ $info = \Payabbhi\Client::getAppInfo();
+
+ $this->assertEquals($name, $info['name']);
+ $this->assertEquals($version, $info['version']);
+ $this->assertEquals($url, $info['url']);
+
+ }
+
+ public function AppInfoProvider()
+ {
+ return [
+ ["TestPayabbhiApplication", null, null],
+ ["TestPayabbhiApplication","beta",null],
+ ["TestPayabbhiApplication" , "beta", "https://testpayabbhiapplication.com"]
+ ];
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/CurlClientTest.php b/payabbhi/payabbhi-php/tests/CurlClientTest.php
new file mode 100644
index 0000000..cdd5d7a
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/CurlClientTest.php
@@ -0,0 +1,264 @@
+assertSame(CurlClient::DEFAULT_TIMEOUT, $curl->getTimeout());
+ $this->assertSame(CurlClient::DEFAULT_CONNECT_TIMEOUT, $curl->getConnectTimeout());
+ $curl = $curl->setConnectTimeout(1)->setTimeout(10);
+ $this->assertSame(1, $curl->getConnectTimeout());
+ $this->assertSame(10, $curl->getTimeout());
+ $curl->setTimeout(-1);
+ $curl->setConnectTimeout(-999);
+ $this->assertSame(0, $curl->getTimeout());
+ $this->assertSame(0, $curl->getConnectTimeout());
+ $curl->setTimeout("a");
+ $curl->setConnectTimeout("b");
+ $this->assertSame(0, $curl->getTimeout());
+ $this->assertSame(0, $curl->getConnectTimeout());
+
+ }
+
+ public function testInvalidHTTPMethod()
+ {
+ try {
+ $method= "GETS";
+ CurlClient::instance()->request("https://www.google.com",$method,null);
+ } catch (Api $e){
+ $msg = 'Unexpected Method: ' . $method;
+ $this->assertNull($e->getHttpStatus());
+ $this->assertEquals($msg, $e->getDescription());
+ $this->assertEquals("message: $msg, field: method\n", $e->getMessage());
+ $this->assertEquals("method", $e->getField());
+ }
+
+ }
+
+ public function testUrlForGETRequestWithParams()
+ {
+ $ch = curl_init();
+ $base = \Payabbhi\Client::baseUrl();
+ \Payabbhi\Client::$apiBase = "https://payabbhi.com";
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ $params = array('foo'=>'bar',
+ 'baz'=>'boom',
+ 'cow'=>'milk',
+ 'php'=>'hypertext processor');
+ CurlClient::buildGETRequest($ch,"/api/v1/check",$params);
+ curl_exec($ch);
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ \Payabbhi\Client::$apiBase = $base;
+ $this->assertEquals($url,"https://payabbhi.com/api/v1/check?foo=bar&baz=boom&cow=milk&php=hypertext+processor");
+ }
+
+ public function testUrlForGETRequestWithNoParams()
+ {
+ $ch = curl_init();
+ $base = \Payabbhi\Client::baseUrl();
+ \Payabbhi\Client::$apiBase = "https://payabbhi.com";
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ CurlClient::buildGETRequest($ch,"/api/v1/check",null);
+ curl_exec($ch);
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ \Payabbhi\Client::$apiBase = $base;
+ $this->assertEquals($url,"https://payabbhi.com/api/v1/check");
+ }
+
+ public function testUrlForPOSTRequestWithParams()
+ {
+ $ch = curl_init();
+ $base = \Payabbhi\Client::baseUrl();
+ \Payabbhi\Client::$apiBase = "https://payabbhi.com";
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ $params = array('foo'=>'bar',
+ 'baz'=>'boom',
+ 'cow'=>'milk',
+ 'php'=>'hypertext processor');
+ CurlClient::buildPOSTRequest($ch,"/api/v1/check",$params);
+ curl_exec($ch);
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ \Payabbhi\Client::$apiBase = $base;
+ $this->assertEquals("https://payabbhi.com/api/v1/check",$url);
+ }
+
+ public function testUrlForPOSTRequestWithParamsAsNull()
+ {
+ $ch = curl_init();
+ $base = \Payabbhi\Client::baseUrl();
+ \Payabbhi\Client::$apiBase = "https://payabbhi.com";
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ CurlClient::buildPOSTRequest($ch,"/api/v1/check",null);
+ curl_exec($ch);
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+ \Payabbhi\Client::$apiBase = $base;
+ $this->assertEquals("https://payabbhi.com/api/v1/check",$url);
+ }
+
+ public function testErrorForInvalidPayload()
+ {
+ try {
+ $ch = curl_init();
+ $params = "\xB1\x31";
+ CurlClient::buildPOSTRequest($ch,"/api/v1/check",$params);
+ } catch (InvalidRequest $e){
+ $msg = "Error in request payload formation";
+ $this->assertNull($e->getHttpStatus());
+ $this->assertEquals($msg, $e->getDescription());
+ $this->assertEquals("message: $msg\n", $e->getMessage());
+ $this->assertNull($e->getField());
+ }
+ }
+
+ public function testGetPathForUrl()
+ {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ $url = "https://payabbhi.com/search?hl=en&client=firefox-a&hs=42F";
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_exec($ch);
+ $this->assertEquals(CurlClient::getPath($ch),"/search?hl=en&client=firefox-a&hs=42F");
+ }
+
+ /**
+ * @dataProvider CredentialProvider
+ */
+ public function testErrorForEmptyCredentials($accessid, $secretkey, $method)
+ {
+ try {
+ $ch = curl_init();
+ \Payabbhi\Client::$apiBase = "https://payabbhi.com";
+ $api = new \Payabbhi\Client($accessid,$secretkey);
+ $api->payment->all();
+ $this->fail("Did not raise error");
+ } catch (Authentication $e){
+ $msg = "Incorrect access_id or secret_key provided";
+ $this->assertEquals(401,$e->getHttpStatus());
+ $this->assertEquals($msg, $e->getDescription());
+ $this->assertEquals("message: $msg, http_code: 401\n", $e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+
+ public function CredentialProvider()
+ {
+ return [
+ ["","secret_key","GET"],
+ ["","secret_key","POST"],
+ ["access_id","","GET"],
+ ["access_id","","POST"],
+ ["","","GET"],
+ ["","","POST"]
+ ];
+ }
+
+ private function parseHeader($header)
+ {
+ $myarray=array();
+ $data=explode("\n",$header);
+ $myarray['status']=$data[0];
+ array_shift($data);
+ array_pop($data);
+ array_pop($data);
+ foreach($data as $part)
+ {
+ $middle=explode(":",$part);
+ $myarray[trim($middle[0])] = trim($middle[1]);
+ }
+ return $myarray;
+ }
+
+ public function testSignRequestInCurlHeaderForGET()
+ {
+ $ch = curl_init();
+ $methodGET = "GET";
+ $url = "http://payabbhi.com/search?hl=en&client=firefox-a&hs=42F";
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch,CURLINFO_HEADER_OUT,true);
+ $api = new \Payabbhi\Client("access_id","secret_key");
+ CurlClient::signRequest($ch,$methodGET);
+ curl_exec($ch);
+ $headerarray = self::parseHeader(curl_getinfo($ch,CURLINFO_HEADER_OUT));
+ $this->assertRegExp('/Basic/', $headerarray['Authorization']);
+ }
+
+ public function testSignRequestInCurlHeaderForPOST()
+ {
+ $ch = curl_init();
+ $methodPOST = "POST";
+ $url = "http://payabbhi.com/search?hl=en&client=firefox-a&hs=42F";
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch,CURLINFO_HEADER_OUT,true);
+ $api = new \Payabbhi\Client("access_id","secret_key");
+ $params = array('foo'=>'bar',
+ 'baz'=>'boom',
+ 'cow'=>'milk',
+ 'php'=>'hypertext processor');
+ $body =json_encode($params);
+ CurlClient::signRequest($ch,$methodPOST,$body);
+ curl_exec($ch);
+ $headerarray = self::parseHeader(curl_getinfo($ch,CURLINFO_HEADER_OUT));
+ $this->assertRegExp('/Basic/', $headerarray['Authorization']);
+ }
+
+ public function testNullResultForParseResult()
+ {
+ $ch = curl_init();
+ curl_exec($ch);
+ $this->assertNull(CurlClient::parseResult($ch,null));
+ }
+
+ public function testJsonResultForParseResult()
+ {
+ $ch = curl_init();
+ curl_exec($ch);
+ $this->assertEquals(CurlClient::parseResult($ch,'{"foo_bar": 12345}')->foo_bar,12345);
+ }
+
+ public function testErrorForBadJsonForParseResult()
+ {
+ try {
+ $ch = curl_init();
+ curl_exec($ch);
+ } catch (Api $e) {
+ $this->assertNull($e->getHttpStatus());
+ $msg = "Something did not work as expected on our side";
+ $this->assertEquals($msg, $e->getDescription());
+ $this->assertEquals("description: $msg\n", $e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+
+ public function testParsingRawHTTPResponse()
+ {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch,CURLINFO_HEADER_OUT,true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+ $url = "https://payabbhi.com/api/v1/payments";
+ curl_setopt($ch, CURLOPT_URL, $url);
+ $params = array('foo'=>'bar',
+ 'baz'=>'boom',
+ 'cow'=>'milk',
+ 'php'=>'hypertext processor');
+ $result =curl_exec($ch);
+ $resp = CurlClient::parseRawResponse($ch,$result);
+ $this->assertTrue(is_array($resp));
+ $this->assertEquals($resp[0],401);
+ $this->assertContains("Content-Type: application/json; charset=UTF-8",$resp[1]);
+ $this->assertCount(3,$resp);
+ }
+
+
+}
diff --git a/payabbhi/payabbhi-php/tests/InvalidRequestErrorTest.php b/payabbhi/payabbhi-php/tests/InvalidRequestErrorTest.php
new file mode 100644
index 0000000..3c0a419
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/InvalidRequestErrorTest.php
@@ -0,0 +1,36 @@
+payment->retrieve("abcd")->refund();
+ } catch (\Payabbhi\Error\InvalidRequest $e) {
+ $this->assertEquals(400, $e->getHttpStatus());
+ $this->assertEquals("The request has invalid parameters",$e->getDescription());
+ $this->assertEquals("message: The request has invalid parameters, http_code: 400, field: payment_id\n",$e->getMessage());
+ $this->assertEquals("payment_id", $e->getField());
+ }
+ }
+
+ public function testBadURLPath()
+ {
+ try {
+ $api = self::authorizeFromEnv();
+ CurlClient::instance()->request("/api/v2/payments","GET",null);
+ } catch (\Payabbhi\Error\InvalidRequest $e) {
+ $this->assertEquals(404, $e->getHttpStatus());
+ $this->assertEquals("Request URL GET /api/v2/payments does not exist. Please check documentation",$e->getDescription());
+ $this->assertEquals("message: Request URL GET /api/v2/payments does not exist. Please check documentation, http_code: 404\n",$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/OrderTest.php b/payabbhi/payabbhi-php/tests/OrderTest.php
new file mode 100644
index 0000000..f78a4d5
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/OrderTest.php
@@ -0,0 +1,18 @@
+order->all();
+ $this->assertTrue(is_int($a->total_count));
+ $this->assertTrue(is_string($a->object));
+ $this->assertTrue(is_array($a->data));
+ $this->assertEquals("list",($a->object));
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/PayabbhiTestCase.php b/payabbhi/payabbhi-php/tests/PayabbhiTestCase.php
new file mode 100644
index 0000000..330ffff
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/PayabbhiTestCase.php
@@ -0,0 +1,20 @@
+payment->all();
+ $this->assertTrue(is_int($a->total_count));
+ $this->assertTrue(is_string($a->object));
+ $this->assertTrue(is_array($a->data));
+ $this->assertEquals("list",($a->object));
+ }
+
+}
diff --git a/payabbhi/payabbhi-php/tests/RefundTest.php b/payabbhi/payabbhi-php/tests/RefundTest.php
new file mode 100644
index 0000000..9246d75
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/RefundTest.php
@@ -0,0 +1,18 @@
+refund->all();
+ $this->assertTrue(is_int($a->total_count));
+ $this->assertTrue(is_string($a->object));
+ $this->assertTrue(is_array($a->data));
+ $this->assertEquals("list",($a->object));
+ }
+}
diff --git a/payabbhi/payabbhi-php/tests/UtilTest.php b/payabbhi/payabbhi-php/tests/UtilTest.php
new file mode 100644
index 0000000..d50c268
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/UtilTest.php
@@ -0,0 +1,24 @@
+assertSame(Util::utf8($x), $x);
+
+ // Latin-1 string
+ $x = "\xe9";
+ $this->assertSame(Util::utf8($x), "\xc3\xa9");
+
+ // Not a string
+ $x = true;
+ $this->assertSame(Util::utf8($x), $x);
+ }
+}
+
+?>
diff --git a/payabbhi/payabbhi-php/tests/UtilityTest.php b/payabbhi/payabbhi-php/tests/UtilityTest.php
new file mode 100644
index 0000000..f6b045e
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/UtilityTest.php
@@ -0,0 +1,50 @@
+assertSame($api->utility->verifyPaymentSignature(array('payment_id'=>'pay_123','order_id'=>'order_test','payment_signature'=>'31b8de6a27e9911fe8828d80fda4c2c7cefdf332c06eeaa734a0708af6cafd87')),true);
+
+ }
+
+ public function testverifyPaymentSignatureError()
+ {
+ try {
+ $api = new \Payabbhi\Client("BKa70BUicoKuQnZ6_ZSOJIWbi6nCO8mL83-4DJTcxdU=", "exq4JxHbTtMS9IM5hnoPNkdMXKW-ws36y4RgGtzGeGg=");
+ $api->utility->verifyPaymentSignature(array('payment_id'=>'pay_123','order_id'=>'order_test','payment_signature'=>'wrong_signature'));
+ $this->fail("Did not raise error");
+ } catch (\Payabbhi\Error\SignatureVerification $e) {
+ $this->assertEquals("Invalid signature passed",$e->getDescription());
+ $this->assertEquals("message: Invalid signature passed\n",$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+
+ public function testverifySignatureError()
+ {
+ try {
+ $api = new \Payabbhi\Client("BKa70BUicoKuQnZ6_ZSOJIWbi6nCO8mL83-4DJTcxdU=", "exq4JxHbTtMS9IM5hnoPNkdMXKW-ws36y4RgGtzGeGg=");
+ $api->utility->verifySignature('pay_123&order_test', 'wrong_signature');
+ $this->fail("Did not raise error");
+ } catch (\Payabbhi\Error\SignatureVerification $e) {
+ $this->assertEquals("Invalid signature passed",$e->getDescription());
+ $this->assertEquals("message: Invalid signature passed\n",$e->getMessage());
+ $this->assertEmpty($e->getField());
+ }
+ }
+
+ public function testverifySignature()
+ {
+ $api = new \Payabbhi\Client("BKa70BUicoKuQnZ6_ZSOJIWbi6nCO8mL83-4DJTcxdU=", "exq4JxHbTtMS9IM5hnoPNkdMXKW-ws36y4RgGtzGeGg=");
+ $this->assertSame($api->utility->verifySignature('pay_123&order_test', '31b8de6a27e9911fe8828d80fda4c2c7cefdf332c06eeaa734a0708af6cafd87'),true);
+
+ }
+
+}
+
+?>
diff --git a/payabbhi/payabbhi-php/tests/bootstrap.php b/payabbhi/payabbhi-php/tests/bootstrap.php
new file mode 100644
index 0000000..9f569c0
--- /dev/null
+++ b/payabbhi/payabbhi-php/tests/bootstrap.php
@@ -0,0 +1,3 @@
+name = 'payabbhi';
+ $this->tab = 'payments_gateways';
+ $this->version = '1.0.0';
+ $this->author = 'Payabbhi Team';
+ $this->need_instance = 0;
+ $this->ps_versions_compliancy = array('min' => '1.6', 'max' => _PS_VERSION_);
+ $this->bootstrap = true;
+
+ $config = Configuration::getMultiple(array('PAYABBHI_ACCESS_ID', 'PAYABBHI_SECRET_KEY', 'PAYABBHI_PAYMENT_AUTO_CAPTURE'));
+
+ $accessID = $config['PAYABBHI_ACCESS_ID'];
+ $secretKey = $config['PAYABBHI_SECRET_KEY'];
+ $paymentAutoCapture = $config['PAYABBHI_PAYMENT_AUTO_CAPTURE'];
+
+ if (!empty($accessID)) {
+ $this->access_id = $config['PAYABBHI_ACCESS_ID'];
+ }
+
+ if (!empty($secretKey)){
+ $this->secret_key = $config['PAYABBHI_SECRET_KEY'];
+ }
+
+ if (!empty($paymentAutoCapture)) {
+ $this->payment_auto_capture = $config['PAYABBHI_PAYMENT_AUTO_CAPTURE'];
+ } else {
+ $this->payment_auto_capture = 'true';
+ }
+
+ parent::__construct();
+
+ $this->displayName = $this->l('Payabbhi');
+ $this->description = $this->l('Prestashop module for accepting payments with Payabbhi.');
+ $this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
+
+
+ if (!isset($this->access_id)) {
+ $this->warning = $this->l('Payabbhi Acccess ID not set.');
+ }
+
+ if (!isset($this->secret_key)) {
+ $this->warning = $this->l('Payabbhi Secret Key not set.');
+ }
+ }
+
+ public function install()
+ {
+ if (Shop::isFeatureActive()) {
+ Shop::setContext(Shop::CONTEXT_ALL);
+ }
+
+ if (!parent::install() || !$this->registerHook('payment') || !$this->registerHook('paymentReturn')) {
+ return false;
+ }
+
+ if (!Configuration::updateValue('PAYABBHI_ACCESS_ID', '')
+ || !Configuration::updateValue('PAYABBHI_SECRET_KEY', '')
+ || !Configuration::updateValue('PAYABBHI_PAYMENT_AUTO_CAPTURE', '')) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function uninstall()
+ {
+ if (!Configuration::deleteByName('PAYABBHI_ACCESS_ID')
+ || !Configuration::deleteByName('PAYABBHI_SECRET_KEY')
+ || !Configuration::deleteByName('PAYABBHI_PAYMENT_AUTO_CAPTURE')
+ || !parent::uninstall()) {
+ return false;
+ }
+ return true;
+ }
+
+ private function checkConfig($accessID, $secretKey)
+ {
+ if (!$accessID || empty($accessID)) {
+ return false;
+ }
+
+ if (!$secretKey || empty($secretKey)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function getContent()
+ {
+ $output = null;
+
+ if (Tools::isSubmit('submit'.$this->name))
+ {
+ $accessID = strval(Tools::getValue('PAYABBHI_ACCESS_ID'));
+ $secretKey = strval(Tools::getValue('PAYABBHI_SECRET_KEY'));
+ $paymentAutoCapture = strval(Tools::getValue('PAYABBHI_PAYMENT_AUTO_CAPTURE'));
+
+ if (!$this->checkConfig($accessID,$secretKey))
+ {
+ $output .= $this->displayError($this->l('Invalid Configuration value'));
+ }
+ else
+ {
+ Configuration::updateValue('PAYABBHI_ACCESS_ID', $accessID);
+ Configuration::updateValue('PAYABBHI_SECRET_KEY', $secretKey);
+ Configuration::updateValue('PAYABBHI_PAYMENT_AUTO_CAPTURE', $paymentAutoCapture);
+ $output .= $this->displayConfirmation($this->l('Settings updated'));
+ }
+ }
+ return $output.$this->displayForm();
+ }
+
+ public function displayForm()
+ {
+ // Get default language
+ $default_lang = (int)Configuration::get('PS_LANG_DEFAULT');
+
+ // Init Fields form array
+ $fields_form[0]['form'] = array(
+ 'legend' => array(
+ 'title' => $this->l('Settings'),
+ ),
+ 'input' => array(
+ array(
+ 'type' => 'text',
+ 'label' => $this->l('Access ID'),
+ 'desc' => $this->l('Access ID is available as part of API keys downloaded from the Portal'),
+ 'name' => 'PAYABBHI_ACCESS_ID',
+ 'size' => 20,
+ 'required' => true
+ ),
+ array(
+ 'type' => 'text',
+ 'label' => $this->l('Secret Key'),
+ 'desc' => $this->l('Secret Key is available as part of API keys downloaded from the Portal'),
+ 'name' => 'PAYABBHI_SECRET_KEY',
+ 'size' => 20,
+ 'required' => true
+ ),
+ array(
+ 'type' => 'select',
+ 'label' => $this->l('Payment Auto Capture'),
+ 'desc' => $this->l('Specify whether the payment should be captured automatically. Refer to Payabbhi API Reference.'),
+ 'name' => 'PAYABBHI_PAYMENT_AUTO_CAPTURE',
+ 'options' => array(
+ 'id' => 'id_option',
+ 'name' => 'name',
+ 'query' => array(
+ array(
+ 'id_option' => 'true',
+ 'name' => 'True'
+ ),
+ array(
+ 'id_option' => 'false',
+ 'name' => 'False'
+ )
+ )
+ )
+ )
+ ),
+ 'submit' => array(
+ 'title' => $this->l('Save'),
+ 'class' => 'btn btn-default pull-right'
+ )
+ );
+
+ $helper = new HelperForm();
+
+ // Module, token and currentIndex
+ $helper->module = $this;
+ $helper->name_controller = $this->name;
+ $helper->token = Tools::getAdminTokenLite('AdminModules');
+ $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name;
+
+ // Language
+ $helper->default_form_language = $default_lang;
+ $helper->allow_employee_form_lang = $default_lang;
+
+ // Title and toolbar
+ $helper->title = $this->displayName;
+ $helper->show_toolbar = true; // false -> remove toolbar
+ $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen.
+ $helper->submit_action = 'submit'.$this->name;
+ $helper->toolbar_btn = array(
+ 'save' =>
+ array(
+ 'desc' => $this->l('Save'),
+ 'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
+ '&token='.Tools::getAdminTokenLite('AdminModules'),
+ ),
+ 'back' => array(
+ 'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'),
+ 'desc' => $this->l('Back to list')
+ )
+ );
+
+ // Load current value
+ $helper->fields_value['PAYABBHI_ACCESS_ID'] = Configuration::get('PAYABBHI_ACCESS_ID');
+ $helper->fields_value['PAYABBHI_SECRET_KEY'] = Configuration::get('PAYABBHI_SECRET_KEY');
+ $helper->fields_value['PAYABBHI_PAYMENT_AUTO_CAPTURE'] = Configuration::get('PAYABBHI_PAYMENT_AUTO_CAPTURE');
+ return $helper->generateForm($fields_form);
+ }
+
+ public function hookPayment($params)
+ {
+ global $smarty,$cart;
+ $smarty->assign(array(
+ 'this_path' => $this->_path,
+ 'this_path_ssl' => Configuration::get('PS_FO_PROTOCOL').$_SERVER['HTTP_HOST'].__PS_BASE_URI__."modules/{$this->name}/"));
+ return $this->display(__FILE__, 'payment.tpl');
+ }
+
+ protected function verify_order_amount($payabbhi_order_id, $cart) {
+ $accessID = Configuration::get('PAYABBHI_ACCESS_ID');
+ $secretKey = Configuration::get('PAYABBHI_SECRET_KEY');
+
+ $client = new \Payabbhi\Client($accessID, $secretKey);
+
+ try {
+ $payabbhi_order = $client->order->retrieve($payabbhi_order_id);
+ } catch(Exception $e) {
+ return false;
+ }
+
+ $payabbhi_order_args = array(
+ 'id' => $payabbhi_order_id,
+ 'amount' => (int) (number_format($cart->getOrderTotal(true, 3), 2, '.', '')*100),
+ 'currency' => 'INR',
+ 'merchant_order_id' => (string) $cart->id,
+ );
+
+ $orderKeys = array_keys($payabbhi_order_args);
+
+ foreach ($orderKeys as $key)
+ {
+ if ($payabbhi_order_args[$key] !== $payabbhi_order[$key])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected function create_payabbhi_order($cart) {
+ global $cookie;
+
+ $accessID = Configuration::get('PAYABBHI_ACCESS_ID');
+ $secretKey = Configuration::get('PAYABBHI_SECRET_KEY');
+ $autoCapture = Configuration::get('PAYABBHI_PAYMENT_AUTO_CAPTURE');
+
+ if (!empty($autoCapture)) {
+ $paymentAutoCapture = ($autoCapture === 'true');
+ } else {
+ $paymentAutoCapture = true;
+ }
+ $orderAmount = number_format($cart->getOrderTotal(true, 3), 2, '.', '')*100;
+ $orderCurrency = 'INR';
+
+ $orderParams = array('merchant_order_id' => $cart->id,
+ 'amount' => $orderAmount,
+ 'currency' => $orderCurrency,
+ 'payment_auto_capture' => $paymentAutoCapture);
+
+ $client = new \Payabbhi\Client($accessID, $secretKey);
+ $payabbhi_order_id = $client->order->create($orderParams)->id;
+ $cookie->payabbhi_order_id = $payabbhi_order_id;
+ return $payabbhi_order_id;
+ }
+
+ public function execPayment($cart)
+ {
+ global $smarty;
+ global $cookie;
+
+ $invoice = new Address((int) $cart->id_address_invoice);
+ $customer = new Customer((int) $cart->id_customer);
+
+ $contact = $invoice->phone;
+
+ if (empty($contact)) {
+ $contact = $invoice->phone_mobile;
+ }
+
+ if ($cart->id_currency != 1)
+ {
+ $error = 'Payabbhi Error: Currency should only be in INR';
+ die(Tools::displayError($error));
+ }
+
+ try {
+ $payabbhi_order_id = $cookie->payabbhi_order_id;
+
+ if (($payabbhi_order_id === false) or
+ (($payabbhi_order_id and ($this->verify_order_amount($payabbhi_order_id, $cart)) === false)))
+ {
+ $payabbhi_order_id = $this->create_payabbhi_order($cart);
+ }
+
+ } catch (\Payabbhi\Error $e) {
+ die(Tools::displayError('Payabbhi Error: ' . $e->getMessage()));
+ } catch (Exception $e) {
+ die(Tools::displayError('Prestashop Error: ' . $e->getMessage()));
+ }
+
+ $checkoutArgs = array(
+ 'access_id' => Configuration::get('PAYABBHI_ACCESS_ID'),
+ 'order_id' => $payabbhi_order_id,
+ 'amount' => number_format($cart->getOrderTotal(true, 3), 2, '.', '')*100,
+ 'name' => Configuration::get('PS_SHOP_NAME'),
+ 'description' => 'Order #' . $cart->id,
+ 'prefill' => array(
+ 'name' => $invoice->firstname . ' ' . $invoice->lastname,
+ 'email' => $customer->email,
+ 'contact' => $contact
+ ),
+ 'notes' => array(
+ 'merchant_order_id' => (string) $cart->id
+ )
+ );
+
+ $checkout_url = 'https://checkout.payabbhi.com/v1/checkout.js';
+ $return_url = __PS_BASE_URI__."?fc=module&module=payabbhi&controller=validation";
+
+ $smarty->assign(array(
+ 'checkout_url' => $checkout_url,
+ 'return_url' => $return_url,
+ 'checkout_args' => Tools::jsonEncode($checkoutArgs),
+ 'cart_id' => $cart->id
+ ));
+
+ return $this->display(__FILE__, 'payment_execution.tpl');
+ }
+}
diff --git a/payabbhi/views/index.php b/payabbhi/views/index.php
new file mode 100644
index 0000000..01f7806
--- /dev/null
+++ b/payabbhi/views/index.php
@@ -0,0 +1,10 @@
+
+
+{capture name=path}
+{l s='Checkout' mod='payabbhi'}{$navigationPipe}{l s='Pay With Payabbhi'
+mod='payabbhi'} {/capture}
+
+
+ {l s='Order summary' mod='payabbhi'}
+
+
+{assign var='current_step' value='payment'}
+{include file="$tpl_dir./order-steps.tpl"}
+
+{if $nbProducts <= 0}
+ {l s='Your shopping cart is empty.' mod='payabbhi'}
+{else}
+
+
+ {l s='Payabbhi payment' mod='payabbhi'}
+
+
+
+ {l s='You have chosen to pay by payabbhi.Here is a short summary of your order:' mod='payabbhi'}
+
+
+
+ - {l s='The total amount of your order is' mod='payabbhi'}
+ {displayPrice price=$total} {if $use_taxes == 1} {l s='(tax incl.)' mod='payabbhi'} {/if}
+
+
+ - {l s='Please confirm your order by clicking "I confirm my order".' mod='payabbhi'}
+
+
+
+
+
+
+
+ {l s='Other payment methods' mod='payabbhi'}
+
+
+
+
+
+{/if}
+
+
diff --git a/payabbhi/views/templates/hook/index.php b/payabbhi/views/templates/hook/index.php
new file mode 100644
index 0000000..01f7806
--- /dev/null
+++ b/payabbhi/views/templates/hook/index.php
@@ -0,0 +1,10 @@
+
+
+ {l s='Pay with Card, NetBanking, Wallet' mod='payabbhi'}
+ (via Payabbhi)
+
+
+
+
diff --git a/payabbhi/views/templates/hook/payment_return.tpl b/payabbhi/views/templates/hook/payment_return.tpl
new file mode 100644
index 0000000..5ba9807
--- /dev/null
+++ b/payabbhi/views/templates/hook/payment_return.tpl
@@ -0,0 +1,5 @@
+{if $status == 'ok'}
+ Payment successful
+{else}
+ Payment failed
+{/if}
diff --git a/payabbhi/views/templates/index.php b/payabbhi/views/templates/index.php
new file mode 100644
index 0000000..01f7806
--- /dev/null
+++ b/payabbhi/views/templates/index.php
@@ -0,0 +1,10 @@
+