Attempt to reuse existing agreements
Implements changes from #10410 but using `Arr::first()` rather than a foreach loop to fix bugs returning invalid or expired agreements. If an agreement exists with at least the requested `$txDays` then that is used, otherwise a new one is created with the given parameters. If it fails, we error out because `createRequisition()` would fail regardless. Skips accepting EUAs: seems it's done automatically during requisition.
This commit is contained in:
parent
3204163e70
commit
f6a7c0ddda
|
|
@ -23,6 +23,7 @@ use App\Models\Company;
|
||||||
use App\Services\Email\Email;
|
use App\Services\Email\Email;
|
||||||
use App\Models\BankIntegration;
|
use App\Models\BankIntegration;
|
||||||
use App\Services\Email\EmailObject;
|
use App\Services\Email\EmailObject;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Mail\Mailables\Address;
|
use Illuminate\Mail\Mailables\Address;
|
||||||
|
|
@ -73,21 +74,90 @@ class Nordigen
|
||||||
* accepted: string
|
* accepted: string
|
||||||
* } Agreement details
|
* } Agreement details
|
||||||
*/
|
*/
|
||||||
public function getAgreement(string $euaId): array {
|
public function getAgreement(string $euaId): array
|
||||||
$eua = $this->client->endUserAgreement->getEndUserAgreement($euaId);
|
{
|
||||||
|
return $this->client->endUserAgreement->getEndUserAgreement($euaId);
|
||||||
|
}
|
||||||
|
|
||||||
return $eua;
|
/**
|
||||||
|
* 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 $txDays): ?array
|
||||||
|
{
|
||||||
|
$requiredScopes = ['balances', 'details', 'transactions'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Arr::first(
|
||||||
|
$this->client->endUserAgreement->getEndUserAgreements()['results'],
|
||||||
|
function (array $eua) use ($institutionId, $requiredScopes, $txDays): bool {
|
||||||
|
$expiresAt = $eua['accepted'] ? (new \DateTimeImmutable($eua['accepted']))->add(
|
||||||
|
new \DateInterval("P{$eua['access_valid_for_days']}D")
|
||||||
|
) : false;
|
||||||
|
|
||||||
|
return $eua['institution_id'] === $institutionId
|
||||||
|
&& $eua['accepted'] === null
|
||||||
|
&& $eua['max_historical_days'] >= $txDays
|
||||||
|
&& !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} $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
|
||||||
|
* }|null Agreement details
|
||||||
|
*/
|
||||||
|
public function createAgreement(array $institution, int $transactionDays): array
|
||||||
|
{
|
||||||
|
$txDays = $transactionDays < 30 ? 30 : $transactionDays;
|
||||||
|
$max = $institution['transaction_total_days'];
|
||||||
|
|
||||||
|
return $this->client->endUserAgreement->createEndUserAgreement(
|
||||||
|
maxHistoricalDays: $txDays > $max ? $max : $txDays,
|
||||||
|
institutionId: $institution['id'],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Bank Requisition
|
* Create a new Bank Requisition
|
||||||
*
|
*
|
||||||
* @param array{id: string} $institution
|
* @param array{id: string} $institution,
|
||||||
|
* @param array{id: string, transaction_total_days: int} $agreement
|
||||||
*/
|
*/
|
||||||
public function createRequisition(
|
public function createRequisition(
|
||||||
string $redirect,
|
string $redirect,
|
||||||
array $institution,
|
array $institution,
|
||||||
int $transactionDays,
|
array $agreement,
|
||||||
string $reference,
|
string $reference,
|
||||||
string $userLanguage,
|
string $userLanguage,
|
||||||
): array {
|
): array {
|
||||||
|
|
@ -95,71 +165,13 @@ class Nordigen
|
||||||
throw new \Exception('invalid institutionId while in test-mode');
|
throw new \Exception('invalid institutionId while in test-mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
$txDays = $transactionDays < 30 ? 30 : $transactionDays;
|
return $this->client->requisition->createRequisition(
|
||||||
$max = $institution['transaction_total_days'];
|
$redirect,
|
||||||
|
$institution['id'],
|
||||||
$eua = $this->client->endUserAgreement->createEndUserAgreement(
|
$agreement['id'] ?? null,
|
||||||
maxHistoricalDays: $txDays > $max ? $max : $txDays,
|
$reference,
|
||||||
institutionId: $institution['id'],
|
$userLanguage
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->client->requisition->createRequisition($redirect, $institution['id'], $eua['id'], $reference, $userLanguage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getExtendedEndUserAggreementId(string $institutionId): string|null
|
|
||||||
{
|
|
||||||
|
|
||||||
$endUserAggreements = null;
|
|
||||||
$endUserAgreement = null;
|
|
||||||
|
|
||||||
// try to fetch endUserAgreements
|
|
||||||
try {
|
|
||||||
$endUserAggreements = $this->client->endUserAgreement->getEndUserAgreements();
|
|
||||||
} catch (\Exception $e) { // not able to accept it
|
|
||||||
nlog("Nordigen: Was not able to fetch endUserAgreements. We continue with defaults to setup bank_integration. {$institutionId} {$e->getMessage()} {$e->getCode()}");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to find an existing valid endUserAgreement
|
|
||||||
foreach ($endUserAggreements["results"] as $row) {
|
|
||||||
$endUserAgreement = $row;
|
|
||||||
|
|
||||||
// Validate Institution
|
|
||||||
if ($endUserAgreement["institution_id"] != $institutionId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Validate Access Scopes
|
|
||||||
$requiredScopes = ["balances", "details", "transactions"];
|
|
||||||
if (isset($endUserAgreement['access_scope']) && array_diff($requiredScopes, $endUserAgreement['access_scope']))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// try to accept the endUserAgreement when not already accepted
|
|
||||||
if (empty($endUserAgreement["accepted"]))
|
|
||||||
try {
|
|
||||||
$this->client->endUserAgreement->acceptEndUserAgreement($endUserAgreement["id"], request()->userAgent(), request()->ip());
|
|
||||||
} catch (\Exception $e) { // not able to accept it
|
|
||||||
nlog("Nordigen: Was not able to confirm an existing outstanding endUserAgreement for this institution. We now try to find another or will create and confirm a new one. {$institutionId} {$endUserAgreement["id"]} {$e->getMessage()} {$e->getCode()}");
|
|
||||||
$endUserAgreement = null;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to create and accept an endUserAgreement
|
|
||||||
if (!$endUserAgreement)
|
|
||||||
try {
|
|
||||||
$endUserAgreement = $this->client->endUserAgreement->createEndUserAgreement($institutionId, ['details', 'balances', 'transactions'], 90, 180);
|
|
||||||
$this->client->endUserAgreement->acceptEndUserAgreement($endUserAgreement["id"], request()->userAgent(), request()->ip());
|
|
||||||
} catch (\Exception $e) { // not able to create this for this institution
|
|
||||||
nlog("Nordigen: Was not able to create and confirm a new endUserAgreement for this institution. We continue with defaults to setup bank_integration. {$institutionId} {$e->getMessage()} {$e->getCode()}");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $endUserAgreement["id"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequisition(string $requisitionId)
|
public function getRequisition(string $requisitionId)
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,25 @@ class NordigenController extends BaseController
|
||||||
$data['tx_days'] = $nordigen->getAgreement($euaId)['max_historical_days'];
|
$data['tx_days'] = $nordigen->getAgreement($euaId)['max_historical_days'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$txDays = $data['tx_days'] ?? 0;
|
||||||
|
|
||||||
|
$agreement = $nordigen->firstValidAgreement($institution['id'], $txDays)
|
||||||
|
?? $nordigen->createAgreement($institution, $txDays);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$debug = "{$e->getMessage()} ({$e->getCode()})";
|
||||||
|
|
||||||
|
nlog("Nordigen: Could not create an agreement with ${institution['name']}: {$debug}");
|
||||||
|
|
||||||
|
return $this->failed('eua-failure', $context, $company);
|
||||||
|
}
|
||||||
|
|
||||||
// redirect to requisition flow
|
// redirect to requisition flow
|
||||||
try {
|
try {
|
||||||
$requisition = $nordigen->createRequisition(
|
$requisition = $nordigen->createRequisition(
|
||||||
config('ninja.app_url') . '/nordigen/confirm',
|
config('ninja.app_url') . '/nordigen/confirm',
|
||||||
$institution,
|
$institution,
|
||||||
(int) ($data['tx_days'] ?? 0),
|
$agreement,
|
||||||
$request->token,
|
$request->token,
|
||||||
$lang,
|
$lang,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue