Skip to content
This repository has been archived by the owner on Jun 8, 2023. It is now read-only.

feat: subscription page #288

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public static function data(User $user): array
{
return [
'is_account_administrator' => $user->is_account_administrator,
'requires_subscription' => config('monica.requires_subscription'),
'url' => [
'preferences' => [
'index' => route('settings.preferences.index'),
Expand All @@ -26,6 +27,9 @@ public static function data(User $user): array
'storage' => [
'index' => route('settings.storage.index'),
],
'subscription' => [
'index' => route('settings.subscription.index'),
],
'cancel' => [
'index' => route('settings.cancel.index'),
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace App\Domains\Settings\ManageSubscription\Services;

use App\Exceptions\LicenceKeyDontExistException;
use App\Exceptions\LicenceKeyErrorException;
use App\Exceptions\LicenceKeyInvalidException;
use App\Exceptions\MissingPrivateKeyException;
use App\Interfaces\ServiceInterface;
use App\Models\Account;
use App\Services\BaseService;

class ActivateLicenceKey extends BaseService implements ServiceInterface
{
private Account $account;

private array $data;

private int $status;

private array $response;

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

/**
* 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
* @return void
*/
public function execute(array $data): void
{
$this->validateRules($data);
$this->data = $data;
$this->account = Account::findOrFail($data['account_id']);

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

private function validateEnvVariables(): void
{
if (config('monica.customer_portal_private_key') === null) {
throw new MissingPrivateKeyException();
}
}

private function makeRequestToCustomerPortal(): void
{
$data = (new CallCustomerPortal())->execute([
'licence_key' => $this->data['licence_key'],
]);

$this->status = $data['status'];
$this->response = $data['data'];
}

private function checkResponseCode(): void
{
if ($this->status === 404) {
throw new LicenceKeyDontExistException();
}

if ($this->status === 410) {
throw new LicenceKeyInvalidException();
}

if ($this->status !== 200) {
throw new LicenceKeyErrorException();
}
}

private function store(): void
{
$licenceKey = $this->decodeKey();

$this->account->licence_key = $this->data['licence_key'];
$this->account->valid_until_at = $this->response['next_check_at'];
$this->account->purchaser_email = $licenceKey['purchaser_email'];
switch ($licenceKey['frequency']) {
case 'month':
$this->account->frequency = 'monthly';
break;
case 'year':
$this->account->frequency = 'annual';
break;
default:
$this->account->frequency = $licenceKey['frequency'];
break;
}

$this->account->save();
}

private function decodeKey(): array
{
$encrypter = app('license.encrypter');

return $encrypter->decrypt($this->data['licence_key']);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace App\Domains\Settings\ManageSubscription\Services;

use App\Exceptions\CustomerPortalWrongCredentials;
use App\Exceptions\NoCustomerPortalSecretsException;
use App\Interfaces\ServiceInterface;
use App\Services\BaseService;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class CallCustomerPortal extends BaseService implements ServiceInterface
{
private array $data;

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

/**
* Make a call to the customer portal.
*
* @param array $data
* @return array
*/
public function execute(array $data): array
{
$this->data = $data;
$this->validateRules($data);

$this->validateEnvVariables();

$accessToken = $this->getAccessToken();
$response = $this->makeRequestToCustomerPortal($accessToken);

return [
'status' => $response->status(),
'data' => $response->json(),
];
}

private function validateEnvVariables(): void
{
if (config('monica.customer_portal_url') === '') {
throw new NoCustomerPortalSecretsException();
}

if (config('monica.customer_portal_client_id') === null || config('monica.customer_portal_client_secret') === null) {
throw new NoCustomerPortalSecretsException();
}
}

private function getAccessToken(): string
{
return Cache::remember('customer_portal.access_token', 31449600 /* 364 days */, function () {
$url = config('monica.customer_portal_url').'/oauth/token';

$response = Http::asForm()->post($url, [
'grant_type' => 'client_credentials',
'client_id' => config('monica.customer_portal_client_id'),
'client_secret' => config('monica.customer_portal_client_secret'),
'scope' => 'manage-key',
]);

$json = $response->json();
if ($response->failed() || ! isset($json['access_token'])) {
throw new CustomerPortalWrongCredentials();
}

return $json['access_token'];
});
}

private function makeRequestToCustomerPortal(string $accessToken): Response
{
$url = config('monica.customer_portal_url').'/api/validate';

return Http::withToken($accessToken)
->acceptJson()
->post($url, [
'licence_key' => $this->data['licence_key'],
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Domains\Settings\ManageSubscription\Web\Controllers;

use App\Domains\Settings\ManageSubscription\Services\ActivateLicenceKey;
use App\Domains\Settings\ManageSubscription\Web\ViewHelpers\SubscriptionViewHelper;
use App\Domains\Vault\ManageVault\Web\ViewHelpers\VaultIndexViewHelper;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Inertia\Inertia;

class SubscriptionController extends Controller
{
public function index()
{
if (! config('monica.requires_subscription')) {
return redirect()->route('settings.index');
}

return Inertia::render('Settings/Subscription/Index', [
'layoutData' => VaultIndexViewHelper::layoutData(),
'data' => SubscriptionViewHelper::data(),
]);
}

public function store(Request $request)
{
(new ActivateLicenceKey())->execute([
'account_id' => auth()->user()->account_id,
'licence_key' => $request->input('licence_key'),
]);

return response()->json([
'data' => true,
], 201);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Domains\Settings\ManageSubscription\Web\ViewHelpers;

class SubscriptionViewHelper
{
public static function data(): array
{
return [
'url' => [
'back' => route('settings.index'),
'store' => route('settings.subscription.store'),
'customer_portal' => 'https://customers.monicahq.com',
],
];
}
}
9 changes: 9 additions & 0 deletions app/Exceptions/CustomerPortalWrongCredentials.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

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

namespace App\Exceptions;

use Exception;

class LicenceKeyDontExistException extends Exception
{
public function __construct($message = '')
{
$message = trans('app.exception_licence_key_dont_exist');
parent::__construct($message);
}
}
9 changes: 9 additions & 0 deletions app/Exceptions/LicenceKeyErrorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

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

namespace App\Exceptions;

use Exception;

class LicenceKeyInvalidException extends Exception
{
public function __construct($message = '')
{
$message = trans('app.exception_licence_key_invalid');
parent::__construct($message);
}
}
9 changes: 9 additions & 0 deletions app/Exceptions/MissingPrivateKeyException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\Exceptions;

use Exception;

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

namespace App\Exceptions;

use Exception;

class NoCustomerPortalSecretsException extends Exception
{
}
Loading