1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 05:02:36 +01:00

feat: institutions endpoint ready

fix: wrong variable names
fix: missing implementation in api-router
This commit is contained in:
paulwer 2023-12-04 08:14:52 +01:00
parent 71ae70ef83
commit 67f94b7fb9
8 changed files with 303 additions and 51 deletions

View File

@ -18,35 +18,7 @@ use App\Helpers\Bank\Nordigen\Transformer\AccountTransformer;
use App\Helpers\Bank\Nordigen\Transformer\IncomeTransformer;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
// Generate new access token. Token is valid for 24 hours
// Token is automatically injected into every response
$token = $client->createAccessToken();
// Get access token
$accessToken = $client->getAccessToken();
// Get refresh token
$refreshToken = $client->getRefreshToken();
// Exchange refresh token for new access token
$newToken = $client->refreshAccessToken($refreshToken);
// Get list of institutions by country. Country should be in ISO 3166 standard.
$institutions = $client->institution->getInstitutionsByCountry("LV");
// Institution id can be gathered from getInstitutions response.
// Example Revolut ID
$institutionId = "REVOLUT_REVOGB21";
$redirectUri = "https://nordigen.com";
// Initialize new bank connection session
$session = $client->initSession($institutionId, $redirectUri);
// Get link to authorize in the bank
// Authorize with your bank via this link, to gain access to account data
$link = $session["link"];
// requisition id is needed to get accountId in the next step
$requisitionId = $session["requisition_id"];
use Illuminate\Support\Facades\Log;
class Nordigen
{
@ -56,17 +28,20 @@ class Nordigen
protected \Nordigen\NordigenPHP\API\NordigenClient $client;
public function __construct(string $client_id, string $client_secret)
public function __construct(string $secret_id, string $secret_key)
{
$this->client = new \Nordigen\NordigenPHP\API\NordigenClient($client_id, $client_secret);
Log::info($secret_id);
Log::info($secret_key);
$this->client = new \Nordigen\NordigenPHP\API\NordigenClient($secret_id, $secret_key);
$this->client->createAccessToken(); // access_token is valid 24h -> so we dont have to implement a refresh-cycle
}
// metadata-section for frontend
public function getInstitutions()
{
if ($this->test_mode)
return (array) $this->client->institution->getInstitution($this->sandbox_institutionId);

View File

@ -13,6 +13,7 @@ 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\Yodlee\YodleeAuthRequest;
use App\Jobs\Bank\ProcessBankTransactionsNordigen;
use App\Models\BankIntegration;
@ -36,11 +37,11 @@ class NordigenController extends BaseController
//ensure user is enterprise!!
if ($company->account->bank_integration_nordigen_client_id && $company->account->bank_integration_nordigen_client_id) {
if ($company->account->bank_integration_nordigen_secret_id && $company->account->bank_integration_nordigen_secret_id) {
$flow = 'edit';
$token = $company->account->bank_integration_nordigen_client_id;
$token = $company->account->bank_integration_nordigen_secret_id;
} else {
@ -50,7 +51,7 @@ class NordigenController extends BaseController
$token = $response->user->loginName;
$company->account->bank_integration_nordigen_client_id = $token;
$company->account->bank_integration_nordigen_secret_id = $token;
$company->push();
@ -183,12 +184,241 @@ class NordigenController extends BaseController
{
$account = auth()->user()->account;
if (!$account->bank_integration_nordigen_client_id || !$account->bank_integration_nordigen_client_secret)
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);
$nordigen = new Nordigen($account->bank_integration_nordigen_client_id, $account->bank_integration_nordigen_client_secret);
$nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key);
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;
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...']);
}
/** Creates a new requisition (oAuth like connection of bank-account)
*
* @param CreateNortigenRequisitionRequest $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(Request $request) // TODO: error, when using class CreateNortigenRequisitionRequest
{
$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: should be moved to CreateNortigenRequisitionRequest
// $this->validate($request, [
// 'redirect' => 'required|string|max:1000',
// 'institutionId' => 'required|string|max:100',
// ]);
$data = $request->all();
$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'])]);
}
/**
* 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 confirm(Request $request)
{
// TODO: use custom-token-auth from reference of request
}
/**
* Process Yodlee Refresh Webhook.
*

View File

@ -586,10 +586,10 @@ class BankIntegrationController extends BaseController
private function refreshAccountsNordigen(Account $account)
{
if (!$account->bank_integration_nordigen_client_id || !$account->bank_integration_nordigen_client_secret)
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);
$nordigen = new Nordigen($account->bank_integration_nordigen_client_id, $account->bank_integration_nordigen_client_secret);
$nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key);
$accounts = $nordigen->getAccounts();
@ -664,8 +664,8 @@ class BankIntegrationController extends BaseController
if ($bank_integration->integration_type == BankIntegration::INTEGRATION_TYPE_YODLEE)
$this->removeAccountYodlee($account, $bank_integration);
else if ($bank_integration->integration_type == BankIntegration::INTEGRATION_TYPE_NORDIGEN)
$this->removeAccountNordigen($account, $bank_integration);
$this->removeAccountNordigen($account, $bank_integration);
$this->bank_integration_repo->delete($bank_integration);
return $this->itemResponse($bank_integration->fresh());
@ -682,10 +682,10 @@ class BankIntegrationController extends BaseController
private function removeAccountNordigen(Account $account, BankIntegration $bank_integration)
{
if (!$account->bank_integration_nordigen_client_id || !$account->bank_integration_nordigen_client_secret)
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);
$nordigen = new Nordigen($account->bank_integration_nordigen_client_id, $account->bank_integration_nordigen_client_secret);
$nordigen = new Nordigen($account->bank_integration_nordigen_secret_id, $account->bank_integration_nordigen_secret_key);
$nordigen->deleteAccount($bank_integration->bank_account_id);
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\Nortigen;
use App\Http\Requests\Request;
class CreateNortigenRequisitionRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'redirect' => 'required|string|max:100',
'institutionId' => 'required|string|max:100',
];
}
}

View File

@ -75,7 +75,7 @@ class ProcessBankTransactionsNordigen implements ShouldQueue
try {
$this->processTransactions();
} catch (\Exception $e) {
nlog("{$this->account->bank_integration_nordigen_client_id} - exited abnormally => " . $e->getMessage());
nlog("{$this->account->bank_integration_nordigen_secret_id} - exited abnormally => " . $e->getMessage());
return;
}
@ -90,7 +90,7 @@ class ProcessBankTransactionsNordigen implements ShouldQueue
private function processTransactions()
{
$nordigen = new Nordigen($this->account->bank_integration_nordigen_client_id, $this->account->bank_integration_nordigen_client_secret); // TODO: maybe implement credentials
$nordigen = new Nordigen($this->account->bank_integration_nordigen_secret_id, $this->account->bank_integration_nordigen_secret_key); // TODO: maybe implement credentials
if (!$nordigen->isAccountActive($this->bank_integration->bank_account_id)) {
$this->bank_integration->disabled_upstream = true;

View File

@ -69,7 +69,7 @@ class BankTransactionSync implements ShouldQueue
nlog("syncing transactions - nordigen");
Account::with('bank_integrations')->whereNotNull('bank_integration_nordigen_client_id')->andWhereNotNull('bank_integration_nordigen_client_secret')->cursor()->each(function ($account) {
Account::with('bank_integrations')->whereNotNull('bank_integration_nordigen_secret_id')->andWhereNotNull('bank_integration_nordigen_secret_key')->cursor()->each(function ($account) {
$account->bank_integrations()->where('integration_type', BankIntegration::INTEGRATION_TYPE_NORDIGEN)->andWhere('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account) {

View File

@ -60,8 +60,8 @@ class Account extends BaseModel
'set_react_as_default_ap',
'inapp_transaction_id',
'num_users',
'bank_integration_nordigen_client_id',
'bank_integration_nordigen_client_secret',
'bank_integration_nordigen_secret_id',
'bank_integration_nordigen_secret_key',
];
/**

View File

@ -18,6 +18,7 @@ use App\Http\Controllers\BankIntegrationController;
use App\Http\Controllers\BankTransactionController;
use App\Http\Controllers\BankTransactionRuleController;
use App\Http\Controllers\Bank\YodleeController;
use App\Http\Controllers\Bank\NordigenController;
use App\Http\Controllers\BaseController;
use App\Http\Controllers\ChartController;
use App\Http\Controllers\ClientController;
@ -103,7 +104,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_secret_check']], function
Route::post('api/v1/oauth_login', [LoginController::class, 'oauthApiLogin']);
});
Route::group(['middleware' => ['throttle:50,1','api_secret_check','email_db']], function () {
Route::group(['middleware' => ['throttle:50,1', 'api_secret_check', 'email_db']], function () {
Route::post('api/v1/login', [LoginController::class, 'apiLogin'])->name('login.submit')->middleware('throttle:20,1');
Route::post('api/v1/reset_password', [ForgotPasswordController::class, 'sendResetLinkEmail']);
});
@ -309,7 +310,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
Route::post('verify', [TwilioController::class, 'generate'])->name('verify.generate')->middleware('throttle:100,1');
Route::post('verify/confirm', [TwilioController::class, 'confirm'])->name('verify.confirm');
Route::resource('vendors', VendorController::class); // name = (vendors. index / create / show / update / destroy / edit
Route::post('vendors/bulk', [VendorController::class, 'bulk'])->name('vendors.bulk');
Route::put('vendors/{vendor}/upload', [VendorController::class, 'upload']);
@ -370,9 +371,15 @@ Route::post('api/v1/get_migration_account', [HostedMigrationController::class, '
Route::post('api/v1/confirm_forwarding', [HostedMigrationController::class, 'confirmForwarding'])->middleware('guest')->middleware('throttle:100,1');
Route::post('api/v1/process_webhook', [AppleController::class, 'process_webhook'])->middleware('throttle:1000,1');
Route::post('api/v1/confirm_purchase', [AppleController::class, 'confirm_purchase'])->middleware('throttle:1000,1');
Route::post('api/v1/yodlee/refresh', [YodleeController::class, 'refreshWebhook'])->middleware('throttle:100,1');
Route::post('api/v1/yodlee/data_updates', [YodleeController::class, 'dataUpdatesWebhook'])->middleware('throttle:100,1');
Route::post('api/v1/yodlee/refresh_updates', [YodleeController::class, 'refreshUpdatesWebhook'])->middleware('throttle:100,1');
Route::post('api/v1/yodlee/balance', [YodleeController::class, 'balanceWebhook'])->middleware('throttle:100,1');
Route::fallback([BaseController::class, 'notFound']);
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');
Route::fallback([BaseController::class, 'notFound']);