Finish up bunq import routine.

This commit is contained in:
James Cole 2018-03-24 18:55:02 +01:00
parent 3c9b7c07af
commit 6a6482dc7f
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
24 changed files with 696 additions and 79 deletions

View File

@ -162,9 +162,14 @@ class AccountFactory
if ($accountTypeId > 0) {
return AccountType::find($accountTypeId);
}
$type = config('firefly.accountTypeByIdentifier.' . strval($accountType));
$type = config('firefly.accountTypeByIdentifier.' . strval($accountType));
$result = AccountType::whereType($type)->first();
if (is_null($result) && !is_null($accountType)) {
// try as full name:
$result = AccountType::whereType($accountType)->first();
}
return AccountType::whereType($type)->first();
return $result;
}

View File

@ -93,7 +93,7 @@ class TransactionJournalFactory
// store date meta fields (if present):
$fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date',
'due_date', 'payment_date', 'invoice_date', 'internal_reference',];
'due_date', 'payment_date', 'invoice_date', 'internal_reference','bunq_payment_id'];
foreach ($fields as $field) {
$this->storeMeta($journal, $data, $field);

View File

@ -56,7 +56,7 @@ class TransactionJournalMetaFactory
if ($data['data'] instanceof Carbon) {
$value = $data['data']->toW3cString();
}
if (strlen($value) === 0) {
if (strlen(strval($value)) === 0) {
// don't store blank strings.
if (!is_null($entry)) {
try {

View File

@ -26,6 +26,7 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Log;
/**
* Class StatusController
@ -117,6 +118,7 @@ class StatusController extends Controller
$result['running'] = true;
}
$result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage'];
Log::debug(sprintf('JOB STATUS: %d/%d', $result['done'], $result['steps']));
return response()->json($result);
}

View File

@ -37,7 +37,7 @@ class Installer
return $next($request);
}
Log::debug(sprintf('URL is %s, will run installer middleware', $url));
// Log::debug(sprintf('URL is %s, will run installer middleware', $url));
// no tables present?
try {

View File

@ -193,7 +193,7 @@ class BunqConfigurator implements ConfiguratorInterface
// set default extended status:
$extendedStatus = $this->repository->getExtendedStatus($job);
$extendedStatus['steps'] = 6;
$extendedStatus['steps'] = 8;
// save to job:
$job = $this->repository->setConfiguration($job, $finalConfig);

View File

@ -23,13 +23,25 @@ declare(strict_types=1);
namespace FireflyIII\Import\Routine;
use Carbon\Carbon;
use DB;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Factory\TransactionJournalFactory;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Services\Bunq\Id\DeviceServerId;
use FireflyIII\Services\Bunq\Object\DeviceServer;
use FireflyIII\Services\Bunq\Object\LabelMonetaryAccount;
use FireflyIII\Services\Bunq\Object\MonetaryAccountBank;
use FireflyIII\Services\Bunq\Object\Payment;
use FireflyIII\Services\Bunq\Object\ServerPublicKey;
use FireflyIII\Services\Bunq\Object\UserCompany;
use FireflyIII\Services\Bunq\Object\UserPerson;
@ -80,9 +92,14 @@ class BunqRoutine implements RoutineInterface
public $journals;
/** @var int */
public $lines = 0;
/** @var AccountFactory */
private $accountFactory;
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var ImportJob */
private $job;
/** @var TransactionJournalFactory */
private $journalFactory;
/** @var ImportJobRepositoryInterface */
private $repository;
@ -140,9 +157,15 @@ class BunqRoutine implements RoutineInterface
*/
public function setJob(ImportJob $job)
{
$this->job = $job;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->job = $job;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->accountFactory = app(AccountFactory::class);
$this->journalFactory = app(TransactionJournalFactory::class);
$this->repository->setUser($job->user);
$this->accountRepository->setUser($job->user);
$this->accountFactory->setUser($job->user);
$this->journalFactory->setUser($job->user);
}
/**
@ -176,27 +199,13 @@ class BunqRoutine implements RoutineInterface
// do nothing in this stage. Job should revert to config routine.
break;
case 'have-account-mapping':
$this->setStatus('running');
$this->runStageHaveAccountMapping();
break;
default:
throw new FireflyException(sprintf('No action for stage %s!', $stage));
break;
// case 'has-token':
// // import routine does nothing at this point:
// break;
// case 'user-logged-in':
// $this->runStageLoggedIn();
// break;
// case 'have-account-mapping':
// $this->runStageHaveMapping();
// break;
// default:
// throw new FireflyException(sprintf('Cannot handle stage %s', $stage));
// }
//
// return true;
}
}
@ -205,17 +214,18 @@ class BunqRoutine implements RoutineInterface
*/
protected function runStageInitial()
{
$this->addStep();
Log::debug('In runStageInitial()');
$this->setStatus('running');
// register the device at Bunq:
$serverId = $this->registerDevice();
$this->addStep();
Log::debug(sprintf('Found device server with id %d', $serverId->getId()));
$config = $this->getConfig();
$config['stage'] = 'registered';
$this->setConfig($config);
$this->addStep();
return;
}
@ -227,6 +237,7 @@ class BunqRoutine implements RoutineInterface
*/
protected function runStageRegistered(): void
{
$this->addStep();
Log::debug('Now in runStageRegistered()');
$apiKey = Preferences::getForUser($this->job->user, 'bunq_api_key')->data;
$serverPublicKey = Preferences::getForUser($this->job->user, 'bunq_server_public_key')->data;
@ -239,7 +250,8 @@ class BunqRoutine implements RoutineInterface
$request->call();
$this->addStep();
// todo store objects in job!
Log::debug('Requested new session.');
$deviceSession = $request->getDeviceSessionId();
$userPerson = $request->getUserPerson();
$userCompany = $request->getUserCompany();
@ -254,6 +266,8 @@ class BunqRoutine implements RoutineInterface
$this->setConfig($config);
$this->addStep();
Log::debug('Session stored in job.');
return;
}
@ -262,7 +276,17 @@ class BunqRoutine implements RoutineInterface
*/
private function addStep()
{
$this->repository->addStepsDone($this->job, 1);
$this->addSteps(1);
}
/**
* Shorthand method.
*
* @param int $count
*/
private function addSteps(int $count)
{
$this->repository->addStepsDone($this->job, $count);
}
/**
@ -275,6 +299,71 @@ class BunqRoutine implements RoutineInterface
$this->repository->addTotalSteps($this->job, $steps);
}
/**
* @param int $paymentId
*
* @return bool
*/
private function alreadyImported(int $paymentId): bool
{
$count = TransactionJournalMeta::where('name', 'bunq_payment_id')
->where('data', json_encode($paymentId))->count();
Log::debug(sprintf('Transaction #%d is %d time(s) in the database.', $paymentId, $count));
return $count > 0;
}
/**
* @param LabelMonetaryAccount $party
* @param string $expectedType
*
* @return Account
*/
private function convertToAccount(LabelMonetaryAccount $party, string $expectedType): Account
{
Log::debug('in convertToAccount()');
// find opposing party by IBAN first.
$result = $this->accountRepository->findByIbanNull($party->getIban(), [$expectedType]);
if (!is_null($result)) {
Log::debug(sprintf('Search for %s resulted in account %s (#%d)', $party->getIban(), $result->name, $result->id));
return $result;
}
// try to find asset account just in case:
if ($expectedType !== AccountType::ASSET) {
$result = $this->accountRepository->findByIbanNull($party->getIban(), [AccountType::ASSET]);
if (!is_null($result)) {
Log::debug(sprintf('Search for Asset "%s" resulted in account %s (#%d)', $party->getIban(), $result->name, $result->id));
return $result;
}
}
// create new account:
$data = [
'user_id' => $this->job->user_id,
'iban' => $party->getIban(),
'name' => $party->getLabelUser()->getDisplayName(),
'account_type_id' => null,
'accountType' => $expectedType,
'virtualBalance' => null,
'active' => true,
];
$account = $this->accountFactory->create($data);
Log::debug(
sprintf(
'Converted label monetary account %s to %s account %s (#%d)',
$party->getLabelUser()->getDisplayName(),
$expectedType,
$account->name, $account->id
)
);
return $account;
}
/**
* This method creates a new public/private keypair for the user. This isn't really secure, since the key is generated on the fly with
* no regards for HSM's, smart cards or other things. It would require some low level programming to get this right. But the private key
@ -498,6 +587,144 @@ class BunqRoutine implements RoutineInterface
return $this->repository->getStatus($this->job);
}
/**
* Import the transactions that were found.
*
* @param array $payments
*
* @throws FireflyException
*/
private function importPayments(array $payments): void
{
Log::debug('Going to run importPayments()');
$journals = new Collection;
$config = $this->getConfig();
foreach ($payments as $accountId => $data) {
Log::debug(sprintf('Now running for bunq account #%d with %d payment(s).', $accountId, count($data['payments'])));
/** @var Payment $payment */
foreach ($data['payments'] as $index => $payment) {
Log::debug(sprintf('Now at payment #%d with ID #%d', $index, $payment->getId()));
// store or find counter party:
$counterParty = $payment->getCounterParty();
$amount = $payment->getAmount();
$paymentId = $payment->getId();
if ($this->alreadyImported($paymentId)) {
Log::error(sprintf('Already imported bunq payment with id #%d', $paymentId));
// add three steps to keep up
$this->addSteps(3);
continue;
}
Log::debug(sprintf('Amount is %s %s', $amount->getCurrency(), $amount->getValue()));
$expected = AccountType::EXPENSE;
if (bccomp($amount->getValue(), '0') === 1) {
// amount + means that its a deposit.
$expected = AccountType::REVENUE;
Log::debug('Will make opposing account revenue.');
}
$opposing = $this->convertToAccount($counterParty, $expected);
$account = $this->accountRepository->findNull($config['accounts-mapped'][$accountId]);
$type = TransactionType::WITHDRAWAL;
$this->addStep();
Log::debug(sprintf('Will store withdrawal between "%s" (%d) and "%s" (%d)', $account->name, $account->id, $opposing->name, $opposing->id));
// start storing stuff:
$source = $account;
$destination = $opposing;
if (bccomp($amount->getValue(), '0') === 1) {
// its a deposit:
$source = $opposing;
$destination = $account;
$type = TransactionType::DEPOSIT;
Log::debug('Will make it a deposit.');
}
if ($account->accountType->type === AccountType::ASSET && $opposing->accountType->type === AccountType::ASSET) {
$type = TransactionType::TRANSFER;
Log::debug('Both are assets, will make transfer.');
}
$storeData = [
'user' => $this->job->user_id,
'type' => $type,
'date' => $payment->getCreated(),
'description' => $payment->getDescription(),
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'bill_id' => null,
'bill_name' => null,
'tags' => [$payment->getType(), $payment->getSubType()],
'internal_reference' => $payment->getId(),
'notes' => null,
'bunq_payment_id' => $payment->getId(),
'transactions' => [
// single transaction:
[
'description' => null,
'amount' => $amount->getValue(),
'currency_id' => null,
'currency_code' => $amount->getCurrency(),
'foreign_amount' => null,
'foreign_currency_id' => null,
'foreign_currency_code' => null,
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
'category_name' => null,
'source_id' => $source->id,
'source_name' => null,
'destination_id' => $destination->id,
'destination_name' => null,
'reconciled' => false,
'identifier' => 0,
],
],
];
$journal = $this->journalFactory->create($storeData);
Log::debug(sprintf('Stored journal with ID #%d', $journal->id));
$this->addStep();
$journals->push($journal);
}
}
// link to tag
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($this->job->user);
$data = [
'tag' => trans('import.import_with_key', ['key' => $this->job->key]),
'date' => new Carbon,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
$tag = $repository->store($data);
$extended = $this->getExtendedStatus();
$extended['tag'] = $tag->id;
$this->setExtendedStatus($extended);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$tagId = $tag->id;
foreach ($journals as $journal) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journal->id, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journal->id, 'tag_id' => $tagId]);
$this->addStep();
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $journals->count(), $tag->id, $tag->tag));
// set status to "finished"?
// update job:
$this->setStatus('finished');
return;
}
/**
* To install Firefly III as a new device:
* - Send an installation token request.
@ -576,13 +803,17 @@ class BunqRoutine implements RoutineInterface
$count = 0;
if (0 === $user->getId()) {
$user = new UserCompany($config['user_company']);
Log::debug(sprintf('Will try to get transactions for company #%d', $user->getId()));
}
$this->addTotalSteps(count($config['accounts']) * 2);
foreach ($config['accounts'] as $accountData) {
$this->addStep();
$account = new MonetaryAccountBank($accountData);
$importId = $account->getId();
if (1 === $mapping[$importId]) {
// grab all transactions
Log::debug(sprintf('Will grab payments for account %s', $account->getDescription()));
$request = new ListPaymentRequest();
$request->setPrivateKey($this->getPrivateKey());
$request->setServerPublicKey($this->getServerPublicKey());
@ -590,22 +821,24 @@ class BunqRoutine implements RoutineInterface
$request->setUserId($user->getId());
$request->setAccount($account);
$request->call();
exit;
$payments = $request->getPayments();
// store in array
$all[$account->getId()] = [
'account' => $account,
'import_id' => $importId,
'transactions' => $transactions,
'account' => $account,
'import_id' => $importId,
'payments' => $payments,
];
$count += count($transactions);
$count += count($payments);
}
Log::debug(sprintf('Total number of transactions: %d', $count));
Log::debug(sprintf('Total number of payments: %d', $count));
$this->addStep();
//$this->importTransactions($all);
// add steps for import:
$this->addTotalSteps($count * 3);
$this->importPayments($all);
}
exit;
// update job to be complete, I think?
}
/**
@ -613,6 +846,7 @@ class BunqRoutine implements RoutineInterface
*/
private function runStageLoggedIn(): void
{
$this->addStep();
// grab new session token:
$config = $this->getConfig();
$token = new SessionToken($config['session_token']);
@ -631,6 +865,7 @@ class BunqRoutine implements RoutineInterface
$accounts = $request->getMonetaryAccounts();
$arr = [];
Log::debug(sprintf('Get monetary accounts, found %d accounts.', $accounts->count()));
$this->addStep();
/** @var MonetaryAccountBank $account */
foreach ($accounts as $account) {
@ -645,6 +880,7 @@ class BunqRoutine implements RoutineInterface
// once the accounts are stored, go to configuring stage:
// update job, set status to "configuring".
$this->setStatus('configuring');
$this->addStep();
return;
}

View File

@ -302,4 +302,5 @@ class AccountRepository implements AccountRepositoryInterface
return $journal;
}
}

View File

@ -66,6 +66,7 @@ interface AccountRepositoryInterface
* @param string $number
* @param array $types
*
* @deprecated
* @return Account
*/
public function findByAccountNumber(string $number, array $types): Account;
@ -74,14 +75,24 @@ interface AccountRepositoryInterface
* @param string $iban
* @param array $types
*
* @deprecated
* @return Account
*/
public function findByIban(string $iban, array $types): Account;
/**
* @param string $iban
* @param array $types
*
* @return Account|null
*/
public function findByIbanNull(string $iban, array $types): ?Account;
/**
* @param string $name
* @param array $types
*
* @deprecated
* @return Account|null
*/
public function findByName(string $name, array $types): ?Account;

View File

@ -41,6 +41,7 @@ trait FindAccountsTrait
/**
* @param $accountId
*
* @deprecated
* @return Account
*/
public function find(int $accountId): Account
@ -58,6 +59,7 @@ trait FindAccountsTrait
* @param string $number
* @param array $types
*
*
* @deprecated
* @return Account
*/
@ -109,10 +111,37 @@ trait FindAccountsTrait
return new Account;
}
/**
* @param string $iban
* @param array $types
*
* @return Account|null
*/
public function findByIbanNull(string $iban, array $types): ?Account
{
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
if (count($types) > 0) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
$accounts = $query->get(['accounts.*']);
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->iban === $iban) {
return $account;
}
}
return null;
}
/**
* @param string $name
* @param array $types
*
* @deprecated
* @return Account|null
*/
public function findByName(string $name, array $types): ?Account

