allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s

This commit is contained in:
2026-03-30 14:54:57 +07:00
parent 66aed7c4e8
commit b5e3a778ce
21316 changed files with 2777892 additions and 13 deletions

View File

@@ -0,0 +1,13 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = false
max_line_length = 120

View File

@@ -0,0 +1,56 @@
name: PHP Composer
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
tests:
name: Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
env:
- AUTOLOAD=0
- AUTOLOAD=1
php-version:
- "5.6"
- "7.0"
- "7.1"
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
- "8.2"
steps:
- uses: actions/checkout@v4
- name: Validate composer.json and composer.lock
run: composer validate --strict
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
# Docs: https://getcomposer.org/doc/articles/scripts.md
- name: Run test suite
run: php ./build.php ${AUTOLOAD}

4
vendor/fedapay/fedapay-php/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/vendor
.DS_Store
composer.lock
.phpunit.result.cache

181
vendor/fedapay/fedapay-php/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,181 @@
pipeline {
agent none
stages {
stage('Notify') {
steps {
slackSend (color: '#FFFF00', message: "STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
stage('test') {
parallel {
stage('php:5.5') {
agent {
docker {
image 'php:5.5'
args '-u root:sudo'
}
}
steps {
withCredentials([
file(credentialsId: 'ssh_private_key_file', variable: 'SSH_PRIVATE_KEY_FILE')
]) {
sh '''
bash -x jenkins-ci.sh
php vendor/bin/phpunit
php vendor/bin/phpcs --standard=PSR2 -n lib tests *.php
'''
}
}
post {
always {
sh 'rm -R vendor/'
}
}
}
stage('php:5.6') {
agent {
docker {
image 'php:5.6'
args '-u root:sudo'
}
}
steps {
withCredentials([
file(credentialsId: 'ssh_private_key_file', variable: 'SSH_PRIVATE_KEY_FILE')
]) {
sh '''
bash -x jenkins-ci.sh
php vendor/bin/phpunit
php vendor/bin/phpcs --standard=PSR2 -n lib tests *.php
'''
}
}
post {
always {
sh 'rm -R vendor/'
}
}
}
stage('php:7.0') {
agent {
docker {
image 'php:7.0'
args '-u root:sudo'
}
}
steps {
withCredentials([
file(credentialsId: 'ssh_private_key_file', variable: 'SSH_PRIVATE_KEY_FILE')
]) {
sh '''
bash -x jenkins-ci.sh
php vendor/bin/phpunit
php vendor/bin/phpcs --standard=PSR2 -n lib tests *.php
'''
}
}
post {
always {
sh 'rm -R vendor/'
}
}
}
stage('php:7.1') {
agent {
docker {
image 'php:7.1'
args '-u root:sudo'
}
}
steps {
withCredentials([
file(credentialsId: 'ssh_private_key_file', variable: 'SSH_PRIVATE_KEY_FILE')
]) {
sh '''
bash -x jenkins-ci.sh
php vendor/bin/phpunit
php vendor/bin/phpcs --standard=PSR2 -n lib tests *.php
'''
}
}
post {
always {
sh 'rm -R vendor/'
}
}
}
stage('php:7.2') {
agent {
docker {
image 'php:7.2'
args '-u root:sudo'
}
}
steps {
withCredentials([
file(credentialsId: 'ssh_private_key_file', variable: 'SSH_PRIVATE_KEY_FILE')
]) {
sh '''
bash -x jenkins-ci.sh
php vendor/bin/phpunit
php vendor/bin/phpcs --standard=PSR2 -n lib tests *.php
'''
}
}
post {
always {
sh 'rm -R vendor/'
}
}
}
}
}
stage('Trigger') {
agent any
when {
anyOf {
branch 'develop'
branch 'master'
}
}
steps {
withCredentials([usernamePassword(credentialsId: 'auth_access', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
sh '''
URL='https://jenkins.fedapay.com/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)'
CRUMB=$(curl -s $URL --user ${USERNAME}:${PASSWORD})
curl -X POST -H "$CRUMB" --user ${USERNAME}:${PASSWORD} https://jenkins.fedapay.com/job/fedapay-checkout/job/${BRANCH_NAME}/build
'''
}
}
}
}
post {
success {
slackSend (color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
failure {
slackSend (color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
}
}

21
vendor/fedapay/fedapay-php/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2017-2018 FedaPay
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, sub-license, 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 NON INFRINGEMENT. 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.

69
vendor/fedapay/fedapay-php/README.md vendored Normal file
View File

@@ -0,0 +1,69 @@
# FedaPay PHP [![Build Status](https://travis-ci.com/fedapay/fedapay-php.svg?branch=master)](https://travis-ci.org/fedapay/fedapay-php)
You can sign up for a FedaPay account at https://fedapay.com.
## Requirements
PHP 5.5 and later.
## Composer
You can install the bindings via [Composer](http://getcomposer.org/). Run the following command:
```bash
composer require fedapay/fedapay-php
```
To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/00-intro.md#autoloading):
```php
require_once('vendor/autoload.php');
```
## Manual Installation
If you do not wish to use Composer, you can download the [latest release](https://github.com/fedapay/fedapay-php/releases). Then, to use the bindings, include the `init.php` file.
```php
require_once('/path/to/fedapay-php/init.php');
```
## Dependencies
The bindings require the following extension in order to work properly:
- [`curl`](https://secure.php.net/manual/en/book.curl.php), although you can use your own non-cURL client if you prefer
- [`json`](https://secure.php.net/manual/en/book.json.php)
- [`mbstring`](https://secure.php.net/manual/en/book.mbstring.php) (Multibyte String)
If you use Composer, these dependencies should be handled automatically. If you install manually, you'll want to make sure that these extensions are available.
## Getting Started
Simple usage looks like:
## Documentation
Please see https://docs.fedapay.com/development/api for up-to-date documentation.
## Development
Install dependencies:
``` bash
composer install
```
## Tests
Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), then you can run the test suite:
```bash
./vendor/bin/phpunit
```
Or to run an individual test file:
```bash
./vendor/bin/phpunit tests/UtilTest.php
```

35
vendor/fedapay/fedapay-php/build.php vendored Normal file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env php
<?php
chdir(dirname(__FILE__));
$autoload = (int) $argv[1];
$returnStatus = null;
if (!$autoload) {
// Modify composer to not autoload FedaPay
$composer = json_decode(file_get_contents('composer.json'), true);
unset($composer['autoload']);
file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT));
}
passthru('composer update', $returnStatus);
if ($returnStatus !== 0) {
exit(1);
}
if ($autoload) {
// Only run CS on 1 of the 2 environments
passthru(
'./vendor/bin/phpcs --standard=PSR2 -n lib tests *.php',
$returnStatus
);
if ($returnStatus !== 0) {
exit(1);
}
}
$config = $autoload ? 'phpunit.xml' : 'phpunit.no_autoload.xml';
passthru("./vendor/bin/phpunit -c $config", $returnStatus);
if ($returnStatus !== 0) {
exit(1);
}

View File

@@ -0,0 +1,36 @@
{
"name": "fedapay/fedapay-php",
"description": "PHP library for FedaPay https://fedapay.com",
"keywords": [
"fedapay",
"payment processing",
"api"
],
"homepage": "https://fedapay.com/",
"license": "MIT",
"authors": [
{
"name": "FedaPay and contributors",
"homepage": "https://github.com/fedapay/fedapay-php/contributors"
}
],
"require": {
"php": ">=5.5",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.7 || ^9.0",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {
"psr-4": {
"FedaPay\\": "lib/"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"style": "vendor/bin/phpcs --standard=PSR2 -n lib tests *.php"
}
}

File diff suppressed because it is too large Load Diff

56
vendor/fedapay/fedapay-php/init.php vendored Normal file
View File

@@ -0,0 +1,56 @@
<?php
// FedaPay singleton
require(dirname(__FILE__) . '/lib/FedaPay.php');
// Utilities
require(dirname(__FILE__) . '/lib/Util/Inflector.php');
require(dirname(__FILE__) . '/lib/Util/Util.php');
require(dirname(__FILE__) . '/lib/Util/RandomGenerator.php');
// HttpClient
require(dirname(__FILE__) . '/lib/HttpClient/ClientInterface.php');
require(dirname(__FILE__) . '/lib/HttpClient/CurlClient.php');
// Errors
require(dirname(__FILE__) . '/lib/Error/Base.php');
require(dirname(__FILE__) . '/lib/Error/ApiConnection.php');
require(dirname(__FILE__) . '/lib/Error/InvalidRequest.php');
require(dirname(__FILE__) . '/lib/Error/SignatureVerification.php');
// API operations
require(dirname(__FILE__) . '/lib/ApiOperations/All.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Create.php');
require(dirname(__FILE__) . '/lib/ApiOperations/CreateInBatch.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Search.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Delete.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Request.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Retrieve.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Save.php');
require(dirname(__FILE__) . '/lib/ApiOperations/Update.php');
// Plumbing
require(dirname(__FILE__) . '/lib/Requestor.php');
require(dirname(__FILE__) . '/lib/FedaPayObject.php');
require(dirname(__FILE__) . '/lib/Resource.php');
// FedaPay API Resources
require(dirname(__FILE__) . '/lib/Account.php');
require(dirname(__FILE__) . '/lib/ApiKey.php');
require(dirname(__FILE__) . '/lib/Currency.php');
require(dirname(__FILE__) . '/lib/Balance.php');
require(dirname(__FILE__) . '/lib/Customer.php');
require(dirname(__FILE__) . '/lib/Event.php');
require(dirname(__FILE__) . '/lib/Log.php');
require(dirname(__FILE__) . '/lib/PhoneNumber.php');
require(dirname(__FILE__) . '/lib/Transaction.php');
require(dirname(__FILE__) . '/lib/Payout.php');
require(dirname(__FILE__) . '/lib/Page.php');
require(dirname(__FILE__) . '/lib/Invoice.php');
// OAuth
require(dirname(__FILE__) . '/lib/OAuth/Client.php');
// Webhooks
require(dirname(__FILE__) . '/lib/Webhook.php');
require(dirname(__FILE__) . '/lib/WebhookSignature.php');

View File

@@ -0,0 +1,37 @@
<?php
namespace FedaPay;
/**
* Class Account
*
* @property int $id
* @property string $name
* @property string $timezone
* @property string $country
* @property string $verify
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Account extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
use ApiOperations\Create;
use ApiOperations\Update;
use ApiOperations\Save;
use ApiOperations\Delete;
public static function light($params = [], $headers = [])
{
$path = static::resourcePath('light');
$className = static::className();
list($response, $opts) = static::_staticRequest('get', $path, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->$className;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FedaPay;
/**
* Class ApiKey
*
* @property int $id
* @property string $public_key
* @property string $private_key
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class ApiKey extends Resource
{
}

View File

@@ -0,0 +1,25 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait All
*/
trait All
{
/**
* Static method to retrive a list of resources
* @param array $params
* @param array $headers
*
* @return array FedaPay\FedaPayObject
*/
public static function all($params = [], $headers = [])
{
self::_validateParams($params);
$path = static::classPath();
list($response, $opts) = static::_staticRequest('get', $path, $params, $headers);
return \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Create
*/
trait Create
{
/**
* Static method to create a resources
* @param array $params
* @param array $headers
*
* @return Resource
*/
public static function create($params = [], $headers = [])
{
self::_validateParams($params);
$path = static::classPath();
$className = static::className();
list($response, $opts) = static::_staticRequest('post', $path, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->$className;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait CreateInBatch
*/
trait CreateInBatch
{
/**
* Create resources in batch
*
* @param array $params
* @param array $headers
* @return FedaPay\FedaPayObject
*/
public static function createInBatch($params = [], $headers = [])
{
$path = static::resourcePath('batch');
$className = static::className();
list($response, $opts) = static::_staticRequest('post', $path, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
$data = "{$className}_batch";
return $object->$data;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Update
*/
trait Delete
{
/**
* Send a detele request
* @param array $headers
*/
public function delete($headers = [])
{
$path = $this->instanceUrl();
static::_staticRequest('delete', $path, [], $headers);
return $this;
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Request
*/
trait Request
{
/**
* Validate request params
* @param array $params
* @throws Error\InvalidRequest
*/
protected static function _validateParams($params = null)
{
if ($params && !is_array($params)) {
$message = 'You must pass an array as the first argument to FedaPay API '
. 'method calls. (HINT: an example call to create a customer '
. "would be: \"FedaPay\\Customer::create(array('firstname' => toto, "
. "'lastname' => 'zoro', 'email' => 'admin@gmail.com', 'phone' => '66666666'))\")";
throw new FedaPay\Error\InvalidRequest($message);
}
}
/**
* Static method to send request
* @param string $method
* @param string $path
* @param array $params
* @param array $headers
*
* @return array
*/
protected static function _staticRequest($method, $path, $params = [], $headers = [])
{
$requestor = self::getRequestor();
$response = $requestor->request($method, $path, $params, $headers);
$options = [
'apiVersion' => \FedaPay\FedaPay::getApiVersion(),
'environment' => \FedaPay\FedaPay::getEnvironment()
];
return [$response, $options];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Retrieve
*/
trait Retrieve
{
/**
* Static method to retrive a resource
* @param mixed $id
* @return FedaPay\FedaPayObject
*/
public static function retrieve($id, $params = [], $headers = [])
{
$url = static::resourcePath($id);
$className = static::className();
list($response, $opts) = static::_staticRequest('get', $url, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->$className;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Save
*/
trait Save
{
/**
* Update the resource
* @param array $headers the request headers
*
* @return Resource the updated API resource
*/
public function save($headers = [])
{
$params = $this->serializeParameters();
$className = static::className();
$path = $this->instanceUrl();
list($response, $opts) = static::_staticRequest('put', $path, $params, $headers);
$klass = $opts['apiVersion'] . '/' . $className;
$json = $response[$klass];
$this->refreshFrom($json, $opts);
return $this;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Search
*/
trait Search
{
/**
* Static method to search resources
* @param array $params
* @param array $headers
*
* @return array FedaPay\FedaPayObject
*/
public static function search($q = '*', $params = [], $headers = [])
{
$params['search'] = $q;
self::_validateParams($params);
$path = static::resourcePath('search');
list($response, $opts) = static::_staticRequest('get', $path, $params, $headers);
return \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace FedaPay\ApiOperations;
/**
* trait Update
*/
trait Update
{
/**
* Static method to update a resource
* @param string $id The ID of the API resource to update.
* @param array $params The request params
* @param array $headers the request headers
*
* @return Resource the updated API resource
*/
public static function update($id, $params = [], $headers = [])
{
self::_validateParams($params);
$path = static::resourcePath($id);
$className = static::className();
list($response, $opts) = static::_staticRequest('put', $path, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->$className;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace FedaPay;
/**
* Class Balance
*
* @property int $id
* @property int $currency_id
* @property int $account_id
* @property int $amount
* @property string $mode
* @property string $deleted_at
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Balance extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
}

View File

@@ -0,0 +1,24 @@
<?php
namespace FedaPay;
/**
* Class Currency
*
* @property int $id
* @property string $name
* @property string $iso
* @property int $code
* @property string $prefix
* @property string $suffix
* @property string $div
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Currency extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace FedaPay;
/**
* Class Customer
*
* @property int $id
* @property string $firstname
* @property string $lastname
* @property string $email
* @property string $phone
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Customer extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
use ApiOperations\Create;
use ApiOperations\Update;
use ApiOperations\Save;
use ApiOperations\Delete;
}

View File

@@ -0,0 +1,12 @@
<?php
namespace FedaPay\Error;
/**
* Class ApiConnection
*
* @package FedaPay\Error
*/
class ApiConnection extends Base
{
}

View File

@@ -0,0 +1,91 @@
<?php
namespace FedaPay\Error;
use Exception;
/**
* Class Base
*
* @package FedaPay\Error
*/
class Base extends Exception
{
/**
* @var int
*/
private $httpStatus;
/**
* @var string
*/
private $httpBody;
/**
* @var json
*/
private $jsonBody;
/**
* @var string
*/
private $errorMessage;
/**
* @var Array
*/
private $errors;
public function __construct(
$message,
$httpStatus = null,
$httpBody = null,
$jsonBody = null,
$httpHeaders = null
) {
parent::__construct($message);
$this->httpStatus = $httpStatus;
$this->httpBody = $httpBody;
$this->jsonBody = $jsonBody;
$this->httpHeaders = $httpHeaders;
$this->errorMessage = isset($jsonBody["message"]) ? $jsonBody["message"]: null;
$this->errors = isset($jsonBody["errors"]) ? $jsonBody["errors"]: null;
}
public function getHttpStatus()
{
return $this->httpStatus;
}
public function getHttpBody()
{
return $this->httpBody;
}
public function getJsonBody()
{
return $this->jsonBody;
}
public function getHttpHeaders()
{
return $this->httpHeaders;
}
public function getErrorMessage()
{
return $this->errorMessage;
}
public function getErrors()
{
return $this->errors;
}
public function hasErrors()
{
return !empty($this->errors);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace FedaPay\Error;
/**
* Class InvalidRequest
*
* @package FedaPay\Error
*/
class InvalidRequest extends Base
{
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FedaPay\Error;
/**
* Class SignatureVerification
*
* @package FedaPay\Error
*/
class SignatureVerification extends Base
{
private $sigHeader;
public function __construct(
$message,
$sigHeader,
$httpBody = null
) {
parent::__construct($message, null, $httpBody, null, null);
$this->sigHeader = $sigHeader;
}
public function getSigHeader()
{
return $this->sigHeader;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace FedaPay;
/**
* Class Event
*
* @property int $id
* @property string $type
* @property string $entity
* @property int $object_id
* @property int $account_id
* @property string $object
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Event extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
}

View File

@@ -0,0 +1,289 @@
<?php
namespace FedaPay;
/**
* Class FedaPay
*
* @package FedaPay
*/
class FedaPay
{
const VERSION = '0.1.7';
// @var string The FedaPay API key to be used for requests.
protected static $apiKey = null;
// @var string The FedaPay API base to be used for requests.
protected static $apiBase = null;
// @var string|null The FedaPay token to be used for oauth requests.
protected static $token = null;
// @var string|null The account ID for connected accounts requests.
public static $accountId = null;
// @var string|null The Oauth client Id.
public static $oauthClientId = null;
// @var string|null The Oauth client Secret.
public static $oauthClientSecret = null;
// @var string The environment for the FedaPay API.
protected static $environment = 'sandbox';
// @var string The environment for the FedaPay API.
protected static $locale = null;
// @var string The api version.
protected static $apiVersion = 'v1';
// @var bool verify ssl certs.
protected static $verifySslCerts = true;
// @var string|null the ca bundle path.
protected static $caBundlePath = null;
// @var int Maximum number of request retries
public static $maxNetworkRetries = 0;
// @var float Maximum delay between retries, in seconds
private static $maxNetworkRetryDelay = 2.0;
// @var float Initial delay between retries, in seconds
private static $initialNetworkRetryDelay = 0.5;
/**
* @return string The API key used for requests.
*/
public static function getApiKey()
{
return self::$apiKey;
}
/**
* Sets the API key to be used for requests.
*
* @param string $apiKey
* @return void
*/
public static function setApiKey($apiKey)
{
self::$apiKey = $apiKey;
self::$token = null;
}
/**
* @return string The API base used for requests.
*/
public static function getApiBase()
{
return self::$apiBase;
}
/**
* Sets the API base to be used for requests.
*
* @param string $apiBase
* @return void
*/
public static function setApiBase($apiBase)
{
self::$apiBase = $apiBase;
}
/**
* @return string The token used for requests.
*/
public static function getToken()
{
return self::$token;
}
/**
* Sets the token to be used for requests.
*
* @param string $token
* @return void
*/
public static function setToken($token)
{
self::$token = $token;
self::$apiKey = null;
}
/**
* @return string The account id used for connected account.
*/
public static function getAccountId()
{
return self::$accountId;
}
/**
* Sets the account id to be used for connected account.
*
* @param string $token
* @return void
*/
public static function setAccountId($accountId)
{
self::$accountId = $accountId;
}
/**
* @return string The Oauth client id.
*/
public static function getOauthClientId()
{
return self::$oauthClientId;
}
/**
* Sets the Oauth client id.
*
* @param string $oauthClientId
* @return void
*/
public static function setOauthClientId($oauthClientId)
{
self::$oauthClientId = $oauthClientId;
}
/**
* @return string The Oauth client secret.
*/
public static function getOauthClientSecret()
{
return self::$oauthClientSecret;
}
/**
* Sets the Oauth client id.
*
* @param string $oauthClientId
* @return void
*/
public static function setOauthClientSecret($oauthClientSecret)
{
self::$oauthClientSecret = $oauthClientSecret;
}
/**
* @return string The API version used for requests.
*/
public static function getApiVersion()
{
return self::$apiVersion;
}
/**
* @param string $apiVersion The API version.
* @return void
*/
public static function setApiVersion($apiVersion)
{
self::$apiVersion = $apiVersion;
}
/**
* @return bool Determine if the request should verify SSL certificate.
*/
public static function getVerifySslCerts()
{
return self::$verifySslCerts;
}
/**
* @param bool $verify The verify ssl certificates value.
* @return void
*/
public static function setVerifySslCerts($verify)
{
self::$verifySslCerts = $verify;
}
/**
* @return string Return the path of the certificate.
*/
public static function getCaBundlePath()
{
if (!self::$caBundlePath) {
self::$caBundlePath = dirname(__FILE__) . '/../data/ca-certificates.crt';
}
return self::$caBundlePath;
}
/**
* @param string $path The path of the certificate.
* @return void
*/
public static function setCaBundlePath($path)
{
self::$caBundlePath = $path;
}
/**
* @return string | null The FedaPay environment
*/
public static function getEnvironment()
{
return self::$environment;
}
/**
* @param string $environment The API environment.
* @return void
*/
public static function setEnvironment($environment)
{
self::$environment = $environment;
}
/**
* @return string | null The FedaPay locale
*/
public static function getLocale()
{
return self::$locale;
}
/**
* @param string $locale The API locale.
* @return void
*/
public static function setLocale($locale)
{
self::$locale = $locale;
}
/**
* @return int Maximum number of request retries
*/
public static function getMaxNetworkRetries()
{
return self::$maxNetworkRetries;
}
/**
* @param int $maxNetworkRetries Maximum number of request retries
*/
public static function setMaxNetworkRetries($maxNetworkRetries)
{
self::$maxNetworkRetries = $maxNetworkRetries;
}
/**
* @return float Maximum delay between retries, in seconds
*/
public static function getMaxNetworkRetryDelay()
{
return self::$maxNetworkRetryDelay;
}
/**
* @return float Initial delay between retries, in seconds
*/
public static function getInitialNetworkRetryDelay()
{
return self::$initialNetworkRetryDelay;
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace FedaPay;
use FedaPay\Util\Util;
/**
* Class FedaPayObject
*
* @package FedaPay
*/
class FedaPayObject implements \ArrayAccess, \JsonSerializable
{
protected $_values;
public function __construct($id = null, $opts = null)
{
$this->_values = [];
if (is_array($id)) {
$this->refreshFrom($id, $opts);
} elseif ($id !== null) {
$this->id = $id;
}
}
// Standard accessor magic methods
public function __set($k, $v)
{
if ($v === '') {
throw new \InvalidArgumentException(
'You cannot set \''.$k.'\'to an empty string. '
.'We interpret empty strings as NULL in requests. '
.'You may set obj->'.$k.' = NULL to delete the property'
);
}
$this->_values[$k] = $v;
}
public function &__get($k)
{
// function should return a reference, using $nullval to return a reference to null
$nullval = null;
if (!empty($this->_values) && array_key_exists($k, $this->_values)) {
return $this->_values[$k];
} else {
$class = get_class($this);
error_log("FedaPay Notice: Undefined property of $class instance: $k");
return $nullval;
}
}
public function __isset($k)
{
return isset($this->_values[$k]);
}
public function __unset($k)
{
unset($this->_values[$k]);
}
// Magic method for var_dump output. Only works with PHP >= 5.6
public function __debugInfo()
{
return $this->_values;
}
// ArrayAccess methods
#[\ReturnTypeWillChange]
public function offsetSet($k, $v)
{
$this->$k = $v;
}
#[\ReturnTypeWillChange]
public function offsetExists($k)
{
return array_key_exists($k, $this->_values);
}
#[\ReturnTypeWillChange]
public function offsetUnset($k)
{
unset($this->$k);
}
#[\ReturnTypeWillChange]
public function offsetGet($k)
{
return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
}
public function keys()
{
return array_keys($this->_values);
}
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->_values;
}
public function __toJSON()
{
return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
}
public function __toString()
{
$class = get_class($this);
return $class . ' JSON: ' . $this->__toJSON();
}
public function __toArray($recursive = false)
{
if ($recursive) {
return Util::convertFedaPayObjectToArray($this->_values);
} else {
return $this->_values;
}
}
public function serializeParameters()
{
$params = [];
foreach ($this->_values as $key => $value) {
if ($key === 'id') {
continue;
}
if ($value instanceof FedaPayObject) {
$serialized = $value->serializeParameters();
if ($serialized) {
$params[$key] = $serialized;
}
} else {
$params[$key] = $value;
}
}
return $params;
}
public function refreshFrom($values, $opts)
{
if (!is_null($values)) {
if ($values instanceof FedaPayObject) {
$values = $values->__toArray(true);
}
foreach ($values as $k => $value) {
if (is_array($value)) {
$k = Util::stripApiVersion($k, $opts);
$this->_values[$k] = Util::arrayToFedaPayObject($value, $opts);
} else {
$this->_values[$k] = $value;
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FedaPay\HttpClient;
interface ClientInterface
{
/**
* @param string $method The HTTP method being used
* @param string $absUrl The URL being requested, including domain and protocol
* @param array $headers Headers to be used in the request (full strings, not KV pairs)
* @param array $params KV pairs for parameters. Can be nested for arrays and hashes
*
* @throws \FedaPay\Error\InvalidRequest
* @throws \FedaPay\Error\ApiConnection
* @return array An array whose first element is raw request body, second
* element is HTTP status code and third array of HTTP headers.
*/
public function request($method, $absUrl, $headers, $params);
}

View File

@@ -0,0 +1,344 @@
<?php
namespace FedaPay\HttpClient;
use FedaPay\FedaPay;
use FedaPay\Error;
use FedaPay\Util;
// cURL constants are not defined in PHP < 5.5
// @codingStandardsIgnoreStart
// PSR2 requires all constants be upper case. Sadly, the CURL_SSLVERSION
// constants do not abide by those rules.
// Note the values 1 and 6 come from their position in the enum that
// defines them in cURL's source code.
if (!defined('CURL_SSLVERSION_TLSv1')) {
define('CURL_SSLVERSION_TLSv1', 1);
}
if (!defined('CURL_SSLVERSION_TLSv1_2')) {
define('CURL_SSLVERSION_TLSv1_2', 6);
}
// @codingStandardsIgnoreEnd
if (!defined('CURL_HTTP_VERSION_2TLS')) {
define('CURL_HTTP_VERSION_2TLS', 4);
}
/**
* CurlClient from https://github.com/stripe/stripe-php
*/
class CurlClient implements ClientInterface
{
private static $instance;
public static function instance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
protected $defaultOptions;
protected $userAgentInfo;
/**
* CurlClient constructor.
*
* Pass in a callable to $defaultOptions that returns an array of CURLOPT_* values to start
* off a request with, or an flat array with the same format used by curl_setopt_array() to
* provide a static set of options. Note that many options are overridden later in the request
* call, including timeouts, which can be set via setTimeout() and setConnectTimeout().
*
* Note that request() will silently ignore a non-callable, non-array $defaultOptions, and will
* throw an exception if $defaultOptions returns a non-array value.
*
* @param array|callable|null $defaultOptions
*/
public function __construct($defaultOptions = null, $randomGenerator = null)
{
$this->defaultOptions = $defaultOptions;
$this->randomGenerator = $randomGenerator ?: new Util\RandomGenerator();
$this->initUserAgentInfo();
}
public function initUserAgentInfo()
{
$curlVersion = curl_version();
$this->userAgentInfo = [
'httplib' => 'curl ' . $curlVersion['version'],
'ssllib' => $curlVersion['ssl_version'],
];
}
public function getDefaultOptions()
{
return $this->defaultOptions;
}
public function getUserAgentInfo()
{
return $this->userAgentInfo;
}
// USER DEFINED TIMEOUTS
const DEFAULT_TIMEOUT = 80;
const DEFAULT_CONNECT_TIMEOUT = 30;
private $timeout = self::DEFAULT_TIMEOUT;
private $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT;
public function setTimeout($seconds)
{
$this->timeout = (int) max($seconds, 0);
return $this;
}
public function setConnectTimeout($seconds)
{
$this->connectTimeout = (int) max($seconds, 0);
return $this;
}
public function getTimeout()
{
return $this->timeout;
}
public function getConnectTimeout()
{
return $this->connectTimeout;
}
// END OF USER DEFINED TIMEOUTS
public function request($method, $absUrl, $params, $headers)
{
$method = strtolower($method);
$opts = [];
if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value
$opts = call_user_func_array($this->defaultOptions, func_get_args());
if (!is_array($opts)) {
throw new Error\ApiConnection("Non-array value returned by defaultOptions CurlClient callback");
}
} elseif (is_array($this->defaultOptions)) { // set default curlopts from array
$opts = $this->defaultOptions;
}
switch ($method) {
case 'post':
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = Util\Util::encodeParameters($params);
break;
case 'put':
$opts[CURLOPT_CUSTOMREQUEST] = 'PUT';
$opts[CURLOPT_POSTFIELDS] = Util\Util::encodeParameters($params);
break;
case 'get':
$opts[CURLOPT_HTTPGET] = 1;
if (count($params) > 0) {
$encoded = Util\Util::encodeParameters($params);
$absUrl = "$absUrl?$encoded";
}
break;
case 'delete':
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (count($params) > 0) {
$encoded = Util\Util::encodeParameters($params);
$absUrl = "$absUrl?$encoded";
}
break;
default:
throw new Error\InvalidRequest("Unrecognized method $method");
}
// It is only safe to retry network failures on POST requests if we
// add an Idempotency-Key header
if (($method == 'post') && (FedaPay::$maxNetworkRetries > 0)) {
if (!isset($headers['Idempotency-Key'])) {
array_push($headers, 'Idempotency-Key: ' . $this->randomGenerator->uuid());
}
}
// Create a callback to capture HTTP headers for the response
$rheaders = [];
$headerCallback = function ($curl, $header_line) use (&$rheaders) {
// Ignore the HTTP request line (HTTP/1.1 200 OK)
if (strpos($header_line, ":") === false) {
return strlen($header_line);
}
list($key, $value) = explode(":", trim($header_line), 2);
$rheaders[trim($key)] = trim($value);
return strlen($header_line);
};
// By default for large request body sizes (> 1024 bytes), cURL will
// send a request without a body and with a `Expect: 100-continue`
// header, which gives the server a chance to respond with an error
// status code in cases where one can be determined right away (say
// on an authentication problem for example), and saves the "large"
// request body from being ever sent.
//
// Unfortunately, the bindings don't currently correctly handle the
// success case (in which the server sends back a 100 CONTINUE), so
// we'll error under that condition. To compensate for that problem
// for the time being, override cURL's behavior by simply always
// sending an empty `Expect:` header.
array_push($headers, 'Expect: ');
$opts[CURLOPT_URL] = $absUrl;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout;
$opts[CURLOPT_TIMEOUT] = $this->timeout;
$opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
$opts[CURLOPT_HTTPHEADER] = $headers;
$opts[CURLOPT_CAINFO] = FedaPay::getCABundlePath();
if (!FedaPay::getVerifySslCerts()) {
$opts[CURLOPT_SSL_VERIFYPEER] = false;
}
// For HTTPS requests, enable HTTP/2, if supported
$opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2TLS;
list($rbody, $rcode) = $this->executeRequestWithRetries($opts, $absUrl);
return [$rbody, $rcode, $rheaders];
}
/**
* @param array $opts cURL options
*/
private function executeRequestWithRetries($opts, $absUrl)
{
$numRetries = 0;
while (true) {
$rcode = 0;
$errno = 0;
$curl = curl_init();
curl_setopt_array($curl, $opts);
$rbody = curl_exec($curl);
if ($rbody === false) {
$errno = curl_errno($curl);
$message = curl_error($curl);
} else {
$rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
}
curl_close($curl);
if ($this->shouldRetry($errno, $rcode, $numRetries)) {
$numRetries += 1;
$sleepSeconds = $this->sleepTime($numRetries);
usleep(intval($sleepSeconds * 1000000));
} else {
break;
}
}
if ($rbody === false) {
$this->handleCurlError($absUrl, $errno, $message, $numRetries);
}
return [$rbody, $rcode];
}
/**
* @param string $url
* @param int $errno
* @param string $message
* @param int $numRetries
* @throws Error\ApiConnection
*/
private function handleCurlError($url, $errno, $message, $numRetries)
{
switch ($errno) {
case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED:
$msg = "Could not connect to FedaPay ($url). Please check your "
. "internet connection and try again. If this problem persists";
break;
case CURLE_SSL_CACERT:
case CURLE_SSL_PEER_CERTIFICATE:
$msg = "Could not verify FedaPay'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 FedaPay. "
. "If this problem persists,";
}
$msg .= " let us know at support@fedapay.com.";
$msg .= "\n\n(Network error [errno $errno]: $message)";
if ($numRetries > 0) {
$msg .= "\n\nRequest was retried $numRetries times.";
}
throw new Error\ApiConnection($msg);
}
/**
* Checks if an error is a problem that we should retry on. This includes both
* socket errors that may represent an intermittent problem and some special
* HTTP statuses.
* @param int $errno
* @param int $rcode
* @param int $numRetries
* @return bool
*/
private function shouldRetry($errno, $rcode, $numRetries)
{
if ($numRetries >= FedaPay::getMaxNetworkRetries()) {
return false;
}
// Retry on timeout-related problems (either on open or read).
if ($errno === CURLE_OPERATION_TIMEOUTED) {
return true;
}
// Destination refused the connection, the connection was reset, or a
// variety of other connection failures. This could occur from a single
// saturated server, so retry in case it's intermittent.
if ($errno === CURLE_COULDNT_CONNECT) {
return true;
}
// 409 conflict
if ($rcode === 409) {
return true;
}
return false;
}
private function sleepTime($numRetries)
{
// Apply exponential backoff with $initialNetworkRetryDelay on the
// number of $numRetries so far as inputs. Do not allow the number to exceed
// $maxNetworkRetryDelay.
$sleepSeconds = min(
FedaPay::getInitialNetworkRetryDelay() * 1.0 * pow(2, $numRetries - 1),
FedaPay::getMaxNetworkRetryDelay()
);
// Apply some jitter by randomizing the value in the range of
// ($sleepSeconds / 2) to ($sleepSeconds).
$sleepSeconds *= 0.5 * (1 + $this->randomGenerator->randFloat());
// But never sleep less than the base sleep seconds.
$sleepSeconds = max(FedaPay::getInitialNetworkRetryDelay(), $sleepSeconds);
return $sleepSeconds;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace FedaPay;
/**
* Class Invoice
*
* @property int $id
* @property integer $number
* @property string $reference
* @property string $status
* @property integer $tax
* @property string $discount_type
* @property integer $discount_amount
* @property integer $ttc
* @property integer $sub_total
* @property integer $discount
* @property integer $before_tax
* @property integer $tax_amount
* @property integer $total_amount_paid
* @property string $notes
* @property integer $invoice_products_count
* @property string $due_at
* @property string $sent_at
* @property array $paid_at
* @property string $partially_paid_at
* @property int $customer_id
* @property int $currency_id
* @property int $account_id
* @property string $created_at
* @property string $updated_at
* @property string $deleted_at
*
* @package FedaPay
*/
class Invoice extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
use ApiOperations\Create;
use ApiOperations\Update;
use ApiOperations\Save;
use ApiOperations\Delete;
public static function verify($reference, $params = [], $headers = [])
{
$base = static::resourcePath($reference);
$url = "$base/verify";
list($response, $opts) = static::_staticRequest('get', $url, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->invoice_verify;
}
}

28
vendor/fedapay/fedapay-php/lib/Log.php vendored Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace FedaPay;
/**
* Class Log
*
* @property int $id
* @property string $method
* @property string $url
* @property string $status
* @property string $ip_address
* @property string $version
* @property string $source
* @property string $query
* @property string $body
* @property string $response
* @property int $account_id
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Log extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace FedaPay\OAuth;
use FedaPay\Resource;
use FedaPay\FedaPay;
/**
* CurlClient from https://github.com/stripe/stripe-php
*/
class Client extends Resource
{
public static function grantClientCredentials()
{
list($response, $opts) = static::_staticRequest(
'post', '/oauth/token',
[
'grant_type' => 'client_credentials',
'client_id' => FedaPay::getOauthClientId(),
'client_secret' => FedaPay::getOauthClientSecret()
]
);
return \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
}
}

57
vendor/fedapay/fedapay-php/lib/Page.php vendored Normal file
View File

@@ -0,0 +1,57 @@
<?php
namespace FedaPay;
/**
* Class Page
*
* @property int $id
* @property string $name
* @property string $description
* @property string $reference
* @property string $published
* @property string $amount
* @property string $enable_phone_number
* @property string $callback_url
* @property array $custom_fields
* @property string $image_url
* @property int $account_id
* @property int $currency_id
* @property string $created_at
* @property string $updated_at
* @property string $deleted_at
*
* @package FedaPay
*/
class Page extends Resource
{
use ApiOperations\All;
use ApiOperations\Retrieve;
use ApiOperations\Create;
use ApiOperations\Update;
use ApiOperations\Save;
use ApiOperations\Delete;
public static function verify($reference, $params = [], $headers = [])
{
$base = static::resourcePath($reference);
$url = "$base/verify";
list($response, $opts) = static::_staticRequest('get', $url, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->page_verify;
}
public static function light($reference, $params = [], $headers = [])
{
$base = static::resourcePath($reference);
$url = "$base/light";
$className = static::className();
list($response, $opts) = static::_staticRequest('get', $url, $params, $headers);
$object = \FedaPay\Util\Util::arrayToFedaPayObject($response, $opts);
return $object->$className;
}
}

View File

@@ -0,0 +1,197 @@
<?php
namespace FedaPay;
use FedaPay\Util\Util;
/**
* Class Payout
*
* @property int $id
* @property string $reference
* @property string $amount
* @property string $status
* @property int $customer_id
* @property int $balance_id
* @property string $mode
* @property int $last_error_code
* @property string $last_error_message
* @property string $created_at
* @property string $updated_at
* @property string $scheduled_at
* @property string $sent_at
* @property string $failed_at
* @property string $deleted_at
*
* @package FedaPay
*/
class Payout extends Resource
{
use ApiOperations\All;
use ApiOperations\Search;
use ApiOperations\Retrieve;
use ApiOperations\Create;
use ApiOperations\CreateInBatch;
use ApiOperations\Update;
use ApiOperations\Save;
use ApiOperations\Delete;
/**
* @param array|string|null $params
* @param array|string|null $headers
*
* @return Payout.
*/
protected function _start($params, $headers)
{
$path = static::resourcePath('start');
list($response, $opts) = static::_staticRequest('put', $path, $params, $headers);
$object = Util::arrayToFedaPayObject($response, $opts);
$this->refreshFrom($object->payouts[0], $opts);
return $this;
}
/**
* @param array|string|null $params
* @param array|string|null $headers
*
* @return FedaPayObject.
*/
protected static function _startAll($params, $headers)
{
$path = static::resourcePath('start');
list($response, $opts) = static::_staticRequest('put', $path, $params, $headers);
return Util::arrayToFedaPayObject($response, $opts);
}
/**
* Start the payout
* @param string $scheduled_at
* @param array|string|null $params
*
* @return FedaPay\FedaPayObject
*/
public function schedule($scheduled_at, $params = [], $headers = [])
{
$scheduled_at = Util::toDateString($scheduled_at);
$payout_params = [ 'id' => $this->id, 'scheduled_at' => $scheduled_at ];
if (isset($params['phone_number'])) {
$payout_params['phone_number'] = $params['phone_number'];
unset($params['phone_number']); // Remove phone_number from params
}
$_params = [ 'payouts' => [ $payout_params ] ];
$params = array_merge($_params, $params);
return $this->_start($params, $headers);
}
/**
* Send the payout now
* @param array|string|null $params
* @param array|string|null $headers
*
* @return FedaPay\FedaPayObject
*/
public function sendNow($params = [], $headers = [])
{
$payout_params = [ 'id' => $this->id ];
if (isset($params['phone_number'])) {
$payout_params['phone_number'] = $params['phone_number'];
unset($params['phone_number']); // Remove phone_number from params
}
$_params = [ 'payouts' => [$payout_params] ];
$params = array_merge($_params, $params);
return $this->_start($params, $headers);
}
/**
* Start a scheduled payout
*
* @param array $payouts list of payouts id to start. One at least
* @param null|DateTime $scheduled_at If null, payouts should be send now.
* @param array $headers
* @return FedaPay\FedaPayObject
*/
public static function scheduleAll($payouts = [], $params = [], $headers = [])
{
$items = [];
foreach ($payouts as $key => $payout) {
$item = [];
if (!array_key_exists('id', $payout)) {
throw new \InvalidArgumentException(
'Invalid id argument. You must specify payout id.'
);
}
$item['id'] = $payout['id'];
if (array_key_exists('scheduled_at', $payout)) {
$item['scheduled_at'] = Util::toDateString($payout['scheduled_at']);
}
if (isset($params[$key]['phone_number'])) {
$item['phone_number'] = $params[$key]['phone_number'];
unset($params[$key]['phone_number']); // Remove phone_number from params
}
$items[] = $item;
}
$_params = [
'payouts' => $items
];
$params = array_merge($_params, $params);
return self::_startAll($params, $headers);
}
/**
* Send all payouts now
*
* @param array $payouts list of payouts id to start. One at least
* @param array $params If null, payouts should be send now.
* @param array $headers
* @return FedaPay\FedaPayObject
*/
public static function sendAllNow($payouts = [], $params = [], $headers = [])
{
$items = [];
foreach ($payouts as $key => $payout) {
$item = [];
if (!array_key_exists('id', $payout)) {
throw new \InvalidArgumentException(
'Invalid id argument. You must specify payout id.'
);
}
$item['id'] = $payout['id'];
if (isset($params[$key]['phone_number'])) {
$item['phone_number'] = $params[$key]['phone_number'];
unset($params[$key]['phone_number']); // Remove phone_number from params
}
$items[] = $item;
}
$_params = [
'payouts' => $items
];
$params = array_merge($_params, $params);
return self::_startAll($params, $headers);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FedaPay;
/**
* Class PhoneNumber
*
* @property int $id
* @property string $number
* @property string $country
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class PhoneNumber extends Resource
{
}

View File

@@ -0,0 +1,215 @@
<?php
namespace FedaPay;
/**
* Class Requestor
*
* @package FedaPay
*/
class Requestor
{
const SANDBOX_BASE = 'https://sandbox-api.fedapay.com';
const PRODUCTION_BASE = 'https://api.fedapay.com';
const DEVELOPMENT_BASE = 'https://dev-api.fedapay.com';
/**
* Http Client
* @var GuzzleHttp\ClientInterface
*/
protected static $httpClient;
/**
* @static
*
* @param HttpClient\ClientInterface $client
*/
public static function setHttpClient($client)
{
self::$httpClient = $client;
}
/**
* @return HttpClient\ClientInterface
*/
private function httpClient()
{
if (!self::$httpClient) {
$options = [];
if (FedaPay::getVerifySslCerts()) {
$options['verify'] = FedaPay::getCaBundlePath();
}
self::$httpClient = HttpClient\CurlClient::instance();
}
return self::$httpClient;
}
/**
* @param string $method
* @param string $url
* @param array|null $params
* @param array|null $headers
*
* @return array An API response.
*/
public function request($method, $path, $params = null, $headers = null)
{
$params = $params ?: [];
$headers = $headers ?: [];
$params = array_merge($this->defaultParams(), $params);
$headers = array_merge($this->defaultHeaders(), $headers);
$url = $this->url($path);
$rawHeaders = [];
foreach ($headers as $h => $v) {
$rawHeaders[] = $h . ': ' . $v;
}
list($rbody, $rcode, $rheaders) = $this->httpClient()->request($method, $url, $params, $rawHeaders);
$json = $this->_interpretResponse($rbody, $rcode, $rheaders);
return $json;
}
/**
* Format http request error
* @param GuzzleHttp\Exception\RequestException $e
* @throws Error\ApiConnection
* @return void
*/
protected function handleRequestException($e)
{
$message = 'Request error: '. $e->getMessage();
$httpStatusCode = $e->hasResponse() ? $e->getResponse()->getStatusCode() : null;
$httpRequest = $e->getRequest();
$httpResponse = $e->getResponse();
throw new Error\ApiConnection(
$message,
$httpStatusCode,
$httpRequest,
$httpResponse
);
}
/**
* Return the default request params
* @return array
*/
protected function defaultParams()
{
$params = [];
if (FedaPay::getLocale()) {
$params['locale'] = FedaPay::getLocale();
}
return $params;
}
/**
* Return the default request headers
* @return array
*/
protected function defaultHeaders()
{
$auth = FedaPay::getApiKey() ?: FedaPay::getToken();
$apiVersion = FedaPay::getApiVersion();
$accountId = FedaPay::getAccountId();
$default = [
'X-Version' => FedaPay::VERSION,
'X-Api-Version' => $apiVersion,
'X-Source' => 'FedaPay PhpLib',
'Authorization' => "Bearer $auth"
];
if ($accountId) {
$default['FedaPay-Account'] = $accountId;
}
return $default;
}
/**
* Return the base url of the requests
* @return string
*/
protected function baseUrl()
{
$apiBase = FedaPay::getApiBase();
$environment = FedaPay::getEnvironment();
if ($apiBase) {
return $apiBase;
}
switch ($environment) {
case 'development':
case 'dev':
return self::DEVELOPMENT_BASE;
case 'sandbox':
case 'test':
case null:
return self::SANDBOX_BASE;
case 'production':
case 'live':
return self::PRODUCTION_BASE;
}
}
/**
* Return the request url
* @return string
*/
protected function url($path)
{
return $this->baseUrl() . '/' . FedaPay::getApiVersion() . $path;
}
/**
* @param string $rbody
* @param int $rcode
* @param array $rheaders
*
* @return mixed
*/
private function _interpretResponse($rbody, $rcode, $rheaders)
{
$resp = json_decode($rbody, true);
$jsonError = json_last_error();
if ($resp === null && $jsonError !== JSON_ERROR_NONE) {
$msg = "Invalid response body from API: $rbody "
. "(HTTP response code was $rcode, json_last_error() was $jsonError)";
throw new Error\ApiConnection($msg, $rcode, $rbody);
}
if ($rcode < 200 || $rcode >= 300) {
$this->handleErrorResponse($rbody, $rcode, $rheaders, $resp);
}
return $resp;
}
/**
* @param string $rbody A JSON string.
* @param int $rcode
* @param array $rheaders
* @param array $resp
*
* @throws Error\InvalidRequest if the error is caused by the user.
*/
public function handleErrorResponse($rbody, $rcode, $rheaders, $resp)
{
$msg = isset($resp['message']) ? $resp['message'] : 'ApiConnection Error' ;
throw new Error\ApiConnection($msg, $rcode, $rbody, $resp, $rheaders);
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace FedaPay;
use FedaPay\Util\Util;
use FedaPay\Util\Inflector;
/**
* Class Resource
*
* @package FedaPay
*/
abstract class Resource extends FedaPayObject
{
use ApiOperations\Request;
/**
* @var FedaPay\Requestor
*/
protected static $requestor;
/**
* Set requestor
* @param FedaPay\Requestor $requestor
*/
public static function setRequestor(Requestor $requestor)
{
self::$requestor = $requestor;
}
/**
* Return the requestor
* @return FedaPay\Requestor
*/
public static function getRequestor()
{
if (self::$requestor) {
return self::$requestor;
}
self::$requestor = new Requestor;
return self::$requestor;
}
public static function className()
{
$class = get_called_class();
// Useful for namespaces: Foo\Charge
if ($postfixNamespaces = strrchr($class, '\\')) {
$class = substr($postfixNamespaces, 1);
}
// Useful for underscored 'namespaces': Foo_Charge
if ($postfixFakeNamespaces = strrchr($class, '')) {
$class = $postfixFakeNamespaces;
}
if (substr($class, 0, strlen('FedaPay')) == 'FedaPay') {
$class = substr($class, strlen('FedaPay'));
}
$class = str_replace('_', '', $class);
$name = urlencode($class);
$name = strtolower($name);
return $name;
}
/**
* @return string The endpoint URL for the given class.
*/
public static function classPath()
{
$base = static::className();
$plurial = Inflector::pluralize($base);
return "/$plurial";
}
/**
* @return string The instance endpoint URL for the given class.
*/
public static function resourcePath($id)
{
if ($id === null) {
$class = get_called_class();
$message = 'Could not determine which URL to request: '
. "$class instance has invalid ID: $id";
throw new Error\InvalidRequest($message, null);
}
$base = static::classPath();
$extn = urlencode($id);
return "$base/$extn";
}
/**
* @return string The full API URL for this API resource.
*/
public function instanceUrl()
{
return static::resourcePath($this['id']);
}
}

View File

@@ -0,0 +1,176 @@
<?php
namespace FedaPay;
use FedaPay\Util\Util;
/**
* Class Transaction
*
* @property int $id
* @property string $reference
* @property string $description
* @property string $callback_url
* @property string $amount
* @property string $status
* @property int $transaction_id
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
class Transaction extends Resource
{
use ApiOperations\All;
use ApiOperations\Search;
use ApiOperations\Retrieve;
use ApiOperations\Create;
use ApiOperations\CreateInBatch;
use ApiOperations\Update;
use ApiOperations\Save;
use ApiOperations\Delete;
/**
* Paid status list
* @var array
*/
private static $PAID_STATUS = [
'approved', 'transferred', 'refunded',
'approved_partially_refunded', 'transferred_partially_refunded'
];
/**
* Check if the transaction was paid
*
* @return boolean
*/
public function wasPaid()
{
return in_array($this->status, self::$PAID_STATUS);
}
/**
* Check if the transacton was refunded. Status must include refunded.
*
* @return boolean
*/
public function wasRefunded()
{
return strpos($this->status, 'refunded') !== false;
}
/**
* Check if the transacton was partially refunded. Status must include partially_refunded.
*
* @return boolean
*/
public function wasPartiallyRefunded()
{
return strpos($this->status, 'partially_refunded') !== false;
}
/**
* Generate a payment token and url
* @return FedaPay\FedaPayObject
*/
public function generateToken($params = [], $headers = [])
{
$url = $this->instanceUrl() . '/token';
list($response, $opts) = static::_staticRequest('post', $url, $params, $headers);
return Util::arrayToFedaPayObject($response, $opts);
}
/**
* Generate a payment token and url from transaction id method
* @return FedaPay\FedaPayObject
*/
public static function generateTokenFromId($id, $params = [], $headers = [])
{
$path = static::resourcePath($id) . '/token';
list($response, $opts) = static::_staticRequest('post', $path, $params, $headers);
return Util::arrayToFedaPayObject($response, $opts);
}
/**
* Send Mobile Money request with token
* @param string $mode
* @param string $token
* @param array $params
* @param array $headers
*
* @return FedaPay\FedaPayObject
*/
public function sendNowWithToken($mode, $token, $params = [], $headers = [])
{
$url = '/' . $mode;
$params = array_merge(['token' => $token], $params);
list($response, $opts) = static::_staticRequest('post', $url, $params, $headers);
return Util::arrayToFedaPayObject($response, $opts);
}
/**
* Return transaction receipt URL
* @param array $force
* @param array $params
* @param array $headers
*
* @return string
*/
public function getReceiptURL($force = false, $params = [], $headers = [])
{
$receipt_url = $this->receipt_url;
if (is_null($receipt_url) || $force) {
$url = $this->instanceUrl() . '/receipt_url';
// Force Api to generate url
if ($force) {
$params['force'] = true;
}
list($response, $opts) = static::_staticRequest('post', $url, $params, $headers);
$urlObject = Util::arrayToFedaPayObject($response, $opts);
$receipt_url = $urlObject->url;
}
return $receipt_url;
}
/**
* Send Mobile Money request
* @param string $mode
* @param array $params
* @param array $headers
*
* @return FedaPay\FedaPayObject
*/
public function sendNow($mode, $params = [], $headers = [])
{
$tokenObject = $this->generateToken([], $headers);
return $this->sendNowWithToken($mode, $tokenObject->token, $params, $headers);
}
/**
* Get transactions fees details
* @param string $token
* @param string $mode
* @param array $params
* @param array $headers
*
* @return FedaPay\FedaPayObject
*/
public function getFees($token, $mode, $params = [], $headers = [])
{
$url = static::classPath() . '/fees';
$params = array_merge(['token' => $token, 'mode' => $mode], $params);
list($response, $opts) = static::_staticRequest('get', $url, $params, $headers);
return Util::arrayToFedaPayObject($response, $opts);
}
}

View File

@@ -0,0 +1,231 @@
<?php
namespace FedaPay\Util;
/**
* Class Inflector
*
* @package FedaPay\Util
*/
abstract class Inflector
{
/**
* Plural inflector rules
*
* @var array
*/
protected static $_plural = [
'/(s)tatus$/i' => '\1tatuses',
'/(quiz)$/i' => '\1zes',
'/^(ox)$/i' => '\1\2en',
'/([m|l])ouse$/i' => '\1ice',
'/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
'/(x|ch|ss|sh)$/i' => '\1es',
'/([^aeiouy]|qu)y$/i' => '\1ies',
'/(hive)$/i' => '\1s',
'/(chef)$/i' => '\1s',
'/(?:([^f])fe|([lre])f)$/i' => '\1\2ves',
'/sis$/i' => 'ses',
'/([ti])um$/i' => '\1a',
'/(p)erson$/i' => '\1eople',
'/(?<!u)(m)an$/i' => '\1en',
'/(c)hild$/i' => '\1hildren',
'/(buffal|tomat)o$/i' => '\1\2oes',
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin)us$/i' => '\1i',
'/us$/i' => 'uses',
'/(alias)$/i' => '\1es',
'/(ax|cris|test)is$/i' => '\1es',
'/s$/' => 's',
'/^$/' => '',
'/$/' => 's',
];
/**
* Singular inflector rules
*
* @var array
*/
protected static $_singular = [
'/(s)tatuses$/i' => '\1\2tatus',
'/^(.*)(menu)s$/i' => '\1\2',
'/(quiz)zes$/i' => '\\1',
'/(matr)ices$/i' => '\1ix',
'/(vert|ind)ices$/i' => '\1ex',
'/^(ox)en/i' => '\1',
'/(alias)(es)*$/i' => '\1',
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
'/([ftw]ax)es/i' => '\1',
'/(cris|ax|test)es$/i' => '\1is',
'/(shoe)s$/i' => '\1',
'/(o)es$/i' => '\1',
'/ouses$/' => 'ouse',
'/([^a])uses$/' => '\1us',
'/([m|l])ice$/i' => '\1ouse',
'/(x|ch|ss|sh)es$/i' => '\1',
'/(m)ovies$/i' => '\1\2ovie',
'/(s)eries$/i' => '\1\2eries',
'/([^aeiouy]|qu)ies$/i' => '\1y',
'/(tive)s$/i' => '\1',
'/(hive)s$/i' => '\1',
'/(drive)s$/i' => '\1',
'/([le])ves$/i' => '\1f',
'/([^rfoa])ves$/i' => '\1fe',
'/(^analy)ses$/i' => '\1sis',
'/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
'/([ti])a$/i' => '\1um',
'/(p)eople$/i' => '\1\2erson',
'/(m)en$/i' => '\1an',
'/(c)hildren$/i' => '\1\2hild',
'/(n)ews$/i' => '\1\2ews',
'/eaus$/' => 'eau',
'/^(.*us)$/' => '\\1',
'/s$/i' => ''
];
/**
* Irregular rules
*
* @var array
*/
protected static $_irregular = [
'atlas' => 'atlases',
'beef' => 'beefs',
'brief' => 'briefs',
'brother' => 'brothers',
'cafe' => 'cafes',
'child' => 'children',
'cookie' => 'cookies',
'corpus' => 'corpuses',
'cow' => 'cows',
'criterion' => 'criteria',
'ganglion' => 'ganglions',
'genie' => 'genies',
'genus' => 'genera',
'graffito' => 'graffiti',
'hoof' => 'hoofs',
'loaf' => 'loaves',
'man' => 'men',
'money' => 'monies',
'mongoose' => 'mongooses',
'move' => 'moves',
'mythos' => 'mythoi',
'niche' => 'niches',
'numen' => 'numina',
'occiput' => 'occiputs',
'octopus' => 'octopuses',
'opus' => 'opuses',
'ox' => 'oxen',
'penis' => 'penises',
'person' => 'people',
'sex' => 'sexes',
'soliloquy' => 'soliloquies',
'testis' => 'testes',
'trilby' => 'trilbys',
'turf' => 'turfs',
'potato' => 'potatoes',
'hero' => 'heroes',
'tooth' => 'teeth',
'goose' => 'geese',
'foot' => 'feet',
'foe' => 'foes',
'sieve' => 'sieves'
];
/**
* Words that should not be inflected
*
* @var array
*/
protected static $_uninflected = [
'.*[nrlm]ese', '.*data', '.*deer', '.*fish', '.*measles', '.*ois',
'.*pox', '.*sheep', 'people', 'feedback', 'stadia', '.*?media',
'chassis', 'clippers', 'debris', 'diabetes', 'equipment', 'gallows',
'graffiti', 'headquarters', 'information', 'innings', 'news', 'nexus',
'pokemon', 'proceedings', 'research', 'sea[- ]bass', 'series', 'species', 'weather'
];
/**
* Method cache array.
*
* @var array
*/
protected static $_cache = [];
/**
* Return $word in plural form.
*
* @param string $word Word in singular
* @return string Word in plural
*/
public static function pluralize($word)
{
if (isset(static::$_cache['pluralize'][$word])) {
return static::$_cache['pluralize'][$word];
}
if (!isset(static::$_cache['irregular']['pluralize'])) {
static::$_cache['irregular']['pluralize'] = '(?:' . implode('|', array_keys(static::$_irregular)) . ')';
}
if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['pluralize'] . ')$/i', $word, $regs)) {
static::$_cache['pluralize'][$word] = $regs[1] . substr($regs[2], 0, 1) .
substr(static::$_irregular[strtolower($regs[2])], 1);
return static::$_cache['pluralize'][$word];
}
if (!isset(static::$_cache['uninflected'])) {
static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')';
}
if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) {
static::$_cache['pluralize'][$word] = $word;
return $word;
}
foreach (static::$_plural as $rule => $replacement) {
if (preg_match($rule, $word)) {
static::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
return static::$_cache['pluralize'][$word];
}
}
}
/**
* Return $word in singular form.
*
* @param string $word Word in plural
* @return string Word in singular
* @link https://book.cakephp.org/3.0/en/core-libraries/inflector.html#creating-plural-singular-forms
*/
public static function singularize($word)
{
if (isset(static::$_cache['singularize'][$word])) {
return static::$_cache['singularize'][$word];
}
if (!isset(static::$_cache['irregular']['singular'])) {
static::$_cache['irregular']['singular'] = '(?:' . implode('|', static::$_irregular) . ')';
}
if (preg_match('/(.*?(?:\\b|_))(' . static::$_cache['irregular']['singular'] . ')$/i', $word, $regs)) {
static::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) .
substr(array_search(strtolower($regs[2]), static::$_irregular), 1);
return static::$_cache['singularize'][$word];
}
if (!isset(static::$_cache['uninflected'])) {
static::$_cache['uninflected'] = '(?:' . implode('|', static::$_uninflected) . ')';
}
if (preg_match('/^(' . static::$_cache['uninflected'] . ')$/i', $word, $regs)) {
static::$_cache['pluralize'][$word] = $word;
return $word;
}
foreach (static::$_singular as $rule => $replacement) {
if (preg_match($rule, $word)) {
static::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
return static::$_cache['singularize'][$word];
}
}
static::$_cache['singularize'][$word] = $word;
return $word;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FedaPay\Util;
/**
* A basic random generator. This is in a separate class so we the generator
* can be injected as a dependency and replaced with a mock in tests.
*/
class RandomGenerator
{
/**
* Returns a random value between 0 and $max.
*
* @param float $max (optional)
* @return float
*/
public function randFloat($max = 1.0)
{
return mt_rand() / mt_getrandmax() * $max;
}
/**
* Returns a v4 UUID.
*
* @return string
*/
public function uuid()
{
$arr = array_values(unpack('N1a/n4b/N1c', openssl_random_pseudo_bytes(16)));
$arr[2] = ($arr[2] & 0x0fff) | 0x4000;
$arr[3] = ($arr[3] & 0x3fff) | 0x8000;
return vsprintf('%08x-%04x-%04x-%04x-%04x%08x', $arr);
}
}

View File

@@ -0,0 +1,255 @@
<?php
namespace FedaPay\Util;
use FedaPay\FedaPayObject;
/**
* Class Util
*
* @package FedaPay\Util
*/
abstract class Util
{
private static $isMbstringAvailable = null;
private static $isHashEqualsAvailable = null;
/**
* Whether the provided array (or other) is a list rather than a dictionary.
*
* @param array|mixed $array
* @return boolean True if the given object is a list.
*/
public static function isList($array)
{
if (!is_array($array)) {
return false;
}
if ($array === []) {
return true;
}
if (array_keys($array) !== range(0, count($array) - 1)) {
return false;
}
return true;
}
/**
* Convert a a response to fedapay object
* @param array $resp The response object
* @param array $opts Additional options.
*
* @return \FedaPay\FedaPayObject
*/
public static function convertToFedaPayObject($resp, $opts)
{
$types = [
'v1/api_key' => 'FedaPay\\ApiKey',
'v1/account' => 'FedaPay\\Account',
'v1/currency' => 'FedaPay\\Currency',
'v1/customer' => 'FedaPay\\Customer',
'v1/event' => 'FedaPay\\Event',
'v1/log' => 'FedaPay\\Log',
'v1/phone_number' => 'FedaPay\\PhoneNumber',
'v1/transaction' => 'FedaPay\\Transaction',
'v1/payout' => 'FedaPay\\Payout',
'v1/page' => 'FedaPay\\Page',
'v1/invoice' => 'FedaPay\\Invoice',
'v1/balance' => 'FedaPay\\Balance',
];
if (self::isList($resp)) {
$mapped = [];
foreach ($resp as $i) {
array_push($mapped, self::arrayToFedaPayObject($i, $opts));
}
return $mapped;
} elseif (is_array($resp)) {
if (isset($resp['klass']) && is_string($resp['klass']) && isset($types[$resp['klass']])) {
$class = $types[$resp['klass']];
} else {
$class = FedaPayObject::class;
}
$object = new $class;
$object->refreshFrom($resp, $opts);
return $object;
} else {
return $resp;
}
}
/**
* Converts an array to FedaPay object.
*
* @param array $array The PHP array to convert.
* @param array $opts Additional options.
*
* @return \FedaPay\FedaPayObject
*/
public static function arrayToFedaPayObject($array, $opts)
{
if (self::isList($array)) {
$mapped = array();
foreach ($array as $i) {
array_push($mapped, self::convertToFedaPayObject($i, $opts));
}
return $mapped;
} else {
return self::convertToFedaPayObject($array, $opts);
}
}
/**
* Recursively converts the PHP FedaPay object to an array.
*
* @param array $values The PHP FedaPay object to convert.
* @return array
*/
public static function convertFedaPayObjectToArray($values)
{
$results = [];
foreach ($values as $k => $v) {
if ($v instanceof FedaPayObject) {
$results[$k] = $v->__toArray(true);
} elseif (is_array($v)) {
$results[$k] = self::convertFedaPayObjectToArray($v);
} else {
$results[$k] = $v;
}
}
return $results;
}
/**
* Strip api version from key
* @param string $key
* @param array $opts
*
* @return string
*/
public static function stripApiVersion($key, $opts)
{
$apiPart = '';
if (is_array($opts) && isset($opts['apiVersion'])) {
$apiPart = $opts['apiVersion'] . '/';
}
return str_replace($apiPart, '', $key);
}
/**
* Check a date falue
* @param mixed $date
* @return mixed
*/
public static function toDateString($date)
{
if ($date instanceof \DateTime) {
return $date->format('Y-m-d H:i:s');
} else if (is_string($date) || is_int($date)) {
return $date;
} else {
throw new \InvalidArgumentException(
'Invalid datetime argument. Should be a date in string format, '
.' a timestamp or an instance of \DateTime.'
);
}
}
/**
* @param array $params
*
* @return string
*/
public static function encodeParameters($params)
{
$flattenedParams = [];
self::flattenParams($params, $flattenedParams);
$pieces = [];
foreach ($flattenedParams as $param) {
list($k, $v) = $param;
array_push($pieces, self::urlEncode($k) . '=' . self::urlEncode($v));
}
return implode('&', $pieces);
}
/**
* Flattens the array so that it can be used with curl.
*
* @param $arrays
* @param array $new
* @param null $prefix
*/
public static function flattenParams($arrays, &$new = array(), $prefix = null)
{
$isList = self::isList($arrays);
foreach ($arrays as $key => $value) {
if (isset($prefix) && $isList) {
$k = $prefix.'[]';
} elseif (isset($prefix)) {
$k = $prefix.'['.$key.']';
} else {
$k = $key;
}
if (is_array($value)) {
self::flattenParams($value, $new, $k);
} else {
array_push($new, [$k, $value]);
}
}
}
/**
* @param string $key A string to URL-encode.
*
* @return string The URL-encoded string.
*/
public static function urlEncode($key)
{
$s = urlencode($key);
// Don't use strict form encoding by changing the square bracket control
// characters back to their literals. This is fine by the server, and
// makes these parameter strings easier to read.
$s = str_replace('%5B', '[', $s);
$s = str_replace('%5D', ']', $s);
return $s;
}
/**
* Compares two strings for equality. The time taken is independent of the
* number of characters that match.
*
* @param string $a one of the strings to compare.
* @param string $b the other string to compare.
* @return bool true if the strings are equal, false otherwise.
*/
public static function secureCompare($a, $b)
{
if (self::$isHashEqualsAvailable === null) {
self::$isHashEqualsAvailable = function_exists('hash_equals');
}
if (self::$isHashEqualsAvailable) {
return hash_equals($a, $b);
} else {
if (strlen($a) != strlen($b)) {
return false;
}
$result = 0;
for ($i = 0; $i < strlen($a); $i++) {
$result |= ord($a[$i]) ^ ord($b[$i]);
}
return ($result == 0);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FedaPay;
/**
* Class Webhook
*
* @property int $id
* @property string $public_key
* @property string $private_key
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
abstract class Webhook
{
const DEFAULT_TOLERANCE = 300;
/**
* Returns an Event instance using the provided JSON payload. Throws a
* \UnexpectedValueException if the payload is not valid JSON, and a
* \FedaPay\SignatureVerificationException if the signature verification
* fails for any reason.
*
* @param string $payload the payload sent by FedaPay.
* @param string $sigHeader the contents of the signature header sent by
* FedaPay.
* @param string $secret secret used to generate the signature.
* @param int $tolerance maximum difference allowed between the header's
* timestamp and the current time
* @return \FedaPay\Event the Event instance
* @throws \UnexpectedValueException if the payload is not valid JSON,
* @throws \FedaPay\Error\SignatureVerification if the verification fails.
*/
public static function constructEvent($payload, $sigHeader, $secret, $tolerance = self::DEFAULT_TOLERANCE)
{
WebhookSignature::verifyHeader($payload, $sigHeader, $secret, $tolerance);
$data = json_decode($payload, true);
$jsonError = json_last_error();
if ($data === null && $jsonError !== JSON_ERROR_NONE) {
$msg = "Invalid payload: $payload "
. "(json_last_error() was $jsonError)";
throw new \UnexpectedValueException($msg);
}
return new Event($data);
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace FedaPay;
/**
* Class WebhookSignature
*
* @property int $id
* @property string $public_key
* @property string $private_key
* @property string $created_at
* @property string $updated_at
*
* @package FedaPay
*/
abstract class WebhookSignature
{
const EXPECTED_SCHEME = 's';
/**
* Verifies the signature header sent by FedaPay. Throws a
* SignatureVerification exception if the verification fails for any
* reason.
*
* @param string $payload the payload sent by FedaPay.
* @param string $header the contents of the signature header sent by
* FedaPay.
* @param string $secret secret used to generate the signature.
* @param int $tolerance maximum difference allowed between the header's
* timestamp and the current time
* @throws \FedaPay\Error\SignatureVerification if the verification fails.
* @return bool
*/
public static function verifyHeader($payload, $header, $secret, $tolerance = null)
{
// Extract timestamp and signatures from header
$timestamp = self::getTimestamp($header);
$signatures = self::getSignatures($header, self::EXPECTED_SCHEME);
if ($timestamp == -1) {
throw new Error\SignatureVerification(
"Unable to extract timestamp and signatures from header",
$header,
$payload
);
}
if (empty($signatures)) {
throw new Error\SignatureVerification(
"No signatures found with expected scheme",
$header,
$payload
);
}
// Check if expected signature is found in list of signatures from
// header
$signedPayload = "$timestamp.$payload";
$expectedSignature = self::computeSignature($signedPayload, $secret);
$signatureFound = false;
foreach ($signatures as $signature) {
if (Util\Util::secureCompare($expectedSignature, $signature)) {
$signatureFound = true;
break;
}
}
if (!$signatureFound) {
throw new Error\SignatureVerification(
"No signatures found matching the expected signature for payload",
$header,
$payload
);
}
// Check if timestamp is within tolerance
if (($tolerance > 0) && (abs(time() - $timestamp) > $tolerance)) {
throw new Error\SignatureVerification(
"Timestamp outside the tolerance zone",
$header,
$payload
);
}
return true;
}
/**
* Extracts the timestamp in a signature header.
*
* @param string $header the signature header
* @return int the timestamp contained in the header, or -1 if no valid
* timestamp is found
*/
private static function getTimestamp($header)
{
$items = explode(",", $header);
foreach ($items as $item) {
$itemParts = explode('=', $item, 2);
if ($itemParts[0] == 't') {
if (!is_numeric($itemParts[1])) {
return -1;
}
return intval($itemParts[1]);
}
}
return -1;
}
/**
* Extracts the signatures matching a given scheme in a signature header.
*
* @param string $header the signature header
* @param string $scheme the signature scheme to look for.
* @return array the list of signatures matching the provided scheme.
*/
private static function getSignatures($header, $scheme)
{
$signatures = [];
$items = explode(",", $header);
foreach ($items as $item) {
$itemParts = explode("=", $item, 2);
if ($itemParts[0] == $scheme) {
array_push($signatures, $itemParts[1]);
}
}
return $signatures;
}
/**
* Computes the signature for a given payload and secret.
*
* The current scheme used by FedaPay ("v1") is HMAC/SHA-256.
*
* @param string $payload the payload to sign.
* @param string $secret the secret used to generate the signature.
* @return string the signature as a string.
*/
private static function computeSignature($payload, $secret)
{
return hash_hmac('sha256', $payload, $secret);
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.no_autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="FedaPay PHP Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>

21
vendor/fedapay/fedapay-php/phpunit.xml vendored Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="FedaPay PHP Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<php>
<ini name="display_errors" value="On" />
<ini name="display_startup_errors" value="On" />
</php>
</phpunit>

View File

@@ -0,0 +1,225 @@
<?php
namespace Tests;
class AccountTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Account
*/
public function testShouldReturnAccounts()
{
$body = [
'v1/accounts' => [[
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => 'Test account',
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]]
];
$this->mockRequest('get', '/v1/accounts', [], $body);
$object = \FedaPay\Account::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertTrue(is_array($object->accounts));
$this->assertInstanceOf(\FedaPay\Account::class, $object->accounts[0]);
$this->assertEquals('Test account', $object->accounts[0]->name);
$this->assertEquals(1, $object->accounts[0]->id);
$this->assertEquals('UTC', $object->accounts[0]->timezone);
}
/**
* Should return array of FedaPay\Account
*/
public function testShouldCreateAccount()
{
$data = [
'name' => 'My account'
];
$body = [
'v1/account' => [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => $data['name'],
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/accounts', $data, $body);
$account = \FedaPay\Account::create($data);
$this->assertInstanceOf(\FedaPay\Account::class, $account);
$this->assertEquals($account->name, $data['name']);
}
/**
* Should retrieve a Account
*/
public function testShouldRetrievedAccount()
{
$body = [
'v1/account' => [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => 'My account',
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/accounts/1', [], $body);
$account = \FedaPay\Account::retrieve(1);
$this->assertInstanceOf(\FedaPay\Account::class, $account);
$this->assertEquals($account->id, 1);
$this->assertEquals($account->name, 'My account');
$this->assertEquals($account->country, 'BJ');
$this->assertEquals($account->timezone, 'UTC');
}
/**
* Should update a account
*/
public function testShouldUpdateAccount()
{
$data = [
'name' => 'Updated Name',
];
$body = [
'v1/account' => [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => $data['name'],
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('put', '/v1/accounts/1', $data, $body);
$account = \FedaPay\Account::update(1, $data);
$this->assertInstanceOf(\FedaPay\Account::class, $account);
$this->assertEquals($account->name, $data['name']);
$this->assertEquals($account->id, 1);
$this->assertEquals($account->country, 'BJ');
$this->assertEquals($account->timezone, 'UTC');
}
/**
* Should update a account with save
*/
public function testShouldUpdateAccountWithSave()
{
$data = [
'name' => 'Updated Name',
];
$body = [
'v1/account' => [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => 'Name',
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/accounts', $data, $body);
$account = \FedaPay\Account::create($data);
$account->name = 'Updated Name';
$updateData = [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'klass' => 'v1/account',
'name' => 'Updated Name',
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
];
$this->mockRequest('put', '/v1/accounts/1', $updateData, $body);
$account->save();
}
/**
* Should delete a account
*/
public function testShouldDeleteAccount()
{
$data = [
'name' => 'My account',
];
$body = [
'v1/account' => [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => $data['name'],
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/accounts', $data, $body);
$account = \FedaPay\Account::create($data);
$this->mockRequest('delete', '/v1/accounts/1');
$account->delete();
}
/**
* Should get account light info
*/
public function testShouldGetAccountLightInfo()
{
$data = [
'name' => 'My account',
];
$body = [
'v1/account' => [
'country' => 'BJ',
'created_at' => '2018-03-12T09:09:03.969Z',
'id' => 1,
'klass' => 'v1/account',
'name' => $data['name'],
'timezone' => 'UTC',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/accounts/light', [], $body, 200, ['FedaPay-Pos-Code' => '0001']);
$account = \FedaPay\Account::light([], ['FedaPay-Pos-Code' => '0001']);
$this->assertInstanceOf(\FedaPay\Account::class, $account);
$this->assertEquals($account->name, $data['name']);
$this->assertEquals($account->id, 1);
$this->assertEquals($account->country, 'BJ');
$this->assertEquals($account->timezone, 'UTC');
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Tests;
class BalanceTest extends BaseTestCase
{
/**
* Should return FedaPay\Balance
*/
public function testShouldReturnCurrencies()
{
$body = [
'v1/balances' => [[
'id' => 1,
'klass' => 'v1/balance',
'currency_id' => 1,
'account_id' => 1,
'amount' => 952,
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]]
];
$this->mockRequest('get', '/v1/balances', [], $body);
$object = \FedaPay\Balance::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertTrue(is_array($object->balances));
$this->assertInstanceOf(\FedaPay\Balance::class, $object->balances[0]);
$this->assertEquals(1, $object->balances[0]->currency_id);
$this->assertEquals(1, $object->balances[0]->account_id);
$this->assertEquals(952, $object->balances[0]->amount);
$this->assertEquals('mtn', $object->balances[0]->mode);
}
/**
* Should retrieve a Customer
*/
public function testShouldRetrievedABalance()
{
$body = [
'v1/balance' => [
'id' => 1,
'klass' => 'v1/balance',
'currency_id' => 1,
'account_id' => 1,
'amount' => 952,
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/balances/1', [], $body);
$balance = \FedaPay\Balance::retrieve(1);
$this->assertInstanceOf(\FedaPay\Balance::class, $balance);
$this->assertEquals(1, $balance->currency_id);
$this->assertEquals(1, $balance->account_id);
$this->assertEquals(952, $balance->amount);
$this->assertEquals('mtn', $balance->mode);
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Tests;
use PHPUnit\Framework\TestCase;
use FedaPay\HttpClient\CurlClient;
abstract class BaseTestCase extends TestCase
{
protected $defaultHeaders = [];
const API_KEY = 'sk_local_123';
const OAUTH_TOKEN = 'oauth_test_token_123';
const API_BASE = 'https://dev-api.fedapay.com';
/** @before */
protected function setUpConfig()
{
\FedaPay\FedaPay::setApiKey(self::API_KEY);
\FedaPay\FedaPay::setApiBase(self::API_BASE);
\FedaPay\Requestor::setHttpClient(\FedaPay\HttpClient\CurlClient::instance());
$this->defaultHeaders = [
'X-Version' => \FedaPay\FedaPay::VERSION,
'X-Api-Version' => \FedaPay\FedaPay::getApiVersion(),
'X-Source' => 'FedaPay PhpLib',
'Authorization' => 'Bearer '. (self::API_KEY ?: self::OAUTH_TOKEN)
];
}
/** @after */
protected function tearDownConfig()
{
// Back to default
\FedaPay\FedaPay::setApiKey(null);
\FedaPay\FedaPay::setApiVersion('v1');
\FedaPay\FedaPay::setEnvironment('sandbox');
\FedaPay\FedaPay::setToken(null);
\FedaPay\FedaPay::setAccountId(null);
\FedaPay\FedaPay::setVerifySslCerts(true);
\FedaPay\FedaPay::setLocale(null);
\FedaPay\Requestor::setHttpClient(null);
}
protected function mockRequest(
$method,
$path,
$params = [],
$response = [],
$rcode = 200,
$headers = []
) {
$mock = $this->setUpMockRequest();
$base = \FedaPay\FedaPay::getApiBase();
$absUrl = $base . $path;
$headers = array_merge($this->defaultHeaders, $headers);
$rawHeaders = [];
foreach ($headers as $k => $v) {
$rawHeaders[] = $k . ': ' . $v;
}
if (is_array($response)) {
$response = json_encode($response);
}
$mock->expects($this->once())
->method('request')
->with(
strtolower($method),
$absUrl,
$params,
$rawHeaders
)
->willReturn([$response, $rcode, []]);
}
protected function mockMultipleRequests($requests)
{
$mock = $this->setUpMockRequest();
$base = \FedaPay\FedaPay::getApiBase();
$withs = [];
$returns = [];
foreach ($requests as $req) {
$req = array_merge(['params' => [], 'response' => [], 'rcode' => 200, 'headers' => []], $req);
$absUrl = $base . $req['path'];
$headers = array_merge($this->defaultHeaders, $req['headers']);
$rawHeaders = [];
foreach ($headers as $k => $v) {
$rawHeaders[] = $k . ': ' . $v;
}
if (is_array($req['response'])) {
$response = json_encode($req['response']);
}
$withs[] = [
strtolower($req['method']),
$absUrl,
$req['params'],
$rawHeaders,
[$response, $req['rcode'], []]
];
}
$mock->expects($this->exactly(count($requests)))
->method('request')
->will($this->returnValueMap($withs));
}
protected function setUpMockRequest()
{
$mock = $this->getMockBuilder('\FedaPay\HttpClient\ClientInterface')
->setMethods(['request'])
->getMock();
\FedaPay\Requestor::setHttpClient($mock);
return $mock;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Tests;
class CurrencyTest extends BaseTestCase
{
/**
* Should return FedaPay\Currency
*/
public function testShouldReturnCurrencies()
{
$body = [
'v1/currencies' => [[
'id' => 1,
'klass' => 'v1/currency',
'name' => 'FCFA',
'iso' => 'XOF',
'code' => 952,
'prefix' => null,
'suffix' => 'CFA',
'div' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]]
];
$this->mockRequest('get', '/v1/currencies', [], $body);
$object = \FedaPay\Currency::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertTrue(is_array($object->currencies));
$this->assertInstanceOf(\FedaPay\Currency::class, $object->currencies[0]);
$this->assertEquals('FCFA', $object->currencies[0]->name);
$this->assertEquals('XOF', $object->currencies[0]->iso);
$this->assertEquals(952, $object->currencies[0]->code);
$this->assertEquals(null, $object->currencies[0]->prefix);
$this->assertEquals('CFA', $object->currencies[0]->suffix);
$this->assertEquals(1, $object->currencies[0]->div);
}
/**
* Should retrieve a Customer
*/
public function testShouldRetrievedACurrency()
{
$body = [
'v1/currency' => [
'id' => 1,
'klass' => 'v1/currency',
'name' => 'FCFA',
'iso' => 'XOF',
'code' => 952,
'prefix' => null,
'suffix' => 'CFA',
'div' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/currencies/1', [], $body);
$currency = \FedaPay\Currency::retrieve(1);
$this->assertInstanceOf(\FedaPay\Currency::class, $currency);
$this->assertEquals('FCFA', $currency->name);
$this->assertEquals('XOF', $currency->iso);
$this->assertEquals(952, $currency->code);
$this->assertEquals(null, $currency->prefix);
$this->assertEquals('CFA', $currency->suffix);
$this->assertEquals(1, $currency->div);
}
}

View File

@@ -0,0 +1,248 @@
<?php
namespace Tests;
class CustomerTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Customer
*/
public function testShouldReturnCustomers()
{
$body = [
'v1/customers' => [[
'id' => 1,
'klass' => 'v1/customer',
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john.doe@example.com',
'phone' => '22967666776',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]],
'meta' => ['page' => 1]
];
$this->mockRequest('get', '/v1/customers', [], $body);
$object = \FedaPay\Customer::all();
$this->assertTrue(is_array($object->customers));
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
$this->assertInstanceOf(\FedaPay\Customer::class, $object->customers[0]);
$this->assertEquals('John', $object->customers[0]->firstname);
$this->assertEquals('Doe', $object->customers[0]->lastname);
$this->assertEquals('john.doe@example.com', $object->customers[0]->email);
$this->assertEquals('22967666776', $object->customers[0]->phone);
}
/**
* Should return array of FedaPay\Customer
*/
public function testCustomerCreationShouldFailed()
{
$data = ['firstname' => 'Myfirstname'];
$body = [
'message' => 'Account creation failed',
'errors' => [
'lastname' => ['lastname field required']
]
];
$this->mockRequest('post', '/v1/customers', $data, $body, 500);
try {
\FedaPay\Customer::create($data);
} catch (\FedaPay\Error\ApiConnection $e) {
$this->assertTrue($e->hasErrors());
$this->assertNotNull($e->getErrorMessage());
$errors = $e->getErrors();
$this->assertArrayHasKey('lastname', $errors);
}
}
/**
* Should return array of FedaPay\Customer
*/
public function testShouldCreateACustomer()
{
$data = [
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john.doe@example.com',
'phone' => '+22966000001'
];
$body = [
'v1/customer' => [
'id' => 1,
'klass' => 'v1/customer',
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'email' => $data['email'],
'phone' => $data['phone'],
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/customers', $data, $body);
$customer = \FedaPay\Customer::create($data);
$this->assertInstanceOf(\FedaPay\Customer::class, $customer);
$this->assertEquals($customer->firstname, $data['firstname']);
$this->assertEquals($customer->lastname, $data['lastname']);
$this->assertEquals($customer->email, $data['email']);
$this->assertEquals($customer->phone, $data['phone']);
$this->assertEquals($customer->id, 1);
}
/**
* Should retrieve a Customer
*/
public function testShouldRetrievedACustomer()
{
$data = [
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john.doe@exemple.com',
'phone' => '+2296600000001'
];
$body = [
'v1/customer' => [
'id' => 1,
'klass' => 'v1/customer',
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'email' => $data['email'],
'phone' => $data['phone'],
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/customers/1', [], $body);
$customer = \FedaPay\Customer::retrieve(1);
$this->assertInstanceOf(\FedaPay\Customer::class, $customer);
$this->assertEquals($customer->firstname, $data['firstname']);
$this->assertEquals($customer->lastname, $data['lastname']);
$this->assertEquals($customer->email, $data['email']);
$this->assertEquals($customer->phone, $data['phone']);
$this->assertEquals($customer->id, 1);
}
/**
* Should update a customer
*/
public function testShouldUpdateACustomer()
{
$data = [
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john.doe@exemple.com',
'phone' => '+2296600000001'
];
$body = [
'v1/customer' => [
'id' => 1,
'klass' => 'v1/customer',
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'email' => $data['email'],
'phone' => $data['phone'],
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('put', '/v1/customers/1', $data, $body);
$customer = \FedaPay\Customer::update(1, $data);
$this->assertInstanceOf(\FedaPay\Customer::class, $customer);
$this->assertEquals($customer->firstname, $data['firstname']);
$this->assertEquals($customer->lastname, $data['lastname']);
$this->assertEquals($customer->email, $data['email']);
$this->assertEquals($customer->phone, $data['phone']);
$this->assertEquals($customer->id, 1);
}
/**
* Should update a customer with save
*/
public function testShouldUpdateACustomerWithSave()
{
$data = [
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john.doe@exemple.com',
'phone' => '+2296600000001'
];
$body = [
'v1/customer' => [
'id' => 1,
'klass' => 'v1/customer',
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'email' => $data['email'],
'phone' => $data['phone'],
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/customers', $data, $body);
$customer = \FedaPay\Customer::create($data);
$customer->firstname = 'First name';
$updateData = [
'klass' => 'v1/customer',
'firstname' => 'First name',
'lastname' => $data['lastname'],
'email' => $data['email'],
'phone' => $data['phone'],
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
];
$this->mockRequest('put', '/v1/customers/1', $updateData, $body);
$customer->save();
}
/**
* Should delete a customer
*/
public function testShouldDeleteACustomer()
{
$data = [
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john.doe@exemple.com',
'phone' => '+2296600000001'
];
$body = [
'v1/customer' => [
'id' => 1,
'klass' => 'v1/customer',
'firstname' => $data['firstname'],
'lastname' => $data['lastname'],
'email' => $data['email'],
'phone' => $data['phone'],
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/customers', $data, $body);
$customer = \FedaPay\Customer::create($data);
$this->mockRequest('delete', '/v1/customers/1');
$customer->delete();
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Tests;
class EventTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Event
*/
public function testShouldReturnEvents()
{
$body = [
'v1/events' => [[
'id' => 1,
'klass' => 'v1/event',
'type' => 'transaction.update',
'entity' => [],
'object_id' => 1,
'account_id' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]],
'meta' => ['page' => 1]
];
$this->mockRequest('get', '/v1/events', [], $body);
$object = \FedaPay\Event::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
$this->assertTrue(is_array($object->events));
$this->assertInstanceOf(\FedaPay\Event::class, $object->events[0]);
$this->assertEquals(1, $object->events[0]->id);
$this->assertEquals('transaction.update', $object->events[0]->type);
$this->assertEquals(1, $object->events[0]->object_id);
$this->assertEquals(1, $object->events[0]->account_id);
}
/**
* Should retrieve a Event
*/
public function testShouldRetrievedAEvent()
{
$body = [
'v1/event' => [
'id' => 1,
'klass' => 'v1/event',
'type' => 'transaction.update',
'entity' => [],
'object_id' => 1,
'account_id' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/events/1', [], $body);
$event = \FedaPay\Event::retrieve(1);
$this->assertInstanceOf(\FedaPay\Event::class, $event);
$this->assertEquals(1, $event->id);
$this->assertEquals('transaction.update', $event->type);
$this->assertEquals(1, $event->object_id);
$this->assertEquals(1, $event->account_id);
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Tests;
class FedaPayObjectTest extends BaseTestCase
{
/**
* Should set and get object attributes
*/
public function testShouldSetObjectAttribute()
{
$object = new \FedaPay\FedaPayObject;
$object->id = 1;
$object->name = 'name';
$this->assertEquals($object->id, 1);
$this->assertEquals($object['id'], 1);
$this->assertEquals($object->name, 'name');
$this->assertEquals($object['name'], 'name');
$object = new \FedaPay\FedaPayObject(2);
$this->assertEquals($object->id, 2);
$object = new \FedaPay\FedaPayObject(['foo' => 'value']);
$this->assertEquals($object->foo, 'value');
$this->assertTrue(isset($object->foo));
$this->assertTrue(isset($object['foo']));
$this->assertFalse(isset($object['doe']));
$this->assertFalse(isset($object->doe));
}
public function testShouldSerializeObject()
{
$object = new \FedaPay\FedaPayObject(['foo' => 'value']);
$json = json_encode($object);
$this->assertEquals($json, '{"foo":"value"}');
}
public function testShouldRefreshObject()
{
$object = new \FedaPay\FedaPayObject;
$object->refreshFrom(['foo' => 'value'], null);
$this->assertEquals($object->foo, 'value');
}
public function testShouldSerializeObjectParams()
{
$object = new \FedaPay\FedaPayObject;
$object->refreshFrom(['foo' => 'value', 'id' => 2], null);
$params = $object->serializeParameters();
$this->assertTrue(is_array($params));
$this->assertEquals($params['foo'], 'value');
$this->assertArrayNotHasKey('id', $params);
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Tests\Fixtures;
class Foo extends \FedaPay\Resource
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Tests\Fixtures;
class FooCurrency extends \FedaPay\Resource
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Tests\Fixtures;
class FooPerson extends \FedaPay\Resource
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Tests\Fixtures;
class FooTest extends \FedaPay\Resource
{
}

View File

@@ -0,0 +1,223 @@
<?php
namespace Tests;
use FedaPay\HttpClient\CurlClient;
use FedaPay\FedaPay;
class CurlClientTest extends BaseTestCase
{
/**
* @before
*/
public function saveOriginalNetworkValues()
{
$this->origMaxNetworkRetries = FedaPay::getMaxNetworkRetries();
$this->origMaxNetworkRetryDelay = FedaPay::getMaxNetworkRetryDelay();
$this->origInitialNetworkRetryDelay = FedaPay::getInitialNetworkRetryDelay();
}
/**
* @before
*/
public function setUpReflectors()
{
$fedaPayReflector = new \ReflectionClass('\FedaPay\FedaPay');
$this->maxNetworkRetryDelayProperty = $fedaPayReflector->getProperty('maxNetworkRetryDelay');
$this->maxNetworkRetryDelayProperty->setAccessible(true);
$this->initialNetworkRetryDelayProperty = $fedaPayReflector->getProperty('initialNetworkRetryDelay');
$this->initialNetworkRetryDelayProperty->setAccessible(true);
$curlClientReflector = new \ReflectionClass('FedaPay\HttpClient\CurlClient');
$this->shouldRetryMethod = $curlClientReflector->getMethod('shouldRetry');
$this->shouldRetryMethod->setAccessible(true);
$this->sleepTimeMethod = $curlClientReflector->getMethod('sleepTime');
$this->sleepTimeMethod->setAccessible(true);
}
/**
* @after
*/
public function restoreOriginalNetworkValues()
{
FedaPay::setMaxNetworkRetries($this->origMaxNetworkRetries);
$this->setMaxNetworkRetryDelay($this->origMaxNetworkRetryDelay);
$this->setInitialNetworkRetryDelay($this->origInitialNetworkRetryDelay);
}
private function setMaxNetworkRetryDelay($maxNetworkRetryDelay)
{
$this->maxNetworkRetryDelayProperty->setValue(null, $maxNetworkRetryDelay);
}
private function setInitialNetworkRetryDelay($initialNetworkRetryDelay)
{
$this->initialNetworkRetryDelayProperty->setValue(null, $initialNetworkRetryDelay);
}
private function createFakeRandomGenerator($returnValue = 1.0)
{
$fakeRandomGenerator = $this->createMock('\FedaPay\Util\RandomGenerator');
$fakeRandomGenerator->method('randFloat')->willReturn($returnValue);
return $fakeRandomGenerator;
}
public function testTimeout()
{
$curl = new CurlClient();
$this->assertSame(CurlClient::DEFAULT_TIMEOUT, $curl->getTimeout());
$this->assertSame(CurlClient::DEFAULT_CONNECT_TIMEOUT, $curl->getConnectTimeout());
// implicitly tests whether we're returning the CurlClient instance
$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());
}
public function testUserAgentInfo()
{
$curl = new CurlClient();
$uaInfo = $curl->getUserAgentInfo();
$this->assertNotNull($uaInfo);
$this->assertNotNull($uaInfo['httplib']);
$this->assertNotNull($uaInfo['ssllib']);
}
public function testDefaultOptions()
{
$withClosure = new CurlClient(function ($method, $absUrl, $headers, $params, $hasFile) use (&$ref) {
$ref = func_get_args();
return [];
});
$withClosure->request('get', 'https://httpbin.org/status/200', [], [], false);
$this->assertSame($ref, ['get', 'https://httpbin.org/status/200', [], [], false]);
// this is the last test case that will run, since it'll throw an exception at the end
$withBadClosure = new CurlClient(function () {
return 'thisShouldNotWork';
});
$this->expectException('FedaPay\Error\ApiConnection');
$this->expectExceptionMessage('Non-array value returned by defaultOptions CurlClient callback');
$withBadClosure->request('get', 'https://httpbin.org/status/200', [], [], false);
}
public function testSslOption()
{
// make sure options array loads/saves properly
$optionsArray = [CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1];
$withOptionsArray = new CurlClient($optionsArray);
$this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray);
}
public function testShouldRetryOnTimeout()
{
FedaPay::setMaxNetworkRetries(2);
$curlClient = new CurlClient();
$this->assertTrue($this->shouldRetryMethod->invoke($curlClient, CURLE_OPERATION_TIMEOUTED, 0, 0));
}
public function testShouldRetryOnConnectionFailure()
{
FedaPay::setMaxNetworkRetries(2);
$curlClient = new CurlClient();
$this->assertTrue($this->shouldRetryMethod->invoke($curlClient, CURLE_COULDNT_CONNECT, 0, 0));
}
public function testShouldRetryOnConflict()
{
FedaPay::setMaxNetworkRetries(2);
$curlClient = new CurlClient();
$this->assertTrue($this->shouldRetryMethod->invoke($curlClient, 0, 409, 0));
}
public function testShouldNotRetryAtMaximumCount()
{
FedaPay::setMaxNetworkRetries(2);
$curlClient = new CurlClient();
$this->assertFalse($this->shouldRetryMethod->invoke($curlClient, 0, 0, FedaPay::getMaxNetworkRetries()));
}
public function testShouldNotRetryOnCertValidationError()
{
FedaPay::setMaxNetworkRetries(2);
$curlClient = new CurlClient();
$this->assertFalse($this->shouldRetryMethod->invoke($curlClient, CURLE_SSL_PEER_CERTIFICATE, -1, 0));
}
public function testSleepTimeShouldGrowExponentially()
{
$this->setMaxNetworkRetryDelay(999);
$curlClient = new CurlClient(null, $this->createFakeRandomGenerator());
$this->assertEquals(
FedaPay::getInitialNetworkRetryDelay() * 1,
$this->sleepTimeMethod->invoke($curlClient, 1)
);
$this->assertEquals(
FedaPay::getInitialNetworkRetryDelay() * 2,
$this->sleepTimeMethod->invoke($curlClient, 2)
);
$this->assertEquals(
FedaPay::getInitialNetworkRetryDelay() * 4,
$this->sleepTimeMethod->invoke($curlClient, 3)
);
$this->assertEquals(
FedaPay::getInitialNetworkRetryDelay() * 8,
$this->sleepTimeMethod->invoke($curlClient, 4)
);
}
public function testSleepTimeShouldEnforceMaxNetworkRetryDelay()
{
$this->setInitialNetworkRetryDelay(1);
$this->setMaxNetworkRetryDelay(2);
$curlClient = new CurlClient(null, $this->createFakeRandomGenerator());
$this->assertEquals(1, $this->sleepTimeMethod->invoke($curlClient, 1));
$this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 2));
$this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 3));
$this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 4));
}
public function testSleepTimeShouldAddSomeRandomness()
{
$randomValue = 0.8;
$this->setInitialNetworkRetryDelay(1);
$this->setMaxNetworkRetryDelay(8);
$curlClient = new CurlClient(null, $this->createFakeRandomGenerator($randomValue));
$baseValue = FedaPay::getInitialNetworkRetryDelay() * (0.5 * (1 + $randomValue));
// the initial value cannot be smaller than the base,
// so the randomness is ignored
$this->assertEquals(FedaPay::getInitialNetworkRetryDelay(), $this->sleepTimeMethod->invoke($curlClient, 1));
// after the first one, the randomness is applied
$this->assertEquals($baseValue * 2, $this->sleepTimeMethod->invoke($curlClient, 2));
$this->assertEquals($baseValue * 4, $this->sleepTimeMethod->invoke($curlClient, 3));
$this->assertEquals($baseValue * 8, $this->sleepTimeMethod->invoke($curlClient, 4));
}
}

View File

@@ -0,0 +1,334 @@
<?php
namespace Tests;
use Faker\Factory;
class InvoiceTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Invoice
*/
public function testShouldReturnInvoices()
{
$body = [
'v1/invoices' => [[
'id' => 2,
'klass' => 'v1/invoice',
'number' => '2',
'reference' => 'v_hZUvFT',
'status' => 'sent',
'tax' => 0,
'discount_type' => 'percentage',
'discount_amount' => 0,
'ttc' => 2500,
'sub_total' => 0,
'discount' => 0,
'before_tax' => 0,
'tax_amount' => 0,
'total_amount_paid' => 2500,
'notes' => 'hi just a test',
'invoice_products_count' => 1,
'due_at' => '2018-03-12T09:09:03.969Z',
'currency' => [
'klass' => 'v1/currency',
'id' => 1,
'klass' => 'v1/currency',
'name' => 'FCFA',
'iso' => 'XOF',
'code' => 952,
'prefix' => null,
'suffix' => 'CFA',
'div' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
],
]],
'meta' => [
'current_page' => 1,
'next_page' => null,
'prev_page' => null,
'total_pages' => 1,
'total_count' => 1,
'per_page' => 25,
]
];
$this->mockRequest('get', '/v1/invoices', [], $body);
$object = \FedaPay\Invoice::all();
$this->assertTrue(is_array($object->invoices));
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
$this->assertInstanceOf(\FedaPay\Invoice::class, $object->invoices[0]);
$this->assertEquals(2, $object->invoices[0]->number);
$this->assertEquals('v_hZUvFT', $object->invoices[0]->reference);
$this->assertEquals('hi just a test', $object->invoices[0]->notes);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->invoices[0]->currency);
}
/**
* Should faild creating the invoice
*/
public function testInvoiceCreationShouldFailed()
{
$data = [
'number' => 1,
'notes' => 'My note'
];
$body = [
'message' => 'Invoice creation failed',
'errors' => [
'notes' => ['notes field required']
]
];
$this->mockRequest('post', '/v1/invoices', $data, $body, 500);
try {
\FedaPay\Invoice::create($data);
} catch (\FedaPay\Error\ApiConnection $e) {
$this->assertTrue($e->hasErrors());
$this->assertNotNull($e->getErrorMessage());
$errors = $e->getErrors();
$this->assertArrayHasKey('notes', $errors);
}
}
/**
* Should create an invoice
*/
public function testShouldCreateAnInvoice()
{
$data = [
'number' => 4,
'reference' => 'reference-invoice',
'notes' => 'Invoice content',
'currency_id' => 1,
'account_id' => 1
];
$body = [
'v1/invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('post', '/v1/invoices', $data, $body);
$invoice = \FedaPay\Invoice::create($data);
$this->assertInstanceOf(\FedaPay\Invoice::class, $invoice);
$this->assertEquals($invoice->number, $data['number']);
$this->assertEquals($invoice->reference, $data['reference']);
$this->assertEquals($invoice->notes, $data['notes']);
$this->assertEquals($invoice->id, 1);
}
/**
* Should retrieve an Invoice
*/
public function testShouldRetrievedAnInvoice()
{
$data = [
'number' => 4,
'reference' => 'reference-invoice',
'notes' => 'Invoice content',
'currency_id' => 1,
'account_id' => 1
];
$body = [
'v1/invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('get', '/v1/invoices/1', [], $body);
$invoice = \FedaPay\Invoice::retrieve(1);
$this->assertInstanceOf(\FedaPay\Invoice::class, $invoice);
$this->assertEquals($invoice->number, $data['number']);
$this->assertEquals($invoice->reference, $data['reference']);
$this->assertEquals($invoice->notes, $data['notes']);
$this->assertEquals($invoice->id, 1);
}
/**
* Should update an invoice
*/
public function testShouldUpdateAnInvoice()
{
$data = [
'number' => 4,
'reference' => 'reference-invoice',
'notes' => 'Invoice content',
'currency_id' => 1,
'account_id' => 1
];
$body = [
'v1/invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('put', '/v1/invoices/1', $data, $body);
$invoice = \FedaPay\Invoice::update(1, $data);
$this->assertInstanceOf(\FedaPay\Invoice::class, $invoice);
$this->assertEquals($invoice->number, $data['number']);
$this->assertEquals($invoice->reference, $data['reference']);
$this->assertEquals($invoice->notes, $data['notes']);
$this->assertEquals($invoice->id, 1);
}
/**
* Should update an invoice with save
*/
public function testShouldUpdateAnInvoiceWithSave()
{
$data = [
'number' => 1,
'reference' => 'reference-invoice',
'notes' => 'Invoice content',
'currency_id' => 1,
'account_id' => 1
];
$body = [
'v1/invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('post', '/v1/invoices', $data, $body);
$invoice = \FedaPay\Invoice::create($data);
$invoice->number = 1;
$updateData = [
'klass' => 'v1/invoice',
'number' => $data['number'],
'notes' => $data['notes'],
'reference' => $data['reference'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
];
$this->mockRequest('put', '/v1/invoices/1', $updateData, $body);
$invoice->save();
}
/**
* Should delete an invoice
*/
public function testShouldDeleteAInvoice()
{
$data = [
'number' => 4,
'reference' => 'reference-invoice',
'notes' => 'Invoice content',
'currency_id' => 1,
'account_id' => 1
];
$body = [
'v1/invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('post', '/v1/invoices', $data, $body);
$invoice = \FedaPay\Invoice::create($data);
$this->mockRequest('delete', '/v1/invoices/1');
$invoice->delete();
}
public function testShouldVerifyInvoice()
{
$data = [
'number' => 4,
'reference' => 'reference-invoice',
'notes' => 'Invoice content',
'currency_id' => 1,
'account_id' => 1
];
$body = [
'v1/invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('post', '/v1/invoices', $data, $body);
$invoice = \FedaPay\Invoice::create($data);
$body = [
'v1/invoice_verify' => [
'invoice' => [
'id' => 1,
'klass' => 'v1/invoice',
'number' => $data['number'],
'reference' => $data['reference'],
'notes' => $data['notes'],
'due_at' => '2018-03-12T09:09:03.969Z',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
],
'sesstings' => []
]
];
$this->mockRequest('get', '/v1/invoices/' . $data['reference'] . '/verify', [], $body);
$object = $invoice->verify($data['reference']);
$this->assertInstanceOf(\FedaPay\Invoice::class, $object->invoice);
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Tests;
class LogTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Log
*/
public function testShouldReturnLogs()
{
$body = [
'v1/logs' => [[
'id' => 1,
'klass' => 'v1/log',
'method' => 'GET',
'url' => '/url',
'status' => 200,
'ip_address' => '189.2.33.9',
'version' => '0.1.1',
'source' => 'FedaPay PhpLib',
'query' => '{"q":"search"}',
'body' => '{}',
'response' => '{}',
'account_id' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]],
'meta' => ['page' => 1]
];
$this->mockRequest('get', '/v1/logs', [], $body);
$object = \FedaPay\Log::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
$this->assertTrue(is_array($object->logs));
$this->assertInstanceOf(\FedaPay\Log::class, $object->logs[0]);
$this->assertEquals(1, $object->logs[0]->id);
$this->assertEquals('GET', $object->logs[0]->method);
$this->assertEquals('/url', $object->logs[0]->url);
$this->assertEquals(200, $object->logs[0]->status);
$this->assertEquals('189.2.33.9', $object->logs[0]->ip_address);
$this->assertEquals('0.1.1', $object->logs[0]->version);
$this->assertEquals('FedaPay PhpLib', $object->logs[0]->source);
$this->assertEquals('{"q":"search"}', $object->logs[0]->query);
$this->assertEquals('{}', $object->logs[0]->body);
$this->assertEquals('{}', $object->logs[0]->response);
$this->assertEquals(1, $object->logs[0]->account_id);
}
/**
* Should retrieve a Log
*/
public function testShouldRetrievedALog()
{
$body = [
'v1/log' => [
'id' => 1,
'klass' => 'v1/log',
'method' => 'GET',
'url' => '/url',
'status' => 200,
'ip_address' => '189.2.33.9',
'version' => '0.1.1',
'source' => 'FedaPay PhpLib',
'query' => '{"q":"search"}',
'body' => '{}',
'response' => '{}',
'account_id' => 1,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/logs/1', [], $body);
$log = \FedaPay\Log::retrieve(1);
$this->assertInstanceOf(\FedaPay\Log::class, $log);
$this->assertEquals(1, $log->id);
$this->assertEquals('GET', $log->method);
$this->assertEquals('/url', $log->url);
$this->assertEquals(200, $log->status);
$this->assertEquals('189.2.33.9', $log->ip_address);
$this->assertEquals('0.1.1', $log->version);
$this->assertEquals('FedaPay PhpLib', $log->source);
$this->assertEquals('{"q":"search"}', $log->query);
$this->assertEquals('{}', $log->body);
$this->assertEquals('{}', $log->response);
$this->assertEquals(1, $log->account_id);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Tests\OAuth;
use Tests\BaseTestCase;
use FedaPay\FedaPay;
class ClientTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Account
*/
public function testShouldSendTokenRequest()
{
$body = [
'access_token' => 'oioio990',
'token_type' => 'Bearer',
'scope' => 'read_write',
'created_at' => '1747346992'
];
FedaPay::setOauthClientId('client id');
FedaPay::setOauthClientSecret('client secret');
$data = [
'grant_type' => 'client_credentials',
'client_id' => 'client id',
'client_secret' => 'client secret'
];
$this->mockRequest('post', '/v1/oauth/token', $data, $body);
$object = \FedaPay\OAuth\Client::grantClientCredentials();
$this->assertEquals('oioio990', $object->access_token);
$this->assertEquals('Bearer', $object->token_type);
$this->assertEquals('read_write', $object->scope);
$this->assertEquals('1747346992', $object->created_at);
}
}

View File

@@ -0,0 +1,335 @@
<?php
namespace Tests;
use Faker\Factory;
class PageTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Page
*/
// public function testShouldReturnPages()
// {
// $body = [
// 'v1/pages' => [[
// 'id' => 2,
// 'klass' => 'v1/page',
// 'name' => 'super Admin',
// 'reference' => 'v_hZUvFT',
// 'description' => 'fdfd',
// 'amount' => 0,
// 'published' => true,
// 'enable_phone_number' => false,
// 'callback_url' => null,
// 'image_url' => null,
// 'custom_fields' => [],
// 'currency' => [
// 'klass' => 'v1/currency',
// 'id' => 1,
// 'klass' => 'v1/currency',
// 'name' => 'FCFA',
// 'iso' => 'XOF',
// 'code' => 952,
// 'prefix' => null,
// 'suffix' => 'CFA',
// 'div' => 1,
// 'created_at' => '2018-03-12T09:09:03.969Z',
// 'updated_at' => '2018-03-12T09:09:03.969Z'
// ]
// ]],
// 'meta' => [
// 'current_page' => 1,
// 'next_page' => null,
// 'prev_page' => null,
// 'total_pages' => 1,
// 'total_count' => 1,
// 'per_page' => 25,
// ]
// ];
// $this->mockRequest('get', '/v1/pages', [], $body);
// $object = \FedaPay\Page::all();
// $this->assertTrue(is_array($object->pages));
// $this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
// $this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
// $this->assertInstanceOf(\FedaPay\Page::class, $object->pages[0]);
// $this->assertEquals('super Admin', $object->pages[0]->name);
// $this->assertEquals('v_hZUvFT', $object->pages[0]->reference);
// $this->assertEquals('fdfd', $object->pages[0]->description);
// $this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->pages[0]->currency);
// }
// /**
// * Should faild creating the page
// */
// public function testPageCreationShouldFailed()
// {
// $data = [
// 'name' => 'Myname',
// 'description' => 'My description'
// ];
// $body = [
// 'message' => 'Page creation failed',
// 'errors' => [
// 'description' => ['description field required']
// ]
// ];
// $this->mockRequest('post', '/v1/pages', $data, $body, 500);
// try {
// \FedaPay\Page::create($data);
// } catch (\FedaPay\Error\ApiConnection $e) {
// $this->assertTrue($e->hasErrors());
// $this->assertNotNull($e->getErrorMessage());
// $errors = $e->getErrors();
// $this->assertArrayHasKey('description', $errors);
// }
// }
// /**
// * Should create a page
// */
// public function testShouldCreateAPage()
// {
// $data = [
// 'name' => 'Page name',
// 'reference' => 'page-reference',
// 'description' => 'Page description',
// 'currency_id' => 1
// ];
// $body = [
// 'v1/page' => [
// 'id' => 1,
// 'klass' => 'v1/page',
// 'name' => $data['name'],
// 'reference' => $data['reference'],
// 'description' => $data['description'],
// 'created_at' => '2019-11-19T10:19:03.969Z',
// 'updated_at' => '2019-11-19T10:19:03.969Z'
// ]
// ];
// $this->mockRequest('post', '/v1/pages', $data, $body);
// $page = \FedaPay\Page::create($data);
// $this->assertInstanceOf(\FedaPay\Page::class, $page);
// $this->assertEquals($page->name, $data['name']);
// $this->assertEquals($page->reference, $data['reference']);
// $this->assertEquals($page->description, $data['description']);
// $this->assertEquals($page->id, 1);
// }
// /**
// * Should retrieve a Page
// */
// public function testShouldRetrievedAPage()
// {
// $data = [
// 'name' => 'Page name',
// 'reference' => 'page-reference',
// 'description' => 'Page description',
// 'currency_id' => 1
// ];
// $body = [
// 'v1/page' => [
// 'id' => 1,
// 'klass' => 'v1/page',
// 'name' => $data['name'],
// 'reference' => $data['reference'],
// 'description' => $data['description'],
// 'created_at' => '2019-11-19T10:19:03.969Z',
// 'updated_at' => '2019-11-19T10:19:03.969Z'
// ]
// ];
// $this->mockRequest('get', '/v1/pages/1', [], $body);
// $page = \FedaPay\Page::retrieve(1);
// $this->assertInstanceOf(\FedaPay\Page::class, $page);
// $this->assertEquals($page->name, $data['name']);
// $this->assertEquals($page->reference, $data['reference']);
// $this->assertEquals($page->description, $data['description']);
// $this->assertEquals($page->id, 1);
// }
// /**
// * Should update a page
// */
// public function testShouldUpdateAPage()
// {
// $data = [
// 'name' => 'Page name',
// 'reference' => 'page-reference',
// 'description' => 'Page description',
// 'currency_id' => 1
// ];
// $body = [
// 'v1/page' => [
// 'id' => 1,
// 'klass' => 'v1/page',
// 'name' => $data['name'],
// 'reference' => $data['reference'],
// 'description' => $data['description'],
// 'created_at' => '2019-11-19T10:19:03.969Z',
// 'updated_at' => '2019-11-19T10:19:03.969Z'
// ]
// ];
// $this->mockRequest('put', '/v1/pages/1', $data, $body);
// $page = \FedaPay\Page::update(1, $data);
// $this->assertInstanceOf(\FedaPay\Page::class, $page);
// $this->assertEquals($page->name, $data['name']);
// $this->assertEquals($page->reference, $data['reference']);
// $this->assertEquals($page->description, $data['description']);
// $this->assertEquals($page->id, 1);
// }
// /**
// * Should update a page with save
// */
// public function testShouldUpdateAPageWithSave()
// {
// $data = [
// 'name' => 'Page name',
// 'reference' => 'page-reference',
// 'description' => 'Page description',
// 'currency_id' => 1
// ];
// $body = [
// 'v1/page' => [
// 'id' => 1,
// 'klass' => 'v1/page',
// 'name' => $data['name'],
// 'reference' => $data['reference'],
// 'description' => $data['description'],
// 'created_at' => '2019-11-19T10:19:03.969Z',
// 'updated_at' => '2019-11-19T10:19:03.969Z'
// ]
// ];
// $this->mockRequest('post', '/v1/pages', $data, $body);
// $page = \FedaPay\Page::create($data);
// $page->name = 'New name';
// $updateData = [
// 'klass' => 'v1/page',
// 'name' => 'New name',
// 'reference' => $data['reference'],
// 'description' => $data['description'],
// 'created_at' => '2019-11-19T10:19:03.969Z',
// 'updated_at' => '2019-11-19T10:19:03.969Z'
// ];
// $this->mockRequest('put', '/v1/pages/1', $updateData, $body);
// $page->save();
// }
// /**
// * Should delete a page
// */
// public function testShouldDeleteAPage()
// {
// $data = [
// 'name' => 'Page name',
// 'reference' => 'page-reference',
// 'description' => 'Page description',
// 'currency_id' => 1
// ];
// $body = [
// 'v1/page' => [
// 'id' => 1,
// 'klass' => 'v1/page',
// 'name' => $data['name'],
// 'reference' => $data['reference'],
// 'description' => $data['description'],
// 'created_at' => '2019-11-19T10:19:03.969Z',
// 'updated_at' => '2019-11-19T10:19:03.969Z'
// ]
// ];
// $this->mockRequest('post', '/v1/pages', $data, $body);
// $page = \FedaPay\Page::create($data);
// $this->mockRequest('delete', '/v1/pages/1');
// $page->delete();
// }
public function testShouldVerifyPage()
{
$data = [
'name' => 'Page name',
'reference' => 'page-reference',
'description' => 'Page description',
'currency_id' => 1
];
$body = [
'v1/page' => [
'id' => 1,
'klass' => 'v1/page',
'name' => $data['name'],
'reference' => $data['reference'],
'description' => $data['description'],
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('post', '/v1/pages', $data, $body);
$page = \FedaPay\Page::create($data);
$body = [
'v1/page_verify' => [
'page' => [
'id' => 1,
'klass' => 'v1/page',
'name' => $data['name'],
'reference' => $data['reference'],
'description' => $data['description'],
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
],
'sesstings' => []
]
];
$this->mockRequest('get', '/v1/pages/' . $data['reference'] . '/verify', [], $body);
$object = $page->verify($data['reference']);
$this->assertInstanceOf(\FedaPay\Page::class, $object->page);
}
public function testShouldGetLightPage()
{
$body = [
'v1/page' => [
'id' => 1,
'klass' => 'v1/page',
'name' => 'Page name',
'reference' => 'XOOO',
'description' => 'Page description',
'created_at' => '2019-11-19T10:19:03.969Z',
'updated_at' => '2019-11-19T10:19:03.969Z'
]
];
$this->mockRequest('get', '/v1/pages/XOOO/light', [], $body);
$page = \FedaPay\Page::light('XOOO');
$this->assertEquals($page->name, 'Page name');
$this->assertEquals($page->reference, 'XOOO');
$this->assertEquals($page->description, 'Page description');
$this->assertEquals($page->id, 1);
}
}

View File

@@ -0,0 +1,712 @@
<?php
namespace Tests;
use Faker\Factory;
class PayoutTest extends BaseTestCase
{
private function createPayout()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'amount' => 1000,
];
$body = [
'v1/payout' => [
'id' => 1,
'klass' => 'v1/payout',
'reference' => '109329828',
'amount' => 1000,
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'balance_id' => 1,
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/payouts', $data, $body);
return \FedaPay\Payout::create($data);
}
/**
* Should return array of FedaPay\Payout
*/
public function testShouldReturnPayouts()
{
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1539796844261',
'amount' => 1000,
'status' => 'pending',
'customer_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-17T17:20:44.261Z',
'updated_at' => '2018-10-17T17:22:47.816Z',
'currency_id' => 1,
'scheduled_at' => '2018-10-17T17:22:06.626Z',
'sent_at' => '2018-10-17T17:22:47.803Z',
'failed_at' => null,
'deleted_at' => null
]
],
'meta' => [
"current_page" => 1,
"next_page" => null,
"prev_page" => null,
"total_pages" => 1,
"total_count" => 11,
"per_page" => 25
]
];
$this->mockRequest('get', '/v1/payouts', [], $body);
$object = \FedaPay\Payout::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
$this->assertInstanceOf(\FedaPay\Payout::class, $object->payouts[0]);
$this->assertEquals(1, $object->payouts[0]->id);
$this->assertEquals('1539796844261', $object->payouts[0]->reference);
$this->assertEquals(1000, $object->payouts[0]->amount);
$this->assertEquals('pending', $object->payouts[0]->status);
$this->assertEquals(1, $object->payouts[0]->customer_id);
$this->assertEquals(1, $object->payouts[0]->currency_id);
$this->assertEquals('mtn', $object->payouts[0]->mode);
}
/**
* Should return array of FedaPay\Payout
*/
public function testShouldCreateAPayout()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'amount' => 1000,
'mode' => 'mtn',
'scheduled_at' => '2018-03-12T09:09:03.969Z',
'include' => 'customer,currency'
];
$body = [
'v1/payout' => [
'amount' => 1000,
'currency_id' => 1,
'created_at' => '2018-10-23T15:21:50.434Z',
'customer_id' => 1,
'deleted_at' => null,
'failed_at' => null,
'id' => 13,
'klass' => 'v1/payout',
'last_error_code' => null,
'last_error_message' => null,
'mode' => 'mtn',
'reference' => '1540308110435',
'scheduled_at' => '2018-11-12T09:09:03.969Z',
'sent_at' => null,
'status' => 'pending',
'updated_at' => '2018-10-23T15:21:50.434Z',
'customer' => [
'klass' => 'v1/customer',
'id' => 1,
'firstname' => 'SOHOU',
'lastname' => 'Zidial',
'email' => 'zinsou@test.com',
'account_id' => 1,
'created_at' => '2018-10-17T16:03:24.061Z',
'updated_at' => '2018-10-17T16:03:24.061Z'
]
]
];
$this->mockRequest('post', '/v1/payouts', $data, $body);
$payout = \FedaPay\Payout::create($data);
$this->assertInstanceOf(\FedaPay\Payout::class, $payout);
$this->assertEquals(13, $payout->id);
$this->assertEquals('1540308110435', $payout->reference);
$this->assertEquals(1000, $payout->amount);
$this->assertEquals('pending', $payout->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $payout->customer);
$this->assertEquals(1, $payout->customer->id);
$this->assertEquals('mtn', $payout->mode);
}
/**
* Should return array of FedaPay\Payout
*/
public function testShouldCreatePayoutInBatch()
{
$data = [
'payouts' => [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'amount' => 1000,
'mode' => 'mtn',
'scheduled_at' => '2018-03-12T09:09:03.969Z'
],
'include' => 'customer,currency'
];
$body = [
'v1/payout_batch' => [
'klass' => 'v1/payout_batch',
'payouts' => [
[
'amount' => 1000,
'currency_id' => 1,
'created_at' => '2018-10-23T15:21:50.434Z',
'customer_id' => 1,
'deleted_at' => null,
'failed_at' => null,
'id' => 13,
'klass' => 'v1/payout',
'last_error_code' => null,
'last_error_message' => null,
'mode' => 'mtn',
'reference' => '1540308110435',
'scheduled_at' => '2018-11-12T09:09:03.969Z',
'sent_at' => null,
'status' => 'pending',
'updated_at' => '2018-10-23T15:21:50.434Z',
'customer' => [
'klass' => 'v1/customer',
'id' => 1,
'firstname' => 'SOHOU',
'lastname' => 'Zidial',
'email' => 'zinsou@test.com',
'account_id' => 1,
'created_at' => '2018-10-17T16:03:24.061Z',
'updated_at' => '2018-10-17T16:03:24.061Z'
]
]
],
'errors' => []
]
];
$this->mockRequest('post', '/v1/payouts/batch', $data, $body);
$object = \FedaPay\Payout::createInBatch($data);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertEquals(13, $object->payouts[0]->id);
$this->assertEquals('1540308110435', $object->payouts[0]->reference);
$this->assertEquals(1000, $object->payouts[0]->amount);
$this->assertEquals('pending', $object->payouts[0]->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $object->payouts[0]->customer);
$this->assertEquals(1, $object->payouts[0]->customer->id);
$this->assertEquals('mtn', $object->payouts[0]->mode);
}
/**
* Should retrieve a Payout
*/
public function testShouldRetrievedAPayout()
{
$body = [
'v1/payout' => [
'amount' => 1000,
'currency_id' => 1,
'created_at' => '2018-10-23T15:21:50.434Z',
'customer_id' => 1,
'deleted_at' => null,
'failed_at' => null,
'id' => 13,
'klass' => 'v1/payout',
'last_error_code' => null,
'last_error_message' => null,
'mode' => 'mtn',
'reference' => '1540308110435',
'scheduled_at' => '2018-11-12T09:09:03.969Z',
'sent_at' => null,
'status' => 'pending',
'updated_at' => '2018-10-23T15:21:50.434Z'
]
];
$this->mockRequest('get', '/v1/payouts/13', [], $body);
$payout = \FedaPay\Payout::retrieve(13);
$this->assertInstanceOf(\FedaPay\Payout::class, $payout);
$this->assertEquals(13, $payout->id);
$this->assertEquals('1540308110435', $payout->reference);
$this->assertEquals(1000, $payout->amount);
$this->assertEquals('pending', $payout->status);
$this->assertEquals('mtn', $payout->mode);
}
/**
* Should start a Payout
*/
public function testShouldScheduleAPayout()
{
$payout = $this->createPayout();
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1540316134325',
'amount' => 1000,
'status' => 'started',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-23T17:35:34.325Z',
'updated_at' => '2018-10-23T17:36:40.086Z',
'scheduled_at' => '2018-11-01 18:30:22',
'sent_at' => null,
'started_at' => '2018-11-01 18:30:22',
'failed_at' => null,
'deleted_at' => null
]
]
];
$data = [
'payouts' => [[
'id' => 1,
'scheduled_at' => '2018-11-01 18:30:22'
]]
];
$this->mockRequest('put', '/v1/payouts/start', $data, $body);
$payout->schedule('2018-11-01 18:30:22');
$this->assertEquals('2018-11-01 18:30:22', $payout->scheduled_at);
$this->assertEquals('2018-11-01 18:30:22', $payout->started_at);
$this->assertEquals('started', $payout->status);
}
/**
* Should start a Payout with phone number
*/
public function testShouldScheduleAPayoutWithPhoneNumber()
{
$payout = $this->createPayout();
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1540316134325',
'amount' => 1000,
'status' => 'started',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-23T17:35:34.325Z',
'updated_at' => '2018-10-23T17:36:40.086Z',
'scheduled_at' => '2018-11-01 18:30:22',
'sent_at' => null,
'started_at' => '2018-11-01 18:30:22',
'failed_at' => null,
'deleted_at' => null
]
]
];
$data = [
'payouts' => [[
'id' => 1,
'scheduled_at' => '2018-11-01 18:30:22',
'phone_number' => [
'number' => '66000001',
'country' => 'BJ'
]
]]
];
$this->mockRequest('put', '/v1/payouts/start', $data, $body);
$payout->schedule('2018-11-01 18:30:22', [
'phone_number' => [
'number' => '66000001',
'country' => 'BJ'
]
]);
$this->assertEquals('2018-11-01 18:30:22', $payout->scheduled_at);
$this->assertEquals('2018-11-01 18:30:22', $payout->started_at);
$this->assertEquals('started', $payout->status);
}
/**
* Should fail schedule all payouts
*/
public function testShouldFailScheduleAllPayouts()
{
$data = [[
'scheduled_at' => '2018-11-01 18:30:22'
]];
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage('Invalid id argument. You must specify payout id.');
\FedaPay\Payout::scheduleAll($data);
}
/**
* Should schedule all Payouts
*/
public function testShouldScheduleAllPayouts()
{
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1540316134325',
'amount' => 1000,
'status' => 'started',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-23T17:35:34.325Z',
'updated_at' => '2018-10-23T17:36:40.086Z',
'scheduled_at' => '2018-11-01 18:30:22',
'sent_at' => null,
'started_at' => '2018-11-01 18:30:22',
'failed_at' => null,
'deleted_at' => null
]
]
];
$data = [[
'id' => 1,
'scheduled_at' => '2018-11-01 18:30:22'
]];
$this->mockRequest('put', '/v1/payouts/start', ['payouts' => $data], $body);
$object = \FedaPay\Payout::scheduleAll($data);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\Payout::class, $object->payouts[0]);
$this->assertEquals(1, $object->payouts[0]->id);
$this->assertEquals('1540316134325', $object->payouts[0]->reference);
$this->assertEquals(1000, $object->payouts[0]->amount);
$this->assertEquals('started', $object->payouts[0]->status);
$this->assertEquals('mtn', $object->payouts[0]->mode);
}
/**
* Should send a Payout now
*/
public function testShouldSendAPayoutNow()
{
$payout = $this->createPayout();
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1540316134325',
'amount' => 1000,
'status' => 'sent',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-23T17:35:34.325Z',
'updated_at' => '2018-10-23T17:36:40.086Z',
'scheduled_at' => '2018-11-01 18:30:22',
'sent_at' => '2018-11-01 18:30:22',
'started_at' => '2018-11-01 18:30:22',
'failed_at' => null,
'deleted_at' => null
]
]
];
$data = [
'payouts' => [[
'id' => 1
]]
];
$this->mockRequest('put', '/v1/payouts/start', $data, $body);
$payout->sendNow();
$this->assertEquals('2018-11-01 18:30:22', $payout->sent_at);
$this->assertEquals('sent', $payout->status);
}
/**
* Should send a Payout now with phone number
*/
public function testShouldSendAPayoutNowWithPhoneNumber()
{
$payout = $this->createPayout();
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1540316134325',
'amount' => 1000,
'status' => 'sent',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-23T17:35:34.325Z',
'updated_at' => '2018-10-23T17:36:40.086Z',
'scheduled_at' => '2018-11-01 18:30:22',
'sent_at' => '2018-11-01 18:30:22',
'started_at' => '2018-11-01 18:30:22',
'failed_at' => null,
'deleted_at' => null
]
]
];
$data = [
'payouts' => [[
'id' => 1,
'phone_number' => [
'number' => '66000001',
'country' => 'BJ'
]
]]
];
$this->mockRequest('put', '/v1/payouts/start', $data, $body);
$payout->sendNow([
'phone_number' => [
'number' => '66000001',
'country' => 'BJ'
]
]);
$this->assertEquals('2018-11-01 18:30:22', $payout->sent_at);
$this->assertEquals('sent', $payout->status);
}
/**
* Should send all payouts now
*/
public function testShouldSendAllPayoutsNow()
{
$body = [
'v1/payouts' => [
[
'klass' => 'v1/payout',
'id' => 1,
'reference' => '1540316134325',
'amount' => 1000,
'status' => 'sent',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'last_error_code' => null,
'last_error_message' => null,
'created_at' => '2018-10-23T17:35:34.325Z',
'updated_at' => '2018-10-23T17:36:40.086Z',
'scheduled_at' => '2018-11-01 18:30:22',
'sent_at' => null,
'started_at' => '2018-11-01 18:30:22',
'failed_at' => null,
'deleted_at' => null
]
]
];
$data = [[
'id' => 1
]];
$this->mockRequest('put', '/v1/payouts/start', ['payouts' => $data], $body);
$object = \FedaPay\Payout::sendAllNow([['id' => 1]]);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\Payout::class, $object->payouts[0]);
$this->assertEquals(1, $object->payouts[0]->id);
$this->assertEquals('1540316134325', $object->payouts[0]->reference);
$this->assertEquals(1000, $object->payouts[0]->amount);
$this->assertEquals('sent', $object->payouts[0]->status);
$this->assertEquals('mtn', $object->payouts[0]->mode);
}
/**
* Should update a payout
*/
public function testShouldUpdateAPayout()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'amount' => 1000
];
$body = [
'v1/payout' => [
'id' => 1,
'klass' => 'v1/payout',
'reference' => '109329828',
'amount' => 1000,
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
]
];
$this->mockRequest('put', '/v1/payouts/1', $data, $body);
$payout = \FedaPay\Payout::update(1, $data);
$this->assertInstanceOf(\FedaPay\Payout::class, $payout);
$this->assertEquals(1, $payout->id);
$this->assertEquals('109329828', $payout->reference);
$this->assertEquals(1000, $payout->amount);
$this->assertEquals('pending', $payout->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $payout->customer);
$this->assertEquals(1, $payout->customer->id);
$this->assertInstanceOf(\FedaPay\Currency::class, $payout->currency);
$this->assertEquals(1, $payout->currency->id);
$this->assertEquals(null, $payout->mode);
}
/**
* Should update a payout with save
*/
public function testShouldUpdateAPayoutWithSave()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'amount' => 1000,
];
$body = [
'v1/payout' => [
'id' => 1,
'klass' => 'v1/payout',
'reference' => '109329828',
'amount' => 1000,
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
]
];
$this->mockRequest('post', '/v1/payouts', $data, $body);
$payout = \FedaPay\Payout::create($data);
$payout->amount = 5000;
$updateData = [
'klass' => 'v1/payout',
'reference' => '109329828',
'amount' => 5000,
'status' => 'pending',
'customer' => [
'klass' => 'v1/customer',
],
'currency' => [
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
];
$this->mockRequest('put', '/v1/payouts/1', $updateData, $body);
$payout->save();
}
/**
* Should delete a payout
*/
public function testShouldDeleteAPayout()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'amount' => 1000,
];
$body = [
'v1/payout' => [
'id' => 1,
'klass' => 'v1/payout',
'reference' => '109329828',
'amount' => 1000,
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
]
];
$this->mockRequest('post', '/v1/payouts', $data, $body);
$payout = \FedaPay\Payout::create($data);
$this->mockRequest('delete', '/v1/payouts/1');
$payout->delete();
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace Tests;
/**
* Class RequestorTest
*
* @package Tests
*/
class RequestorTest extends BaseTestCase
{
public function testHttpClientInjection()
{
$reflector = new \ReflectionClass('FedaPay\\Requestor');
$method = $reflector->getMethod('httpClient');
$method->setAccessible(true);
$curl = new \FedaPay\HttpClient\CurlClient();
$curl->setTimeout(10);
\FedaPay\Requestor::setHttpClient($curl);
$injectedCurl = $method->invoke(new \FedaPay\Requestor());
$this->assertSame($injectedCurl, $curl);
}
public function testRequestParams()
{
$this->mockRequest(
'get',
'/v1/path',
['foo' => '2'],
[],
500,
[
'X-Custom' => 'foo'
]
);
$requestor = new \FedaPay\Requestor;
$this->expectException('\FedaPay\Error\ApiConnection');
$requestor->request('get', '/path', ['foo' => '2'], ['X-Custom' => 'foo']);
}
public function testRequestSetParams()
{
\FedaPay\FedaPay::setApiKey(null);
\FedaPay\FedaPay::setApiVersion('v3');
\FedaPay\FedaPay::setEnvironment('production');
\FedaPay\FedaPay::setToken('mytoken');
\FedaPay\FedaPay::setAccountId(898);
\FedaPay\FedaPay::setLocale('en');
$this->mockRequest(
'get',
'/v3/path',
['foo' => '2', 'locale' => 'en'],
[],
500,
[
'Authorization' => 'Bearer mytoken',
'FedaPay-Account' => 898,
'X-Api-Version' => 'v3',
'X-Custom' => 'foo'
]
);
$requestor = new \FedaPay\Requestor;
$this->expectException('\FedaPay\Error\ApiConnection');
$requestor->request('get', '/path', ['foo' => '2'], [
'X-Custom' => 'foo'
]);
}
public function testShouldFaildParsingResponse()
{
$this->mockRequest(
'get',
'/v1/path',
[],
'unable to parse',
200
);
$requestor = new \FedaPay\Requestor;
$this->expectException('\FedaPay\Error\ApiConnection', 'unable to parse');
$requestor->request('get', '/path');
}
public function testShouldParseApiErrors()
{
$this->mockRequest(
'get',
'/v1/path',
[],
['message' => 'Error Message'],
400
);
$requestor = new \FedaPay\Requestor;
$this->expectException('\FedaPay\Error\ApiConnection', 'Error Message');
$requestor->request('get', '/path');
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Tests;
class ResourceTest extends BaseTestCase
{
/**
* Should return the right class name
* @return void
*/
public function testReturnClassName()
{
$this->assertEquals(Fixtures\Foo::className(), 'foo');
$this->assertEquals(Fixtures\FooTest::className(), 'footest');
}
/**
* Should return the right class url
* @return void
*/
public function testShouldReturnClassUrl()
{
$this->assertEquals(Fixtures\Foo::classPath(), '/foos');
$this->assertEquals(Fixtures\FooTest::classPath(), '/footests');
$this->assertEquals(Fixtures\FooPerson::classPath(), '/foopeople');
$this->assertEquals(Fixtures\FooCurrency::classPath(), '/foocurrencies');
}
/**
* Should return throw InvalidRequest exception if id is null
* @return void
*/
public function testShouldThrowInvalidRequest()
{
$this->expectException(\FedaPay\Error\InvalidRequest::class);
$this->expectExceptionMessage(
'Could not determine which URL to request: Tests\Fixtures\Foo instance has invalid ID: '
);
Fixtures\Foo::resourcePath(null);
}
/**
* Should return return resource url
* @return void
*/
public function testReturnResourceUrl()
{
$this->assertEquals(Fixtures\Foo::resourcePath(1), '/foos/1');
}
/**
* Should return return resource url
* @return void
*/
public function testReturnInstanceUrl()
{
$object = new Fixtures\Foo;
$object->id = 1;
$this->assertEquals($object->instanceUrl(), '/foos/1');
}
}

View File

@@ -0,0 +1,653 @@
<?php
namespace Tests;
use Faker\Factory;
class TransactionTest extends BaseTestCase
{
/**
* Should return array of FedaPay\Transaction
*/
public function testShouldReturnTransactions()
{
$body = [
'v1/transactions' => [[
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer_id' => 1,
'currency_id' => 1,
'mode' => 'mtn',
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]],
'meta' => ['page' => 1]
];
$this->mockRequest('get', '/v1/transactions', [], $body);
$object = \FedaPay\Transaction::all();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object->meta);
$this->assertInstanceOf(\FedaPay\Transaction::class, $object->transactions[0]);
$this->assertEquals(1, $object->transactions[0]->id);
$this->assertEquals('0KJAU01', $object->transactions[0]->transaction_key);
$this->assertEquals('109329828', $object->transactions[0]->reference);
$this->assertEquals(100, $object->transactions[0]->amount);
$this->assertEquals('Description', $object->transactions[0]->description);
$this->assertEquals('http://e-shop.com', $object->transactions[0]->callback_url);
$this->assertEquals('pending', $object->transactions[0]->status);
$this->assertEquals(1, $object->transactions[0]->customer_id);
$this->assertEquals(1, $object->transactions[0]->currency_id);
$this->assertEquals('mtn', $object->transactions[0]->mode);
}
/**
* Should return array of FedaPay\Transaction
*/
public function testShouldCreateATransaction()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$this->assertInstanceOf(\FedaPay\Transaction::class, $transaction);
$this->assertEquals(1, $transaction->id);
$this->assertEquals('0KJAU01', $transaction->transaction_key);
$this->assertEquals('109329828', $transaction->reference);
$this->assertEquals(100, $transaction->amount);
$this->assertEquals('Description', $transaction->description);
$this->assertEquals('http://e-shop.com', $transaction->callback_url);
$this->assertEquals('pending', $transaction->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $transaction->customer);
$this->assertEquals(1, $transaction->customer->id);
$this->assertInstanceOf(\FedaPay\Currency::class, $transaction->currency);
$this->assertEquals(1, $transaction->currency->id);
$this->assertEquals(null, $transaction->mode);
}
/**
* Should return array of FedaPay\Transaction
*/
public function testShouldCreateTransactionInBatch()
{
$data = [
'transactions' => [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000
],
'include' => 'customer,currency'
];
$body = [
'v1/transaction_batch' => [
'klass' => 'v1/transaction_batch',
'transactions' => [
[
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
],
'errors' => []
]
];
$this->mockRequest('post', '/v1/transactions/batch', $data, $body);
$object = \FedaPay\Transaction::createInBatch($data);
$this->assertEquals(1, $object->transactions[0]->id);
$this->assertEquals('0KJAU01', $object->transactions[0]->transaction_key);
$this->assertEquals('109329828', $object->transactions[0]->reference);
$this->assertEquals(100, $object->transactions[0]->amount);
$this->assertEquals('Description', $object->transactions[0]->description);
$this->assertEquals('http://e-shop.com', $object->transactions[0]->callback_url);
$this->assertEquals('pending', $object->transactions[0]->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $object->transactions[0]->customer);
$this->assertEquals(1, $object->transactions[0]->customer->id);
$this->assertInstanceOf(\FedaPay\Currency::class, $object->transactions[0]->currency);
$this->assertEquals(1, $object->transactions[0]->currency->id);
$this->assertEquals(null, $object->transactions[0]->mode);
}
/**
* Should retrieve a Transaction
*/
public function testShouldRetrievedATransaction()
{
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('get', '/v1/transactions/1', [], $body);
$transaction = \FedaPay\Transaction::retrieve(1);
$this->assertInstanceOf(\FedaPay\Transaction::class, $transaction);
$this->assertEquals(1, $transaction->id);
$this->assertEquals('0KJAU01', $transaction->transaction_key);
$this->assertEquals('109329828', $transaction->reference);
$this->assertEquals(100, $transaction->amount);
$this->assertEquals('Description', $transaction->description);
$this->assertEquals('http://e-shop.com', $transaction->callback_url);
$this->assertEquals('pending', $transaction->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $transaction->customer);
$this->assertEquals(1, $transaction->customer->id);
$this->assertInstanceOf(\FedaPay\Currency::class, $transaction->currency);
$this->assertEquals(1, $transaction->currency->id);
$this->assertEquals(null, $transaction->mode);
}
/**
* Should update a transaction
*/
public function testShouldUpdateATransaction()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('put', '/v1/transactions/1', $data, $body);
$transaction = \FedaPay\Transaction::update(1, $data);
$this->assertInstanceOf(\FedaPay\Transaction::class, $transaction);
$this->assertEquals(1, $transaction->id);
$this->assertEquals('0KJAU01', $transaction->transaction_key);
$this->assertEquals('109329828', $transaction->reference);
$this->assertEquals(100, $transaction->amount);
$this->assertEquals('Description', $transaction->description);
$this->assertEquals('http://e-shop.com', $transaction->callback_url);
$this->assertEquals('pending', $transaction->status);
$this->assertInstanceOf(\FedaPay\Customer::class, $transaction->customer);
$this->assertEquals(1, $transaction->customer->id);
$this->assertInstanceOf(\FedaPay\Currency::class, $transaction->currency);
$this->assertEquals(1, $transaction->currency->id);
$this->assertEquals(null, $transaction->mode);
}
/**
* Should update a transaction with save
*/
public function testShouldUpdateATransactionWithSave()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$transaction->description = 'Update description';
$updateData = [
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Update description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'klass' => 'v1/customer',
],
'currency' => [
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
];
$this->mockRequest('put', '/v1/transactions/1', $updateData, $body);
$transaction->save();
}
/**
* Should delete a transaction
*/
public function testShouldDeleteATransaction()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$this->mockRequest('delete', '/v1/transactions/1');
$transaction->delete();
}
/**
* Should update a transaction with save
*/
public function testShouldGenerateTransactionToken()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$body = [
'token' => 'PAYEMENT_TOKEN',
'url' => 'https://process.fedapay.com/PAYEMENT_TOKEN',
];
$this->mockRequest('post', '/v1/transactions/1/token', [], $body);
$tokenObject = $transaction->generateToken();
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $tokenObject);
$this->assertEquals('PAYEMENT_TOKEN', $tokenObject->token);
$this->assertEquals('https://process.fedapay.com/PAYEMENT_TOKEN', $tokenObject->url);
}
/**
* Should update a transaction with save
*/
public function testShouldGenerateTransactionTokenFromId()
{
$body = [
'token' => 'PAYEMENT_TOKEN',
'url' => 'https://process.fedapay.com/PAYEMENT_TOKEN',
];
$this->mockRequest('post', '/v1/transactions/1/token', [], $body);
$tokenObject = \FedaPay\Transaction::generateTokenFromId(1);
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $tokenObject);
$this->assertEquals('PAYEMENT_TOKEN', $tokenObject->token);
$this->assertEquals('https://process.fedapay.com/PAYEMENT_TOKEN', $tokenObject->url);
}
/**
* Should update a transaction with save
*/
public function testShouldSendMtnRequestWithToken()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$this->mockRequest('post', '/v1/mtn', ['token' => 'PAYEMENT_TOKEN'], ['message' => 'success']);
$object = $transaction->sendNowWithToken('mtn', 'PAYEMENT_TOKEN');
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertEquals('success', $object->message);
}
/**
* Should send mtn request
*/
public function testShouldSendMtnRequest()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$body = [
'token' => 'PAYEMENT_TOKEN',
'url' => 'https://process.fedapay.com/PAYEMENT_TOKEN',
];
$this->mockMultipleRequests([
['method' => 'post', 'path' => '/v1/transactions/1/token', 'params' => [], 'response' => $body],
['method' => 'post', 'path' => '/v1/mtn', 'params' => ['token' => 'PAYEMENT_TOKEN'], 'response' => ['message' => 'success']]
]);
$object = $transaction->sendNow('mtn');
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertEquals('success', $object->message);
}
/**
* Should send fees request
*/
public function testShouldSendFeesRequest()
{
$data = [
'customer' => ['id' => 1],
'currency' => ['iso' => 'XOF'],
'description' => 'Description',
'callback_url' => 'http://localhost/callback',
'amount' => 1000,
'include' => 'customer,currency'
];
$body = [
'v1/transaction' => [
'id' => 1,
'klass' => 'v1/transaction',
'transaction_key' => '0KJAU01',
'reference' => '109329828',
'amount' => 100,
'description' => 'Description',
'callback_url' => 'http://e-shop.com',
'status' => 'pending',
'customer' => [
'id' => 1,
'klass' => 'v1/customer',
],
'currency' => [
'id' => 1,
'klass' => 'v1/currency',
'iso' => 'XOF'
],
'mode' => null,
'created_at' => '2018-03-12T09:09:03.969Z',
'updated_at' => '2018-03-12T09:09:03.969Z',
'paid_at' => '2018-03-12T09:09:03.969Z'
]
];
$this->mockRequest('post', '/v1/transactions', $data, $body);
$transaction = \FedaPay\Transaction::create($data);
$body = [
'amount_debited' => 51020,
'amount_transferred' => 50000,
'apply_fees_to_merchant' => false,
'commission' => 0.02,
'fees' => 1020,
'fixed_commission' => 0,
'message' => '{fees} de frais supplémentaires sont appliqués sur votre paiement.',
];
$this->mockRequest('get', '/v1/transactions/fees', ['token' => 'TOKEN', 'mode' => 'mtn'], $body);
$object = $transaction->getFees('TOKEN', 'mtn');
$this->assertInstanceOf(\FedaPay\FedaPayObject::class, $object);
$this->assertEquals(51020, $object->amount_debited);
$this->assertEquals(50000, $object->amount_transferred);
$this->assertEquals(false, $object->apply_fees_to_merchant);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Tests;
use FedaPay\Util;
class UtilTest extends BaseTestCase
{
public function testIsList()
{
$list = [5, 'nstaoush', []];
$this->assertTrue(Util\Util::isList($list));
$notlist = [5, 'nstaoush', [], 'bar' => 'baz'];
$this->assertFalse(Util\Util::isList($notlist));
}
public function testConvertFedaPayObjectToArrayIncludesId()
{
$customer = Util\Util::convertToFedaPayObject(
[
'id' => '123',
'object' => 'v1/customer',
'value' => ['a', 'b']
],
[]
);
$array = $customer->__toArray(true);
$this->assertTrue(array_key_exists('id', $array));
$this->assertEquals($array['id'], '123');
$this->assertEquals(['a', 'b'], $array['value']);
}
public function testEncodeParameters()
{
$params = [
'a' => 3,
'b' => '+foo?',
'c' => 'bar&baz',
'd' => ['a' => 'a', 'b' => 'b'],
'e' => [0, 1],
'f' => '',
// note the empty hash won't even show up in the request
'g' => [],
];
$this->assertSame(
"a=3&b=%2Bfoo%3F&c=bar%26baz&d[a]=a&d[b]=b&e[]=0&e[]=1&f=",
Util\Util::encodeParameters($params)
);
}
public function testUrlEncode()
{
$this->assertSame("foo", Util\Util::urlEncode("foo"));
$this->assertSame("foo%2B", Util\Util::urlEncode("foo+"));
$this->assertSame("foo%26", Util\Util::urlEncode("foo&"));
$this->assertSame("foo[bar]", Util\Util::urlEncode("foo[bar]"));
}
public function testFlattenParams()
{
$params = [
'a' => 3,
'b' => '+foo?',
'c' => 'bar&baz',
'd' => ['a' => 'a', 'b' => 'b'],
'e' => [0, 1],
'f' => [
['foo' => '1', 'ghi' => '2'],
['foo' => '3', 'bar' => '4'],
],
];
$encoded = [];
Util\Util::flattenParams($params, $encoded);
$this->assertSame(
[
['a', 3],
['b', '+foo?'],
['c', 'bar&baz'],
['d[a]', 'a'],
['d[b]', 'b'],
['e[]', 0],
['e[]', 1],
['f[][foo]', '1'],
['f[][ghi]', '2'],
['f[][foo]', '3'],
['f[][bar]', '4'],
],
$encoded
);
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Tests;
use FedaPay\Webhook;
use FedaPay\WebhookSignature;
class WebhookTest extends BaseTestCase
{
const EVENT_PAYLOAD = "{
\"id\": \"evt_test_webhook\",
\"object\": \"event\"
}";
const SECRET = "whsec_test_secret";
private function generateHeader($opts = [])
{
$timestamp = array_key_exists('timestamp', $opts) ? $opts['timestamp'] : time();
$payload = array_key_exists('payload', $opts) ? $opts['payload'] : self::EVENT_PAYLOAD;
$secret = array_key_exists('secret', $opts) ? $opts['secret'] : self::SECRET;
$scheme = array_key_exists('scheme', $opts) ? $opts['scheme'] : WebhookSignature::EXPECTED_SCHEME;
$signature = array_key_exists('signature', $opts) ? $opts['signature'] : null;
if ($signature === null) {
$signedPayload = "$timestamp.$payload";
$signature = hash_hmac("sha256", $signedPayload, $secret);
}
return "t=$timestamp,$scheme=$signature";
}
public function testValidJsonAndHeader()
{
$sigHeader = $this->generateHeader();
$event = Webhook::constructEvent(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
$this->assertInstanceOf(\FedaPay\Event::class, $event);
$this->assertEquals("evt_test_webhook", $event->id);
}
public function testInvalidJson()
{
$this->expectException('\UnexpectedValueException');
$payload = "this is not valid JSON";
$sigHeader = $this->generateHeader(["payload" => $payload]);
Webhook::constructEvent($payload, $sigHeader, self::SECRET);
}
public function testValidJsonAndInvalidHeader()
{
$this->expectException('\FedaPay\Error\SignatureVerification');
$sigHeader = "bad_header";
Webhook::constructEvent(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
}
public function testMalformedHeader()
{
$this->expectException('\FedaPay\Error\SignatureVerification');
$this->expectExceptionMessage('Unable to extract timestamp and signatures from header');
$sigHeader = "i'm not even a real signature header";
WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
}
/**
* @expectedException \FedaPay\Error\SignatureVerification
* @expectedExceptionMessage No signatures found with expected scheme
*/
public function testNoSignaturesWithExpectedScheme()
{
$this->expectException('\FedaPay\Error\SignatureVerification');
$this->expectExceptionMessage('No signatures found with expected scheme');
$sigHeader = $this->generateHeader(["scheme" => "v0"]);
WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
}
public function testNoValidSignatureForPayload()
{
$this->expectException('\FedaPay\Error\SignatureVerification');
$this->expectExceptionMessage('No signatures found matching the expected signature for payload');
$sigHeader = $this->generateHeader(["signature" => "bad_signature"]);
WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
}
public function testTimestampTooOld()
{
$this->expectException('\FedaPay\Error\SignatureVerification');
$this->expectExceptionMessage('Timestamp outside the tolerance zone');
$sigHeader = $this->generateHeader(["timestamp" => time() - 15]);
WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10);
}
public function testTimestampTooRecent()
{
$this->expectException('\FedaPay\Error\SignatureVerification');
$this->expectExceptionMessage('Timestamp outside the tolerance zone');
$sigHeader = $this->generateHeader(["timestamp" => time() + 15]);
WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10);
}
public function testValidHeaderAndSignature()
{
$sigHeader = $this->generateHeader();
$this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10));
}
public function testHeaderContainsValidSignature()
{
$sigHeader = $this->generateHeader() . ",v1=bad_signature";
$this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10));
}
public function testTimestampOffButNoTolerance()
{
$sigHeader = $this->generateHeader(["timestamp" => 12345]);
$this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET));
}
}

View File

@@ -0,0 +1,5 @@
<?php
require_once __DIR__ . '/../init.php';
require_once __DIR__ . '/bootstrap.php';

View File

@@ -0,0 +1,7 @@
<?php
require_once __DIR__ . '/Fixtures/Foo.php';
require_once __DIR__ . '/Fixtures/FooCurrency.php';
require_once __DIR__ . '/Fixtures/FooPerson.php';
require_once __DIR__ . '/Fixtures/FooTest.php';
require_once __DIR__ . '/BaseTestCase.php';

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env php
<?php
\chdir(__DIR__);
\set_time_limit(0); // unlimited max execution time
$fp = \fopen(__DIR__ . '/data/ca-certificates.crt', 'w+b');
$options = [
\CURLOPT_FILE => $fp,
\CURLOPT_TIMEOUT => 3600,
\CURLOPT_URL => 'https://curl.se/ca/cacert.pem',
];
$ch = \curl_init();
\curl_setopt_array($ch, $options);
\curl_exec($ch);
\curl_close($ch);
\fclose($fp);