diff --git a/CHANGELOG.md b/CHANGELOG.md index 4878f7d..0d06073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ CoinGate PHP library release notes ============================ +v4.2.0 +--- +* Added support for Refund API calls. + - Added support for [Create Order Refund](https://developer.coingate.com/reference/create-refund) + - Added support for [Get Order Refund](https://developer.coingate.com/reference/get-order-refund) + - Added support for [Get Order Refunds](https://developer.coingate.com/reference/get-refund) + - Added support for [Get Refunds](https://developer.coingate.com/reference/get-refunds) +* Added support for Ledger API calls. + - Added support for [Get Account](https://developer.coingate.com/reference/get-account) + - Added support for [List Accounts](https://developer.coingate.com/reference/accounts) +* Added support for Withdrawal API calls. + - Added support for [Get Withdrawals](https://developer.coingate.com/reference/get-withdrawals) + - Added support for [Get Withdrawal](https://developer.coingate.com/reference/get-withdrawal) + v4.1.0 --- * ApiKey is no more mandatory when creating a Client. Useful when you want to perform Public API calls only. diff --git a/README.md b/README.md index b7c2acb..ec7b01a 100644 --- a/README.md +++ b/README.md @@ -1,216 +1,333 @@ -# CoinGate PHP library for API v2 - -The CoinGate PHP library provides convenient access to the CoinGate API from applications written in the PHP language. - -## Requirements - -PHP 7.3.0 and later. - -## Composer - -You can install library via [Composer](http://getcomposer.org/). Run the following command: - -```bash -composer require coingate/coingate-php -``` - -## Manual Installation - -If you do not wish to use Composer, you can download the [latest release](https://github.com/coingate/coingate-php/releases). Then, to use the library, include the `init.php` file. - -```php -require_once('/path/to/coingate-php/init.php'); -``` - -## Dependencies - -The library require the following extensions 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) - -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 - -You can sign up for a CoinGate account at for production and for testing (sandbox). - -Please note, that for Sandbox you must generate separate API credentials on . API credentials generated on will not work for Sandbox mode. - -Usage of CoinGate PHP library looks like: - -```php -$client = new \CoinGate\Client('YOUR_API_TOKEN'); -``` - -In order, to use sandbox mode, you need to set second parameter to `true`. - -```php -$client = new \CoinGate\Client('YOUR_API_TOKEN', true); -``` - -If you plan to use Public API endpoints only, authentication is not required. - -```php -$client = new CoinGate\Client(); - -// if needed you can set configuration parameters later -$client->setApiKey('YOUR_API_TOKEN'); -$client->setEnvironment('sandbox'); -``` -Full documentation of the CoinGate API can be found [here](https://developer.coingate.com/reference/api-overview) - -### Example -An example of an app using this library can be found [here](https://github.com/coingate/laravel-demo) - -## Payment Gateway API - -### Create Order - -Create order at CoinGate and redirect shopper to invoice (payment_url). - -```php -$params = [ - 'order_id' => 'YOUR-CUSTOM-ORDER-ID-115', - 'price_amount' => 1050.99, - 'price_currency' => 'USD', - 'receive_currency' => 'EUR', - 'callback_url' => 'https://example.com/payments?token=6tCENGUYI62ojkuzDPX7Jg', - 'cancel_url' => 'https://example.com/cart', - 'success_url' => 'https://example.com/account/orders', - 'title' => 'Order #112', - 'description' => 'Apple Iphone 13' -]; - -try { - $order = $client->order->create($params); -} catch (\CoinGate\Exception\ApiErrorException $e) { - // something went wrong... - // var_dump($e->getErrorDetails()); -} - -echo $order->id; -``` - -### Checkout - -Placing created order with pre-selected payment currency (BTC, LTC, ETH, etc). Display payment_address and pay_amount for shopper or redirect to payment_url. Can be used to white-label invoices. - -```php -$checkout = $client->order->checkout(7294, [ - 'pay_currency' => 'BTC' -]); -``` - -### Get Order - -After creating an order, you will get an ORDER ID. This ID will be used for GET ORDER requests. - -```php -$order = $client->order->get(7294); -``` - -### List Orders - -Retrieving information of all placed orders. - -```php -$orders = $client->order->list([ - 'created_at' => [ - 'from' => '2022-01-25' - ] -]); -``` - -## Public API - -### Get Exchange Rate - -Current exchange rate for any two currencies, fiat or crypto. This endpoint is public, authentication is not required. - -```php -$client->getExchangeRate('BTC', 'EUR'); -``` - -### List Exchange Rates - -Current CoinGate exchange rates for Merchants and Traders. This endpoint is public, authentication is not required. - -```php -$client->listExchangeRates(); -``` - -### Ping - -A health check endpoint for CoinGate API. This endpoint is public, authentication is not required. - -```php -$client->ping(); -``` - -### IP Addresses - -Get IP addresses of CoinGate servers - -```php -$client->getIPAddresses(); -``` - -### Currencies - -```php -$client->getCurrencies(); - -// Crypto + Native + Merchant Pay -$client->getCheckoutCurrencies(); - -// get Merchant Pay currencies only -$client->getMerchantPayCurrencies(); - -// get Merchant Receive currencies only -$client->getMerchantPayoutCurrencies(); -``` - -### Platforms - -```php -$client->getPlatforms(); -``` - -## Custom Request Timeout - -To modify request timeouts (connect or total, in seconds) you'll need to tell the API client to use a CurlClient other than its default. You'll set the timeouts in that CurlClient. - -```php -// set up your tweaked Curl client -$curl = new \CoinGate\HttpClient\CurlClient(); -$curl->setTimeout(10); -$curl->setConnectTimeout(5); - -// tell CoinGate Library to use the tweaked Curl client -\CoinGate\Client::setHttpClient($curl); - -// use the CoinGate API client as you normally would -``` - -## Test API Connection - -```php -$result = \CoinGate\Client::testConnection('YOUR_API_TOKEN'); -``` - -In order, to test API connection on sandbox mode, you need to set second parameter to `true`. - -```php -$result = \CoinGate\Client::testConnection('YOUR_API_TOKEN', true); -``` - -## Attention plugin developers - -Are you writing a plugin that integrates CoinGate and embeds our library? Then please use the setAppInfo function to identify your plugin. For example: - -```php -\CoinGate\Client::setAppInfo("MyAwesomePlugin", "1.0.0"); -``` - -The method should be called once, before any request is sent to the API. The second parameter is optional. +# CoinGate PHP library for API v2 + +The CoinGate PHP library provides convenient access to the CoinGate API from applications written in the PHP language. + +## Requirements + +PHP 7.3.0 and later. + +## Composer + +You can install library via [Composer](http://getcomposer.org/). Run the following command: + +```bash +composer require coingate/coingate-php +``` + +## Manual Installation + +If you do not wish to use Composer, you can download the [latest release](https://github.com/coingate/coingate-php/releases). Then, to use the library, include the `init.php` file. + +```php +require_once('/path/to/coingate-php/init.php'); +``` + +## Dependencies + +The library require the following extensions 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) + +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 + +You can sign up for a CoinGate account at for production and for testing (sandbox). + +Please note, that for Sandbox you must generate separate API credentials on . API credentials generated on will not work for Sandbox mode. + +Usage of CoinGate PHP library looks like: + +```php +$client = new \CoinGate\Client('YOUR_API_TOKEN'); +``` + +In order, to use sandbox mode, you need to set second parameter to `true`. + +```php +$client = new \CoinGate\Client('YOUR_API_TOKEN', true); +``` + +If you plan to use Public API endpoints only, authentication is not required. + +```php +$client = new CoinGate\Client(); + +// if needed you can set configuration parameters later +$client->setApiKey('YOUR_API_TOKEN'); +$client->setEnvironment('sandbox'); +``` +Full documentation of the CoinGate API can be found [here](https://developer.coingate.com/reference/api-overview) + +### Example +An example of an app using this library can be found [here](https://github.com/coingate/laravel-demo) + +## Payment Gateway API + +### Create Order + +Create order at CoinGate and redirect shopper to invoice (payment_url). + +```php +$params = [ + 'order_id' => 'YOUR-CUSTOM-ORDER-ID-115', + 'price_amount' => 1050.99, + 'price_currency' => 'USD', + 'receive_currency' => 'EUR', + 'callback_url' => 'https://example.com/payments?token=6tCENGUYI62ojkuzDPX7Jg', + 'cancel_url' => 'https://example.com/cart', + 'success_url' => 'https://example.com/account/orders', + 'title' => 'Order #112', + 'description' => 'Apple Iphone 13' +]; + +try { + $order = $client->order->create($params); +} catch (\CoinGate\Exception\ApiErrorException $e) { + // something went wrong... + // var_dump($e->getErrorDetails()); +} + +echo $order->id; +``` + +### Checkout + +Placing created order with pre-selected payment currency (BTC, LTC, ETH, etc). Display payment_address and pay_amount for shopper or redirect to payment_url. Can be used to white-label invoices. + +```php +$checkout = $client->order->checkout(7294, [ + 'pay_currency' => 'BTC' +]); +``` + +### Get Order + +After creating an order, you will get an ORDER ID. This ID will be used for GET ORDER requests. + +```php +$order = $client->order->get(7294); +``` + +### List Orders + +Retrieving information of all placed orders. + +```php +$orders = $client->order->list([ + 'created_at' => [ + 'from' => '2022-01-25' + ] +]); +``` + +## Refund API + +### Create Order Refund + +Creating a refund for an order. + +```php +$params = [ + 'amount' => 50, + 'address' => '2ExAmPl3dyFiqkMUUksTJ3Qey1s2Q3f4i59', + 'address_memo' => 'Blue house', // optional + 'currency_id' => 1, + 'platform_id' => 2, + 'reason' => 'Iphone refund', + 'email' => 'example.guy@xmpl.com', + 'ledger_account_id' => 'ID of the trader balance' +]; + +try { + $refund = $client->refund->create(7294, $params); +} catch (\CoinGate\Exception\ApiErrorException $e) { + // something went wrong... +} + +echo $refund->id; +``` + +### Get Order Refund + +Retrieves a specific refund for an order. + +```php +$refund = $client->refund->get(7294, 4927); +``` + +### Get Order Refunds + +Retrieves all refunds for an order. + +```php +// optional filters +$params = [ + 'page' => 2, + 'per_page' => 30 +] + +$orderRefunds = $client->refund->list(7294, $params); +``` + +### Get Refunds + +Retrieves all refunds. + +```php +$refunds = $client->refund->list(); +``` + +or + +```php +// optional filters +$params = [ + 'page' => 2, + 'per_page' => 30 +] + +$refunds = $client->refund->list(null, $params); +``` + +## Ledger API + +### Get Account + +Retrieves a specific ledger account. + +```php +$account = $client->ledger->get('01G0EM0RRVREB4WD5KDB6VJVPM'); +``` + +### Get Accounts + +Retrieves all ledger accounts. + +```php +// optional filters +$params = [ + 'page' => 2, + 'per_page' => 30 +] + +$accounts = $client->ledger->list($params); +``` + +## Withdrawal API + +### Get Withdrawal + +Retrieving specific withdrawal. + +```php +$withdrawal = $client->withdrawal->get(1234); +``` + +### Get Withdrawals + +Retrieving all withdrawals. + +```php +// optional filters +$params = [ + 'page' => 2, + 'per_page' => 30 +] + +$withdrawals = $client->withdrawal->list($params); +``` + +## Public API + +### Get Exchange Rate + +Current exchange rate for any two currencies, fiat or crypto. This endpoint is public, authentication is not required. + +```php +$client->getExchangeRate('BTC', 'EUR'); +``` + +### List Exchange Rates + +Current CoinGate exchange rates for Merchants and Traders. This endpoint is public, authentication is not required. + +```php +$client->listExchangeRates(); +``` + +### Ping + +A health check endpoint for CoinGate API. This endpoint is public, authentication is not required. + +```php +$client->ping(); +``` + +### IP Addresses + +Get IP addresses of CoinGate servers + +```php +$client->getIPAddresses(); +``` + +### Currencies + +```php +$client->getCurrencies(); + +// Crypto + Native + Merchant Pay +$client->getCheckoutCurrencies(); + +// get Merchant Pay currencies only +$client->getMerchantPayCurrencies(); + +// get Merchant Receive currencies only +$client->getMerchantPayoutCurrencies(); +``` + +### Platforms + +```php +$client->getPlatforms(); +``` + +## Custom Request Timeout + +To modify request timeouts (connect or total, in seconds) you'll need to tell the API client to use a CurlClient other than its default. You'll set the timeouts in that CurlClient. + +```php +// set up your tweaked Curl client +$curl = new \CoinGate\HttpClient\CurlClient(); +$curl->setTimeout(10); +$curl->setConnectTimeout(5); + +// tell CoinGate Library to use the tweaked Curl client +\CoinGate\Client::setHttpClient($curl); + +// use the CoinGate API client as you normally would +``` + +## Test API Connection + +```php +$result = \CoinGate\Client::testConnection('YOUR_API_TOKEN'); +``` + +In order, to test API connection on sandbox mode, you need to set second parameter to `true`. + +```php +$result = \CoinGate\Client::testConnection('YOUR_API_TOKEN', true); +``` + +## Attention plugin developers + +Are you writing a plugin that integrates CoinGate and embeds our library? Then please use the setAppInfo function to identify your plugin. For example: + +```php +\CoinGate\Client::setAppInfo("MyAwesomePlugin", "1.0.0"); +``` + +The method should be called once, before any request is sent to the API. The second parameter is optional. diff --git a/composer.json b/composer.json index e3663c9..17e2374 100644 --- a/composer.json +++ b/composer.json @@ -1,49 +1,49 @@ -{ - "name": "coingate/coingate-php", - "description": "CoinGate library for PHP", - "keywords": [ - "coingate", - "bitcoin", - "litecoin", - "altcoin", - "merchant", - "gateway", - "payment" - ], - "homepage": "https://coingate.com", - "license": "MIT", - "authors": [ - { - "name": "CoinGate and contributors", - "homepage": "https://github.com/coingate/coingate-php/graphs/contributors" - }, - - { - "name": "Linas Pašviestis", - "email": "linas.pasviestis@gmail.com" - } - ], - "require": { - "php": ">=7.3.0", - "ext-curl": "*", - "ext-json": "*" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^9.0", - "squizlabs/php_codesniffer": "~3.6.2", - "phpstan/phpstan": "~1.4.10" - }, - "autoload": { - "psr-4": { - "CoinGate\\": "lib/" - } - }, - "autoload-dev": { - "psr-4": { - "CoinGate\\": [ - "tests/", - "tests/CoinGate/" - ] - } - } -} \ No newline at end of file +{ + "name": "coingate/coingate-php", + "description": "CoinGate library for PHP", + "keywords": [ + "coingate", + "bitcoin", + "litecoin", + "altcoin", + "merchant", + "gateway", + "payment" + ], + "homepage": "https://coingate.com", + "license": "MIT", + "authors": [ + { + "name": "CoinGate and contributors", + "homepage": "https://github.com/coingate/coingate-php/graphs/contributors" + }, + + { + "name": "Linas Pašviestis", + "email": "linas.pasviestis@gmail.com" + } + ], + "require": { + "php": ">=7.3.0", + "ext-curl": "*", + "ext-json": "*" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^9.0", + "squizlabs/php_codesniffer": "~3.6.2", + "phpstan/phpstan": "~1.4.10" + }, + "autoload": { + "psr-4": { + "CoinGate\\": "lib/" + } + }, + "autoload-dev": { + "psr-4": { + "CoinGate\\": [ + "tests/", + "tests/CoinGate/" + ] + } + } +} diff --git a/init.php b/init.php index c65edd4..d8f1cd6 100644 --- a/init.php +++ b/init.php @@ -15,6 +15,9 @@ require __DIR__ . '/lib/Services/ServiceFactory.php'; require __DIR__ . '/lib/Services/OrderService.php'; require __DIR__ . '/lib/Services/PublicService.php'; +require __DIR__ . '/lib/Services/RefundService.php'; +require __DIR__ . '/lib/Services/LedgerService.php'; +require __DIR__ . '/lib/Services/WithdrawalService.php'; // Exceptions require __DIR__ . '/lib/Exception/ApiConnectionException.php'; @@ -33,3 +36,7 @@ require __DIR__ . '/lib/Exception/Api/OrderNotFound.php'; require __DIR__ . '/lib/Exception/Api/Unauthorized.php'; require __DIR__ . '/lib/Exception/Api/UnprocessableEntity.php'; +require __DIR__ . '/lib/Exception/Api/RefundIsNotValid.php'; +require __DIR__ . '/lib/Exception/Api/RefundNotFound.php'; +require __DIR__ . '/lib/Exception/Api/LedgerAccountNotFound.php'; +require __DIR__ . '/lib/Exception/Api/WithdrawalNotFound.php'; diff --git a/lib/BaseClient.php b/lib/BaseClient.php index 1469cc0..a0ede9f 100644 --- a/lib/BaseClient.php +++ b/lib/BaseClient.php @@ -7,8 +7,12 @@ use CoinGate\Exception\Api\NotFound; use CoinGate\Exception\Api\OrderIsNotValid; use CoinGate\Exception\Api\OrderNotFound; +use CoinGate\Exception\Api\RefundIsNotValid; +use CoinGate\Exception\Api\RefundNotFound; +use CoinGate\Exception\Api\LedgerAccountNotFound; use CoinGate\Exception\Api\Unauthorized; use CoinGate\Exception\Api\UnprocessableEntity; +use CoinGate\Exception\Api\WithdrawalNotFound; use CoinGate\Exception\ApiErrorException; use CoinGate\Exception\InvalidArgumentException; use CoinGate\Exception\InternalServerError; @@ -18,12 +22,18 @@ use CoinGate\HttpClient\CurlClient; use CoinGate\Services\OrderService; use CoinGate\Services\PublicService; +use CoinGate\Services\RefundService; +use CoinGate\Services\LedgerService; +use CoinGate\Services\WithdrawalService; use Exception; /** * Client used to send requests to CoinGate's API * * @property OrderService $order + * @property RefundService $refund + * @property LedgerService $ledger + * @property WithdrawalService $withdrawal * @mixin PublicService */ class BaseClient implements ClientInterface @@ -31,7 +41,7 @@ class BaseClient implements ClientInterface /** * @var string */ - public const VERSION = '4.1.0'; + public const VERSION = '4.2.0'; /** * @var string default base URL for CoinBase's API @@ -293,14 +303,26 @@ public function handleErrorResponse($response, int $httpStatus) } } elseif ($httpStatus === 404) { switch ($reason) { + case 'RefundNotFound': + throw RefundNotFound::factory($response, $httpStatus); + + case 'LedgerAccountNotFound': + throw LedgerAccountNotFound::factory($response, $httpStatus); + case 'OrderNotFound': throw OrderNotFound::factory($response, $httpStatus); + case 'WithdrawalNotFound': + throw WithdrawalNotFound::factory($response, $httpStatus); + default: throw NotFound::factory($response, $httpStatus); } } elseif ($httpStatus === 422) { switch ($reason) { + case 'RefundIsNotValid': + throw RefundIsNotValid::factory($response, $httpStatus); + case 'OrderIsNotValid': throw OrderIsNotValid::factory($response, $httpStatus); diff --git a/lib/Exception/Api/LedgerAccountNotFound.php b/lib/Exception/Api/LedgerAccountNotFound.php new file mode 100644 index 0000000..149b0c8 --- /dev/null +++ b/lib/Exception/Api/LedgerAccountNotFound.php @@ -0,0 +1,12 @@ +request('get', $this->buildPath('/v2/ledger/accounts/%s', $id)); + } + + /** + * Retrieves all ledger accounts. + * + * @param string[] $params + * @return mixed + */ + public function list(array $params = []) + { + return $this->request('get', '/v2/ledger/accounts', $params); + } +} diff --git a/lib/Services/RefundService.php b/lib/Services/RefundService.php new file mode 100644 index 0000000..c70e81a --- /dev/null +++ b/lib/Services/RefundService.php @@ -0,0 +1,46 @@ +request('post', $this->buildPath('/v2/orders/%s/refunds', $orderId), $params); + } + + /** + * Retrieves a specific refund for an order. + * + * @param int $orderId + * @param int $id + * @return mixed + */ + public function get(int $orderId, int $id) + { + return $this->request('get', $this->buildPath('/v2/orders/%s/refunds/%s', $orderId, $id)); + } + + /** + * Retrieves all refunds (optionally could be filtered by an order). + * + * @param int|null $orderId + * @param string[] $params + * @return mixed + */ + public function list(int $orderId = null, array $params = []) + { + if ($orderId === null) { + return $this->request('get', '/v2/refunds', $params); + } + + return $this->request('get', $this->buildPath('/v2/orders/%s/refunds', $orderId), $params); + } +} diff --git a/lib/Services/ServiceFactory.php b/lib/Services/ServiceFactory.php index e6a5c5b..50070c8 100644 --- a/lib/Services/ServiceFactory.php +++ b/lib/Services/ServiceFactory.php @@ -14,7 +14,10 @@ class ServiceFactory extends AbstractServiceFactory * @var array */ private static $classMap = [ - 'order' => OrderService::class + 'order' => OrderService::class, + 'refund' => RefundService::class, + 'ledger' => LedgerService::class, + 'withdrawal' => WithdrawalService::class ]; /** diff --git a/lib/Services/WithdrawalService.php b/lib/Services/WithdrawalService.php new file mode 100644 index 0000000..84da39e --- /dev/null +++ b/lib/Services/WithdrawalService.php @@ -0,0 +1,27 @@ +request('get', $this->buildPath('/v2/withdrawals/%s', $id)); + } + + /** + * Retrieves all withdrawals + * + * @return mixed + */ + public function list() + { + return $this->request('get', '/v2/withdrawals'); + } +} diff --git a/tests/CoinGate/Services/LedgerServiceTest.php b/tests/CoinGate/Services/LedgerServiceTest.php new file mode 100644 index 0000000..710a66c --- /dev/null +++ b/tests/CoinGate/Services/LedgerServiceTest.php @@ -0,0 +1,76 @@ +mockHttpClient = $this->mockHttpClient(); + + $client = $this->createSandboxClient($this->mockHttpClient); + + $this->service = new LedgerService($client); + } + + public function testGetLedgerAccount() + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => '01G0EM0RRVREB4WD5KDB6VJVPM' + ]), + 200 + ]); + + $account = $this->service->get('01G0EM0RRVREB4WD5KDB6VJVPM'); + + $this->assertObjectHasAttribute('id', $account); + } + + public function testNotFoundGetLedgerAccount() + { + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\LedgerAccountNotFound::factory('Ledger Account not found', 404) + ); + + $this->expectException(\CoinGate\Exception\Api\LedgerAccountNotFound::class); + + $this->service->get('01G0EM0RRVREB4WD5KDB6VJVPM'); + } + + public function testListLedgerAccounts() + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'current_page' => 1, + 'per_page' => 20, + 'total_accounts' => 1, + 'total_pages' => 1, + 'accounts' => [ + [ 'id' => '01G0EM0RRVREB4WD5KDB6VJVPM' ] + ] + ]), + 200 + ]); + + $response = $this->service->list(); + + $this->assertNotEmpty($response); + } +} diff --git a/tests/CoinGate/Services/OrderServiceTest.php b/tests/CoinGate/Services/OrderServiceTest.php index 537a284..2d8c68f 100644 --- a/tests/CoinGate/Services/OrderServiceTest.php +++ b/tests/CoinGate/Services/OrderServiceTest.php @@ -6,7 +6,9 @@ class OrderServiceTest extends TestCase { - /** @var OrderService */ + private $mockHttpClient; + + /** @var \CoinGate\Services\OrderService */ private $service; /** @@ -14,26 +16,54 @@ class OrderServiceTest extends TestCase */ protected function setUpService() { - $client = $this->createSandboxClient(); + $this->mockHttpClient = $this->mockHttpClient(); + + $client = $this->createSandboxClient($this->mockHttpClient); $this->service = new OrderService($client); } public function testCreateOrder() { - $params = self::getGoodPostParams(); - - $order = $this->service->create($params); + $myCustomOrderId = 'YOUR-CUSTOM-ORDER-ID-115'; + + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => 1, + 'order_id' => $myCustomOrderId + ]), + 200 + ]); + + $order = $this->service->create([ + 'order_id' => $myCustomOrderId, + 'price_amount' => 1050.99, + 'price_currency' => 'USD', + 'receive_currency' => 'EUR', + 'callback_url' => 'https://example.com/payments?token=6tCENGUYI62ojkuzDPX7Jg', + 'cancel_url' => 'https://example.com/cart', + 'success_url' => 'https://example.com/account/orders', + 'title' => 'Order #112', + 'description' => 'Apple Iphone 6' + ]); $this->assertObjectHasAttribute('id', $order); - $this->assertSame($order->order_id, $params['order_id']); + $this->assertSame($order->order_id, $myCustomOrderId); return $order; } public function testInvalidCreateOrder() { - $this->expectException('CoinGate\Exception\Api\OrderIsNotValid'); + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\OrderIsNotValid::factory('Order is not valid', 422) + ); + + $this->expectException(\CoinGate\Exception\Api\OrderIsNotValid::class); $this->service->create([]); } @@ -43,6 +73,15 @@ public function testInvalidCreateOrder() */ public function testCheckout($order) { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => $order->id + ]), + 200 + ]); + $response = $this->service->checkout($order->id, [ 'pay_currency' => 'BTC' ]); @@ -50,9 +89,15 @@ public function testCheckout($order) $this->assertSame($order->id, $response->id); } - public function testInvalidCheckout() + public function testNotFoundCheckout() { - $this->expectException('CoinGate\Exception\Api\OrderNotFound'); + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\OrderNotFound::factory('Order not found', 404) + ); + + $this->expectException(\CoinGate\Exception\Api\OrderNotFound::class); $this->service->checkout(0, [ 'pay_currency' => 'BTC' @@ -64,20 +109,50 @@ public function testInvalidCheckout() */ public function testGetOrder($order) { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => $order->id + ]), + 200 + ]); + $response = $this->service->get($order->id); $this->assertSame($order->id, $response->id); } - public function testInvalidGetOrder() + public function testNotFoundGetOrder() { - $this->expectException('CoinGate\Exception\Api\OrderNotFound'); + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\OrderNotFound::factory('Order not found', 404) + ); + + $this->expectException(\CoinGate\Exception\Api\OrderNotFound::class); $this->service->get(0); } public function testListOrders() { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'current_page' => 1, + 'per_page' => 20, + 'total_orders' => 1, + 'total_pages' => 1, + 'orders' => [ + [ 'id' => 1 ] + ] + ]), + 200 + ]); + $response = $this->service->list([ 'created_at' => [ 'from' => '2022-01-25' @@ -86,19 +161,4 @@ public function testListOrders() $this->assertNotEmpty($response); } - - public static function getGoodPostParams(): array - { - return [ - 'order_id' => 'YOUR-CUSTOM-ORDER-ID-115', - 'price_amount' => 1050.99, - 'price_currency' => 'USD', - 'receive_currency' => 'EUR', - 'callback_url' => 'https://example.com/payments?token=6tCENGUYI62ojkuzDPX7Jg', - 'cancel_url' => 'https://example.com/cart', - 'success_url' => 'https://example.com/account/orders', - 'title' => 'Order #112', - 'description' => 'Apple Iphone 6' - ]; - } } diff --git a/tests/CoinGate/Services/RefundServiceTest.php b/tests/CoinGate/Services/RefundServiceTest.php new file mode 100644 index 0000000..d2c67f6 --- /dev/null +++ b/tests/CoinGate/Services/RefundServiceTest.php @@ -0,0 +1,129 @@ +mockHttpClient = $this->mockHttpClient(); + + $client = $this->createSandboxClient($this->mockHttpClient); + + $this->service = new RefundService($client); + } + + public function testCreateOrderRefund() + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => 1 + ]), + 200 + ]); + + $refund = $this->service->create($this->orderId, []); + + $this->assertObjectHasAttribute('id', $refund); + + return $refund; + } + + public function testInvalidCreateRefundOrder() + { + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\RefundIsNotValid::factory('Refund is not valid', 422) + ); + + $this->expectException(\CoinGate\Exception\Api\RefundIsNotValid::class); + + $this->service->create($this->orderId, []); + } + + /** + * @depends testCreateOrderRefund + */ + public function testGetOrderRefund($refund) + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => $refund->id + ]), + 200 + ]); + + $response = $this->service->get($this->orderId, $refund->id); + + $this->assertSame($refund->id, $response->id); + } + + public function testNotFoundGetOrderRefund() + { + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\RefundNotFound::factory('Refund not found', 404) + ); + + $this->expectException(\CoinGate\Exception\Api\RefundNotFound::class); + + $this->service->get($this->orderId, 0); + } + + public function testGetOrderRefundsOrRefunds() + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'current_page' => 1, + 'per_page' => 20, + 'total_refunds' => 1, + 'total_pages' => 1, + 'refunds' => [ + [ 'id' => 1 ] + ] + ]), + 200 + ]); + + $response = $this->service->list($this->orderId); + + $this->assertNotEmpty($response); + + $response = $this->service->list(); + + $this->assertNotEmpty($response); + } + + public function testNotFoundGetOrderRefunds() + { + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\RefundNotFound::factory('Refund not found', 404) + ); + + $this->expectException(\CoinGate\Exception\Api\RefundNotFound::class); + + $this->service->list(0); + } +} diff --git a/tests/CoinGate/Services/WithdrawalServiceTest.php b/tests/CoinGate/Services/WithdrawalServiceTest.php new file mode 100644 index 0000000..1006794 --- /dev/null +++ b/tests/CoinGate/Services/WithdrawalServiceTest.php @@ -0,0 +1,76 @@ +mockHttpClient = $this->mockHttpClient(); + + $client = $this->createSandboxClient($this->mockHttpClient); + + $this->service = new WithdrawalService($client); + } + + public function testGetWithdrawal() + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'id' => '10' + ]), + 200 + ]); + + $withdrawal = $this->service->get(10); + + $this->assertObjectHasAttribute('id', $withdrawal); + } + + public function testNotFoundWithdrawal() + { + $this->mockHttpClient + ->method('request') + ->willThrowException( + \CoinGate\Exception\Api\WithdrawalNotFound::factory('Withdrawal does not exist', 404) + ); + + $this->expectException(\CoinGate\Exception\Api\WithdrawalNotFound::class); + + $this->service->get(10); + } + + public function testWithdrawals() + { + $this->mockHttpClient + ->method('request') + ->willReturn([ + json_encode([ + 'current_page' => 1, + 'per_page' => 20, + 'total_withdrawal' => 1, + 'total_pages' => 1, + 'withdrawals' => [ + [ 'id' => 1 ] + ] + ]), + 200 + ]); + + $response = $this->service->list(); + + $this->assertNotEmpty($response); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index ebb7d15..5fd8c29 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,12 +2,23 @@ namespace CoinGate; +use CoinGate\HttpClient\CurlClient; + class TestCase extends \PHPUnit\Framework\TestCase { public const APIKEY = '-Lj8CYksfaJw_VwoCPMEPUKiLDnFFGKz7szNKXYp'; - protected function createSandboxClient(): Client + protected function mockHttpClient() { + return $this->createMock(CurlClient::class); + } + + protected function createSandboxClient($httpClient = null): Client + { + if ($httpClient !== null) { + Client::setHttpClient($httpClient); + } + return new Client(self::APIKEY, true); } }