whoami7 - Manager
:
/
home
/
qbizpnmr
/
arif.umairtax.com
/
app
/
Helpers
/
Bank
/
Nordigen
/
Upload File:
files >> /home/qbizpnmr/arif.umairtax.com/app/Helpers/Bank/Nordigen/Nordigen.php
<?php /** * Invoice Ninja (https://invoiceninja.com). * * @link https://github.com/invoiceninja/invoiceninja source repository * * @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license * * Documentation of Api-Usage: https://developer.gocardless.com/bank-account-data/overview * * Institutions: Are Banks or Payment-Providers, which manages bankaccounts. * * Accounts: Accounts are existing bank_accounts at a specific institution. * * Requisitions: Are registered/active user-flows to authenticate one or many accounts. After completition, the accoundId could be used to fetch data for this account. After the access expires, the user could create a new requisition to connect accounts again. */ namespace App\Helpers\Bank\Nordigen; use App\Models\Company; use App\Services\Email\Email; use App\Models\BankIntegration; use App\Services\Email\EmailObject; use Illuminate\Support\Arr; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Cache; use Illuminate\Mail\Mailables\Address; use App\Helpers\Bank\Nordigen\Transformer\AccountTransformer; use App\Helpers\Bank\Nordigen\Transformer\TransactionTransformer; class Nordigen { public bool $test_mode; // https://developer.gocardless.com/bank-account-data/sandbox public string $sandbox_institutionId = 'SANDBOXFINANCE_SFIN0000'; protected \Nordigen\NordigenPHP\API\NordigenClient $client; public function __construct() { $this->test_mode = config('ninja.nordigen.test_mode'); if (!(config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key'))) { throw new \Exception('missing nordigen credentials'); } $this->client = new \Nordigen\NordigenPHP\API\NordigenClient(config('ninja.nordigen.secret_id'), config('ninja.nordigen.secret_key')); $this->client->createAccessToken(); } // metadata-section for frontend public function getInstitutions() { if ($this->test_mode) { return [$this->client->institution->getInstitution($this->sandbox_institutionId)]; } return $this->client->institution->getInstitutions(); } /** * Get end user agreement details by ID. * * @return array{ * id: string, * created: string, * institution_id: string, * max_historical_days: int, * access_valid_for_days: int, * access_scope: string[], * accepted: string * } Agreement details */ public function getAgreement(string $euaId): array { return $this->client->endUserAgreement->getEndUserAgreement($euaId); } /** * Get a list of end user agreements * * @return array{ * id: string, * created: string, * institution_id: string, * max_historical_days: int, * access_valid_for_days: int, * access_scope: string[], * accepted: ?string, * }[] EndUserAgreement list */ public function firstValidAgreement(string $institutionId, int $accessDays, int $txDays): ?array { $requiredScopes = ['balances', 'details', 'transactions']; try { return Arr::first( $this->client->endUserAgreement->getEndUserAgreements()['results'], function (array $eua) use ($institutionId, $requiredScopes, $accessDays, $txDays): bool { $isNotExpired = !isset($eua['status']) || $eua['status'] !== 'EXPIRED'; return $eua['institution_id'] === $institutionId && $eua['accepted'] === null && $isNotExpired && $eua['max_historical_days'] >= $txDays && $eua['access_valid_for_days'] >= $accessDays && !array_diff($requiredScopes, $eua['access_scope'] ?? []); }, null ); } catch (\Exception $e) { $debug = "{$e->getMessage()} ({$e->getCode()})"; nlog("Nordigen: Unable to fetch End User Agreements for institution '{$institutionId}': {$debug}"); return null; } } /** * Create a new End User Agreement with the given parameters * * @param array{id: string, transaction_total_days: int, max_access_valid_for_days: int} $institution * * @throws \Nordigen\NordigenPHP\Exceptions\NordigenExceptions\NordigenException * * @return array{ * id: string, * created: string, * institution_id: string, * max_historical_days: int, * access_valid_for_days: int, * access_scope: string[], * accepted: string * } Agreement details */ public function createAgreement(array $institution, int $accessDays, int $transactionDays): array { $txDays = $transactionDays < 30 ? 30 : $transactionDays; $maxAccess = $institution['max_access_valid_for_days']; $maxTx = $institution['transaction_total_days']; return $this->client->endUserAgreement->createEndUserAgreement( accessValidForDays: $accessDays > $maxAccess ? $maxAccess : $accessDays, maxHistoricalDays: $txDays > $maxTx ? $maxTx : $txDays, institutionId: $institution['id'], ); } /** * Create a new Bank Requisition * * @param array{id: ?string} $institution, * @param array{id: ?string, transaction_total_days: int} $agreement */ public function createRequisition( string $redirect, array $institution, array $agreement, string $reference, string $userLanguage, ): array { if ($this->test_mode && $institution['id'] != $this->sandbox_institutionId) { throw new \Exception('invalid institutionId while in test-mode'); } return $this->client->requisition->createRequisition( $redirect, $institution['id'], $agreement['id'] ?? null, $reference, $userLanguage ); } /** * validAgreement * @param string $institution_id * @param array $_accounts * @return array|null * @todo - very expensive! */ public function validAgreement($institution_id, $_accounts) { $nc = new \App\Helpers\Bank\Nordigen\Http\NordigenClient($this->client->getAccessToken()); $requisitions = $nc->getAllRequisitions(); $requisition = $requisitions->filter(function($requisition) use ($institution_id, $_accounts){ if($requisition['institution_id'] == $institution_id && !empty(array_intersect($requisition['accounts'], $_accounts))){ return $requisition; } }); return $requisition->first()->toArray() ?? null; } public function getRequisition(string $requisitionId) { try { return $this->client->requisition->getRequisition($requisitionId); } catch (\Exception $e) { if (strpos($e->getMessage(), 'Invalid Requisition ID') !== false) { return false; } throw $e; } } // TODO: return null on not found public function getAccount(string $account_id) { try { $out = new \stdClass(); $out->metadata = $this->client->account($account_id)->getAccountMetaData(); $out->institution = $this->client->institution->getInstitution($out->metadata['institution_id']); // if($out->metadata['status'] == 'READY'){ // $out->data = $this->client->account($account_id)->getAccountDetails()['account']; // $out->balances = $this->client->account($account_id)->getAccountBalances()['balances']; // } // else{ $out->data = [ 'iban' => $out->metadata['iban'], 'ownerName' => $out->metadata['owner_name'], ]; $out->balances = [ [ 'balanceType' => '', 'balanceAmount' => [ 'amount' => 0, 'currency' => '', ], ], ]; // } $it = new AccountTransformer(); return $it->transform($out); } catch (\GuzzleHttp\Exception\ClientException $e) { $response = $e->getResponse(); $statusCode = $response->getStatusCode(); if ($statusCode === 429) { nlog("Nordigen Rate Limit hit for account {$account_id}"); return ['error' => 'Nordigen Institution Rate Limit Reached', 'code' => 429]; } } catch (\Exception $e) { nlog("Nordigen getAccount() failed => {$account_id} => " . $e->getMessage()); return ['error' => $e->getMessage(), 'requisition' => true, 'code' => 401]; } } /** * isAccountActive * * @param string $account_id * @return array */ public function isAccountActive(string $account_id): array { try { $account = $this->client->account($account_id)->getAccountMetaData(); if ($account['status'] != 'READY') { nlog("Nordigen account '{$account_id}' is not ready (status={$account['status']})"); } return $account; } catch (\Exception $e) { nlog("Nordigen:: AccountActiveStatus:: {$e->getMessage()} {$e->getCode()}"); if (strpos($e->getMessage(), 'Invalid Account ID') !== false) { ['status' => 'Invalid Account ID']; } return ['status' => 'EXPIRED']; } } /** * getTransactions * * @param string $accountId * @param string $dateFrom * @return array */ public function getTransactions(Company $company, string $accountId, string $dateFrom = null): array { $transactionResponse = $this->client->account($accountId)->getAccountTransactions($dateFrom); $it = new TransactionTransformer($company); return $it->transform($transactionResponse); } public function disabledAccountEmail(BankIntegration $bank_integration): void { $cache_key = "email_quota:{$bank_integration->company->company_key}:{$bank_integration->id}"; if (Cache::has($cache_key)) { return; } Cache::put($cache_key, true, 60 * 60 * 24); App::setLocale($bank_integration->company->getLocale()); $mo = new EmailObject(); $mo->subject = ctrans('texts.nordigen_requisition_subject'); $mo->body = ctrans('texts.nordigen_requisition_body'); $mo->text_body = ctrans('texts.nordigen_requisition_body'); $mo->company_key = $bank_integration->company->company_key; $mo->html_template = 'email.template.generic'; $mo->to = [new Address($bank_integration->company->owner()->email, $bank_integration->company->owner()->present()->name())]; $mo->email_template_body = 'nordigen_requisition_body'; $mo->email_template_subject = 'nordigen_requisition_subject'; Email::dispatch($mo, $bank_integration->company); } }
Copyright ©2021 || Defacer Indonesia