2022-07-28 06:09:13 +02:00
< ? php
/**
* Invoice Ninja ( https :// invoiceninja . com ) .
*
* @ link https :// github . com / invoiceninja / invoiceninja source repository
*
2024-04-12 06:15:41 +02:00
* @ copyright Copyright ( c ) 2024. Invoice Ninja LLC ( https :// invoiceninja . com )
2022-07-28 06:09:13 +02:00
*
* @ license https :// www . elastic . co / licensing / elastic - license
*/
namespace App\Http\Controllers\Bank ;
2023-08-10 01:34:52 +02:00
use App\Helpers\Bank\Yodlee\DTO\AccountSummary ;
2022-07-28 08:29:42 +02:00
use App\Helpers\Bank\Yodlee\Yodlee ;
2022-07-28 06:09:13 +02:00
use App\Http\Controllers\BaseController ;
2023-08-10 01:34:52 +02:00
use App\Http\Requests\Yodlee\YodleeAdminRequest ;
2022-08-10 03:56:46 +02:00
use App\Http\Requests\Yodlee\YodleeAuthRequest ;
2023-12-01 14:30:33 +01:00
use App\Jobs\Bank\ProcessBankTransactionsYodlee ;
2022-08-11 06:19:35 +02:00
use App\Models\BankIntegration ;
2022-07-28 06:09:13 +02:00
use Illuminate\Http\Request ;
class YodleeController extends BaseController
{
2022-08-10 03:56:46 +02:00
public function auth ( YodleeAuthRequest $request )
2022-07-28 06:09:13 +02:00
{
2022-08-10 03:56:46 +02:00
$yodlee = new Yodlee ();
$company = $request -> getCompany ();
2023-12-13 16:32:51 +01:00
if ( $company -> account -> bank_integration_account_id ) {
2022-08-10 03:56:46 +02:00
$flow = 'edit' ;
2022-08-15 22:03:12 +02:00
2023-12-13 16:32:51 +01:00
$token = $company -> account -> bank_integration_account_id ;
2023-12-01 14:30:33 +01:00
} else {
2022-08-10 03:56:46 +02:00
$flow = 'add' ;
2022-08-15 22:03:12 +02:00
2022-08-10 03:56:46 +02:00
$response = $yodlee -> createUser ( $company );
$token = $response -> user -> loginName ;
2023-12-13 16:32:51 +01:00
$company -> account -> bank_integration_account_id = $token ;
2022-08-15 22:03:12 +02:00
2022-08-10 03:56:46 +02:00
$company -> push ();
}
2023-12-01 14:30:33 +01:00
2022-08-10 11:49:27 +02:00
$yodlee = new Yodlee ( $token );
2022-08-10 03:56:46 +02:00
2023-02-16 02:36:09 +01:00
if ( $request -> has ( 'window_closed' ) && $request -> input ( " window_closed " ) == " true " ) {
2022-09-07 07:24:08 +02:00
$this -> getAccounts ( $company , $token );
2023-02-16 02:36:09 +01:00
}
2022-09-07 07:24:08 +02:00
2023-11-24 00:23:40 +01:00
$redirect_url = isset ( $request -> getTokenContent ()[ 'is_react' ]) && $request -> getTokenContent ()[ 'is_react' ] ? config ( 'ninja.react_url' ) : config ( 'ninja.app_url' );
2022-09-07 07:24:08 +02:00
2022-07-28 06:09:13 +02:00
$data = [
2022-08-10 03:56:46 +02:00
'access_token' => $yodlee -> getAccessToken (),
'fasttrack_url' => $yodlee -> getFastTrackUrl (),
2022-08-15 22:03:12 +02:00
'config_name' => config ( 'ninja.yodlee.config_name' ),
2022-08-10 03:56:46 +02:00
'flow' => $flow ,
2022-08-10 11:49:27 +02:00
'company' => $company ,
'account' => $company -> account ,
2022-09-07 07:24:08 +02:00
'completed' => $request -> has ( 'window_closed' ) ? true : false ,
2023-10-28 00:52:15 +02:00
'redirect_url' => $redirect_url ,
2022-07-28 06:09:13 +02:00
];
return view ( 'bank.yodlee.auth' , $data );
}
2022-09-07 07:24:08 +02:00
private function getAccounts ( $company , $token )
{
$yodlee = new Yodlee ( $token );
2023-12-01 14:30:33 +01:00
$accounts = $yodlee -> getAccounts ();
2022-09-07 07:24:08 +02:00
2023-12-01 14:30:33 +01:00
foreach ( $accounts as $account ) {
if ( ! BankIntegration :: where ( 'bank_account_id' , $account [ 'id' ]) -> where ( 'company_id' , $company -> id ) -> exists ()) {
2022-09-07 07:24:08 +02:00
$bank_integration = new BankIntegration ();
$bank_integration -> company_id = $company -> id ;
$bank_integration -> account_id = $company -> account_id ;
$bank_integration -> user_id = $company -> owner () -> id ;
$bank_integration -> bank_account_id = $account [ 'id' ];
$bank_integration -> bank_account_type = $account [ 'account_type' ];
$bank_integration -> bank_account_name = $account [ 'account_name' ];
$bank_integration -> bank_account_status = $account [ 'account_status' ];
$bank_integration -> bank_account_number = $account [ 'account_number' ];
$bank_integration -> provider_id = $account [ 'provider_id' ];
$bank_integration -> provider_name = $account [ 'provider_name' ];
$bank_integration -> nickname = $account [ 'nickname' ];
$bank_integration -> balance = $account [ 'current_balance' ];
$bank_integration -> currency = $account [ 'account_currency' ];
2022-09-21 13:05:12 +02:00
$bank_integration -> from_date = now () -> subYear ();
2024-03-21 02:48:49 +01:00
$bank_integration -> integration_type = BankIntegration :: INTEGRATION_TYPE_YODLEE ;
2023-10-24 03:50:10 +02:00
$bank_integration -> auto_sync = true ;
2022-09-07 07:24:08 +02:00
$bank_integration -> save ();
}
}
2022-09-21 13:03:04 +02:00
2023-12-19 08:37:04 +01:00
$company -> account -> bank_integrations -> where ( " integration_type " , BankIntegration :: INTEGRATION_TYPE_YODLEE ) -> where ( 'auto_sync' , true ) -> each ( function ( $bank_integration ) use ( $company ) { // TODO: filter to yodlee only
2023-12-24 10:02:45 +01:00
ProcessBankTransactionsYodlee :: dispatch ( $company -> account -> id , $bank_integration );
2022-09-21 13:03:04 +02:00
});
2022-11-05 02:27:01 +01:00
}
2023-12-01 14:30:33 +01:00
/**
2022-11-05 02:27:01 +01:00
* Process Yodlee Refresh Webhook .
*
*
* @ OA\Post (
* path = " /api/v1/yodlee/refresh " ,
* operationId = " yodleeRefreshWebhook " ,
* tags = { " yodlee " },
* summary = " Processing webhooks from Yodlee " ,
* description = " Notifies the system when a data point can be refreshed " ,
2023-12-10 08:18:31 +01:00
* @ OA\Parameter ( ref = " #/components/parameters/X-API-TOKEN " ),
2022-11-05 02:27:01 +01:00
* @ 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 " ),
* ),
* )
*/
2023-12-01 14:30:33 +01:00
/*
{
" 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 "
}
]
}
]
}
2022-11-05 02:27:01 +01:00
}
} */
public function refreshWebhook ( Request $request )
{
2023-12-01 14:30:33 +01:00
//we should ignore this one
2023-10-22 12:54:17 +02:00
// nlog("yodlee refresh");
// nlog($request->all());
2022-11-05 02:27:01 +01:00
return response () -> json ([ 'message' => 'Success' ], 200 );
2023-12-01 14:30:33 +01:00
2022-11-05 02:27:01 +01:00
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
2023-12-01 14:30:33 +01:00
/*
{
" event " : {
" notificationId " : " 63c73475-4db5-49ef-8553-8303337ca7c3 " ,
" info " : " LATEST_BALANCE_UPDATES " ,
" loginName " : " user1 " ,
" data " : {
" providerAccountId " : 658552 ,
" latestBalanceEvent " : [
{
" accountId " : 12345 ,
" status " : " SUCCESS "
},
{
" accountId " : 12346 ,
" status " : " FAILED "
}
]
}
}
}
*/
2022-11-05 02:27:01 +01:00
public function balanceWebhook ( Request $request )
{
nlog ( " yodlee refresh " );
nlog ( $request -> all ());
return response () -> json ([ 'message' => 'Success' ], 200 );
2023-12-01 14:30:33 +01:00
2022-11-05 02:27:01 +01:00
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
2023-12-01 14:30:33 +01:00
/*
{
" event " : {
" data " : [
{
" autoRefresh " : {
" additionalStatus " : " SCHEDULED " ,
" status " : " ENABLED "
},
" accountIds " : [
1112645899 ,
1112645898
],
" loginName " : " YSL1555332811628 " ,
" providerAccountId " : 11381459
}
],
" notificationTime " : " 2019-06-14T04:49:39Z " ,
" notificationId " : " 4e672150-156048777 " ,
" info " : " AUTO_REFRESH_UPDATES "
}
}
*/
2022-11-05 02:27:01 +01:00
public function refreshUpdatesWebhook ( Request $request )
{
2023-12-01 14:30:33 +01:00
//notifies a user if there are problems with yodlee accessing the data
2023-10-22 12:54:17 +02:00
// nlog("update refresh");
// nlog($request->all());
2022-11-05 02:27:01 +01:00
return response () -> json ([ 'message' => 'Success' ], 200 );
2023-12-01 14:30:33 +01:00
2022-11-05 02:27:01 +01:00
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
2023-12-01 14:30:33 +01:00
/*
" event " : {
" notificationId " : " 64b7ed1a-1530523285 " ,
" info " : " DATA_UPDATES.USER_DATA " ,
" data " : {
" userCount " : 1 ,
" fromDate " : " 2017-11-10T10:18:44Z " ,
" toDate " : " 2017-11-10T11:18:43Z " ,
" userData " : [{
" user " : {
" loginName " : " YSL1484052178554 "
},
" links " : [{
" methodType " : " GET " ,
" rel " : " getUserData " ,
" href " : " dataExtracts/userData?fromDate=2017-11-10T10:18:44Z&toDate=2017-11-10T11:18:43Z&loginName=YSL1484052178554 "
}]
2022-11-05 02:27:01 +01:00
}]
2023-12-01 14:30:33 +01:00
}
2022-11-05 02:27:01 +01:00
}
2023-12-01 14:30:33 +01:00
*/
2022-11-05 02:27:01 +01:00
public function dataUpdatesWebhook ( Request $request )
{
2023-12-01 14:30:33 +01:00
//this is the main hook we use for notifications
2022-11-05 02:27:01 +01:00
return response () -> json ([ 'message' => 'Success' ], 200 );
2023-12-01 14:30:33 +01:00
2022-11-05 02:27:01 +01:00
//
2022-09-21 13:03:04 +02:00
2022-11-05 02:27:01 +01:00
// return response()->json(['message' => 'Unauthorized'], 403);
2022-09-07 07:24:08 +02:00
}
2023-08-10 01:34:52 +02:00
public function accountStatus ( YodleeAdminRequest $request , $account_number )
{
/** @var \App\Models\User $user */
$user = auth () -> user ();
$bank_integration = BankIntegration :: query ()
2023-12-10 08:18:31 +01:00
-> withTrashed ()
-> where ( 'company_id' , $user -> company () -> id )
-> where ( 'account_id' , $account_number )
-> exists ();
2023-08-10 01:34:52 +02:00
2023-12-10 08:18:31 +01:00
if ( ! $bank_integration ) {
2023-08-10 01:34:52 +02:00
return response () -> json ([ 'message' => 'Account does not exist.' ], 400 );
2023-10-26 04:57:44 +02:00
}
2023-08-10 01:34:52 +02:00
2023-12-13 16:32:51 +01:00
$yodlee = new Yodlee ( $user -> account -> bank_integration_account_id );
2023-08-10 01:34:52 +02:00
$summary = $yodlee -> getAccountSummary ( $account_number );
2023-12-10 08:18:31 +01:00
2024-05-26 00:54:24 +02:00
//@todo remove laravel-data
// $transformed_summary = AccountSummary::from($summary[0]);
$transformed_summary = $this -> transformSummary ( $summary [ 0 ]);
2023-08-10 01:34:52 +02:00
return response () -> json ( $transformed_summary , 200 );
}
2024-05-26 00:54:24 +02:00
private function transformSummary ( $summary ) : array
{
$dto = new \stdClass ;
$dto -> id = $summary [ 'id' ] ? ? 0 ;
$dto -> account_type = $summary [ 'CONTAINER' ] ? ? '' ;
$dto -> account_status = $summary [ 'accountStatus' ] ? ? '' ;
$dto -> account_number = $summary [ 'accountNumber' ] ? ? '' ;
$dto -> provider_account_id = $summary [ 'providerAccountId' ] ? ? '' ;
$dto -> provider_id = $summary [ 'providerId' ] ? ? '' ;
$dto -> provider_name = $summary [ 'providerName' ] ? ? '' ;
$dto -> nickname = $summary [ 'nickname' ] ? ? '' ;
$dto -> account_name = $summary [ 'accountName' ] ? ? '' ;
$dto -> current_balance = $summary [ 'currentBalance' ][ 'amount' ] ? ? 0 ;
$dto -> account_currency = $summary [ 'currentBalance' ][ 'currency' ] ? ? 0 ;
return ( array ) $dto ;
}
2022-07-28 06:09:13 +02:00
}