From 34fc9bc50a769201cb3fae7e160ccc5798dd1cf9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 19 Dec 2017 05:20:37 +0100 Subject: [PATCH] Fix Spectre, first code to create customer. --- .../Import/PrerequisitesController.php | 4 +- .../Prerequisites/SpectrePrerequisites.php | 2 +- app/Import/Routine/SpectreRoutine.php | 133 +++++++++++++ app/Services/Spectre/Object/Customer.php | 90 +++++++++ app/Services/Spectre/Object/SpectreObject.php | 32 ++++ .../Spectre/Request/NewCustomerRequest.php | 63 +++++++ .../Spectre/Request/SpectreRequest.php | 175 +++++------------- resources/lang/en_US/form.php | 1 + .../views/import/spectre/input-fields.twig | 8 +- .../views/import/spectre/prerequisites.twig | 8 +- .../views/import/spectre/select-country.twig | 4 +- .../views/import/spectre/select-provider.twig | 4 +- 12 files changed, 384 insertions(+), 140 deletions(-) create mode 100644 app/Import/Routine/SpectreRoutine.php create mode 100644 app/Services/Spectre/Object/Customer.php create mode 100644 app/Services/Spectre/Object/SpectreObject.php create mode 100644 app/Services/Spectre/Request/NewCustomerRequest.php diff --git a/app/Http/Controllers/Import/PrerequisitesController.php b/app/Http/Controllers/Import/PrerequisitesController.php index 256d94e828..e7b46c4f7d 100644 --- a/app/Http/Controllers/Import/PrerequisitesController.php +++ b/app/Http/Controllers/Import/PrerequisitesController.php @@ -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)); } diff --git a/app/Import/Prerequisites/SpectrePrerequisites.php b/app/Import/Prerequisites/SpectrePrerequisites.php index cf03ba0048..f9a1102def 100644 --- a/app/Import/Prerequisites/SpectrePrerequisites.php +++ b/app/Import/Prerequisites/SpectrePrerequisites.php @@ -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'); diff --git a/app/Import/Routine/SpectreRoutine.php b/app/Import/Routine/SpectreRoutine.php new file mode 100644 index 0000000000..5306237cc3 --- /dev/null +++ b/app/Import/Routine/SpectreRoutine.php @@ -0,0 +1,133 @@ +. + */ +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 '
';
+        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;
+    }
+
+
+}
diff --git a/app/Services/Spectre/Object/Customer.php b/app/Services/Spectre/Object/Customer.php
new file mode 100644
index 0000000000..21f2f2f0f2
--- /dev/null
+++ b/app/Services/Spectre/Object/Customer.php
@@ -0,0 +1,90 @@
+.
+ */
+
+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;
+    }
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/app/Services/Spectre/Object/SpectreObject.php b/app/Services/Spectre/Object/SpectreObject.php
new file mode 100644
index 0000000000..e41db10601
--- /dev/null
+++ b/app/Services/Spectre/Object/SpectreObject.php
@@ -0,0 +1,32 @@
+.
+ */
+
+declare(strict_types=1);
+
+namespace FireflyIII\Services\Spectre\Object;
+
+/**
+ * Class SpectreObject
+ */
+class SpectreObject
+{
+
+}
\ No newline at end of file
diff --git a/app/Services/Spectre/Request/NewCustomerRequest.php b/app/Services/Spectre/Request/NewCustomerRequest.php
new file mode 100644
index 0000000000..93cbef13c7
--- /dev/null
+++ b/app/Services/Spectre/Request/NewCustomerRequest.php
@@ -0,0 +1,63 @@
+.
+ */
+
+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;
+    }
+
+
+}
\ No newline at end of file
diff --git a/app/Services/Spectre/Request/SpectreRequest.php b/app/Services/Spectre/Request/SpectreRequest.php
index eaaa284694..c93e77c036 100644
--- a/app/Services/Spectre/Request/SpectreRequest.php
+++ b/app/Services/Spectre/Request/SpectreRequest.php
@@ -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;
+    }
 }
diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php
index 6b7076a00c..84f57f87fc 100644
--- a/resources/lang/en_US/form.php
+++ b/resources/lang/en_US/form.php
@@ -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',
diff --git a/resources/views/import/spectre/input-fields.twig b/resources/views/import/spectre/input-fields.twig
index 03aae3faf2..93c21d595d 100644
--- a/resources/views/import/spectre/input-fields.twig
+++ b/resources/views/import/spectre/input-fields.twig
@@ -10,18 +10,18 @@
             
-

{{ trans('bank.spectre_input_fields_title') }}

+

{{ trans('import.spectre_input_fields_title') }}

- {{ 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 }}

- {{ trans('bank.spectre_instructions_english') }} + {{ trans('import.spectre_instructions_english') }}

-

+

{{ data.provider.data.instruction|nl2br }}

diff --git a/resources/views/import/spectre/prerequisites.twig b/resources/views/import/spectre/prerequisites.twig index 1b225e6a12..c521d532f0 100644 --- a/resources/views/import/spectre/prerequisites.twig +++ b/resources/views/import/spectre/prerequisites.twig @@ -5,18 +5,18 @@ {% endblock %} {% block content %}
-
+
-

{{ trans('bank.spectre_prerequisites_title') }}

+

{{ trans('import.spectre_prerequisites_title') }}

- {{ trans('bank.spectre_prerequisites_text')|raw }} + {{ trans('import.spectre_prerequisites_text')|raw }}

@@ -30,7 +30,7 @@
-

{{ trans('bank.spectre_enter_pub_key')|raw }}

+

{{ trans('import.spectre_enter_pub_key')|raw }}

diff --git a/resources/views/import/spectre/select-country.twig b/resources/views/import/spectre/select-country.twig index 6f27099bba..60f4d58343 100644 --- a/resources/views/import/spectre/select-country.twig +++ b/resources/views/import/spectre/select-country.twig @@ -10,13 +10,13 @@
-

{{ trans('bank.spectre_select_country_title') }}

+

{{ trans('import.spectre_select_country_title') }}

- {{ trans('bank.spectre_select_country_text')|raw }} + {{ trans('import.spectre_select_country_text')|raw }}

diff --git a/resources/views/import/spectre/select-provider.twig b/resources/views/import/spectre/select-provider.twig index d4c554d379..bf41f8f506 100644 --- a/resources/views/import/spectre/select-provider.twig +++ b/resources/views/import/spectre/select-provider.twig @@ -10,13 +10,13 @@
-

{{ trans('bank.spectre_select_provider_title') }}

+

{{ trans('import.spectre_select_provider_title') }}

- {{ trans('bank.spectre_select_provider_text',{country: data.country})|raw }} + {{ trans('import.spectre_select_provider_text',{country: data.country})|raw }}