15 Commits
0.0.1 ... 2.0.0

Author SHA1 Message Date
René Preuß
ca57ff1c65 Merge pull request #4 from 1elf-me/patch-1
Update to Laravel 7.x
2020-07-08 06:24:48 +02:00
1elf-me
9207f38f93 Update to Laravel 7.x 2020-06-21 14:08:53 +02:00
René Preuß
c182baab17 Add method to check if a email exists 2019-12-28 18:42:59 +01:00
René Preuß
0e07c295e3 Update composer 2019-12-11 11:41:52 +01:00
René Preuß
940617af2e Fix unit test 2019-12-11 11:37:48 +01:00
René Preuß
f2d064caef Fix oauth token generation 2019-12-09 22:46:23 +01:00
René Preuß
29a46a9ef9 Add oauth token method 2019-12-09 21:58:35 +01:00
René Preuß
553167d108 Fix user registration 2019-12-02 15:26:43 +01:00
René Preuß
918bcd4644 Add methods to change base url 2019-12-02 14:18:58 +01:00
René Preuß
2bd19efb3c Add create user method 2019-11-19 17:32:10 +01:00
René Preuß
b5ad7786f2 Fix parameter type and generate documentation 2019-11-19 17:21:13 +01:00
René Preuß
203dc18766 Add test implementation for charges, documents & payment intents 2019-11-19 17:12:27 +01:00
René Preuß
da1b6b7796 Fix Directory Path 2019-10-16 16:16:59 +02:00
René Preuß
a1ef9075e8 Update to Laravel 6 2019-10-16 16:09:43 +02:00
René Preuß
228c845bb5 Improve api operations
Change directory structure
Add bitinflow-accounts socialite provider
Improve docs generation
2019-09-22 21:53:49 +02:00
42 changed files with 1693 additions and 645 deletions

View File

