Fix Spectre, first code to create customer.

This commit is contained in:
James Cole
2017-12-19 05:20:37 +01:00
parent 79f700c622
commit 34fc9bc50a
12 changed files with 384 additions and 140 deletions

View File

@@ -85,10 +85,10 @@ class PrerequisitesController extends Controller
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/
public function postPrerequisites(Request $request, string $bank)
public function post(Request $request, string $bank)
{
Log::debug(sprintf('Now in postPrerequisites for %s', $bank));
$class = config(sprintf('firefly.import_pre.%s', $bank));
$class = strval(config(sprintf('import.prerequisites.%s', $bank)));
if (!class_exists($class)) {
throw new FireflyException(sprintf('Cannot find class %s', $class));
}

View File

@@ -56,7 +56,7 @@ class SpectrePrerequisites implements PrerequisitesInterface
public function getViewParameters(): array
{
$publicKey = $this->getPublicKey();
$subTitle = strval(trans('bank.spectre_title'));
$subTitle = strval(trans('import.spectre_title'));
$subTitleIcon = 'fa-archive';
return compact('publicKey', 'subTitle', 'subTitleIcon');

View File

@@ -0,0 +1,133 @@
<?php
/**
* SpectreRoutine.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Import\Routine;
use FireflyIII\Models\ImportJob;
use FireflyIII\Services\Spectre\Object\Customer;
use FireflyIII\Services\Spectre\Request\NewCustomerRequest;
use Preferences;
use Illuminate\Support\Collection;
use Log;
/**
* Class FileRoutine
*/
class SpectreRoutine implements RoutineInterface
{
/** @var Collection */
public $errors;
/** @var Collection */
public $journals;
/** @var int */
public $lines = 0;
/** @var ImportJob */
private $job;
/**
* ImportRoutine constructor.
*/
public function __construct()
{
$this->journals = new Collection;
$this->errors = new Collection;
}
/**
* @return Collection
*/
public function getErrors(): Collection
{
return $this->errors;
}
/**
* @return Collection
*/
public function getJournals(): Collection
{
return $this->journals;
}
/**
* @return int
*/
public function getLines(): int
{
return $this->lines;
}
/**
*
*/
public function run(): bool
{
if ('configured' !== $this->job->status) {
Log::error(sprintf('Job %s is in state "%s" so it cannot be started.', $this->job->key, $this->job->status));
return false;
}
set_time_limit(0);
Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key));
// create customer if user does not have one:
$customer = $this->getCustomer();
return true;
}
/**
* @param ImportJob $job
*/
public function setJob(ImportJob $job)
{
$this->job = $job;
}
/**
* @return Customer
*/
protected function createCustomer(): Customer
{
$newCustomerRequest = new NewCustomerRequest($this->job->user);
$newCustomerRequest->call();
echo '<pre>';
print_r($newCustomerRequest->getCustomer());
exit;
}
/**
* @return Customer
*/
protected function getCustomer(): Customer
{
$preference = Preferences::getForUser($this->job->user, 'spectre_customer', null);
if (is_null($preference)) {
return $this->createCustomer();
}
var_dump($preference->data);
exit;
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* Customer.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Services\Spectre\Object;
/**
* Class Customer
*/
class Customer extends SpectreObject
{
/** @var int */
private $id;
/** @var string */
private $identifier;
/** @var string */
private $secret;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* @return string
*/
public function getIdentifier(): string
{
return $this->identifier;
}
/**
* @param string $identifier
*/
public function setIdentifier(string $identifier): void
{
$this->identifier = $identifier;
}
/**
* @return string
*/
public function getSecret(): string
{
return $this->secret;
}
/**
* @param string $secret
*/
public function setSecret(string $secret): void
{
$this->secret = $secret;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* SpectreObject.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Services\Spectre\Object;
/**
* Class SpectreObject
*/
class SpectreObject
{
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* NewCustomerRequest.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Services\Spectre\Request;
/**
* Class NewCustomerRequest
*/
class NewCustomerRequest extends SpectreRequest
{
/** @var array */
protected $customer = [];
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function call(): void
{
$data = [
'data' => [
'identifier' => 'default_ff3_customer',
],
];
$uri = '/api/v3/customers/';
$response = $this->sendSignedSpectrePost($uri, $data);
// create customer:
$this->customer = $response['data'];
return;
}
/**
* @return array
*/
public function getCustomer(): array
{
return $this->customer;
}
}

View File

@@ -28,6 +28,7 @@ use FireflyIII\User;
use Log;
use Requests;
use Requests_Exception;
use Requests_Response;
//use FireflyIII\Services\Bunq\Object\ServerPublicKey;
@@ -37,7 +38,7 @@ use Requests_Exception;
abstract class SpectreRequest
{
/** @var string */
protected $clientId = '';
protected $clientId = '';
/**
* @var int
*/
@@ -174,7 +175,7 @@ abstract class SpectreRequest
// base64(sha1_signature(private_key, "Expires-at|request_method|original_url|post_body|md5_of_uploaded_file|")))
// Prepare the signature
$toSign = $this->expiresAt . '|' . strtoupper($method) . '|' . $uri . '|' . $data . ''; // no file so no content there.
Log::debug(sprintf('String to sign: %s', $toSign));
Log::debug(sprintf('String to sign: "%s"', $toSign));
$signature = '';
// Sign the data
@@ -202,92 +203,12 @@ abstract class SpectreRequest
];
}
/**
* @param string $uri
* @param array $headers
*
* @return array
*
* @throws Exception
*/
protected function sendSignedBunqDelete(string $uri, array $headers): array
{
if (0 === strlen($this->server)) {
throw new FireflyException('No bunq server defined');
}
$fullUri = $this->server . $uri;
$signature = $this->generateSignature('delete', $uri, $headers, '');
$headers['X-Bunq-Client-Signature'] = $signature;
try {
$response = Requests::delete($fullUri, $headers);
} catch (Requests_Exception $e) {
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]];
}
$body = $response->body;
$array = json_decode($body, true);
$responseHeaders = $response->headers->getAll();
$statusCode = intval($response->status_code);
$array['ResponseHeaders'] = $responseHeaders;
$array['ResponseStatusCode'] = $statusCode;
Log::debug(sprintf('Response to DELETE %s is %s', $fullUri, $body));
if ($this->isErrorResponse($array)) {
$this->throwResponseError($array);
}
if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) {
throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri));
}
return $array;
}
/**
* @param string $uri
* @param array $data
* @param array $headers
*
* @return array
*
* @throws Exception
*/
protected function sendSignedBunqPost(string $uri, array $data, array $headers): array
{
$body = json_encode($data);
$fullUri = $this->server . $uri;
$signature = $this->generateSignature('post', $uri, $headers, $body);
$headers['X-Bunq-Client-Signature'] = $signature;
try {
$response = Requests::post($fullUri, $headers, $body);
} catch (Requests_Exception $e) {
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]];
}
$body = $response->body;
$array = json_decode($body, true);
$responseHeaders = $response->headers->getAll();
$statusCode = intval($response->status_code);
$array['ResponseHeaders'] = $responseHeaders;
$array['ResponseStatusCode'] = $statusCode;
if ($this->isErrorResponse($array)) {
$this->throwResponseError($array);
}
if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) {
throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri));
}
return $array;
}
/**
* @param string $uri
* @param array $data
* @return array
*
* @throws FireflyException
*/
protected function sendSignedSpectreGet(string $uri, array $data): array
@@ -308,11 +229,9 @@ abstract class SpectreRequest
} catch (Requests_Exception $e) {
throw new FireflyException(sprintf('Request Exception: %s', $e->getMessage()));
}
$this->detectError($response);
$statusCode = intval($response->status_code);
if ($statusCode !== 200) {
throw new FireflyException(sprintf('Status code %d: %s', $statusCode, $response->body));
}
$body = $response->body;
$array = json_decode($body, true);
@@ -320,32 +239,9 @@ abstract class SpectreRequest
$array['ResponseHeaders'] = $responseHeaders;
$array['ResponseStatusCode'] = $statusCode;
return $array;
}
/**
* @param string $uri
* @param array $headers
*
* @return array
*/
protected function sendUnsignedBunqDelete(string $uri, array $headers): array
{
$fullUri = $this->server . $uri;
try {
$response = Requests::delete($fullUri, $headers);
} catch (Requests_Exception $e) {
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]];
}
$body = $response->body;
$array = json_decode($body, true);
$responseHeaders = $response->headers->getAll();
$statusCode = $response->status_code;
$array['ResponseHeaders'] = $responseHeaders;
$array['ResponseStatusCode'] = $statusCode;
if ($this->isErrorResponse($array)) {
$this->throwResponseError($array);
if (isset($array['error_class'])) {
$message = $array['error_message'] ?? '(no message)';
throw new FireflyException(sprintf('Error of class %s: %s', $array['error_class'], $message));
}
return $array;
@@ -354,30 +250,59 @@ abstract class SpectreRequest
/**
* @param string $uri
* @param array $data
* @param array $headers
*
* @return array
*
* @throws FireflyException
*/
protected function sendUnsignedBunqPost(string $uri, array $data, array $headers): array
protected function sendSignedSpectrePost(string $uri, array $data): array
{
$body = json_encode($data);
$fullUri = $this->server . $uri;
try {
$response = Requests::post($fullUri, $headers, $body);
} catch (Requests_Exception $e) {
return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]];
if (0 === strlen($this->server)) {
throw new FireflyException('No Spectre server defined');
}
$headers = $this->getDefaultHeaders();
$body = json_encode($data);
$fullUri = $this->server . $uri;
$signature = $this->generateSignature('post', $fullUri, $body);
$headers['Signature'] = $signature;
Log::debug('Final headers for spectre signed POST request:', $headers);
try {
$response = Requests::get($fullUri, $headers);
} catch (Requests_Exception $e) {
throw new FireflyException(sprintf('Request Exception: %s', $e->getMessage()));
}
$this->detectError($response);
$body = $response->body;
$array = json_decode($body, true);
$responseHeaders = $response->headers->getAll();
$statusCode = $response->status_code;
$array['ResponseHeaders'] = $responseHeaders;
$array['ResponseStatusCode'] = $statusCode;
if ($this->isErrorResponse($array)) {
$this->throwResponseError($array);
}
$array['ResponseStatusCode'] = $response->status_code;
return $array;
}
/**
* @param Requests_Response $response
*
* @throws FireflyException
*/
private function detectError(Requests_Response $response): void
{
$body = $response->body;
$array = json_decode($body, true);
if (isset($array['error_class'])) {
$message = $array['error_message'] ?? '(no message)';
throw new FireflyException(sprintf('Error of class %s: %s', $array['error_class'], $message));
}
$statusCode = intval($response->status_code);
if ($statusCode !== 200) {
throw new FireflyException(sprintf('Status code %d: %s', $statusCode, $response->body));
}
return;
}
}

