diff --git a/app/Helpers/Bank/Nordigen/Nordigen.php b/app/Helpers/Bank/Nordigen/Nordigen.php index a83bd5031067..e0718e01fb5a 100644 --- a/app/Helpers/Bank/Nordigen/Nordigen.php +++ b/app/Helpers/Bank/Nordigen/Nordigen.php @@ -16,9 +16,6 @@ namespace App\Helpers\Bank\Nordigen; use App\Exceptions\NordigenApiException; use App\Helpers\Bank\Nordigen\Transformer\AccountTransformer; use App\Helpers\Bank\Nordigen\Transformer\IncomeTransformer; -use Illuminate\Support\Facades\Http; -use Illuminate\Support\Str; -use Illuminate\Support\Facades\Log; class Nordigen { @@ -46,12 +43,12 @@ class Nordigen } // requisition-section - public function createRequisition(string $redirect, string $initutionId, string $nAccountId) + public function createRequisition(string $redirect, string $initutionId, string $reference) { if ($this->test_mode && $initutionId != $this->sandbox_institutionId) throw new \Exception('invalid institutionId while in test-mode'); - return $this->client->requisition->createRequisition($redirect, $initutionId, null, $nAccountId); // we dont reuse existing requisitions, to prevent double usage of them. see: deleteAccount + return $this->client->requisition->createRequisition($redirect, $initutionId, null, $reference); // we dont reuse existing requisitions, to prevent double usage of them. see: deleteAccount } public function getRequisition(string $requisitionId) @@ -59,6 +56,7 @@ class Nordigen return $this->client->requisition->getRequisition($requisitionId); } + // NOTE: this will only cleanup the requisitions from nordigen and not within the table: bank_integration_nordigen_requisitions public function cleanupRequisitions() { $requisitions = $this->client->requisition->getRequisitions(); @@ -73,7 +71,7 @@ class Nordigen } // account-section: these methods should be used to get data of connected accounts - public function getAccounts() + public function getAccounts(?array $requisitionIds) { // get all valid requisitions @@ -82,6 +80,10 @@ class Nordigen // fetch all valid accounts for activated requisitions $nordigen_accountIds = []; foreach ($requisitions["results"] as $requisition) { + // FILTER: for requisitionIds + if ($requisitionIds && !in_array($requisition["id"], $requisitionIds)) + continue; + foreach ($requisition["accounts"] as $accountId) { array_push($nordigen_accountIds, $accountId); } diff --git a/app/Http/Controllers/Bank/NordigenController.php b/app/Http/Controllers/Bank/NordigenController.php index a8eb96d5d025..88648adb0b09 100644 --- a/app/Http/Controllers/Bank/NordigenController.php +++ b/app/Http/Controllers/Bank/NordigenController.php @@ -13,13 +13,13 @@ namespace App\Http\Controllers\Bank; use App\Helpers\Bank\Nordigen\Nordigen; use App\Http\Controllers\BaseController; -use App\Http\Requests\Nortigen\CreateNortigenRequisitionRequest; +use App\Http\Requests\Nordigen\CreateNordigenRequisitionRequest; use App\Http\Requests\Yodlee\YodleeAuthRequest; use App\Jobs\Bank\ProcessBankTransactionsNordigen; -use App\Models\Account; use App\Models\BankIntegration; +use App\Models\Company; +use Cache; use Illuminate\Http\Request; -use Log; class NordigenController extends BaseController { @@ -77,58 +77,6 @@ class NordigenController extends BaseController return view('bank.yodlee.auth', $data); } - - private function getAccounts(Account $account) - { - 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(); - - $account->companies()->each(function ($company) use ($accounts) { - - foreach ($accounts as $account) { - - if (!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists()) { - - Log::info("Creating new BankIntegration"); - - $bank_integration = new BankIntegration(); - $bank_integration->integration_type = BankIntegration::INTEGRATION_TYPE_NORDIGEN; - $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(); - } - - } - - - $company->account->bank_integrations->each(function ($bank_integration) use ($company) { - - ProcessBankTransactionsNordigen::dispatch($company->account, $bank_integration); - - }); - - }); - - } - - /** * Process Nordigen Institutions GETTER. * @@ -204,80 +152,9 @@ class NordigenController extends BaseController return response()->json($nordigen->getInstitutions()); } - /** - * Process Nordigen Institutions GETTER. - * - * - * @OA\Post( - * path="/api/v1/nordigen/institutions", - * operationId="nordigenRefreshWebhook", - * tags={"nordigen"}, - * summary="Getting available institutions from nordigen", - * description="Used to determine the available institutions for sending and creating a new connect-link", - * @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 refresh(Request $request) - { - $account = auth()->user()->account; - - return $this->getAccounts($account); - } - /** Creates a new requisition (oAuth like connection of bank-account) * - * @param CreateNortigenRequisitionRequest $request + * @param CreateNordigenRequisitionRequest $request * * @OA\Post( * path="/api/v1/nordigen/institutions", @@ -339,27 +216,25 @@ class NordigenController extends BaseController } } }*/ - public function connect(Request $request) // TODO: error, when using class CreateNortigenRequisitionRequest + public function connect(CreateNordigenRequisitionRequest $request) // TODO: error, when using class CreateNordigenRequisitionRequest { + $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 Nordigen Bank Integration service'], 400); - // TODO: should be moved to CreateNortigenRequisitionRequest - // $this->validate($request, [ - // 'redirect' => 'required|string|max:1000', - // 'institutionId' => 'required|string|max:100', - // ]); - $data = $request->all(); + $context = Cache::get($data["context"]); + + if (!$context || $context->context != "nordigen") + return response()->json(['message' => 'Invalid context provided. Call /api/v1/one_time_token with context: \'nordigen\' first.'], 400); + $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'], [ - "account_id" => $account->id, - ]) + 'result' => $nordigen->createRequisition($data['redirect'], $data['institutionId'], $data["context"]) ]); } @@ -430,16 +305,62 @@ class NordigenController extends BaseController public function confirm(Request $request) { - // TODO: should be moved to ConfirmNortigenRequisitionRequest - // $this->validate($request, [ - // 'account_id' => 'required|string|max:100', - // ]); - $data = $request->all(); - $account = Account::where('id', $data["ref"])->first(); + $context = Cache::get($data["reference"]); - return $this->getAccounts($account); + if (!$context || $context->context != "nordigen") + return response()->json(['message' => 'Invalid context provided. Call /api/v1/one_time_token with context: \'nordigen\' first.'], 400); + + $company = Company::where('id', $context["company_key"])->first(); // TODO: get from one-time-token + + $account = $company->account; + + 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); + + $requisition = $nordigen->getRequisition($data["requisitionId"]); + + foreach ($requisition["accounts"] as $accountId) { + + $account = $nordigen->getAccount($accountId); + + if (!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists()) { + + $bank_integration = new BankIntegration(); + $bank_integration->integration_type = BankIntegration::INTEGRATION_TYPE_NORDIGEN; + $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(); + + } + + } + + + $company->account->bank_integrations->each(function ($bank_integration) use ($company) { + + ProcessBankTransactionsNordigen::dispatch($company->account, $bank_integration); + + }); + + // TODO: get current frontend-url from hash + response()->redirectTo(); } diff --git a/app/Http/Requests/Nordigen/CreateNortigenRequisitionRequest.php b/app/Http/Requests/Nordigen/CreateNordigenRequisitionRequest.php similarity index 82% rename from app/Http/Requests/Nordigen/CreateNortigenRequisitionRequest.php rename to app/Http/Requests/Nordigen/CreateNordigenRequisitionRequest.php index 85ea8bf3b1f7..008599f1425e 100644 --- a/app/Http/Requests/Nordigen/CreateNortigenRequisitionRequest.php +++ b/app/Http/Requests/Nordigen/CreateNordigenRequisitionRequest.php @@ -9,11 +9,11 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Http\Requests\Nortigen; +namespace App\Http\Requests\Nordigen; use App\Http\Requests\Request; -class CreateNortigenRequisitionRequest extends Request +class CreateNordigenRequisitionRequest extends Request { /** * Determine if the user is authorized to make this request. @@ -35,6 +35,7 @@ class CreateNortigenRequisitionRequest extends Request return [ 'redirect' => 'required|string|max:100', 'institutionId' => 'required|string|max:100', + 'context' => 'required|string|max:1000', // One Time Token ]; } } diff --git a/app/Jobs/Bank/ProcessBankTransactionsNordigen.php b/app/Jobs/Bank/ProcessBankTransactionsNordigen.php index 21b70875d8a8..0ef3f9dfee96 100644 --- a/app/Jobs/Bank/ProcessBankTransactionsNordigen.php +++ b/app/Jobs/Bank/ProcessBankTransactionsNordigen.php @@ -23,7 +23,6 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Carbon; class ProcessBankTransactionsNordigen implements ShouldQueue { @@ -96,6 +95,7 @@ class ProcessBankTransactionsNordigen implements ShouldQueue $this->bank_integration->disabled_upstream = true; $this->bank_integration->save(); $this->stop_loop = false; + // @turbo124 @todo send email for expired account return; } diff --git a/database/migrations/2023_11_26_082959_add_bank_integration_id.php b/database/migrations/2023_11_26_082959_add_bank_integration_id.php index 1e0fa0d8d6b9..1c5de199a325 100644 --- a/database/migrations/2023_11_26_082959_add_bank_integration_id.php +++ b/database/migrations/2023_11_26_082959_add_bank_integration_id.php @@ -31,34 +31,6 @@ return new class extends Migration { $table->string('bank_integration_nordigen_secret_key')->nullable(); }); - // TODO: assign requisitions, to determine, which requisitions belong to which account and which can be leaned up, when necessary - Schema::create('bank_integration_nordigen_requisitions', function (Blueprint $table) { - $table->id(); - $table->unsignedInteger('account_id'); - $table->unsignedInteger('company_id'); - $table->unsignedInteger('user_id'); - - $table->text('provider_name'); //providerName ie Chase - $table->bigInteger('provider_id'); //id of the bank - $table->bigInteger('bank_account_id'); //id - $table->text('bank_account_name')->nullable(); //accountName - $table->text('bank_account_number')->nullable(); //accountNumber - $table->text('bank_account_status')->nullable(); //accountStatus - $table->text('bank_account_type')->nullable(); //CONTAINER - $table->decimal('balance', 20, 6)->default(0); //currentBalance.amount - $table->text('currency')->nullable(); //currentBalance.currency - $table->text('nickname')->default(''); //accountName - $table->date('from_date')->nullable(); - - $table->boolean('is_deleted')->default(0); - - $table->timestamps(6); - $table->softDeletes('deleted_at', 6); - - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade'); - $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade')->onUpdate('cascade'); - $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); - }); } /** diff --git a/routes/api.php b/routes/api.php index 7e3a78cec209..3109cc001dd8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -378,7 +378,6 @@ Route::post('api/v1/yodlee/refresh_updates', [YodleeController::class, 'refreshU Route::post('api/v1/yodlee/balance', [YodleeController::class, 'balanceWebhook'])->middleware('throttle:100,1'); Route::get('api/v1/nordigen/institutions', [NordigenController::class, 'institutions'])->middleware('throttle:100,1')->middleware('token_auth')->name('nordigen_institutions'); -Route::any('api/v1/nordigen/refresh', [NordigenController::class, 'refresh'])->middleware('throttle:100,1')->middleware('token_auth')->name('nordigen_refresh'); Route::post('api/v1/nordigen/connect', [NordigenController::class, 'connect'])->middleware('throttle:100,1')->middleware('token_auth')->name('nordigen_connect'); Route::any('api/v1/nordigen/callback', [NordigenController::class, 'callback'])->middleware('throttle:100,1')->name('nordigen_callback');