@@ -9,9 +9,10 @@ PHP bitinflow Accounts API Client for Laravel 5+
## Table of contents
1. [Installation](#installation)
2. [Configuration](#configuration)
3. [Examples](#examples)
4. [Documentation](#documentation)
2. [Event Listener](#event-listener)
3. [Configuration](#configuration)
4. [Examples](#examples)
5. [Documentation](#documentation)
6. [Development](#Development)
## Installation
@@ -28,6 +29,28 @@ Add Service Provider to your `app.php` configuration file:
GhostZero\BitinflowAccounts\Providers\BitinflowAccountsServiceProvider::class,
```
## Event Listener
- Add `SocialiteProviders\Manager\SocialiteWasCalled` event to your `listen[]` array in `app/Providers/EventServiceProvider`.
- Add your listeners (i.e. the ones from the providers) to the `SocialiteProviders\Manager\SocialiteWasCalled[]` that you just created.
- The listener that you add for this provider is `'GhostZero\\BitinflowAccounts\\Socialite\\BitinflowExtendSocialite@handle',`.
- Note: You do not need to add anything for the built-in socialite providers unless you override them with your own providers.
```
/**
* The event handler mappings for the application.
*
* @var array
*/
protected $listen = [
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
// add your listeners (aka providers) here
'GhostZero\\BitinflowAccounts\\Socialite\\BitinflowExtendSocialite@handle',
],
];
```
## Configuration
Copy configuration to config folder:
@@ -44,6 +67,18 @@ BITINFLOW_ACCOUNTS_SECRET=
BITINFLOW_ACCOUNTS_REDIRECT_URI=http://localhost
```
You will need to add an entry to the services configuration file so that after config files are cached for usage in production environment (Laravel command `artisan config:cache`) all config is still available.
**Add to `config/services.php`:**
```php
'bitinflow-accounts' => [
'client_id' => env('BITINFLOW_ACCOUNTS_KEY'),
'client_secret' => env('BITINFLOW_ACCOUNTS_SECRET'),
'redirect' => env('BITINFLOW_ACCOUNTS_REDIRECT_URI')
],
```
## Examples
#### Basic
@@ -114,10 +149,33 @@ BitinflowAccounts::withClientId('abc123')->withToken('abcdef123456')->getAuthedU
## Documentation
### Users
### Charges
```php
public function getAuthedUser()
public function createCharge(array $parameters)
public function getCharge(string $id)
public function updateCharge(string $id, array $parameters)
public function captureCharge(string $id, array $parameters = array ())
```
### Documents
```php
public function createDocument(array $parameters)
public function createDocumentDownloadUrl(string $identifier, CarbonInterface $expiresAt = NULL)
```
### Oauth
```php
public function retrievingToken(string $grantType, array $attributes)
```
### PaymentIntents
```php
public function getPaymentIntent(string $id)
public function createPaymentIntent(array $parameters)
```
### SshKeys
@@ -128,7 +186,14 @@ public function createSshKey(string $publicKey, string $name = NULL)
public function deleteSshKey(int $id)
```
[**OAuth Scopes Enums**](https://git.preuss.io/ghostzero/bitinflow-accounts/blob/master/src/Enums/Scope.php)
### Users
```php
public function getAuthedUser()
public function createUser(array $parameters)
```
[**OAuth Scopes Enums**](https://github.com/ghostzero/bitinflow-accounts/blob/master/src/Enums/Scope.php)
## Development
@@ -139,7 +204,7 @@ composer test
```
```shell
CLIENT_ID=xxxx CLIENT_KEY=yyyy CLIENT_ACCESS_TOKEN=zzzz composer test
BASE_URL=xxxx CLIENT_ID=xxxx CLIENT_KEY=yyyy CLIENT_ACCESS_TOKEN=zzzz composer test
```
#### Generate Documentation

View File

@@ -9,9 +9,10 @@ PHP bitinflow Accounts API Client for Laravel 5+
## Table of contents
1. [Installation](#installation)
2. [Configuration](#configuration)
3. [Examples](#examples)
4. [Documentation](#documentation)
2. [Event Listener](#event-listener)
3. [Configuration](#configuration)
4. [Examples](#examples)
5. [Documentation](#documentation)
6. [Development](#Development)
## Installation
@@ -28,6 +29,28 @@ Add Service Provider to your `app.php` configuration file:
GhostZero\BitinflowAccounts\Providers\BitinflowAccountsServiceProvider::class,
```
## Event Listener
- Add `SocialiteProviders\Manager\SocialiteWasCalled` event to your `listen[]` array in `app/Providers/EventServiceProvider`.
- Add your listeners (i.e. the ones from the providers) to the `SocialiteProviders\Manager\SocialiteWasCalled[]` that you just created.
- The listener that you add for this provider is `'GhostZero\\BitinflowAccounts\\Socialite\\BitinflowExtendSocialite@handle',`.
- Note: You do not need to add anything for the built-in socialite providers unless you override them with your own providers.
```
/**
* The event handler mappings for the application.
*
* @var array
*/
protected $listen = [
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
// add your listeners (aka providers) here
'GhostZero\\BitinflowAccounts\\Socialite\\BitinflowExtendSocialite@handle',
],
];
```
## Configuration
Copy configuration to config folder:
@@ -44,6 +67,18 @@ BITINFLOW_ACCOUNTS_SECRET=
BITINFLOW_ACCOUNTS_REDIRECT_URI=http://localhost
```
You will need to add an entry to the services configuration file so that after config files are cached for usage in production environment (Laravel command `artisan config:cache`) all config is still available.
**Add to `config/services.php`:**
```php
'bitinflow-accounts' => [
'client_id' => env('BITINFLOW_ACCOUNTS_KEY'),
'client_secret' => env('BITINFLOW_ACCOUNTS_SECRET'),
'redirect' => env('BITINFLOW_ACCOUNTS_REDIRECT_URI')
],
```
## Examples
#### Basic
@@ -116,7 +151,7 @@ BitinflowAccounts::withClientId('abc123')->withToken('abcdef123456')->getAuthedU
<!-- GENERATED-DOCS -->
[**OAuth Scopes Enums**](https://git.preuss.io/ghostzero/bitinflow-accounts/blob/master/src/Enums/Scope.php)
[**OAuth Scopes Enums**](https://github.com/ghostzero/bitinflow-accounts/blob/master/src/Enums/Scope.php)
## Development
@@ -127,7 +162,7 @@ composer test
```
```shell
CLIENT_ID=xxxx CLIENT_KEY=yyyy CLIENT_ACCESS_TOKEN=zzzz composer test
BASE_URL=xxxx CLIENT_ID=xxxx CLIENT_KEY=yyyy CLIENT_ACCESS_TOKEN=zzzz composer test
```
#### Generate Documentation

View File

@@ -10,23 +10,25 @@
],
"require": {
"php": ">=7.2",
"illuminate/support": "^5.5",
"illuminate/console": "^5.5",
"guzzlehttp/guzzle": "^6.3"
"ext-json": "*",
"illuminate/support": "^7.0",
"illuminate/console": "^7.0",
"guzzlehttp/guzzle": "^6.3",
"socialiteproviders/manager": "^3.4"
},
"require-dev": {
"phpunit/phpunit": "^8.0",
"orchestra/testbench": "~3.8.0",
"orchestra/testbench": "~4.0",
"codedungeon/phpunit-result-printer": "^0.26.2"
},
"autoload": {
"psr-4": {
"GhostZero\\BitinflowAccounts\\": "src"
"GhostZero\\BitinflowAccounts\\": "src/GhostZero/BitinflowAccounts"
}
},
"autoload-dev": {
"psr-4": {
"GhostZero\\BitinflowAccounts\\Tests\\": "tests"
"GhostZero\\BitinflowAccounts\\Tests\\": "tests/GhostZero/BitinflowAccounts"
}
},
"scripts": {

1260
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,4 +4,5 @@ return [
'client_id' => env('BITINFLOW_ACCOUNTS_KEY', ''),
'client_secret' => env('BITINFLOW_ACCOUNTS_SECRET', ''),
'redirect_url' => env('BITINFLOW_ACCOUNTS_REDIRECT_URI', ''),
'base_url' => env('BITINFLOW_ACCOUNTS_BASE_URI', ''),
];

View File

@@ -76,6 +76,8 @@ $markdown = collect(class_uses(BitinflowAccounts::class))
return $markdown;
})->join(PHP_EOL . PHP_EOL);
$markdown = str_replace("array (\n)", '[]', $markdown);
$content = file_get_contents(__DIR__ . '/../README.stub');
$content = str_replace('<!-- GENERATED-DOCS -->', $markdown, $content);

View File

@@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Enums;
/**
* @author René Preuß <rene@preuss.io>
*/
class Scope
{
/*
* v0 API
*/
// Deprecated scope.
const API = 'api';
// Read nonpublic user information, including email address.
const READ_USER = 'read_user';
}

View File

@@ -5,8 +5,13 @@ declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\ApiOperations;
use GhostZero\BitinflowAccounts\Helpers\Paginator;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait Delete
{
abstract public function delete(string $path = '', array $parameters = [], Paginator $paginator = null);
abstract public function delete(string $path = '', array $parameters = [], Paginator $paginator = null): Result;
}

View File

@@ -5,8 +5,13 @@ declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\ApiOperations;
use GhostZero\BitinflowAccounts\Helpers\Paginator;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait Get
{
abstract public function get(string $path = '', array $parameters = [], Paginator $paginator = null);
abstract public function get(string $path = '', array $parameters = [], Paginator $paginator = null): Result;
}

View File

@@ -5,8 +5,13 @@ declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\ApiOperations;
use GhostZero\BitinflowAccounts\Helpers\Paginator;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait Post
{
abstract public function post(string $path = '', array $parameters = [], Paginator $paginator = null);
abstract public function post(string $path = '', array $parameters = [], Paginator $paginator = null): Result;
}

View File

@@ -5,8 +5,13 @@ declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\ApiOperations;
use GhostZero\BitinflowAccounts\Helpers\Paginator;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait Put
{
abstract public function put(string $path = '', array $parameters = [], Paginator $paginator = null);
abstract public function put(string $path = '', array $parameters = [], Paginator $paginator = null): Result;
}

View File

@@ -2,6 +2,7 @@
namespace GhostZero\BitinflowAccounts;
use GhostZero\BitinflowAccounts\ApiOperations;
use GhostZero\BitinflowAccounts\Exceptions\RequestRequiresAuthenticationException;
use GhostZero\BitinflowAccounts\Exceptions\RequestRequiresClientIdException;
use GhostZero\BitinflowAccounts\Exceptions\RequestRequiresRedirectUriException;
@@ -17,11 +18,19 @@ use GuzzleHttp\Exception\RequestException;
class BitinflowAccounts
{
use Traits\UsersTrait;
use Traits\ChargesTrait;
use Traits\DocumentsTrait;
use Traits\OauthTrait;
use Traits\PaymentIntentsTrait;
use Traits\SshKeysTrait;
use Traits\UsersTrait;
const BASE_URI = 'https://accounts.bitinflow.com/api/';
const OAUTH_BASE_URI = 'https://accounts.bitinflow.com/api/';
use ApiOperations\Delete;
use ApiOperations\Get;
use ApiOperations\Post;
use ApiOperations\Put;
private static $baseUrl = 'https://accounts.bitinflow.com/api/';
/**
* Guzzle is used to make http requests.
@@ -73,11 +82,24 @@ class BitinflowAccounts
if ($redirectUri = config('bitinflow-accounts-api.redirect_url')) {
$this->setRedirectUri($redirectUri);
}
if ($redirectUri = config('bitinflow-accounts-api.base_url')) {
self::setBaseUrl($redirectUri);
}
$this->client = new Client([
'base_uri' => self::BASE_URI,
'base_uri' => self::$baseUrl,
]);
}
/**
* @param string $baseUrl
*
* @internal only for internal and debug purposes.
*/
public static function setBaseUrl(string $baseUrl): void
{
self::$baseUrl = $baseUrl;
}
/**
* Get client id.
* @return string
@@ -95,7 +117,7 @@ class BitinflowAccounts
/**
* Set client id.
*
* @param string $clientId bitinflow Accounts client id
* @param string $clientId bitinflow Accounts client id
*
* @return void
*/
@@ -107,7 +129,7 @@ class BitinflowAccounts
/**
* Fluid client id setter.
*
* @param string $clientId bitinflow Accounts client id.
* @param string $clientId bitinflow Accounts client id.
*
* @return self
*/
@@ -135,7 +157,7 @@ class BitinflowAccounts
/**
* Set client secret.
*
* @param string $clientSecret bitinflow Accounts client secret
* @param string $clientSecret bitinflow Accounts client secret
*
* @return void
*/
@@ -147,7 +169,7 @@ class BitinflowAccounts
/**
* Fluid client secret setter.
*
* @param string $clientSecret bitinflow Accounts client secret
* @param string $clientSecret bitinflow Accounts client secret
*
* @return self
*/
@@ -175,7 +197,7 @@ class BitinflowAccounts
/**
* Set redirect url.
*
* @param string $redirectUri
* @param string $redirectUri
*
* @return void
*/
@@ -187,7 +209,7 @@ class BitinflowAccounts
/**
* Fluid redirect url setter.
*
* @param string $redirectUri
* @param string $redirectUri
*
* @return self
*/
@@ -216,7 +238,7 @@ class BitinflowAccounts
/**
* Set OAuth token.
*
* @param string $token bitinflow Accounts OAuth token
* @param string $token bitinflow Accounts OAuth token
*
* @return void
*/
@@ -228,7 +250,7 @@ class BitinflowAccounts
/**
* Fluid OAuth token setter.
*
* @param string $token bitinflow Accounts OAuth token
* @param string $token bitinflow Accounts OAuth token
*
* @return self
*/
@@ -248,7 +270,7 @@ class BitinflowAccounts
* @throws GuzzleException
* @throws RequestRequiresClientIdException
*/
public function get(string $path = '', array $parameters = [], Paginator $paginator = null)
public function get(string $path = '', array $parameters = [], Paginator $paginator = null): Result
{
return $this->query('GET', $path, $parameters, $paginator);
}
@@ -262,7 +284,7 @@ class BitinflowAccounts
* @throws GuzzleException
* @throws RequestRequiresClientIdException
*/
public function post(string $path = '', array $parameters = [], Paginator $paginator = null)
public function post(string $path = '', array $parameters = [], Paginator $paginator = null): Result
{
return $this->query('POST', $path, $parameters, $paginator);
}
@@ -276,7 +298,7 @@ class BitinflowAccounts
* @throws GuzzleException
* @throws RequestRequiresClientIdException
*/
public function delete(string $path = '', array $parameters = [], Paginator $paginator = null)
public function delete(string $path = '', array $parameters = [], Paginator $paginator = null): Result
{
return $this->query('DELETE', $path, $parameters, $paginator);
}
@@ -290,7 +312,7 @@ class BitinflowAccounts
* @throws GuzzleException
* @throws RequestRequiresClientIdException
*/
public function put(string $path = '', array $parameters = [], Paginator $paginator = null)
public function put(string $path = '', array $parameters = [], Paginator $paginator = null): Result
{
return $this->query('PUT', $path, $parameters, $paginator);
}
@@ -304,7 +326,7 @@ class BitinflowAccounts
* @throws GuzzleException
* @throws RequestRequiresClientIdException
*/
public function json(string $method, string $path = '', array $body = null)
public function json(string $method, string $path = '', array $body = null): Result
{
if ($body) {
$body = json_encode(['data' => $body]);
@@ -316,11 +338,11 @@ class BitinflowAccounts
/**
* Build query & execute.
*
* @param string $method HTTP method
* @param string $path Query path
* @param array $parameters Query parameters
* @param Paginator $paginator Paginator object
* @param mixed|null $jsonBody JSON data
* @param string $method HTTP method
* @param string $path Query path
* @param array $parameters Query parameters
* @param Paginator $paginator Paginator object
* @param mixed|null $jsonBody JSON data
*
* @return Result Result object
* @throws GuzzleException
@@ -335,7 +357,7 @@ class BitinflowAccounts
$response = $this->client->request($method, $path, [
'headers' => $this->buildHeaders($jsonBody ? true : false),
'query' => $this->buildQuery($parameters),
'json' => $jsonBody ? $jsonBody : null,
'json' => $jsonBody ?: null,
]);
$result = new Result($response, null, $paginator);
} catch (RequestException $exception) {
@@ -349,7 +371,7 @@ class BitinflowAccounts
/**
* Build query with support for multiple smae first-dimension keys.
*
* @param array $query
* @param array $query
*
* @return string
*/
@@ -369,7 +391,7 @@ class BitinflowAccounts
/**
* Build headers for request.
*
* @param bool $json Body is JSON
* @param bool $json Body is JSON
*
* @return array
* @throws RequestRequiresClientIdException
@@ -378,13 +400,13 @@ class BitinflowAccounts
{
$headers = [
'Client-ID' => $this->getClientId(),
'Accept' => 'application/json',
];
if ($this->token) {
$headers['Authorization'] = 'Bearer ' . $this->token;
}
if ($json) {
$headers['Content-Type'] = 'application/json';
$headers['Accept'] = 'application/json';
}
return $headers;

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Enums;
/**
* @author René Preuß <rene@preuss.io>
*/
class DocumentType
{
// Read authorized user´s email address.
public const TYPE_PDF_INVOICE = 'pdf.invoice';
// Manage a authorized user object.
public const TYPE_PDF_ORDER = 'pdf.order';
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Enums;
/**
* @author René Preuß <rene@preuss.io>
*/
class Scope
{
/*
* v0 API
*/
// Deprecated scope.
public const API = 'api';
// Read nonpublic user information, including email address.
public const READ_USER = 'read_user';
/*
* v1 API
*/
// Read authorized user´s email address.
public const USERS_READ_EMAIL = 'users:read:email';
// Manage a authorized user object.
public const USERS_EDIT = 'users:edit';
public const USERS_CREATE = 'users:create';
// Read authorized user´s transactions.
public const TRANSACTIONS_READ = 'transactions:read';
// Create a new charge for the authorized user.
public const CHARGES_CREATE = 'charges:create';
}

View File

@@ -17,7 +17,7 @@ class BitinflowAccountsServiceProvider extends ServiceProvider
public function boot()
{
$this->publishes([
dirname(__DIR__) . '/../config/bitinflow-accounts-api.php' => config_path('bitinflow-accounts-api.php'),
dirname(__DIR__) . '/../../../config/bitinflow-accounts-api.php' => config_path('bitinflow-accounts-api.php'),
], 'config');
}
@@ -28,7 +28,7 @@ class BitinflowAccountsServiceProvider extends ServiceProvider
public function register()
{
$this->mergeConfigFrom(
dirname(__DIR__) . '/../config/bitinflow-accounts-api.php', 'bitinflow-accounts-api'
dirname(__DIR__) . '/../../../config/bitinflow-accounts-api.php', 'bitinflow-accounts-api'
);
$this->app->singleton(BitinflowAccounts::class, function () {
return new BitinflowAccounts;
@@ -43,4 +43,4 @@ class BitinflowAccountsServiceProvider extends ServiceProvider
{
return [BitinflowAccounts::class];
}
}
}

View File

@@ -6,7 +6,7 @@ namespace GhostZero\BitinflowAccounts;
use Exception;
use GhostZero\BitinflowAccounts\Helpers\Paginator;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use stdClass;
@@ -60,7 +60,7 @@ class Result
/**
* Original Guzzle HTTP Response.
* @var Response
* @var ResponseInterface|null
*/
public $response;
@@ -73,17 +73,17 @@ class Result
/**
* Constructor,
*
* @param Response $response HTTP response
* @param ResponseInterface|null $response HTTP response
* @param Exception|mixed $exception Exception, if present
* @param null|Paginator $paginator Paginator, if present
*/
public function __construct(Response $response, Exception $exception = null, Paginator $paginator = null)
public function __construct(?ResponseInterface $response, Exception $exception = null, Paginator $paginator = null)
{
$this->response = $response;
$this->success = $exception === null;
$this->exception = $exception;
$this->status = $response->getStatusCode();
$jsonResponse = @json_decode($response->getBody()->getContents());
$this->status = $response ? $response->getStatusCode() : 500;
$jsonResponse = $response ? @json_decode($response->getBody()->getContents(), false) : null;
if ($jsonResponse !== null) {
$this->setProperty($jsonResponse, 'data');
$this->setProperty($jsonResponse, 'total');
@@ -99,10 +99,10 @@ class Result
* @param string $responseProperty Response property name
* @param string|null $attribute Class property name
*/
private function setProperty(stdClass $jsonResponse, string $responseProperty, string $attribute = null)
private function setProperty(stdClass $jsonResponse, string $responseProperty, string $attribute = null): void
{
$classAttribute = $attribute ?? $responseProperty;
if (!empty($jsonResponse) && property_exists($jsonResponse, $responseProperty)) {
if ($jsonResponse !== null && property_exists($jsonResponse, $responseProperty)) {
$this->{$classAttribute} = $jsonResponse->{$responseProperty};
} elseif ($responseProperty === 'data') {
$this->{$classAttribute} = $jsonResponse;
@@ -174,7 +174,7 @@ class Result
* Set the Paginator to fetch the first set of results.
* @return null|Paginator
*/
public function first()
public function first(): ?Paginator
{
return $this->paginator !== null ? $this->paginator->first() : null;
}
@@ -183,7 +183,7 @@ class Result
* Set the Paginator to fetch the next set of results.
* @return null|Paginator
*/
public function next()
public function next(): ?Paginator
{
return $this->paginator !== null ? $this->paginator->next() : null;
}
@@ -192,7 +192,7 @@ class Result
* Set the Paginator to fetch the last set of results.
* @return null|Paginator
*/
public function back()
public function back(): ?Paginator
{
return $this->paginator !== null ? $this->paginator->back() : null;
}
@@ -200,7 +200,7 @@ class Result
/**
* Get rate limit information.
*
* @param string|null $key Get defined index
* @param string|null $key Get defined index
*
* @return string|array|null
*/
@@ -224,8 +224,8 @@ class Result
/**
* Insert users in data response.
*
* @param string $identifierAttribute Attribute to identify the users
* @param string $insertTo Data index to insert user data
* @param string $identifierAttribute Attribute to identify the users
* @param string $insertTo Data index to insert user data
*
* @return self
*/
@@ -235,7 +235,7 @@ class Result
$userIds = collect($data)->map(function ($item) use ($identifierAttribute) {
return $item->{$identifierAttribute};
})->toArray();
if (count($userIds) == 0) {
if (count($userIds) === 0) {
return $this;
}
$users = collect($this->bitinflow->getUsersByIds($userIds)->data);

View File

@@ -0,0 +1,24 @@
<?php
namespace GhostZero\BitinflowAccounts\Socialite;
use SocialiteProviders\Manager\SocialiteWasCalled;
/**
* @author René Preuß <rene@preuss.io>
*/
class BitinflowExtendSocialite
{
/**
* Register the provider.
*
* @param SocialiteWasCalled $socialiteWasCalled
*/
public function handle(SocialiteWasCalled $socialiteWasCalled)
{
$socialiteWasCalled->extendSocialite(
'bitinflow-accounts', __NAMESPACE__ . '\Provider'
);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace GhostZero\BitinflowAccounts\Socialite;
use GhostZero\BitinflowAccounts\Enums\Scope;
use Illuminate\Support\Arr;
use Laravel\Socialite\Two\ProviderInterface;
use SocialiteProviders\Manager\OAuth2\AbstractProvider;
use SocialiteProviders\Manager\OAuth2\User;
/**
* @author René Preuß <rene@preuss.io>
*/
class Provider extends AbstractProvider implements ProviderInterface
{
/**
* Unique Provider Identifier.
*/
const IDENTIFIER = 'BITINFLOW_ACCOUNTS';
/**
* {@inheritdoc}
*/
protected $scopes = [Scope::READ_USER];
/**
* {@inherticdoc}.
*/
protected $scopeSeparator = ' ';
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase(
'https://accounts.bitinflow.com/oauth/authorize', $state
);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://accounts.bitinflow.com/oauth/token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get(
'https://accounts.bitinflow.com/api/user', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $token,
],
]);
return json_decode($response->getBody()->getContents(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User())->setRaw($user)->map([
'id' => $user['id'],
'nickname' => $user['name'],
'name' => $user['name'],
'email' => Arr::get($user, 'email'),
'avatar' => $user['avatar'],
]);
}
/**
* {@inheritdoc}
*/
protected function getTokenFields($code)
{
return array_merge(parent::getTokenFields($code), [
'grant_type' => 'authorization_code',
]);
}
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Traits;
use GhostZero\BitinflowAccounts\ApiOperations\Get;
use GhostZero\BitinflowAccounts\ApiOperations\Post;
use GhostZero\BitinflowAccounts\ApiOperations\Put;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait ChargesTrait
{
use Get, Post, Put;
/**
* Create a Charge object
*
* @param array $parameters
*
* @return Result Result object
*/
public function createCharge(array $parameters): Result
{
return $this->post('charges', $parameters);
}
/**
* Get a Charge object
*
* @param string $id
*
* @return Result Result object
*/
public function getCharge(string $id): Result
{
return $this->get("charges/$id");
}
/**
* Update a Charge object
*
* @param string $id
* @param array $parameters
*
* @return Result Result object
*/
public function updateCharge(string $id, array $parameters): Result
{
return $this->put("charges/$id", $parameters);
}
/**
* Capture a Charge object
*
* @param string $id
* @param array $parameters
*
* @return Result Result object
*/
public function captureCharge(string $id, array $parameters = []): Result
{
return $this->post("charges/$id/capture", $parameters);
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Traits;
use Carbon\CarbonInterface;
use GhostZero\BitinflowAccounts\ApiOperations\Get;
use GhostZero\BitinflowAccounts\ApiOperations\Post;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait DocumentsTrait
{
use Get, Post;
/**
* Create a Documents object
*
* @param array $parameters
*
* @return Result
*/
public function createDocument(array $parameters): Result
{
return $this->post('documents', $parameters);
}
/**
* Create a Documents download url
*
* @param string $identifier
* @param CarbonInterface|null $expiresAt
*
* @return Result
*/
public function createDocumentDownloadUrl(string $identifier, ?CarbonInterface $expiresAt = null): Result
{
return $this->post("documents/$identifier/download-url", [
'expires_at' => $expiresAt
? $expiresAt->toDateTimeString()
: now()->addHour()->toDateTimeString(),
]);
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Traits;
use GhostZero\BitinflowAccounts\Result;
use GuzzleHttp\Exception\RequestException;
/**
* @author René Preuß <rene@preuss.io>
*/
trait OauthTrait
{
/**
* Retrieving a oauth token using a given grant type.
*
* @param string $grantType
* @param array $attributes
*
* @return Result
*/
public function retrievingToken(string $grantType, array $attributes): Result
{
try {
$response = $this->client->request('POST', '/oauth/token', [
'form_params' => $attributes + [
'grant_type' => $grantType,
'client_id' => $this->getClientId(),
'client_secret' => $this->getClientSecret(),
],
]);
$result = new Result($response, null);
} catch (RequestException $exception) {
$result = new Result($exception->getResponse(), $exception);
}
$result->bitinflow = $this;
return $result;
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Traits;
use GhostZero\BitinflowAccounts\ApiOperations\Get;
use GhostZero\BitinflowAccounts\ApiOperations\Post;
use GhostZero\BitinflowAccounts\Result;
/**
* @author René Preuß <rene@preuss.io>
*/
trait PaymentIntentsTrait
{
use Get, Post;
/**
* Get a Payment Intent object
*
* @param string $id
*
* @return Result Result object
*/
public function getPaymentIntent(string $id): Result
{
return $this->get("payment-intents/$id");
}
/**
* Create a Payment Intent object
*
* @param array $parameters
*
* @return Result
*/
public function createPaymentIntent(array $parameters): Result
{
return $this->post('payment-intents', $parameters);
}
}

View File

@@ -27,7 +27,7 @@ trait SshKeysTrait
}
/**
* Get currently authed user with Bearer Token
* Creates ssh key for the currently authed user
*
* @param string $publicKey
* @param string|null $name
@@ -39,11 +39,11 @@ trait SshKeysTrait
return $this->post('ssh-keys', [
'public_key' => $publicKey,
'name' => $name,
], null);
]);
}
/**
* Get currently authed user with Bearer Token
* Deletes a given ssh key for the currently authed user
*
* @param int $id
*
@@ -51,6 +51,6 @@ trait SshKeysTrait
*/
public function deleteSshKey(int $id): Result
{
return $this->delete("ssh-keys/$id", [], null);
return $this->delete("ssh-keys/$id", []);
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Traits;
use GhostZero\BitinflowAccounts\ApiOperations\Get;
use GhostZero\BitinflowAccounts\Result;
trait UsersTrait
{
use Get;
/**
* Get currently authed user with Bearer Token
* @return Result Result object
*/
public function getAuthedUser(): Result
{
return $this->get('users/me');
}
/**
* Creates a new user on behalf of the current user.
*
* @param array $parameters
*
* @return Result
*/
public function createUser(array $parameters): Result
{
return $this->post('v2/users', $parameters);
}
/**
* Checks if the given email exists.
*
* @param string $email
*
* @return Result
*/
public function isEmailExisting(string $email): Result
{
return $this->post('v2/users/check-email', [
'email' => $email,
]);
}
}

View File

@@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Traits;
use GhostZero\BitinflowAccounts\ApiOperations\Get;
use GhostZero\BitinflowAccounts\Result;
trait UsersTrait
{
use Get;
/**
* Get currently authed user with Bearer Token
* @return Result Result object
*/
public function getAuthedUser(): Result
{
return $this->get('users/me', [], null);
}
}

View File

@@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Tests;
use GhostZero\BitinflowAccounts\Result;
use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
/**
* @author René Preuß <rene@preuss.io>
*/
class ApiUsersTest extends ApiTestCase
{
public function testGetAuthedUser()
{
$this->getClient()->withToken($this->getToken());
$this->registerResult($result = $this->getClient()->getAuthedUser());
$this->assertInstanceOf(Result::class, $result);
$this->assertEquals('rene@preuss.io', $result->data()->email);
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Tests;
use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
/**
* @author René Preuß <rene@preuss.io>
*/
class ApiChargesTest extends ApiTestCase
{
public function testCaptureWithoutCapture(): void
{
$this->getClient()->withToken($this->getToken());
$result = $this->getClient()->createCharge([
'amount' => 2000,
'currency' => 'usd',
'source' => 'tok_visa',
'description' => 'Charge for jenny.rosen@example.com',
]);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('id', $result->data());
$this->assertEquals(2000, $result->data()->amount);
$this->assertTrue($result->data()->captured);
}
public function testChargeWithCapture(): void
{
$this->getClient()->withToken($this->getToken());
$result = $this->getClient()->createCharge([
'amount' => 2000,
'currency' => 'usd',
'source' => 'tok_visa',
'description' => 'Charge for jenny.rosen@example.com',
'capture' => false, // default is true for instant capture
'metadata' => [
'foo' => 'bar',
],
'receipt_email' => 'rene+unittest@bitinflow.com',
]);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('id', $result->data());
$this->assertEquals(2000, $result->data()->amount);
$this->assertFalse($result->data()->captured);
$charge = $result->data();
$result = $this->getClient()->captureCharge($charge->id);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('id', $result->data());
$this->assertEquals(2000, $result->data()->amount);
$this->assertTrue($result->data()->captured);
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Tests;
use GhostZero\BitinflowAccounts\Enums\DocumentType;
use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
/**
* @author René Preuß <rene@preuss.io>
*/
class ApiDocumentsTest extends ApiTestCase
{
public function testCreateDocument(): void
{
$this->getClient()->withToken($this->getToken());
$result = $this->getClient()->createDocument([
'branding' => [
'primary_color' => '#8284df',
'watermark_url' => 'https://fbs.streamkit.gg/img/pdf/wm.png',
'logo_url' => 'https://fbs.streamkit.gg/img/pdf/logo_dark_small.png',
],
'locale' => 'de',
'type' => DocumentType::TYPE_PDF_INVOICE,
'data' => $this->createDummyInvoiceData(),
'receipt_email' => 'rene+unittest@bitinflow.com',
]);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('id', $result->data());
$this->assertArrayHasKey('download_url', $result->data());
$this->assertEquals(
'rene+unittest@bitinflow.com',
$result->data()->receipt_email
);
}
public function testGenerateDocumentStoragePath(): void
{
$this->getClient()->withToken($this->getToken());
$expiresAt = now()->addHours(2);
$result = $this->getClient()->createDocumentDownloadUrl('1', $expiresAt);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('download_url', $result->data());
$this->assertEquals(
$expiresAt->toDateTimeString(),
$result->data()->expires_at
);
}
private function createDummyInvoiceData(): array
{
return [
'id' => 'FBS-IN-1337',
'customer' => [
'name' => 'GhostZero',
'email' => 'rene@preuss.io',
'address' => [
'Example Street 123',
'50733 Cologne',
'GERMANY',
],
],
'line_items' => [
[
'name' => 'T-shirt',
'description' => 'Comfortable cotton t-shirt',
'unit' => 'T-shirt', // optional unit name
'amount' => 1500,
'currency' => 'usd',
'quantity' => 2,
],
],
'legal_notice' => 'According to the German §19 UStG no sales tax is calculated. However, the product is a digital good delivered via Internet we generally offer no refunds. The delivery date corresponds to the invoice date.',
'already_paid' => true,
'created_at' => now()->format('d.m.Y'),
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Tests;
use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
/**
* @author René Preuß <rene@preuss.io>
*/
class ApiOauthTest extends ApiTestCase
{
public function testGetOauthToken(): void
{
$this->registerResult($result = $this->getClient()->retrievingToken('client_credentials', [
'scope' => '',
]));
$this->assertTrue($result->success());
$this->assertNotEmpty($result->data()->access_token);
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Tests;
use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
/**
* @author René Preuß <rene@preuss.io>
*/
class ApiPaymentIntentsTest extends ApiTestCase
{
private $paymentIntent;
public function testCreatePaymentIntent(): void
{
$this->getClient()->withToken($this->getToken());
$result = $this->getClient()->createPaymentIntent([
'payment_method_types' => ['card'],
'amount' => 1000,
'currency' => 'usd',
'application_fee_amount' => 123,
]);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('id', $result->data());
$this->assertArrayHasKey('redirect_url', $result->data());
$this->assertEquals(1000, $result->data()->amount);
// use this payment intent for our next tests
$this->paymentIntent = $result->data();
}
public function testGetPaymentIntent(): void
{
$this->getClient()->withToken($this->getToken());
$result = $this->getClient()->getPaymentIntent($this->paymentIntent->id);
$this->registerResult($result);
$this->assertTrue($result->success());
$this->assertArrayHasKey('id', $result->data());
$this->assertEquals(1000, $result->data()->amount);
}
}

View File

@@ -13,15 +13,14 @@ use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
class ApiSshKeysTest extends ApiTestCase
{
public function testGetSshKeyByUserId()
public function testGetSshKeyByUserId(): void
{
$this->registerResult($result = $this->getClient()->getSshKeysByUserId(38));
$this->assertInstanceOf(Result::class, $result);
$this->assertEquals('rene.preuss@check24.de', $result->shift()->name);
$this->assertGreaterThanOrEqual(2, $result->count());
}
public function testSshKeyManagement()
public function testSshKeyManagement(): void
{
$customName = 'Hello World!';
$publicKey = 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3H7sYVrVCwwYIuRm3on3S9n/BCd2mBJrgCk6xTerbNmt0RyUZ+RtGsK6UYkgnRR2WWq9/Pv2s3RXJXPxbsIEYmKCcTdLUvDk56x9385cIVUX4w016mpe/8lyu+mIdqWYKsJMoab0oReCDX8Y9qBcaffDh8AgmYVN+86gXgoP1ITe9BDYrFiR6U571VyLDVN3OYOYPMF3/L9f0knMfM0T4LrS8oi6faVBCxZHRoBGtGmsTBkE0KwplYQFN2aa4Mxab+rTUFmJr3LYEcJF0J8wNJ3eEDFNOR0254jrjbGGAXGsc+cxJoNzech+GBkRMKMpNU0lds6VxP0ZB25VfzjEmQ== René Preuß';

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace GhostZero\BitinflowAccounts\Tests;
use GhostZero\BitinflowAccounts\Enums\Scope;
use GhostZero\BitinflowAccounts\Tests\TestCases\ApiTestCase;
use Illuminate\Support\Str;
/**
* @author René Preuß <rene@preuss.io>
*/
class ApiUsersTest extends ApiTestCase
{
public function testGetAuthedUser(): void
{
$this->getClient()->withToken($this->getToken());
$this->registerResult($result = $this->getClient()->getAuthedUser());
$this->assertTrue($result->success());
$this->assertEquals('rene@preuss.io', $result->data()->email);
}
public function testEmailAvailabilityNonExisting(): void
{
$this->getClient()->withToken($this->getToken());
$this->registerResult($result = $this->getClient()->isEmailExisting('rene+non-existing@preuss.io'));
$this->assertTrue(!$result->success());
}
public function testEmailAvailabilityExisting(): void
{
$this->getClient()->withToken($this->getToken());
$this->registerResult($result = $this->getClient()->isEmailExisting('rene@preuss.io'));
$this->assertTrue($result->success());
}
public function testCreateUser(): void
{
$testEmailAddress = $this->createRandomEmail();
$this->registerResult($result = $this->getClient()->retrievingToken('client_credentials', [
'scope' => Scope::USERS_CREATE,
]));
$this->getClient()->withToken($result->data()->access_token);
$this->registerResult($result = $this->getClient()->createUser([
'first_name' => 'René',
'last_name' => 'Preuß',
'email' => $testEmailAddress,
'password' => 'Password1',
'password_confirmation' => 'Password1',
'terms_accepted' => true,
]));
$this->assertTrue($result->success(), $result->error());
$this->assertEquals($testEmailAddress, $result->data()->email);
}
private function createRandomEmail(): string
{
return sprintf('rene+unittest.%s@bitinflow.com', Str::random());
}
}

View File

@@ -14,12 +14,12 @@ use GhostZero\BitinflowAccounts\Tests\TestCases\TestCase;
class ServiceInstantiationTest extends TestCase
{
public function testInstance()
public function testInstance(): void
{
$this->assertInstanceOf(BitinflowAccounts::class, app(BitinflowAccounts::class));
}
public function testFacade()
public function testFacade(): void
{
$this->assertInstanceOf(BitinflowAccounts::class, BitinflowAccountsFacade::getFacadeRoot());
}

View File

@@ -18,6 +18,11 @@ abstract class ApiTestCase extends TestCase
protected function setUp(): void
{
parent::setUp();
if ($this->getBaseUrl()) {
BitinflowAccounts::setBaseUrl($this->getBaseUrl());
}
if (!$this->getClientId()) {
$this->markTestSkipped('No Client-ID given');
}
@@ -34,6 +39,11 @@ abstract class ApiTestCase extends TestCase
return $result;
}
protected function getBaseUrl()
{
return getenv('BASE_URL');
}
protected function getClientId()
{
return getenv('CLIENT_ID');