Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable subscriptions #1461

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/Exceptions/NoCustomerPortalSetException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

class NoCustomerPortalSetException extends Exception
{
}
9 changes: 9 additions & 0 deletions app/Exceptions/NoLicenceKeyEncryptionSetException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

class NoLicenceKeyEncryptionSetException extends Exception
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use App\Models\Company\CompanyInvoice;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use App\Http\ViewHelpers\Adminland\AdminBillingViewHelper;
use App\Services\Company\Adminland\Subscription\ActivateLicenceKey;

class AdminBillingController extends Controller
{
Expand All @@ -28,7 +29,7 @@ public function index(Request $request)
$loggedCompany = InstanceHelper::getLoggedCompany();

return Inertia::render('Adminland/Billing/Index', [
'invoices' => AdminBillingViewHelper::index($loggedCompany),
'data' => AdminBillingViewHelper::index($loggedCompany),
'notifications' => NotificationHelper::getNotifications(InstanceHelper::getLoggedEmployee()),
]);
}
Expand Down Expand Up @@ -61,4 +62,31 @@ public function show(Request $request, int $companyId, int $invoiceId)
'notifications' => NotificationHelper::getNotifications(InstanceHelper::getLoggedEmployee()),
]);
}

/**
* Store the licence key.
*
* @param Request $request
* @param int $companyId
* @return mixed
*/
public function store(Request $request, int $companyId)
{
if (! config('officelife.enable_paid_plan')) {
return redirect('home');
}

$company = InstanceHelper::getLoggedCompany();

$data = [
'company_id' => $company->id,
'licence_key' => $request->input('licence_key'),
];

(new ActivateLicenceKey)->execute($data);

return response()->json([
'data' => AdminBillingViewHelper::index($company->refresh()),
], 200);
}
}
23 changes: 19 additions & 4 deletions app/Http/ViewHelpers/Adminland/AdminBillingViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use App\Helpers\DateHelper;
use App\Models\Company\Company;
use Illuminate\Support\Collection;
use App\Models\Company\CompanyInvoice;

class AdminBillingViewHelper
Expand All @@ -13,11 +12,22 @@ class AdminBillingViewHelper
* Get all the information about the account usage.
*
* @param Company $company
* @return Collection|null
* @return array|null
*/
public static function index(Company $company): ?Collection
public static function index(Company $company): ?array
{
return $company->invoices()
// billing information
$billingInformation = [
'licence_key' => $company->licence_key,
'frequency' => $company->frequency == 'annual' ? trans('account.billing_annual') : trans('account.billing_monthly'),
'valid_until_at' => DateHelper::formatDate($company->valid_until_at),
'purchaser_email' => $company->purchaser_email,
'seats' => $company->quantity,
'customer_portal_url' => config('officelife.customer_portal_url'),
];

// list of invoices
$invoicesCollection = $company->invoices()
->with('companyUsageHistory')
->latest()
->get()
Expand All @@ -32,6 +42,11 @@ public static function index(Company $company): ?Collection
]),
];
});

return [
'invoices' => $invoicesCollection,
'billing_information' => $billingInformation,
];
}

/**
Expand Down
6 changes: 6 additions & 0 deletions app/Models/Company/Company.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ class Company extends Model
'work_from_home_enabled',
'founded_at',
'code_to_join_company',
'licence_key',
'valid_until_at',
'purchaser_email',
'frequency',
'quantity',
];

/**
Expand All @@ -51,6 +56,7 @@ class Company extends Model
*/
protected $dates = [
'founded_at',
'valid_until_at',
];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ private function log(): void
'audited_at' => Carbon::now(),
'objects' => json_encode([
'ask_me_anything_session_id' => $this->session->id,
'ask_me_anything_session_theme' => $this->valueOrNull($this->data, 'theme'),
]),
])->onQueue('low');
}
Expand Down
103 changes: 103 additions & 0 deletions app/Services/Company/Adminland/Subscription/ActivateLicenceKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace App\Services\Company\Adminland\Subscription;