View File

@ -311,19 +311,11 @@ class ImportJobRepository implements ImportJobRepositoryInterface
*/
public function setExtendedStatus(ImportJob $job, array $array): ImportJob
{
// remove 'errors' because it gets larger and larger and larger...
$display = $array;
unset($display['errors']);
Log::debug(sprintf('Incoming extended status for job "%s" is (except errors): ', $job->key), $display);
$currentStatus = $job->extended_status;
$newStatus = array_merge($currentStatus, $array);
$job->extended_status = $newStatus;
$job->save();
// remove 'errors' because it gets larger and larger and larger...
unset($newStatus['errors']);
Log::debug(sprintf('Set extended status of job "%s" to (except errors): ', $job->key), $newStatus);
return $job;
}

View File

@ -27,4 +27,30 @@ namespace FireflyIII\Services\Bunq\Object;
*/
class Avatar extends BunqObject
{
/** @var string */
private $anchorUuid;
/** @var Image */
private $image;
/** @var string */
private $uuid;
/**
* Avatar constructor.
*
* @param array $data
*/
public function __construct(array $data)
{
$this->uuid = $data['uuid'];
$this->anchorUuid = $data['anchor_uuid'];
$this->image = new Image($data['image']);
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -25,6 +25,12 @@ namespace FireflyIII\Services\Bunq\Object;
/**
* Class BunqObject.
*/
class BunqObject
abstract class BunqObject
{
/**
* Convert this object to array.
*
* @return array
*/
abstract public function toArray(): array;
}

View File

@ -75,4 +75,12 @@ class DeviceServer extends BunqObject
{
return $this->ip;
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -0,0 +1,62 @@
<?php
/**
* Image.php
* Copyright (c) 2018 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\Bunq\Object;
/**
* Class Image
*/
class Image extends BunqObject
{
/** @var string */
private $attachmentPublicUuid;
/** @var string */
private $contentType;
/** @var int */
private $height;
/** @var int */
private $width;
/**
* Image constructor.
*
* @param array $data
*/
public function __construct(array $data)
{
$this->attachmentPublicUuid = $data['attachment_public_uuid'] ?? null;
$this->height = $data['height'] ?? null;
$this->width = $data['width'] ?? null;
$this->contentType = $data['content_type'] ?? null;
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -29,5 +29,54 @@ namespace FireflyIII\Services\Bunq\Object;
*/
class LabelMonetaryAccount extends BunqObject
{
/** @var Avatar */
private $avatar;
/** @var string */
private $country;
/** @var string */
private $iban;
/** @var bool */
private $isLight;
/** @var LabelUser */
private $labelUser;
/**
* @return LabelUser
*/
public function getLabelUser(): LabelUser
{
return $this->labelUser;
}
/**
* LabelMonetaryAccount constructor.
*
* @param array $data
*/
public function __construct(array $data)
{
$this->iban = $data['iban'];
$this->isLight = $data['is_light'];
$this->avatar = new Avatar($data['avatar']);
$this->labelUser = new LabelUser($data['label_user']);
$this->country = $data['country'];
}
/**
* @return string
*/
public function getIban(): string
{
return $this->iban;
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -0,0 +1,90 @@
<?php
/**
* LabelUser.php
* Copyright (c) 2018 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\Bunq\Object;
/**
* Class LabelUser
*/
class LabelUser extends BunqObject
{
/** @var Avatar */
private $avatar;
/** @var string */
private $country;
/** @var string */
private $displayName;
/** @var string */
private $publicNickName;
/** @var string */
private $uuid;
/**
* @return Avatar
*/
public function getAvatar(): Avatar
{
return $this->avatar;
}
/**
* @return string
*/
public function getDisplayName(): string
{
return $this->displayName;
}
/**
* @return string
*/
public function getPublicNickName(): string
{
return $this->publicNickName;
}
/**
* LabelUser constructor.
*
* @param array $data
*/
public function __construct(array $data)
{
$this->uuid = $data['uuid'];
$this->displayName = $data['display_name'];
$this->country = $data['country'];
$this->publicNickName = $data['public_nick_name'];
$this->avatar = new Avatar($data['avatar']);
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -22,6 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Services\Bunq\Object;
use Carbon\Carbon;
@ -30,28 +31,30 @@ use Carbon\Carbon;
*/
class Payment extends BunqObject
{
/** @var int */
private $id;
/** @var Carbon */
private $created;
/** @var Carbon */
private $updated;
/** @var int */
private $monetaryAccountId;
/** @var LabelMonetaryAccount */
private $alias;
/** @var Amount */
private $amount;
/** @var string */
private $description;
/** @var string */
private $type;
/** @var string */
private $merchantReference;
/** @var LabelMonetaryAccount */
private $counterParty;
/** @var array */
private $attachments = [];
/** @var LabelMonetaryAccount */
private $counterParty;
/** @var Carbon */
private $created;
/** @var string */
private $description;
/** @var int */
private $id;
/** @var string */
private $merchantReference;
/** @var int */
private $monetaryAccountId;
/** @var string */
private $subType;
/** @var string */
private $type;
/** @var Carbon */
private $updated;
/**
* Payment constructor.
@ -60,11 +63,81 @@ class Payment extends BunqObject
*/
public function __construct(array $data)
{
$this->id = $data['id'];
$this->created = new Carbon();
$this->id = $data['id'];
$this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']);
$this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']);
$this->monetaryAccountId = (int)$data['monetary_account_id'];
$this->amount = new Amount($data['amount']);
$this->description = $data['description'];
$this->type = $data['type'];
$this->merchantReference = $data['merchant_reference'];
$this->alias = new LabelMonetaryAccount($data['alias']);
$this->counterParty = new LabelMonetaryAccount($data['counterparty_alias']);
$this->subType = $data['sub_type'];
}
var_dump($data);
exit;
/**
* @return Amount
*/
public function getAmount(): Amount
{
return $this->amount;
}
/**
* @return LabelMonetaryAccount|null
*/
public function getCounterParty(): ?LabelMonetaryAccount
{
return $this->counterParty;
}
/**
* @return Carbon
*/
public function getCreated(): Carbon
{
return $this->created;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getSubType(): string
{
return $this->subType;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -55,4 +55,12 @@ class ServerPublicKey extends BunqObject
{
$this->publicKey = $publicKey;
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -74,4 +74,12 @@ class UserLight extends BunqObject
$this->legalName = $data['legal_name'];
// aliases
}
/**
* @return array
*/
public function toArray(): array
{
die(sprintf('Cannot convert %s to array.', get_class($this)));
}
}

View File

@ -48,14 +48,9 @@ class DeviceServerRequest extends BunqRequest
Log::debug('Now in DeviceServerRequest::call()');
$uri = 'device-server';
$data = ['description' => $this->description, 'secret' => $this->secret, 'permitted_ips' => $this->permittedIps];
Log::debug('Data we send along: ', $data);
$headers = $this->getDefaultHeaders();
$headers['X-Bunq-Client-Authentication'] = $this->installationToken->getToken();
Log::debug('Headers we send along: ', $headers);
$response = $this->sendSignedBunqPost($uri, $data, $headers);
$deviceServerId = new DeviceServerId;
$deviceServerId->setId(intval($response['Response'][0]['Id']['id']));

View File

@ -51,24 +51,36 @@ class ListPaymentRequest extends BunqRequest
*/
public function call(): void
{
$break = false;
$this->payments = new Collection;
$uri = sprintf('user/%d/monetary-account/%d/payment', $this->userId, $this->account->getId());
$data = [];
$headers = $this->getDefaultHeaders();
$headers['X-Bunq-Client-Authentication'] = $this->sessionToken->getToken();
$response = $this->sendSignedBunqGet($uri, $data, $headers);
while ($break === false) {
$response = $this->sendSignedBunqGet($uri, [], $headers);
$uri = str_replace('/v1/', '', $response['Pagination']['future_url']);
$break = true;
// create payment objects:
$raw = $this->getArrayFromResponse('Payment', $response);
foreach ($raw as $entry) {
$account = new Payment($entry);
$this->payments->push($account);
// create payment objects:
$raw = $this->getArrayFromResponse('Payment', $response);
foreach ($raw as $entry) {
$payment = new Payment($entry);
$this->payments->push($payment);
}
}
return;
}
/**
* @return Collection
*/
public function getPayments(): Collection
{
return $this->payments;
}
/**
* @param MonetaryAccountBank $account
*/

View File

@ -163,6 +163,9 @@ return [
// bunq
'bunq_prerequisites_title' => 'Prerequisites for an import from bunq',
'bunq_prerequisites_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app.',
'bunq_do_import' => 'Yes, import from this account',
'bunq_accounts_title' => 'Bunq accounts',
'bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.',
// Spectre
'spectre_title' => 'Import using Spectre',

View File

@ -110,4 +110,5 @@ return [
'sepa-cc' => 'SEPA Clearing Code',
'sepa-ep' => 'SEPA External Purpose',
'sepa-ci' => 'SEPA Creditor Identifier',
'account_at_bunq' => 'Account with bunq',
];