From d80ec520f967a203887b9fe7b3d5295a3c22493a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 10 Aug 2022 11:56:46 +1000 Subject: [PATCH] Fast link provisioning --- .../Yodlee/Transformer/AccountTransformer.php | 5 +++ app/Helpers/Bank/Yodlee/Yodlee.php | 28 ++++++++++----- .../Controllers/Bank/YodleeController.php | 34 ++++++++++++++++--- app/Http/Livewire/BillingPortalPurchase.php | 21 ++++++++++-- app/Jobs/Mail/NinjaMailer.php | 5 +-- app/Jobs/Util/ReminderJob.php | 2 +- app/Mail/Engine/InvoiceEmailEngine.php | 1 - app/Services/Invoice/UpdateReminder.php | 20 +++++------ .../2022_08_05_023357_bank_integration.php | 23 ++++++++++++- resources/views/bank/yodlee/auth.blade.php | 4 +-- tests/Feature/Bank/YodleeApiTest.php | 10 ++++++ 11 files changed, 118 insertions(+), 35 deletions(-) diff --git a/app/Helpers/Bank/Yodlee/Transformer/AccountTransformer.php b/app/Helpers/Bank/Yodlee/Transformer/AccountTransformer.php index 7065a2e8ab..d45eb6b60f 100644 --- a/app/Helpers/Bank/Yodlee/Transformer/AccountTransformer.php +++ b/app/Helpers/Bank/Yodlee/Transformer/AccountTransformer.php @@ -61,6 +61,7 @@ use App\Helpers\Bank\AccountTransformerInterface; ) */ + class AccountTransformer implements AccountTransformerInterface { @@ -86,6 +87,10 @@ class AccountTransformer implements AccountTransformerInterface 'account_name' => $account->accountName, 'account_status' => $account->accountStatus, 'account_number' => $account->accountNumber, + 'provider_account_id' => $account->providerAccountId, + 'provider_id' => $account->providerId, + 'provider_name' => $account->providerName, + 'nickname' => $account?->nickname, 'current_balance' => property_exists($account, 'currentBalance') ? $account->currentBalance->amount : 0, 'account_currency' => property_exists($account, 'currency') ? $account->currentBalance->currency : '', ]; diff --git a/app/Helpers/Bank/Yodlee/Yodlee.php b/app/Helpers/Bank/Yodlee/Yodlee.php index fa905d1673..e67ae4eb7b 100644 --- a/app/Helpers/Bank/Yodlee/Yodlee.php +++ b/app/Helpers/Bank/Yodlee/Yodlee.php @@ -14,6 +14,7 @@ namespace App\Helpers\Bank\Yodlee; use App\Helpers\Bank\Yodlee\Transformer\AccountTransformer; use App\Helpers\Bank\Yodlee\Transformer\IncomeTransformer; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Str; class Yodlee { @@ -22,9 +23,15 @@ class Yodlee private string $api_endpoint = 'https://production.api.yodlee.com/ysl'; - private string $test_api_endpoint = 'https://sandbox.api.yodlee.com/ysl'; + // private string $test_api_endpoint = 'https://sandbox.api.yodlee.com/ysl'; + + private string $test_api_endpoint = 'https://development.api.yodlee.com/ysl'; - public string $fast_track_url = 'https://fl4.sandbox.yodlee.com/authenticate/restserver/fastlink'; + //public string $test_fast_track_url = 'https://fl4.sandbox.yodlee.com/authenticate/restserver/fastlink'; + + public string $test_fast_track_url = 'https://fl4.preprod.yodlee.com/authenticate/USDevexPreProd3-449/fastlink?channelAppName=usdevexpreprod3'; + + public string $production_track_url = 'https://fl4.prod.yodlee.com/authenticate/USDevexProd3-331/fastlink?channelAppName=usdevexprod3'; protected string $client_id; @@ -47,6 +54,11 @@ class Yodlee } + public function getFastTrackUrl() + { + return $this->test_mode ? $this->test_fast_track_url : $this->production_track_url; + } + public function setTestMode() { $this->test_mode = true; @@ -56,9 +68,7 @@ class Yodlee public function getEndpoint() { - return $this->test_mode ? $this->test_api_endpoint : $this->api_endpoint; - } /** @@ -73,19 +83,19 @@ class Yodlee $user = $this->bank_account_id ?: $this->admin_name; $response = $this->bankFormRequest('/auth/token', 'post', [], ['loginName' => $user]); -//catch failures here - // nlog($response); + //catch failures here + nlog($response); return $response->token->accessToken; } - public function createUser() + public function createUser($company) { $token = $this->getAccessToken(true); $user['user'] = [ - 'loginName' => 'test123', + 'loginName' => Str::uuid(), ]; /* @@ -139,10 +149,10 @@ class Yodlee $response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/accounts", $params); - if($response->successful()){ $at = new AccountTransformer(); + nlog($response->object()); return $at->transform($response->object()); // return $response->object(); } diff --git a/app/Http/Controllers/Bank/YodleeController.php b/app/Http/Controllers/Bank/YodleeController.php index f90f2f2dff..03751bbc58 100644 --- a/app/Http/Controllers/Bank/YodleeController.php +++ b/app/Http/Controllers/Bank/YodleeController.php @@ -13,12 +13,13 @@ namespace App\Http\Controllers\Bank; use App\Helpers\Bank\Yodlee\Yodlee; use App\Http\Controllers\BaseController; +use App\Http\Requests\Yodlee\YodleeAuthRequest; use Illuminate\Http\Request; class YodleeController extends BaseController { - public function auth(Request $request) + public function auth(YodleeAuthRequest $request) { // create a user at this point @@ -26,11 +27,36 @@ class YodleeController extends BaseController //store the user_account_id on the accounts table - $yodlee = new Yodlee(true); + $yodlee = new Yodlee(); + $yodlee->setTestMode(); + + $company = $request->getCompany(); + + if($company->account->bank_integration_account_id){ + $flow = 'edit'; + $token = $company->account->bank_integration_account_id; + } + else{ + $flow = 'add'; + $response = $yodlee->createUser($company); + + $token = $response->user->loginName; + + $company->account->bank_integration_account_id = $token; + $company->push(); + + $yodlee = new Yodlee($token); + $yodlee->setTestMode(); + } + + if(!is_string($token)) + dd($token); $data = [ - 'access_token' => $yodlee->getAccessToken('sbMem62e1e69547bfb1'), - 'fasttrack_url' => $yodlee->fast_track_url + 'access_token' => $yodlee->getAccessToken(), + 'fasttrack_url' => $yodlee->getFastTrackUrl(), + 'config_name' => 'testninja', + 'flow' => $flow, ]; return view('bank.yodlee.auth', $data); diff --git a/app/Http/Livewire/BillingPortalPurchase.php b/app/Http/Livewire/BillingPortalPurchase.php index ebe0ca6c26..e4cfaaeef9 100644 --- a/app/Http/Livewire/BillingPortalPurchase.php +++ b/app/Http/Livewire/BillingPortalPurchase.php @@ -98,6 +98,8 @@ class BillingPortalPurchase extends Component */ public $payment_method_id; + private $user_coupon; + /** * List of steps that frontend form follows. * @@ -436,32 +438,45 @@ class BillingPortalPurchase extends Component */ public function updateQuantity(string $option): int { + $this->handleCoupon(); + if ($this->quantity == 1 && $option == 'decrement') { + $this->price = $this->price * 1; return $this->quantity; } - if ($this->quantity >= $this->subscription->max_seats_limit && $option == 'increment') { + if ($this->quantity > $this->subscription->max_seats_limit && $option == 'increment') { + $this->price = $this->price * $this->subscription->max_seats_limit; return $this->quantity; } if ($option == 'increment') { $this->quantity++; - $this->price = $this->subscription->promo_price * $this->quantity; + $this->price = $this->price * $this->quantity; return $this->quantity; } $this->quantity--; - $this->price = $this->subscription->promo_price * $this->quantity; + $this->price = $this->price * $this->quantity; return $this->quantity; } public function handleCoupon() { + + if($this->steps['discount_applied']){ + $this->price = $this->subscription->promo_price; + return; + } + if ($this->coupon == $this->subscription->promo_code) { $this->price = $this->subscription->promo_price; + $this->quantity = 1; $this->steps['discount_applied'] = true; } + else + $this->price = $this->subscription->price; } public function passwordlessLogin() diff --git a/app/Jobs/Mail/NinjaMailer.php b/app/Jobs/Mail/NinjaMailer.php index 726327dba8..0a0edbd21a 100644 --- a/app/Jobs/Mail/NinjaMailer.php +++ b/app/Jobs/Mail/NinjaMailer.php @@ -42,10 +42,7 @@ class NinjaMailer extends Mailable $ninja_mailable = $this->from(config('mail.from.address'), $from_name) ->subject($this->mail_obj->subject) - ->view($this->mail_obj->markdown, $this->mail_obj->data) - ->withSymfonyMessage(function ($message) { - $message->getHeaders()->addTextHeader('Tag', $this->mail_obj->tag); - }); + ->view($this->mail_obj->markdown, $this->mail_obj->data); if (property_exists($this->mail_obj, 'text_view')) { $ninja_mailable->text($this->mail_obj->text_view, $this->mail_obj->data); diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 4aef5a9a3a..29f5382e41 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -95,7 +95,7 @@ class ReminderJob implements ShouldQueue (Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) { $invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) { EmailEntity::dispatch($invitation, $invitation->company, $reminder_template); - nlog("Firing reminder email for invoice {$invoice->number}"); + nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}"); }); if ($invoice->invitations->count() > 0) { diff --git a/app/Mail/Engine/InvoiceEmailEngine.php b/app/Mail/Engine/InvoiceEmailEngine.php index b62f402ec0..000d2f7274 100644 --- a/app/Mail/Engine/InvoiceEmailEngine.php +++ b/app/Mail/Engine/InvoiceEmailEngine.php @@ -100,7 +100,6 @@ class InvoiceEmailEngine extends BaseEmailEngine $subject_template = $this->client->getSetting('email_subject_'.$this->reminder_template); } else { $subject_template = EmailTemplateDefaults::getDefaultTemplate('email_subject_'.$this->reminder_template, $this->client->locale()); - // $subject_template = $this->client->getSetting('email_subject_'.$this->reminder_template); } if (iconv_strlen($subject_template) == 0) { diff --git a/app/Services/Invoice/UpdateReminder.php b/app/Services/Invoice/UpdateReminder.php index 642c36b532..e324613bbe 100644 --- a/app/Services/Invoice/UpdateReminder.php +++ b/app/Services/Invoice/UpdateReminder.php @@ -51,7 +51,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder1_sent) && $this->settings->schedule_reminder1 == 'after_invoice_date') { - $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder1)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder1); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -61,7 +61,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder1_sent) && $this->invoice->due_date && $this->settings->schedule_reminder1 == 'before_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder1)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder1); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -71,7 +71,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder1_sent) && $this->invoice->due_date && $this->settings->schedule_reminder1 == 'after_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder1)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder1); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -80,7 +80,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder2_sent) && $this->settings->schedule_reminder2 == 'after_invoice_date') { - $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder2)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder2); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -90,7 +90,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder2_sent) && $this->invoice->due_date && $this->settings->schedule_reminder2 == 'before_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder2)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder2); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -100,7 +100,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder2_sent) && $this->invoice->due_date && $this->settings->schedule_reminder2 == 'after_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder2)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder2); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -109,7 +109,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder3_sent) && $this->settings->schedule_reminder3 == 'after_invoice_date') { - $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder3)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder3); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -119,7 +119,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder3_sent) && $this->invoice->due_date && $this->settings->schedule_reminder3 == 'before_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder3)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder3); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -129,7 +129,7 @@ class UpdateReminder extends AbstractService if (is_null($this->invoice->reminder3_sent) && $this->invoice->due_date && $this->settings->schedule_reminder3 == 'after_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder3)->addSeconds($offset); + $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder3); if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { $date_collection->push($reminder_date); @@ -150,7 +150,7 @@ class UpdateReminder extends AbstractService } if ($date_collection->count() >= 1 && $date_collection->sort()->first()->gte(now())) { - $this->invoice->next_send_date = $date_collection->sort()->first(); + $this->invoice->next_send_date = $date_collection->sort()->first()->addSeconds($offset); } else { $this->invoice->next_send_date = null; } diff --git a/database/migrations/2022_08_05_023357_bank_integration.php b/database/migrations/2022_08_05_023357_bank_integration.php index e3f0c32693..4c9564945f 100644 --- a/database/migrations/2022_08_05_023357_bank_integration.php +++ b/database/migrations/2022_08_05_023357_bank_integration.php @@ -40,6 +40,27 @@ return new class extends Migration Schema::table('accounts', function (Blueprint $table) { $table->text('bank_integration_account_id')->nullable(); }); + + Schema::create('bank_transactions', function (Blueprint $table){ + $table->id(); + $table->unsignedInteger('company_id'); + $table->unsignedInteger('user_id'); + + $table->unsignedBigInteger('transaction_id')->nullable(); + $table->decimal('amount', 20, 6); + $table->string('currency_code'); + $table->string('account_type'); + $table->unsignedInteger('category_id'); + $table->string('category_type'); + $table->date('date'); + $table->unsignedBigInteger('account_id'); + $table->text('description'); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade'); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); + + }); + } /** @@ -49,6 +70,6 @@ return new class extends Migration */ public function down() { - // + } }; diff --git a/resources/views/bank/yodlee/auth.blade.php b/resources/views/bank/yodlee/auth.blade.php index ffc005d53d..ac970ac3c6 100644 --- a/resources/views/bank/yodlee/auth.blade.php +++ b/resources/views/bank/yodlee/auth.blade.php @@ -27,11 +27,11 @@ 'click', function() { window.fastlink.open({ - flow: 'edit',//flow changes depending on what we are doing sometimes it could be add/edit etc etc + flow: '{{ $flow }}',//flow changes depending on what we are doing sometimes it could be add/edit etc etc fastLinkURL: '{{ $fasttrack_url }}', accessToken: 'Bearer {{ $access_token }}', params: { - configName : 'Aggregation' + configName : '{{ $config_name }}' }, onSuccess: function (data) { // will be called on success. For list of possible message, refer to onSuccess(data) Method. diff --git a/tests/Feature/Bank/YodleeApiTest.php b/tests/Feature/Bank/YodleeApiTest.php index 91fcc9e3f6..4cc37cb00b 100644 --- a/tests/Feature/Bank/YodleeApiTest.php +++ b/tests/Feature/Bank/YodleeApiTest.php @@ -73,6 +73,16 @@ class YodleeApiTest extends TestCase return str_contains($value->description, 'tinker'); }); + + $invoice = $transaction->first(function ($value, $key) { + + return str_contains($value->number, 'tinker'); + + }); + + $this->assertNotNull($invoice); + + } public function testYodleeInstance()