Merge pull request #11170 from turbo124/v5-develop

Fixes for incorrect Carbon method
This commit is contained in:
David Bomba 2025-08-08 11:48:49 +10:00 committed by GitHub
commit 1d5ed436cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 114 additions and 133 deletions

View File

@ -1 +1 @@
5.12.10 5.12.11

View File

@ -85,10 +85,10 @@ class Kernel extends ConsoleKernel
// Run for 26 hours starting from UTC 10:00 on last day of month // Run for 26 hours starting from UTC 10:00 on last day of month
// This covers the transition period when timezones move to next month // This covers the transition period when timezones move to next month
if ($now->isLastOfMonth()) { if ($now->isSameDay($now->copy()->endOfMonth())) {
// Start at UTC 10:00 (when UTC+14 moves to next day) // Start at UTC 10:00 (when UTC+14 moves to next day)
return $hour >= 10; return $hour >= 10;
} elseif ($now->isFirstOfMonth()) { } elseif ($now->isSameDay($now->copy()->startOfMonth())) {
// Continue until UTC 12:00 (when UTC-12 moves to next day) // Continue until UTC 12:00 (when UTC-12 moves to next day)
return $hour <= 12; return $hour <= 12;
} }

View File

@ -8,15 +8,14 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class Client class NordigenClient
{ {
private string $baseUrl; private string $baseUrl = 'https://bankaccountdata.gocardless.com/api/v2';
private string $accessToken;
private PendingRequest $httpClient; private PendingRequest $httpClient;
public function __construct(string $baseUrl, string $accessToken) public function __construct(private string $accessToken)
{ {
$this->baseUrl = rtrim($baseUrl, '/');
$this->accessToken = $accessToken; $this->accessToken = $accessToken;
$this->httpClient = Http::withHeaders([ $this->httpClient = Http::withHeaders([
'Authorization' => "Bearer {$this->accessToken}", 'Authorization' => "Bearer {$this->accessToken}",
@ -39,7 +38,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/requisitions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/requisitions/", $params);
return $this->handlePaginatedResponse($response, 'requisitions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -94,6 +93,7 @@ class Client
do { do {
$requisitions = $this->getRequisitions($limit, $offset); $requisitions = $this->getRequisitions($limit, $offset);
nlog($requisitions);
if ($requisitions->isEmpty()) { if ($requisitions->isEmpty()) {
break; break;
} }
@ -122,7 +122,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/agreements/", $params); $response = $this->httpClient->get("{$this->baseUrl}/agreements/", $params);
return $this->handlePaginatedResponse($response, 'agreements'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -198,14 +198,11 @@ class Client
*/ */
public function getInstitutions(int $limit = 100, ?string $offset = null): Collection public function getInstitutions(int $limit = 100, ?string $offset = null): Collection
{ {
$params = ['limit' => $limit]; $params = [];
if ($offset) {
$params['offset'] = $offset;
}
$response = $this->httpClient->get("{$this->baseUrl}/institutions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/institutions/", $params);
return $this->handlePaginatedResponse($response, 'institutions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -230,7 +227,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/institutions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/institutions/", $params);
return $this->handlePaginatedResponse($response, 'institutions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -273,7 +270,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/enduser-agreements/", $params); $response = $this->httpClient->get("{$this->baseUrl}/enduser-agreements/", $params);
return $this->handlePaginatedResponse($response, 'enduser_agreements'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -330,7 +327,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/accounts/", $params); $response = $this->httpClient->get("{$this->baseUrl}/accounts/", $params);
return $this->handlePaginatedResponse($response, 'accounts'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -447,7 +444,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params);
return $this->handlePaginatedResponse($response, 'transactions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -475,7 +472,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/transactions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/transactions/", $params);
return $this->handlePaginatedResponse($response, 'transactions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -527,7 +524,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params);
return $this->handlePaginatedResponse($response, 'transactions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -550,7 +547,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params);
return $this->handlePaginatedResponse($response, 'transactions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -573,7 +570,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params);
return $this->handlePaginatedResponse($response, 'transactions'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -598,7 +595,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params); $response = $this->httpClient->get("{$this->baseUrl}/accounts/{$accountId}/transactions/", $params);
return $this->handlePaginatedResponse($response, 'transactions'); return $this->handlePaginatedResponse($response);
} }
// ==================== PAYMENTS ==================== // ==================== PAYMENTS ====================
@ -615,7 +612,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/payments/", $params); $response = $this->httpClient->get("{$this->baseUrl}/payments/", $params);
return $this->handlePaginatedResponse($response, 'payments'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -672,7 +669,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/mandates/", $params); $response = $this->httpClient->get("{$this->baseUrl}/mandates/", $params);
return $this->handlePaginatedResponse($response, 'mandates'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -729,7 +726,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/refunds/", $params); $response = $this->httpClient->get("{$this->baseUrl}/refunds/", $params);
return $this->handlePaginatedResponse($response, 'refunds'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -766,7 +763,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/events/", $params); $response = $this->httpClient->get("{$this->baseUrl}/events/", $params);
return $this->handlePaginatedResponse($response, 'events'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -793,7 +790,7 @@ class Client
$response = $this->httpClient->get("{$this->baseUrl}/webhooks/", $params); $response = $this->httpClient->get("{$this->baseUrl}/webhooks/", $params);
return $this->handlePaginatedResponse($response, 'webhooks'); return $this->handlePaginatedResponse($response);
} }
/** /**
@ -841,20 +838,16 @@ class Client
/** /**
* Handle paginated response * Handle paginated response
*/ */
private function handlePaginatedResponse(Response $response, string $dataKey): Collection private function handlePaginatedResponse(Response $response): Collection
{ {
if (!$response->successful()) { if (!$response->successful()) {
$this->logError('Paginated request failed', $response); $this->logError('Paginated request failed', $response);
return collect(); return collect();
} }
$data = $response->json(); $data = $response->json()['results'];
if (!isset($data[$dataKey])) { return collect($data);
return collect();
}
return collect($data[$dataKey]);
} }
/** /**
@ -875,7 +868,8 @@ class Client
*/ */
private function logError(string $message, Response $response): void private function logError(string $message, Response $response): void
{ {
Log::error($message, [ nlog([
'message' => $message,
'status' => $response->status(), 'status' => $response->status(),
'body' => $response->body(), 'body' => $response->body(),
'headers' => $response->headers() 'headers' => $response->headers()

View File

@ -177,6 +177,21 @@ class Nordigen
); );
} }
public function validAgreement($institution_id, $_accounts)
{
$nc = new \App\Helpers\Bank\Nordigen\Http\NordigenClient($this->client->getAccessToken());
$requisitions = $nc->getAllRequisitions();
$requisitions->filter(function($requisition) use ($institution_id, $_accounts){
if($requisition['institution_id'] == $institution_id && !empty(array_intersect($requisition['accounts'], $_accounts))){
return $requisition;
}
});
}
public function getRequisition(string $requisitionId) public function getRequisition(string $requisitionId)
{ {
try { try {
@ -196,11 +211,30 @@ class Nordigen
try { try {
$out = new \stdClass(); $out = new \stdClass();
$out->data = $this->client->account($account_id)->getAccountDetails()['account'];
$out->metadata = $this->client->account($account_id)->getAccountMetaData(); $out->metadata = $this->client->account($account_id)->getAccountMetaData();
$out->balances = $this->client->account($account_id)->getAccountBalances()['balances'];
$out->institution = $this->client->institution->getInstitution($out->metadata['institution_id']); $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(); $it = new AccountTransformer();
return $it->transform($out); return $it->transform($out);

View File

@ -139,7 +139,7 @@ class TransactionTransformer implements BankRevenueInterface
(array_key_exists('creditorName', $transaction) ? (array_key_exists('creditorName', $transaction) ?
$transaction['creditorName'] : null); $transaction['creditorName'] : null);
return [ $data = [
'transaction_id' => 0, 'transaction_id' => 0,
'nordigen_transaction_id' => $transactionId, 'nordigen_transaction_id' => $transactionId,
'amount' => abs($amount), 'amount' => abs($amount),
@ -153,8 +153,16 @@ class TransactionTransformer implements BankRevenueInterface
'base_type' => $base_type, 'base_type' => $base_type,
]; ];
// $data['currency_code'] = $this->makeHash($data);
return $data;
} }
// private function makeHash($data)
// {
// return hash('sha1', $data['amount'].$data['date'].$data['description'].$data['participant'].$data['participant_name'].$data['base_type']);
// }
private function convertCurrency(string $code) private function convertCurrency(string $code)
{ {

View File

@ -1,63 +0,0 @@
<?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
*/
namespace App\Http\Controllers\Bank;
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\Jobs\Bank\ProcessBankTransactionsNordigen;
use App\Models\BankIntegration;
use App\Models\Company;
use App\Utils\Ninja;
use Cache;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Nordigen\NordigenPHP\Exceptions\NordigenExceptions\NordigenException;
class NordigenController extends BaseController
{
/**
* Handles the initial bank connection flow
*/
public function connect(ConnectNordigenBankIntegrationRequest $request): View|RedirectResponse
{
}
/**
* Handles reconnects confirm / requisition updates
*
*/
public function confirm(ConfirmNordigenBankIntegrationRequest $request): View|RedirectResponse
{
}
/**
* Returns the list of available banking institutions from Nordigen
*
*/
public function institutions(Request $request): JsonResponse
{
if (!(config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key'))) {
return response()->json(['message' => 'Not yet authenticated with Nordigen Bank Integration service'], 400);
}
$nordigen = new Nordigen();
return response()->json($nordigen->getInstitutions());
}
}

View File

@ -46,6 +46,10 @@ class ConnectNordigenBankIntegrationRequest extends Request
$input['institution_id'] = $context['institution_id']; $input['institution_id'] = $context['institution_id'];
} }
if(isset($context['bank_account_id'])){
$input['bank_account_id'] = $context['bank_account_id'];
}
$input['redirect'] = ($context['is_react'] ?? false) $input['redirect'] = ($context['is_react'] ?? false)
? config('ninja.react_url') . '/#/settings/bank_accounts' ? config('ninja.react_url') . '/#/settings/bank_accounts'
: config('ninja.app_url'); : config('ninja.app_url');

View File

@ -90,7 +90,8 @@ class BankTransaction extends BaseModel
'vendor_id', 'vendor_id',
'amount', 'amount',
'participant', 'participant',
'participant_name' 'participant_name',
'currency_code'
]; ];

55
composer.lock generated
View File

@ -2552,20 +2552,20 @@
}, },
{ {
"name": "elasticsearch/elasticsearch", "name": "elasticsearch/elasticsearch",
"version": "v8.18.0", "version": "v8.19.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/elastic/elasticsearch-php.git", "url": "https://github.com/elastic/elasticsearch-php.git",
"reference": "df8ee73046c688ee9ce2d69cb5c54a03ca38cc5c" "reference": "1771284cb43a7b653634d418b6f5f0ec84ff8a6d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/df8ee73046c688ee9ce2d69cb5c54a03ca38cc5c", "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/1771284cb43a7b653634d418b6f5f0ec84ff8a6d",
"reference": "df8ee73046c688ee9ce2d69cb5c54a03ca38cc5c", "reference": "1771284cb43a7b653634d418b6f5f0ec84ff8a6d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"elastic/transport": "^8.10", "elastic/transport": "^8.11",
"guzzlehttp/guzzle": "^7.0", "guzzlehttp/guzzle": "^7.0",
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"psr/http-client": "^1.0", "psr/http-client": "^1.0",
@ -2603,9 +2603,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/elastic/elasticsearch-php/issues", "issues": "https://github.com/elastic/elasticsearch-php/issues",
"source": "https://github.com/elastic/elasticsearch-php/tree/v8.18.0" "source": "https://github.com/elastic/elasticsearch-php/tree/v8.19.0"
}, },
"time": "2025-05-02T10:38:56+00:00" "time": "2025-08-06T16:58:06+00:00"
}, },
{ {
"name": "endroid/qr-code", "name": "endroid/qr-code",
@ -5534,16 +5534,16 @@
}, },
{ {
"name": "laravel/octane", "name": "laravel/octane",
"version": "v2.12.0", "version": "v2.12.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/octane.git", "url": "https://github.com/laravel/octane.git",
"reference": "d606f3dffc785032f11c23a017334c99800f2e40" "reference": "4ca38b90d76f31b8c1e27873316c2db34450151c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/octane/zipball/d606f3dffc785032f11c23a017334c99800f2e40", "url": "https://api.github.com/repos/laravel/octane/zipball/4ca38b90d76f31b8c1e27873316c2db34450151c",
"reference": "d606f3dffc785032f11c23a017334c99800f2e40", "reference": "4ca38b90d76f31b8c1e27873316c2db34450151c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5620,7 +5620,7 @@
"issues": "https://github.com/laravel/octane/issues", "issues": "https://github.com/laravel/octane/issues",
"source": "https://github.com/laravel/octane" "source": "https://github.com/laravel/octane"
}, },
"time": "2025-07-18T15:50:14+00:00" "time": "2025-07-25T15:03:05+00:00"
}, },
{ {
"name": "laravel/prompts", "name": "laravel/prompts",
@ -8300,29 +8300,29 @@
}, },
{ {
"name": "nette/utils", "name": "nette/utils",
"version": "v4.0.7", "version": "v4.0.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nette/utils.git", "url": "https://github.com/nette/utils.git",
"reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2" "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2", "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede",
"reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2", "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "8.0 - 8.4" "php": "8.0 - 8.5"
}, },
"conflict": { "conflict": {
"nette/finder": "<3", "nette/finder": "<3",
"nette/schema": "<1.2.2" "nette/schema": "<1.2.2"
}, },
"require-dev": { "require-dev": {
"jetbrains/phpstorm-attributes": "dev-master", "jetbrains/phpstorm-attributes": "^1.2",
"nette/tester": "^2.5", "nette/tester": "^2.5",
"phpstan/phpstan": "^1.0", "phpstan/phpstan-nette": "^2.0@stable",
"tracy/tracy": "^2.9" "tracy/tracy": "^2.9"
}, },
"suggest": { "suggest": {
@ -8340,6 +8340,9 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Nette\\": "src"
},
"classmap": [ "classmap": [
"src/" "src/"
] ]
@ -8380,9 +8383,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nette/utils/issues", "issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.0.7" "source": "https://github.com/nette/utils/tree/v4.0.8"
}, },
"time": "2025-06-03T04:55:08+00:00" "time": "2025-08-06T21:43:34+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
@ -8823,16 +8826,16 @@
}, },
{ {
"name": "open-telemetry/context", "name": "open-telemetry/context",
"version": "1.2.1", "version": "1.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/opentelemetry-php/context.git", "url": "https://github.com/opentelemetry-php/context.git",
"reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020" "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/1eb2b837ee9362db064a6b65d5ecce15a9f9f020", "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc",
"reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020", "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -8878,7 +8881,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php" "source": "https://github.com/open-telemetry/opentelemetry-php"
}, },
"time": "2025-05-07T23:36:50+00:00" "time": "2025-08-04T03:25:06+00:00"
}, },
{ {
"name": "paragonie/constant_time_encoding", "name": "paragonie/constant_time_encoding",

View File

@ -17,8 +17,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => env('APP_VERSION', '5.12.10'), 'app_version' => env('APP_VERSION', '5.12.11'),
'app_tag' => env('APP_TAG', '5.12.10'), 'app_tag' => env('APP_TAG', '5.12.11'),
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false), 'api_secret' => env('API_SECRET', false),