use Exception;
use Illuminate\Support\Str;
use App\Services\BaseService;
use App\Models\Company\Company;
use Illuminate\Support\Facades\App;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use App\Exceptions\NoCustomerPortalSetException;
use App\Exceptions\NoLicenceKeyEncryptionSetException;

class ActivateLicenceKey extends BaseService
{
private Company $company;
private array $data;
private Response $response;

/**
* Get the validation rules that apply to the service.
*
* @return array
*/
public function rules(): array
{
return [
'company_id' => 'required|integer|exists:companies,id',
'licence_key' => 'required|string:255',
];
}

/**
* Check if the licence key given by the user is a valid licence key.
* If it is, activate the licence key and set the valid_until_at date.
*
* @param array $data
*/
public function execute(array $data): void
{
$this->validateRules($data);
$this->data = $data;
$this->company = Company::findOrFail($data['company_id']);

$this->validateEnvVariables();
$this->makeRequestToCustomerPortal();
$this->checkResponseCode();
$this->decodeAndStoreKey();
}

private function validateEnvVariables(): void
{
if (! config('officelife.licence_key_encryption_key')) {
throw new NoLicenceKeyEncryptionSetException();
}

if (config('officelife.customer_portal_url') == '') {
throw new NoCustomerPortalSetException();
}
}

private function makeRequestToCustomerPortal(): void
{
$url = config('officelife.customer_portal_url').'/'.config('officelife.customer_portal_secret_key').'/validate/'.$this->data['licence_key'];

// necessary for testing purposes
if (App::environment('production')) {
$this->response = Http::get($url);
} else {
$this->response = Http::withOptions(['verify' => false])->get($url);
}
}

private function checkResponseCode(): void
{
if ($this->response->status() == 404) {
throw new Exception(trans('settings.subscriptions_licence_key_does_not_exist'));
}

if ($this->response->status() == 900) {
throw new Exception(trans('settings.subscriptions_licence_key_invalid'));
}

if ($this->response->status() != 200) {
throw new Exception(trans('settings.subscriptions_licence_key_problem'));
}
}

private function decodeAndStoreKey(): void
{
$licenceKey = base64_decode($this->data['licence_key']);
$licenceKey = Str::replace('123', '', $licenceKey);
$array = json_decode($licenceKey, true);

$this->company->licence_key = $this->data['licence_key'];
$this->company->valid_until_at = $array[0]['next_check_at'];
$this->company->purchaser_email = $array[0]['purchaser_email'];
$this->company->frequency = $array[0]['frequency'];
$this->company->quantity = $array[0]['quantity'];
$this->company->save();
}
}
33 changes: 33 additions & 0 deletions config/officelife.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,39 @@
*/
'name' => 'OfficeLife',

/*
|--------------------------------------------------------------------------
| Licence key server
|--------------------------------------------------------------------------
|
| We need to check if the user has a valid licence key to unlock paid
| features. Licence keys are managed on our own customer portal.
|
*/
'customer_portal_url' => env('CUSTOMER_PORTAL_URL', ''),

/*
|--------------------------------------------------------------------------
| Licence key encryption key
|--------------------------------------------------------------------------
|
| All licence keys are encrypted with this key on the customer portal when
| the key is generated.
|
*/
'licence_key_encryption_key' => env('LICENCE_KEY_ENCRYPTION_KEY', null),

/*
|--------------------------------------------------------------------------
| Secret key to communicate with the customer portal
|--------------------------------------------------------------------------
|
| We need to communicate with the customer portal to check licence keys.
| This is done through an HTTP call that we need to secure.
|
*/
'customer_portal_secret_key' => env('CUSTOMER_PORTAL_SECRET_KEY', null),

/*
|--------------------------------------------------------------------------
| Demo mode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

return new class extends Migration {
/**
* Run the migrations.
*/
public function up()
{
Schema::table('companies', function (Blueprint $table) {
$table->string('licence_key')->after('code_to_join_company')->nullable();
$table->datetime('valid_until_at')->after('licence_key')->nullable();
$table->string('purchaser_email')->after('valid_until_at')->nullable();
$table->string('frequency')->after('purchaser_email')->nullable();
$table->string('quantity')->after('purchaser_email')->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down()
{
Schema::dropIfExists('licence_keys');
}
};
Loading