diff --git a/app/Http/Controllers/Bank/NordigenController.php b/app/Http/Controllers/Bank/NordigenController.php index 9d5089c8e0a2..1e10f3ae8c2c 100644 --- a/app/Http/Controllers/Bank/NordigenController.php +++ b/app/Http/Controllers/Bank/NordigenController.php @@ -15,7 +15,6 @@ use App\Helpers\Bank\Nordigen\Nordigen; use App\Http\Controllers\BaseController; use App\Http\Requests\Nordigen\ConfirmNordigenBankIntegrationRequest; use App\Http\Requests\Nordigen\ConnectNordigenBankIntegrationRequest; -use App\Http\Requests\Yodlee\YodleeAuthRequest; use App\Jobs\Bank\ProcessBankTransactionsNordigen; use App\Models\BankIntegration; use App\Models\Company; @@ -26,163 +25,39 @@ use Nordigen\NordigenPHP\Exceptions\NordigenExceptions\NordigenException; class NordigenController extends BaseController { - /** - * 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 institutions(Request $request) - { - $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); - - $nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key); - return response()->json($nordigen->getInstitutions()); - } - - /** Creates a new requisition (oAuth like connection of bank-account) - * - * @param ConnectNordigenBankIntegrationRequest $request - * - * @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"), - * ), - * ) - */ - - /* TODO - { - "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 connect(ConnectNordigenBankIntegrationRequest $request) { $data = $request->all(); - - $context = Cache::get($data["one_time_token"]); + $context = $request->getTokenContent(); if (!$context || $context["context"] != "nordigen" || array_key_exists("requisitionId", $context)) - return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=one-time-token-invalid"); + return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=token-invalid"); - $company = Company::where('company_key', $context["company_key"])->first(); - $account = $company->account; + $company = $request->getCompany(); - if (!$account->bank_integration_nordigen_secret_id || !$account->bank_integration_nordigen_secret_key) + if (!$company->account->bank_integration_nordigen_secret_id || !$company->account->bank_integration_nordigen_secret_key) return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=account-config-invalid"); - $nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key); + $nordigen = new Nordigen($company->account->bank_integration_nordigen_secret_id, $company->account->bank_integration_nordigen_secret_key); + + // show bank_selection_screen, when institution_id is not present + if (!array_key_exists("institution_id", $data)) { + $data = [ + 'token' => $request->token, + 'context' => $context, + 'institutions' => $nordigen->getInstitutions(), + 'company' => $company, + 'account' => $company->account, + 'redirect' => config('ninja.app_url') . '/nordigen/connect', + ]; + + return view('bank.nordigen.connect', $data); + } + + // redirect to requisition flow try { - $requisition = $nordigen->createRequisition(config('ninja.app_url') . '/api/v1/nordigen/confirm', $data['institution_id'], "1"); + $requisition = $nordigen->createRequisition(config('ninja.app_url') . '/api/v1/nordigen/confirm', $data['institution_id'], $request->token); } catch (NordigenException $e) { // TODO: property_exists returns null in these cases... => why => therefore we just get unknown error everytime $responseBody is typeof GuzzleHttp\Psr7\Stream Log::error($e); $responseBody = $e->getResponse()->getBody(); @@ -190,8 +65,8 @@ class NordigenController extends BaseController if (property_exists($responseBody, "institution_id")) // provided institution_id was wrong return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=institution-invalid"); - else if (property_exists($responseBody, "reference")) // this error can occur, when a reference was used double or is invalid => therefor we suggest the frontend to use another one-time-token - return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=one-time-token-invalid"); + else if (property_exists($responseBody, "reference")) // this error can occur, when a reference was used double or is invalid => therefor we suggest the frontend to use another token + return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=token-invalid"); else return response()->redirectTo($data["redirect"] . "?action=nordigen_connect&status=failed&reason=unknown"); } @@ -200,7 +75,7 @@ class NordigenController extends BaseController if (array_key_exists("redirect", $data)) $context["redirect"] = $data["redirect"]; $context["requisitionId"] = $requisition["id"]; - Cache::put($data["one_time_token"], $context, 3600); + Cache::put($request->token, $context, 3600); return response()->redirectTo($requisition["link"]); } @@ -360,4 +235,79 @@ class NordigenController extends BaseController } + /** + * 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 institutions(Request $request) + { + $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); + + $nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key); + return response()->json($nordigen->getInstitutions()); + } + } diff --git a/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php b/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php index 639e7c0c546f..a35f9b7ab698 100644 --- a/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php +++ b/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php @@ -12,6 +12,9 @@ namespace App\Http\Requests\Nordigen; use App\Http\Requests\Request; +use App\Libraries\MultiDB; +use App\Models\Company; +use App\Models\User; use Cache; use Log; @@ -35,8 +38,7 @@ class ConnectNordigenBankIntegrationRequest extends Request public function rules() { return [ - 'institution_id' => 'required|string', - 'one_time_token' => 'required|string', // One Time Token + 'institution_id' => 'string', 'redirect' => 'string', // TODO: @turbo124 @todo validate, that this is a url without / at the end ]; } @@ -47,17 +49,42 @@ class ConnectNordigenBankIntegrationRequest extends Request $input = $this->all(); if (!array_key_exists('redirect', $input)) { - $context = Cache::get($input['one_time_token']); + $context = $this->getTokenContent(); - if (array_key_exists('is_react', $context)) + if ($context && array_key_exists('is_react', $context)) $input["redirect"] = $context["is_react"] ? config("ninja.react_url") : config("ninja.app_url"); else $input["redirect"] = config("ninja.app_url"); - Log::info($input); - $this->replace($input); } } + public function getTokenContent() + { + if ($this->state) { + $this->token = $this->state; + } + + $data = Cache::get($this->token); + + return $data; + } + + public function getContact() + { + MultiDB::findAndSetDbByCompanyKey($this->getTokenContent()['company_key']); + + return User::findOrFail($this->getTokenContent()['user_id']); + + } + + public function getCompany() + { + + MultiDB::findAndSetDbByCompanyKey($this->getTokenContent()['company_key']); + + return Company::where('company_key', $this->getTokenContent()['company_key'])->firstOrFail(); + + } } diff --git a/resources/views/bank/nordigen/connect.blade.php b/resources/views/bank/nordigen/connect.blade.php new file mode 100644 index 000000000000..5eafe708f3b4 --- /dev/null +++ b/resources/views/bank/nordigen/connect.blade.php @@ -0,0 +1,67 @@ +@extends('layouts.ninja') +@section('meta_title', ctrans('texts.new_bank_account')) + +@push('head') + + + +@endpush + +@section('body') + +
+ +@endsection + +@push('footer') + + + + + +@endpush \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index 3cd306fdaa4d..bd50851abd27 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/connect', [NordigenController::class, 'connect'])->middleware('throttle:100,1')->name('nordigen_connect'); Route::any('api/v1/nordigen/confirm', [NordigenController::class, 'confirm'])->middleware('throttle:100,1')->name('nordigen_callback'); Route::fallback([BaseController::class, 'notFound']); diff --git a/routes/web.php b/routes/web.php index 26ddc312bec6..32bf59ee956b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Auth\ForgotPasswordController; use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\Auth\ResetPasswordController; +use App\Http\Controllers\Bank\NordigenController; use App\Http\Controllers\Bank\YodleeController; use App\Http\Controllers\BaseController; use App\Http\Controllers\ClientPortal\ApplePayDomainController; @@ -18,7 +19,7 @@ use Illuminate\Support\Facades\Route; //Auth::routes(['password.reset' => false]); Route::get('/', [BaseController::class, 'flutterRoute'])->middleware('guest'); - // Route::get('self-update', [SelfUpdateController::class, 'update'])->middleware('guest'); +// Route::get('self-update', [SelfUpdateController::class, 'update'])->middleware('guest'); Route::get('setup', [SetupController::class, 'index'])->middleware('guest'); Route::post('setup', [SetupController::class, 'doSetup'])->middleware('guest'); @@ -54,8 +55,9 @@ Route::get('stripe/signup/{token}', [StripeConnectController::class, 'initialize Route::get('stripe/completed', [StripeConnectController::class, 'completed'])->name('stripe_connect.return'); Route::get('yodlee/onboard/{token}', [YodleeController::class, 'auth'])->name('yodlee.auth'); +Route::get('nordigen/connect/{token}', [NordigenController::class, 'connect'])->name('nordigen.connect'); Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [Checkout3dsController::class, 'index'])->middleware('domain_db')->name('checkout.3ds_redirect'); Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [Mollie3dsController::class, 'index'])->middleware('domain_db')->name('mollie.3ds_redirect'); Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', [GoCardlessController::class, 'ibpRedirect'])->middleware('domain_db')->name('gocardless.ibp_redirect'); -Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); \ No newline at end of file +Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']);