View File

@@ -200,6 +200,7 @@ return [
'app_secret' => 'App secret',
'public_key' => 'Public key',
'country_code' => 'Country code',
'provider_code' => 'Bank or data-provider',
'due_date' => 'Due date',

View File

@@ -10,18 +10,18 @@
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('bank.spectre_input_fields_title') }}</h3>
<h3 class="box-title">{{ trans('import.spectre_input_fields_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-8">
<p>
{{ trans('bank.spectre_input_fields_text',{provider: data.provider.data.name, country: data.country})|raw }}
{{ trans('import.spectre_input_fields_text',{provider: data.provider.data.name, country: data.country})|raw }}
</p>
<p>
{{ trans('bank.spectre_instructions_english') }}
{{ trans('import.spectre_instructions_english') }}
</p>
<p>
<p class="well">
{{ data.provider.data.instruction|nl2br }}
</p>
</div>

View File

@@ -5,18 +5,18 @@
{% endblock %}
{% block content %}
<div class="row">
<form class="form-horizontal" action="{{ route('import.bank.prerequisites.post',['spectre']) }}" method="post">
<form class="form-horizontal" action="{{ route('import.prerequisites.post',['spectre']) }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('bank.spectre_prerequisites_title') }}</h3>
<h3 class="box-title">{{ trans('import.spectre_prerequisites_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-8">
<p>
{{ trans('bank.spectre_prerequisites_text')|raw }}
{{ trans('import.spectre_prerequisites_text')|raw }}
</p>
</div>
</div>
@@ -30,7 +30,7 @@
</div>
<div class="row">
<div class="col-lg-8">
<p>{{ trans('bank.spectre_enter_pub_key')|raw }}</p>
<p>{{ trans('import.spectre_enter_pub_key')|raw }}</p>
<div class="form-group" id="pub_key_holder">
<label for="ffInput_pub_key_holder" class="col-sm-4 control-label">{{ trans('form.public_key') }}</label>

View File

@@ -10,13 +10,13 @@
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('bank.spectre_select_country_title') }}</h3>
<h3 class="box-title">{{ trans('import.spectre_select_country_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-8">
<p>
{{ trans('bank.spectre_select_country_text')|raw }}
{{ trans('import.spectre_select_country_text')|raw }}
</p>
</div>
</div>

View File

@@ -10,13 +10,13 @@
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('bank.spectre_select_provider_title') }}</h3>
<h3 class="box-title">{{ trans('import.spectre_select_provider_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-8">
<p>
{{ trans('bank.spectre_select_provider_text',{country: data.country})|raw }}
{{ trans('import.spectre_select_provider_text',{country: data.country})|raw }}
</p>
</div>
</div>