diff --git a/app/Helpers/Bank/Nordigen/Nordigen.php b/app/Helpers/Bank/Nordigen/Nordigen.php index ca62fd3af719..83b22def5923 100644 --- a/app/Helpers/Bank/Nordigen/Nordigen.php +++ b/app/Helpers/Bank/Nordigen/Nordigen.php @@ -49,12 +49,12 @@ class Nordigen } // requisition-section - public function createRequisition(string $redirect, string $initutionId) + public function createRequisition(string $redirect, string $initutionId, string $nAccountId) { if ($this->test_mode && $initutionId != $this->sandbox_institutionId) throw new \Exception('invalid institutionId while in test-mode'); - return $this->client->requisition->createRequisition($redirect, $initutionId); + return $this->client->requisition->createRequisition($redirect, $initutionId, null, $nAccountId); // we dont reuse existing requisitions, to prevent double usage of them. see: deleteAccount } public function getRequisition(string $requisitionId) @@ -80,18 +80,20 @@ class Nordigen { // get all valid requisitions - $requisitions = $this->client->requisition->getRequisitions(); + $requisitions = $this->client->requisition->getRequisitions(); // no pagination used?! // fetch all valid accounts for activated requisitions $nordigen_accountIds = []; - foreach ($requisitions as $requisition) { - foreach ($requisition->accounts as $accountId) { + foreach ($requisitions["results"] as $requisition) { + foreach ($requisition["accounts"] as $accountId) { array_push($nordigen_accountIds, $accountId); } } $nordigen_accountIds = array_unique($nordigen_accountIds); + Log::info($nordigen_accountIds); + $nordigen_accounts = []; foreach ($nordigen_accountIds as $accountId) { $nordigen_account = $this->getAccount($accountId); @@ -99,6 +101,7 @@ class Nordigen array_push($nordigen_accounts, $nordigen_account); } + Log::info($nordigen_accounts); return $nordigen_accounts; @@ -109,11 +112,13 @@ class Nordigen $out = new \stdClass(); - $out->data = $this->client->account($account_id)->getAccountDetails(); + $out->data = $this->client->account($account_id)->getAccountDetails()["account"]; $out->metadata = $this->client->account($account_id)->getAccountMetaData(); - $out->balances = $this->client->account($account_id)->getAccountBalances(); + $out->balances = $this->client->account($account_id)->getAccountBalances()["balances"]; $out->institution = $this->client->institution->getInstitution($out->metadata["institution_id"]); + Log::info($out->data); + $it = new AccountTransformer(); return $it->transform($out); diff --git a/app/Helpers/Bank/Nordigen/Transformer/AccountTransformer.php b/app/Helpers/Bank/Nordigen/Transformer/AccountTransformer.php index 48c43559d2a2..250bc2ceca81 100644 --- a/app/Helpers/Bank/Nordigen/Transformer/AccountTransformer.php +++ b/app/Helpers/Bank/Nordigen/Transformer/AccountTransformer.php @@ -36,9 +36,7 @@ use App\Helpers\Bank\AccountTransformerInterface; [status] => READY [owner_name] => Max Mustermann ) - [balances] => stdClass Object - ( - [balances]: [ + [balances] => [ { [balanceAmount]: { [amount] => 9825.64 @@ -57,7 +55,6 @@ use App\Helpers\Bank\AccountTransformerInterface; [referenceDate] => 2023-12-01 } ] - ) [institution] => stdClass Object ( [id] => STADT_KREISSPARKASSE_LEIPZIG_WELADE8LXXX @@ -92,43 +89,32 @@ class AccountTransformer implements AccountTransformerInterface public function transform($nordigen_account) { - $data = []; - if (!property_exists($nordigen_account, 'data') || !property_exists($nordigen_account, 'metadata') || !property_exists($nordigen_account, 'balances') || !property_exists($nordigen_account, 'institution')) - return $data; + throw new \Exception('invalid dataset'); - foreach ($nordigen_account->account as $account) { - $data[] = $this->transformAccount($account); - } - - return $data; - } - - public function transformAccount($account) - { - - $used_balance = $account->balances[0]; + $used_balance = $nordigen_account->balances[0]; // prefer entry with closingBooked - foreach ($account->balances as $entry) { - if ($entry->balanceType === 'closingBooked') { // available: closingBooked, interimAvailable + foreach ($nordigen_account->balances as $entry) { + if ($entry["balanceType"] === 'closingBooked') { // available: closingBooked, interimAvailable $used_balance = $entry; break; } } return [ - 'id' => $account->data->id, - 'account_type' => $account->CONTAINER, - 'account_name' => $account->data->iban, - 'account_status' => $account->metadata->status, - 'account_number' => '**** ' . substr($account->data->iban, -7), - 'provider_account_id' => $account->data->iban, - 'provider_id' => $account->institution_id, - 'provider_name' => $account->institution->name, - 'nickname' => property_exists($account->data, 'owner_name') ? $account->data->owner_name : '', - 'current_balance' => (int) property_exists($used_balance, 'balanceAmount') ? $used_balance->balanceAmount->amount : 0, - 'account_currency' => property_exists($used_balance, 'balanceAmount') ? $used_balance->balanceAmount->currency : '', + 'id' => $nordigen_account->metadata["id"], + 'account_type' => "bank_account", // TODO: not creditCard + 'account_name' => $nordigen_account->data["iban"], + 'account_status' => $nordigen_account->metadata["status"], + 'account_number' => '**** ' . substr($nordigen_account->data["iban"], -7), + 'provider_account_id' => $nordigen_account->data["iban"], + 'provider_id' => $nordigen_account->institution["id"], + 'provider_name' => $nordigen_account->institution["name"], + 'nickname' => $nordigen_account->data?["ownerName"] ? $nordigen_account->data["ownerName"] : '', + 'current_balance' => (int) $used_balance ? $used_balance["balanceAmount"]["amount"] : 0, + 'account_currency' => $used_balance ? $used_balance["balanceAmount"]["currency"] : '', ]; + } } diff --git a/app/Http/Controllers/Bank/NordigenController.php b/app/Http/Controllers/Bank/NordigenController.php index 920519b375a2..d99a43ee2f49 100644 --- a/app/Http/Controllers/Bank/NordigenController.php +++ b/app/Http/Controllers/Bank/NordigenController.php @@ -16,6 +16,7 @@ use App\Http\Controllers\BaseController; use App\Http\Requests\Nortigen\CreateNortigenRequisitionRequest; use App\Http\Requests\Yodlee\YodleeAuthRequest; use App\Jobs\Bank\ProcessBankTransactionsNordigen; +use App\Models\Account; use App\Models\BankIntegration; use Illuminate\Http\Request; @@ -76,43 +77,50 @@ class NordigenController extends BaseController } - private function getAccounts($company, $token) + private function getAccounts(Account $account) { - $nordigen = new Nordigen($token); + if (!$account->bank_integration_nordigen_secret_id || !$account->bank_integration_nordigen_secret_key) + return response()->json(['message' => 'Not yet authenticated with Nordigen Bank Integration service'], 400); + + $nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key); $accounts = $nordigen->getAccounts(); - foreach ($accounts as $account) { + foreach ($account->companies() as $company) { - if (!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists()) { - $bank_integration = new BankIntegration(); - $bank_integration->company_id = $company->id; - $bank_integration->account_id = $company->account_id; - $bank_integration->user_id = $company->owner()->id; - $bank_integration->bank_account_id = $account['id']; - $bank_integration->bank_account_type = $account['account_type']; - $bank_integration->bank_account_name = $account['account_name']; - $bank_integration->bank_account_status = $account['account_status']; - $bank_integration->bank_account_number = $account['account_number']; - $bank_integration->provider_id = $account['provider_id']; - $bank_integration->provider_name = $account['provider_name']; - $bank_integration->nickname = $account['nickname']; - $bank_integration->balance = $account['current_balance']; - $bank_integration->currency = $account['account_currency']; - $bank_integration->from_date = now()->subYear(); + foreach ($accounts as $account) { + + if (!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists()) { + $bank_integration = new BankIntegration(); + $bank_integration->company_id = $company->id; + $bank_integration->account_id = $company->account_id; + $bank_integration->user_id = $company->owner()->id; + $bank_integration->bank_account_id = $account['id']; + $bank_integration->bank_account_type = $account['account_type']; + $bank_integration->bank_account_name = $account['account_name']; + $bank_integration->bank_account_status = $account['account_status']; + $bank_integration->bank_account_number = $account['account_number']; + $bank_integration->provider_id = $account['provider_id']; + $bank_integration->provider_name = $account['provider_name']; + $bank_integration->nickname = $account['nickname']; + $bank_integration->balance = $account['current_balance']; + $bank_integration->currency = $account['account_currency']; + $bank_integration->from_date = now()->subYear(); + + $bank_integration->save(); + } - $bank_integration->save(); } + + $company->account->bank_integrations->each(function ($bank_integration) use ($company) { + + ProcessBankTransactionsNordigen::dispatch($company->account, $bank_integration); + + }); + } - - $company->account->bank_integrations->each(function ($bank_integration) use ($company) { - - ProcessBankTransactionsNordigen::dispatch($company->account, $bank_integration); - - }); - } @@ -185,7 +193,7 @@ class NordigenController extends BaseController $account = auth()->user()->account; if (!$account->bank_integration_nordigen_secret_id || !$account->bank_integration_nordigen_secret_key) - return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400); + return response()->json(['message' => 'Not yet authenticated with Nordigen Bank Integration service'], 400); $nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key); return response()->json($nordigen->getInstitutions()); @@ -259,12 +267,7 @@ class NordigenController extends BaseController { $account = auth()->user()->account; - if (!$account->bank_integration_nordigen_secret_id || !$account->bank_integration_nordigen_secret_key) - return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400); - - // TODO: call job execution - - return response()->json(['message' => 'Refresh Cycle started. This may take a while...']); + return $this->getAccounts($account); } /** Creates a new requisition (oAuth like connection of bank-account) @@ -336,7 +339,7 @@ class NordigenController extends BaseController $account = auth()->user()->account; if (!$account->bank_integration_nordigen_secret_id || !$account->bank_integration_nordigen_secret_key) - return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400); + return response()->json(['message' => 'Not yet authenticated with Nordigen Bank Integration service'], 400); // TODO: should be moved to CreateNortigenRequisitionRequest // $this->validate($request, [ @@ -348,7 +351,11 @@ class NordigenController extends BaseController $nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key); - return response()->json(['result' => $nordigen->createRequisition($data['redirect'], $data['institutionId'])]); + return response()->json([ + 'result' => $nordigen->createRequisition($data['redirect'], $data['institutionId'], [ + "account_id" => $account->id, + ]) + ]); } /** @@ -417,190 +424,18 @@ class NordigenController extends BaseController }*/ public function confirm(Request $request) { - // TODO: use custom-token-auth from reference of request - } - /** - * Process Yodlee Refresh Webhook. - * - * - * @OA\Post( - * path="/api/v1/yodlee/refresh", - * operationId="yodleeRefreshWebhook", - * tags={"yodlee"}, - * summary="Processing webhooks from Yodlee", - * description="Notifies the system when a data point can be refreshed", - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Response( - * response=200, - * description="", - * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Credit"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - */ - /* - { - "event":{ - "info":"REFRESH.PROCESS_COMPLETED", - "loginName":"fri21", - "data":{ - "providerAccount":[ - { - "id":10995860, - "providerId":16441, - "isManual":false, - "createdDate":"2017-12-22T05:47:35Z", - "aggregationSource":"USER", - "status":"SUCCESS", - "requestId":"NSyMGo+R4dktywIu3hBIkc3PgWA=", - "dataset":[ - { - "name":"BASIC_AGG_DATA", - "additionalStatus":"AVAILABLE_DATA_RETRIEVED", - "updateEligibility":"ALLOW_UPDATE", - "lastUpdated":"2017-12-22T05:48:16Z", - "lastUpdateAttempt":"2017-12-22T05:48:16Z" - } - ] - } - ] - } - } - }*/ - public function refreshWebhook(Request $request) - { - //we should ignore this one - nlog("yodlee refresh"); - nlog($request->all()); + // TODO: should be moved to ConfirmNortigenRequisitionRequest + // $this->validate($request, [ + // 'account_id' => 'required|string|max:100', + // ]); - return response()->json(['message' => 'Success'], 200); + $data = $request->all(); - // + $account = Account::where('id', $data["ref"])->first(); - // return response()->json(['message' => 'Unauthorized'], 403); - } + return $this->getAccounts($account); - /* - { - "event":{ - "notificationId":"63c73475-4db5-49ef-8553-8303337ca7c3", - "info":"LATEST_BALANCE_UPDATES", - "loginName":"user1", - "data":{ - "providerAccountId":658552, - "latestBalanceEvent":[ - { - "accountId":12345, - "status":"SUCCESS" - }, - { - "accountId":12346, - "status":"FAILED" - } - ] - } - } - } - */ - public function balanceWebhook(Request $request) - { - - nlog("yodlee refresh"); - nlog($request->all()); - - return response()->json(['message' => 'Success'], 200); - - // - - // return response()->json(['message' => 'Unauthorized'], 403); - } - - /* - { - "event":{ - "data":[ - { - "autoRefresh":{ - "additionalStatus":"SCHEDULED", - "status":"ENABLED" - }, - "accountIds":[ - 1112645899, - 1112645898 - ], - "loginName":"YSL1555332811628", - "providerAccountId":11381459 - } - ], - "notificationTime":"2019-06-14T04:49:39Z", - "notificationId":"4e672150-156048777", - "info":"AUTO_REFRESH_UPDATES" - } - } - */ - public function refreshUpdatesWebhook(Request $request) - { - //notifies a user if there are problems with yodlee accessing the data - nlog("update refresh"); - nlog($request->all()); - - return response()->json(['message' => 'Success'], 200); - - // - - // return response()->json(['message' => 'Unauthorized'], 403); - } - - - /* - "event": { - "notificationId": "64b7ed1a-1530523285", - "info": "DATA_UPDATES.USER_DATA", - "data": { - "userCount": 1, - "fromDate": "2017-11-10T10:18:44Z", - "toDate": "2017-11-10T11:18:43Z", - "userData": [{ - "user": { - "loginName": "YSL1484052178554" - }, - "links": [{ - "methodType": "GET", - "rel": "getUserData", - "href": "dataExtracts/userData?fromDate=2017-11-10T10:18:44Z&toDate=2017-11-10T11:18:43Z&loginName=YSL1484052178554" - }] - }] - } - } - */ - public function dataUpdatesWebhook(Request $request) - { - //this is the main hook we use for notifications - - nlog("data refresh"); - nlog($request->all()); - - return response()->json(['message' => 'Success'], 200); - - // - - // return response()->json(['message' => 'Unauthorized'], 403); } }