'X-Bunq-Client-Response-Id', 'x-bunq-client-request-id' => 'X-Bunq-Client-Request-Id', ]; public function __construct() { // create a log channel $this->logger = new Logger('bunq-request'); $this->logger->pushHandler(new StreamHandler('logs/bunq.log', Logger::DEBUG)); $this->logger->debug('Hallo dan'); } /** * */ abstract public function call(): void; /** * @return string */ public function getServer(): string { return $this->server; } /** * @param string $server */ public function setServer(string $server) { $this->server = $server; } /** * @param bool $fake */ public function setFake(bool $fake) { $this->fake = $fake; } /** * @param string $privateKey */ public function setPrivateKey(string $privateKey) { $this->privateKey = $privateKey; } /** * @param string $secret */ public function setSecret(string $secret) { $this->secret = $secret; } /** * @param ServerPublicKey $serverPublicKey */ public function setServerPublicKey(ServerPublicKey $serverPublicKey) { $this->serverPublicKey = $serverPublicKey; } /** * @param string $method * @param string $uri * @param array $headers * @param string $data * * @return string */ protected function generateSignature(string $method, string $uri, array $headers, string $data): string { if (strlen($this->privateKey) === 0) { throw new Exception('No private key present.'); } if (strtolower($method) === 'get') { $data = ''; } $uri = str_replace(['https://api.bunq.com', 'https://sandbox.public.api.bunq.com'], '', $uri); $toSign = strtoupper($method) . ' ' . $uri . "\n"; $headersToSign = ['Cache-Control', 'User-Agent']; ksort($headers); foreach ($headers as $name => $value) { if (in_array($name, $headersToSign) || substr($name, 0, 7) === 'X-Bunq-') { $toSign .= $name . ': ' . $value . "\n"; } } $toSign .= "\n" . $data; $signature = ''; openssl_sign($toSign, $signature, $this->privateKey, OPENSSL_ALGO_SHA256); $signature = base64_encode($signature); return $signature; } protected function getDefaultHeaders(): array { return [ 'X-Bunq-Client-Request-Id' => uniqid('sander'), 'Cache-Control' => 'no-cache', 'User-Agent' => 'pre-Firefly III test thing', 'X-Bunq-Language' => 'en_US', 'X-Bunq-Region' => 'nl_NL', 'X-Bunq-Geolocation' => '0 0 0 0 NL', ]; } /** * @param string $key * @param array $response * * @return array */ protected function getKeyFromResponse(string $key, array $response): array { if (isset($response['Response'])) { foreach ($response['Response'] as $entry) { $currentKey = key($entry); $data = current($entry); if ($currentKey === $key) { return $data; } } } return []; } /** * @param string $uri * @param array $data * @param array $headers * * @return array * @throws Exception */ protected function sendSignedBunqGet(string $uri, array $data, array $headers): array { if (strlen($this->server) === 0) { throw new Exception('No bunq server defined'); } $body = json_encode($data); $fullUri = $this->server . $uri; $signature = $this->generateSignature('get', $uri, $headers, $body); $headers['X-Bunq-Client-Signature'] = $signature; try { $response = Requests::get($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); if ($this->isErrorResponse($array)) { $this->throwResponseError($array); } $responseHeaders = $response->headers->getAll(); $statusCode = $response->status_code; if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) { throw new Exception(sprintf('Could not verify signature for request to "%s"', $uri)); } $array['ResponseHeaders'] = $responseHeaders; 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); if ($this->isErrorResponse($array)) { $this->throwResponseError($array); } $responseHeaders = $response->headers->getAll(); $statusCode = $response->status_code; if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) { throw new Exception(sprintf('Could not verify signature for request to "%s"', $uri)); } $array['ResponseHeaders'] = $responseHeaders; return $array; } /** * @param string $uri * @param array $data * @param array $headers * * @return array */ protected function sendUnsignedBunqPost(string $uri, array $data, array $headers): 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()],]]; } $body = $response->body; $responseHeaders = $response->headers->getAll(); $array = json_decode($body, true); if ($this->isErrorResponse($array)) { $this->throwResponseError($array); } $array['ResponseHeaders'] = $responseHeaders; return $array; } /** * @param array $response * * @return bool */ private function isErrorResponse(array $response): bool { $key = key($response); if ($key === 'Error') { return true; } return false; } /** * @param array $response * * @throws Exception */ private function throwResponseError(array $response) { echo '
' . print_r($response, true) . '