From 1599711deeabfcc0b33bba265949f64528d4f8db Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 5 Mar 2021 21:18:28 +1100 Subject: [PATCH 01/20] Observers and policy scaffold --- app/Console/Kernel.php | 3 + app/Jobs/Cron/BillingSubscriptionCron.php | 52 ++++++++++++++ app/Models/BillingSubscription.php | 19 +++++ app/Models/ClientSubscription.php | 19 +++++ app/Observers/BillingSubscriptionObserver.php | 72 +++++++++++++++++++ app/Observers/ClientSubscriptionObserver.php | 72 +++++++++++++++++++ app/Policies/BillingSubscriptionPolicy.php | 31 ++++++++ app/Providers/AppServiceProvider.php | 8 ++- app/Providers/AuthServiceProvider.php | 3 + 9 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 app/Jobs/Cron/BillingSubscriptionCron.php create mode 100644 app/Models/BillingSubscription.php create mode 100644 app/Models/ClientSubscription.php create mode 100644 app/Observers/BillingSubscriptionObserver.php create mode 100644 app/Observers/ClientSubscriptionObserver.php create mode 100644 app/Policies/BillingSubscriptionPolicy.php diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 24a661eae68f..1d3be1016fce 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -11,6 +11,7 @@ namespace App\Console; +use App\Jobs\Cron\BillingSubscriptionCron; use App\Jobs\Cron\RecurringInvoicesCron; use App\Jobs\Ninja\AdjustEmailQuota; use App\Jobs\Ninja\CompanySizeCheck; @@ -53,6 +54,8 @@ class Kernel extends ConsoleKernel $schedule->job(new UpdateExchangeRates)->daily()->withoutOverlapping(); + $schedule->job(new BillingSubscriptionCron)->daily()->withoutOverlapping(); + $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping(); /* Run hosted specific jobs */ diff --git a/app/Jobs/Cron/BillingSubscriptionCron.php b/app/Jobs/Cron/BillingSubscriptionCron.php new file mode 100644 index 000000000000..993605593365 --- /dev/null +++ b/app/Jobs/Cron/BillingSubscriptionCron.php @@ -0,0 +1,52 @@ +isAdmin() || $user->hasPermission('create_billing_subscription') || $user->hasPermission('create_all'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3d96377fa950..c017572692c3 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -12,7 +12,9 @@ namespace App\Providers; use App\Models\Account; +use App\Models\BillingSubscription; use App\Models\Client; +use App\Models\ClientSubscription; use App\Models\Company; use App\Models\CompanyGateway; use App\Models\CompanyToken; @@ -26,7 +28,9 @@ use App\Models\Quote; use App\Models\Task; use App\Models\User; use App\Observers\AccountObserver; +use App\Observers\BillingSubscriptionObserver; use App\Observers\ClientObserver; +use App\Observers\ClientSubscriptionObserver; use App\Observers\CompanyGatewayObserver; use App\Observers\CompanyObserver; use App\Observers\CompanyTokenObserver; @@ -75,9 +79,10 @@ class AppServiceProvider extends ServiceProvider Schema::defaultStringLength(191); - User::observe(UserObserver::class); Account::observe(AccountObserver::class); + BillingSubscription::observe(BillingSubscriptionObserver::class); Client::observe(ClientObserver::class); + ClientSubscription::observe(ClientSubscriptionObserver::class); Company::observe(CompanyObserver::class); CompanyGateway::observe(CompanyGatewayObserver::class); CompanyToken::observe(CompanyTokenObserver::class); @@ -89,6 +94,7 @@ class AppServiceProvider extends ServiceProvider Proposal::observe(ProposalObserver::class); Quote::observe(QuoteObserver::class); Task::observe(TaskObserver::class); + User::observe(UserObserver::class); // Queue::before(function (JobProcessing $event) { // // \Log::info('Event Job '.$event->connectionName); diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 23d6f3a59220..60281db3b3fe 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -12,6 +12,7 @@ namespace App\Providers; use App\Models\Activity; +use App\Models\BillingSubscription; use App\Models\Client; use App\Models\Company; use App\Models\CompanyGateway; @@ -37,6 +38,7 @@ use App\Models\User; use App\Models\Vendor; use App\Models\Webhook; use App\Policies\ActivityPolicy; +use App\Policies\BillingSubscriptionPolicy; use App\Policies\ClientPolicy; use App\Policies\CompanyGatewayPolicy; use App\Policies\CompanyPolicy; @@ -73,6 +75,7 @@ class AuthServiceProvider extends ServiceProvider */ protected $policies = [ Activity::class => ActivityPolicy::class, + BillingSubscription::class => BillingSubscriptionPolicy::class, Client::class => ClientPolicy::class, Company::class => CompanyPolicy::class, CompanyToken::class => CompanyTokenPolicy::class, From 097d9ac1b8909d5a0284df1c45bdce81fac2b626 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Mar 2021 11:47:05 +1100 Subject: [PATCH 02/20] Padding out functionality --- app/Jobs/Cron/BillingSubscriptionCron.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Jobs/Cron/BillingSubscriptionCron.php b/app/Jobs/Cron/BillingSubscriptionCron.php index 993605593365..453ab3befa02 100644 --- a/app/Jobs/Cron/BillingSubscriptionCron.php +++ b/app/Jobs/Cron/BillingSubscriptionCron.php @@ -49,4 +49,14 @@ class BillingSubscriptionCron } } } + + /* Our daily cron should check + + 1. Is the subscription still in trial phase? + 2. Check the recurring invoice and its remaining_cycles to see whether we need to cancel or perform any other function. + */ + private function processSubscription($client_subscription) + { + + } } From 93493116ed5ede879f062c327b191469b012cb7b Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Mar 2021 17:19:57 +1100 Subject: [PATCH 03/20] Scaffolds for billing_subscriptions --- app/Jobs/Cron/BillingSubscriptionCron.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/Jobs/Cron/BillingSubscriptionCron.php b/app/Jobs/Cron/BillingSubscriptionCron.php index 453ab3befa02..091cb10c5f83 100644 --- a/app/Jobs/Cron/BillingSubscriptionCron.php +++ b/app/Jobs/Cron/BillingSubscriptionCron.php @@ -35,25 +35,34 @@ class BillingSubscriptionCron */ public function handle() : void { - /* Get all invoices where the send date is less than NOW + 30 minutes() */ if (! config('ninja.db.multi_db_enabled')) { - + $this->loopSubscriptions(); } else { //multiDB environment, need to foreach (MultiDB::$dbs as $db) { MultiDB::setDB($db); - + $this->loopSubscriptions(); } } } + private function loopSubscriptions() + { + $client_subs = ClientSubscription::whereNull('deleted_at') + ->cursor() + ->each(function ($cs){ + $this->processSubscription($cs); + }); + } + /* Our daily cron should check 1. Is the subscription still in trial phase? 2. Check the recurring invoice and its remaining_cycles to see whether we need to cancel or perform any other function. + 3. Any notifications that need to fire? */ private function processSubscription($client_subscription) { From 2de12ea973f87d67798a35b9fadf7b26f99e5200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 8 Mar 2021 15:17:40 +0100 Subject: [PATCH 04/20] Routes for billing subscriptions --- routes/api.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/routes/api.php b/routes/api.php index a503f03d9765..a2122998f186 100644 --- a/routes/api.php +++ b/routes/api.php @@ -68,7 +68,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('emails', 'EmailController@send')->name('email.send')->middleware('user_verified'); Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit - Route::put('expenses/{expense}/upload', 'ExpenseController@upload'); + Route::put('expenses/{expense}/upload', 'ExpenseController@upload'); Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); Route::resource('expense_categories', 'ExpenseCategoryController'); // name = (expense_categories. index / create / show / update / destroy / edit @@ -98,7 +98,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund'); Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); - Route::put('payments/{payment}/upload', 'PaymentController@upload'); + Route::put('payments/{payment}/upload', 'PaymentController@upload'); Route::resource('payment_terms', 'PaymentTermController'); // name = (payments. index / create / show / update / destroy / edit Route::post('payment_terms/bulk', 'PaymentTermController@bulk')->name('payment_terms.bulk'); @@ -107,20 +107,20 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); - Route::put('products/{product}/upload', 'ProductController@upload'); + Route::put('products/{product}/upload', 'ProductController@upload'); Route::resource('projects', 'ProjectController'); // name = (projects. index / create / show / update / destroy / edit Route::post('projects/bulk', 'ProjectController@bulk')->name('projects.bulk'); Route::put('projects/{project}/upload', 'ProjectController@upload')->name('projects.upload'); - + Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit Route::get('quotes/{quote}/{action}', 'QuoteController@action')->name('quotes.action'); Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk'); - Route::put('quotes/{quote}/upload', 'QuoteController@upload'); + Route::put('quotes/{quote}/upload', 'QuoteController@upload'); Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk'); - Route::put('recurring_invoices/{recurring_invoice}/upload', 'RecurringInvoiceController@upload'); + Route::put('recurring_invoices/{recurring_invoice}/upload', 'RecurringInvoiceController@upload'); Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk'); @@ -137,7 +137,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); - Route::put('tasks/{task}/upload', 'TaskController@upload'); + Route::put('tasks/{task}/upload', 'TaskController@upload'); Route::resource('task_statuses', 'TaskStatusController'); // name = (task_statuses. index / create / show / update / destroy / edit Route::post('task_statuses/bulk', 'TaskStatusController@bulk')->name('task_statuses.bulk'); @@ -155,7 +155,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk'); - Route::put('vendors/{vendor}/upload', 'VendorController@upload'); + Route::put('vendors/{vendor}/upload', 'VendorController@upload'); Route::get('users', 'UserController@index'); Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); @@ -173,7 +173,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a // Route::post('hooks', 'SubscriptionController@subscribe')->name('hooks.subscribe'); // Route::delete('hooks/{subscription_id}', 'SubscriptionController@unsubscribe')->name('hooks.unsubscribe'); - + Route::resource('billing/subscriptions', 'BillingSubscriptionController'); }); Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id}', 'PaymentWebhookController') From ea5117ecbe007659cbd994d1f3de8407e9680f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 8 Mar 2021 15:18:14 +0100 Subject: [PATCH 05/20] Billing subscriptions: Factory, repository & transformer --- app/Factory/BillingSubscriptionFactory.php | 17 +++++ .../BillingSubscriptionRepository.php | 28 ++++++++ .../BillingSubscriptionTransformer.php | 70 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 app/Factory/BillingSubscriptionFactory.php create mode 100644 app/Repositories/BillingSubscriptionRepository.php create mode 100644 app/Transformers/BillingSubscriptionTransformer.php diff --git a/app/Factory/BillingSubscriptionFactory.php b/app/Factory/BillingSubscriptionFactory.php new file mode 100644 index 000000000000..38708bd6ed79 --- /dev/null +++ b/app/Factory/BillingSubscriptionFactory.php @@ -0,0 +1,17 @@ +fill($data) + ->save(); + + return $billing_subscription; + } +} diff --git a/app/Transformers/BillingSubscriptionTransformer.php b/app/Transformers/BillingSubscriptionTransformer.php new file mode 100644 index 000000000000..4969f4dfa018 --- /dev/null +++ b/app/Transformers/BillingSubscriptionTransformer.php @@ -0,0 +1,70 @@ + $this->encodePrimaryKey($billing_subscription->id), + 'user_id' => $this->encodePrimaryKey($billing_subscription->user_id), + 'product_id' => $this->encodePrimaryKey($billing_subscription->product_id), + 'assigned_user_id' => $this->encodePrimaryKey($billing_subscription->assigned_user_id), + 'company_id' => $this->encodePrimaryKey($billing_subscription->company_id), + 'is_recurring' => (bool)$billing_subscription->is_recurring, + 'frequency_id' => (string)$billing_subscription->frequency_id, + 'auto_bill' => (string)$billing_subscription->auto_bill, + 'promo_code' => (string)$billing_subscription->promo_code, + 'promo_discount' => (string)$billing_subscription->promo_discount, + 'is_amount_discount' => (bool)$billing_subscription->is_amount_discount, + 'allow_cancellation' => (bool)$billing_subscription->allow_cancellation, + 'per_set_enabled' => (bool)$billing_subscription->per_set_enabled, + 'min_seats_limit' => (int)$billing_subscription->min_seats_limit, + 'max_seats_limit' => (int)$billing_subscription->max_seats_limit, + 'trial_enabled' => (bool)$billing_subscription->trial_enabled, + 'trial_duration' => (string)$billing_subscription->trial_duration, + 'allow_query_overrides' => (bool)$billing_subscription->allow_query_overrides, + 'allow_plan_changes' => (bool)$billing_subscription->allow_plan_changes, + 'plan_map' => (string)$billing_subscription->plan_map, + 'refund_period' => (string)$billing_subscription->refund_period, + 'webhook_configuration' => (string)$billing_subscription->webhook_configuration, + 'is_deleted' => (bool)$billing_subscription->is_deleted, + ]; + } + + public function includeProducts(BillingSubscription $billing_subscription): \League\Fractal\Resource\Item + { + $transformer = new ProductTransformer($this->serializer); + + return $this->includeItem($billing_subscription->product, $transformer, Product::class); + } +} From 9c1a3368e94d1295cf072f39f19f55ef345fea97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 8 Mar 2021 15:18:34 +0100 Subject: [PATCH 06/20] Billing subscriptions: Database schema & model --- app/Models/BillingSubscription.php | 61 ++++++++++++++++ ...729_create_billing_subscriptions_table.php | 71 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 app/Models/BillingSubscription.php create mode 100644 database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php diff --git a/app/Models/BillingSubscription.php b/app/Models/BillingSubscription.php new file mode 100644 index 000000000000..57f8706f2075 --- /dev/null +++ b/app/Models/BillingSubscription.php @@ -0,0 +1,61 @@ +belongsTo(Company::class); + } + + public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(User::class); + } + + public function product(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(Product::class); + } +} diff --git a/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php b/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php new file mode 100644 index 000000000000..640a21becc0c --- /dev/null +++ b/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php @@ -0,0 +1,71 @@ +increments('id'); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('assigned_user_id'); + $table->unsignedInteger('company_id'); + $table->unsignedInteger('product_id'); + $table->boolean('is_recurring')->default(false); + $table->unsignedInteger('frequency_id'); + $table->string('auto_bill')->default(''); + $table->string('promo_code')->default(''); + $table->float('promo_discount')->default(0); + $table->boolean('is_amount_discount')->default(false); + $table->boolean('allow_cancellation')->default(true); + $table->boolean('per_set_enabled')->default(false); + $table->unsignedInteger('min_seats_limit'); + $table->unsignedInteger('max_seats_limit'); + $table->boolean('trial_enabled')->default(false); + $table->unsignedInteger('trial_duration'); + $table->boolean('allow_query_overrides')->default(false); + $table->boolean('allow_plan_changes')->default(false); + $table->mediumText('plan_map'); + $table->unsignedInteger('refund_period')->nullable(); + $table->mediumText('webhook_configuration'); + $table->softDeletes('deleted_at', 6); + $table->boolean('is_deleted')->default(false); + $table->timestamps(); + $table->foreign('product_id')->references('id')->on('products'); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->index(['company_id', 'deleted_at']); + }); + + Schema::create('client_subscriptions', function (Blueprint $table) { + $table->increments('id'); + $table->unsignedInteger('subscription_id'); + $table->unsignedInteger('recurring_invoice_id'); + $table->unsignedInteger('client_id'); + $table->unsignedInteger('trial_started')->nullable(); + $table->unsignedInteger('trial_ends')->nullable(); + $table->timestamps(); + $table->foreign('subscription_id')->references('id')->on('billing_subscriptions'); + $table->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices'); + $table->foreign('client_id')->references('id')->on('clients'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('billing_subscriptions'); + Schema::dropIfExists('client_subscriptions'); + } +} From 420d9045510c7705c9292cb8ba1dc460944a7a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 8 Mar 2021 15:18:48 +0100 Subject: [PATCH 07/20] Client subscriptions: Model --- app/Models/ClientSubscription.php | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 app/Models/ClientSubscription.php diff --git a/app/Models/ClientSubscription.php b/app/Models/ClientSubscription.php new file mode 100644 index 000000000000..8ca8caa87d37 --- /dev/null +++ b/app/Models/ClientSubscription.php @@ -0,0 +1,11 @@ + Date: Mon, 8 Mar 2021 15:19:04 +0100 Subject: [PATCH 08/20] Billing subscriptions: Controller --- .../BillingSubscriptionController.php | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 app/Http/Controllers/BillingSubscriptionController.php diff --git a/app/Http/Controllers/BillingSubscriptionController.php b/app/Http/Controllers/BillingSubscriptionController.php new file mode 100644 index 000000000000..421e2e2768f4 --- /dev/null +++ b/app/Http/Controllers/BillingSubscriptionController.php @@ -0,0 +1,93 @@ +billing_subscription_repo = $billing_subscription_repo; + } + + public function index(): \Illuminate\Http\Response + { + $billing_subscriptions = BillingSubscription::query()->company(); + + return $this->listResponse($billing_subscriptions); + } + + public function create(CreateBillingSubscriptionRequest $request): \Illuminate\Http\Response + { + $billing_subscription = BillingSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id); + + return $this->itemResponse($billing_subscription); + } + + public function store(StoreBillingSubscriptionRequest $request): \Illuminate\Http\Response + { + $billing_subscription = $this->billing_subscription_repo->save($request->all(), BillingSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id)); + + event(new BillingsubscriptionWasCreated($billing_subscription, $billing_subscription->company, Ninja::eventVars())); + + return $this->itemResponse($billing_subscription); + } + + public function show(ShowBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response + { + return $this->itemResponse($billing_subscription); + } + + public function edit(EditBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response + { + return $this->itemResponse($billing_subscription); + } + + public function update(UpdateBillingSubscriptionRequest $request, BillingSubscription $billing_subscription) + { + if ($request->entityIsDeleted($billing_subscription)) { + return $request->disallowUpdate(); + } + + $billing_subscription = $this->billing_subscription_repo->save($request->all(), $billing_subscription); + + return $this->itemResponse($billing_subscription); + } + + public function destroy(DestroyBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response + { + $this->billing_subscription_repo->delete($billing_subscription); + + return $this->listResponse($billing_subscription->fresh()); + } +} From 29922a24a0352987c0bb97623422294c5cc58862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 8 Mar 2021 15:19:23 +0100 Subject: [PATCH 09/20] Billing subscriptions: Event for created --- .../BillingSubscriptionWasCreated.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/Events/BillingSubscription/BillingSubscriptionWasCreated.php diff --git a/app/Events/BillingSubscription/BillingSubscriptionWasCreated.php b/app/Events/BillingSubscription/BillingSubscriptionWasCreated.php new file mode 100644 index 000000000000..73fc2d1f2f87 --- /dev/null +++ b/app/Events/BillingSubscription/BillingSubscriptionWasCreated.php @@ -0,0 +1,55 @@ +billing_subscription = $billing_subscription; + $this->company = $company; + $this->event_vars = $event_vars; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} From 2660b8fa584db925a6b8f0572a176a57224aeb1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Mon, 8 Mar 2021 15:19:45 +0100 Subject: [PATCH 10/20] Billing subscriptions: Requests for crud endpoints --- .../CreateBillingSubscriptionRequest.php | 31 ++++++++++++ .../DestroyBillingSubscriptionRequest.php | 31 ++++++++++++ .../EditBillingSubscriptionRequest.php | 33 ++++++++++++ .../ShowBillingSubscriptionRequest.php | 32 ++++++++++++ .../StoreBillingSubscriptionRequest.php | 50 +++++++++++++++++++ .../UpdateBillingSubscriptionRequest.php | 33 ++++++++++++ 6 files changed, 210 insertions(+) create mode 100644 app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php create mode 100644 app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php create mode 100644 app/Http/Requests/BillingSubscription/EditBillingSubscriptionRequest.php create mode 100644 app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php create mode 100644 app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php create mode 100644 app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php diff --git a/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php new file mode 100644 index 000000000000..f7f0a907343d --- /dev/null +++ b/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php @@ -0,0 +1,31 @@ +user()->can('create', BillingSubscription::class); // TODO + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php new file mode 100644 index 000000000000..c263836d9442 --- /dev/null +++ b/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php @@ -0,0 +1,31 @@ +user()->can('view', $this->billing_subscription); // TODO + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php new file mode 100644 index 000000000000..f5d08bec9445 --- /dev/null +++ b/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php @@ -0,0 +1,32 @@ +user()->can('view', $this->billing_subscription); // TODO + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php new file mode 100644 index 000000000000..c897edc5b299 --- /dev/null +++ b/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php @@ -0,0 +1,50 @@ + ['sometimes'], + 'product_id' => ['sometimes'], + 'assigned_user_id' => ['sometimes'], + 'company_id' => ['sometimes'], + 'is_recurring' => ['sometimes'], + 'frequency_id' => ['sometimes'], + 'auto_bill' => ['sometimes'], + 'promo_code' => ['sometimes'], + 'promo_discount' => ['sometimes'], + 'is_amount_discount' => ['sometimes'], + 'allow_cancellation' => ['sometimes'], + 'per_set_enabled' => ['sometimes'], + 'min_seats_limit' => ['sometimes'], + 'max_seats_limit' => ['sometimes'], + 'trial_enabled' => ['sometimes'], + 'trial_duration' => ['sometimes'], + 'allow_query_overrides' => ['sometimes'], + 'allow_plan_changes' => ['sometimes'], + 'plan_map' => ['sometimes'], + 'refund_period' => ['sometimes'], + 'webhook_configuration' => ['sometimes'], + ]; + } +} diff --git a/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php new file mode 100644 index 000000000000..a92d14ff0b33 --- /dev/null +++ b/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php @@ -0,0 +1,33 @@ + Date: Tue, 9 Mar 2021 08:29:59 +1100 Subject: [PATCH 11/20] billing_subscriptions cleanup --- .../BillingSubscriptionController.php | 37 +++++++++++++++++++ .../OpenAPI/BillingSubscription.php | 31 ++++++++++++++++ app/Models/BillingSubscription.php | 9 +++++ .../BillingSubscriptionTransformer.php | 15 +++++--- ...729_create_billing_subscriptions_table.php | 2 +- routes/api.php | 2 +- 6 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 app/Http/Controllers/OpenAPI/BillingSubscription.php diff --git a/app/Http/Controllers/BillingSubscriptionController.php b/app/Http/Controllers/BillingSubscriptionController.php index 421e2e2768f4..0dff2f31c962 100644 --- a/app/Http/Controllers/BillingSubscriptionController.php +++ b/app/Http/Controllers/BillingSubscriptionController.php @@ -40,6 +40,43 @@ class BillingSubscriptionController extends BaseController $this->billing_subscription_repo = $billing_subscription_repo; } + /** + * Show the list of BillingSubscriptions. + * * + * @return Response + * + * @OA\Get( + * path="/api/v1/billing_subscriptions", + * operationId="getBillingSubscriptions", + * tags={"billing_subscriptions"}, + * summary="Gets a list of billing_subscriptions", + * description="Lists billing_subscriptions.", + * + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="A list of billing_subscriptions", + * @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/BillingSubscription"), + * ), + * @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"), + * ), + * ) + */ + public function index(): \Illuminate\Http\Response { $billing_subscriptions = BillingSubscription::query()->company(); diff --git a/app/Http/Controllers/OpenAPI/BillingSubscription.php b/app/Http/Controllers/OpenAPI/BillingSubscription.php new file mode 100644 index 000000000000..bde43b87c618 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/BillingSubscription.php @@ -0,0 +1,31 @@ + 'boolean', + 'plan_map' => 'object', + 'webhook_configuration' => 'object', + 'updated_at' => 'timestamp', + 'created_at' => 'timestamp', + 'deleted_at' => 'timestamp', + ]; + public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(Company::class); diff --git a/app/Transformers/BillingSubscriptionTransformer.php b/app/Transformers/BillingSubscriptionTransformer.php index 4969f4dfa018..8b2f376f9762 100644 --- a/app/Transformers/BillingSubscriptionTransformer.php +++ b/app/Transformers/BillingSubscriptionTransformer.php @@ -29,7 +29,7 @@ class BillingSubscriptionTransformer extends EntityTransformer * @var array */ protected $availableIncludes = [ - 'products', + 'product', ]; public function transform(BillingSubscription $billing_subscription): array @@ -44,24 +44,27 @@ class BillingSubscriptionTransformer extends EntityTransformer 'frequency_id' => (string)$billing_subscription->frequency_id, 'auto_bill' => (string)$billing_subscription->auto_bill, 'promo_code' => (string)$billing_subscription->promo_code, - 'promo_discount' => (string)$billing_subscription->promo_discount, + 'promo_discount' => (float)$billing_subscription->promo_discount, 'is_amount_discount' => (bool)$billing_subscription->is_amount_discount, 'allow_cancellation' => (bool)$billing_subscription->allow_cancellation, - 'per_set_enabled' => (bool)$billing_subscription->per_set_enabled, + 'per_seat_enabled' => (bool)$billing_subscription->per_set_enabled, 'min_seats_limit' => (int)$billing_subscription->min_seats_limit, 'max_seats_limit' => (int)$billing_subscription->max_seats_limit, 'trial_enabled' => (bool)$billing_subscription->trial_enabled, - 'trial_duration' => (string)$billing_subscription->trial_duration, + 'trial_duration' => (int)$billing_subscription->trial_duration, 'allow_query_overrides' => (bool)$billing_subscription->allow_query_overrides, 'allow_plan_changes' => (bool)$billing_subscription->allow_plan_changes, 'plan_map' => (string)$billing_subscription->plan_map, - 'refund_period' => (string)$billing_subscription->refund_period, + 'refund_period' => (int)$billing_subscription->refund_period, 'webhook_configuration' => (string)$billing_subscription->webhook_configuration, 'is_deleted' => (bool)$billing_subscription->is_deleted, + 'created_at' => (int) $credit->created_at, + 'updated_at' => (int) $credit->updated_at, + 'archived_at' => (int) $credit->deleted_at, ]; } - public function includeProducts(BillingSubscription $billing_subscription): \League\Fractal\Resource\Item + public function includeProduct(BillingSubscription $billing_subscription): \League\Fractal\Resource\Item { $transformer = new ProductTransformer($this->serializer); diff --git a/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php b/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php index 640a21becc0c..27400a8fa679 100644 --- a/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php +++ b/database/migrations/2021_03_08_123729_create_billing_subscriptions_table.php @@ -26,7 +26,7 @@ class CreateBillingSubscriptionsTable extends Migration $table->float('promo_discount')->default(0); $table->boolean('is_amount_discount')->default(false); $table->boolean('allow_cancellation')->default(true); - $table->boolean('per_set_enabled')->default(false); + $table->boolean('per_seat_enabled')->default(false); $table->unsignedInteger('min_seats_limit'); $table->unsignedInteger('max_seats_limit'); $table->boolean('trial_enabled')->default(false); diff --git a/routes/api.php b/routes/api.php index a2122998f186..1af077d5fbeb 100644 --- a/routes/api.php +++ b/routes/api.php @@ -173,7 +173,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a // Route::post('hooks', 'SubscriptionController@subscribe')->name('hooks.subscribe'); // Route::delete('hooks/{subscription_id}', 'SubscriptionController@unsubscribe')->name('hooks.unsubscribe'); - Route::resource('billing/subscriptions', 'BillingSubscriptionController'); + Route::resource('billing_subscriptions', 'BillingSubscriptionController'); }); Route::match(['get', 'post'], 'payment_webhook/{company_key}/{company_gateway_id}', 'PaymentWebhookController') From 68f0e99f26d629591254c3cdde5a12c06c746c67 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 9 Mar 2021 09:53:04 +1100 Subject: [PATCH 12/20] Fixes for connected account --- app/Http/Controllers/ConnectedAccountController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/ConnectedAccountController.php b/app/Http/Controllers/ConnectedAccountController.php index 456f519c2634..a6f011bde5f3 100644 --- a/app/Http/Controllers/ConnectedAccountController.php +++ b/app/Http/Controllers/ConnectedAccountController.php @@ -14,6 +14,7 @@ namespace App\Http\Controllers; use App\Libraries\MultiDB; use App\Libraries\OAuth\Providers\Google; use Illuminate\Http\Request; +use Google_Client; class ConnectedAccountController extends BaseController { From 67cedb834865338c1fa681db6ce7e423d349340e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 9 Mar 2021 17:03:22 +1100 Subject: [PATCH 13/20] minor fixes --- app/Http/Controllers/BillingSubscriptionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/BillingSubscriptionController.php b/app/Http/Controllers/BillingSubscriptionController.php index 0dff2f31c962..c2f0892a6376 100644 --- a/app/Http/Controllers/BillingSubscriptionController.php +++ b/app/Http/Controllers/BillingSubscriptionController.php @@ -42,7 +42,7 @@ class BillingSubscriptionController extends BaseController /** * Show the list of BillingSubscriptions. - * * + * * @return Response * * @OA\Get( From 0a3b5e0665cbb09d12ac6714e4d001e5f72ba77f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 9 Mar 2021 17:06:03 +1100 Subject: [PATCH 14/20] minor fixes --- app/Factory/BillingSubscriptionFactory.php | 12 +++++- .../BillingSubscriptionController.php | 39 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/app/Factory/BillingSubscriptionFactory.php b/app/Factory/BillingSubscriptionFactory.php index 38708bd6ed79..f9f051293e9c 100644 --- a/app/Factory/BillingSubscriptionFactory.php +++ b/app/Factory/BillingSubscriptionFactory.php @@ -1,5 +1,13 @@ company_id = $company_id; + $billing_subscription->user_id = $user_id; return $billing_subscription; } diff --git a/app/Http/Controllers/BillingSubscriptionController.php b/app/Http/Controllers/BillingSubscriptionController.php index c2f0892a6376..348d0e89b2af 100644 --- a/app/Http/Controllers/BillingSubscriptionController.php +++ b/app/Http/Controllers/BillingSubscriptionController.php @@ -84,6 +84,45 @@ class BillingSubscriptionController extends BaseController return $this->listResponse($billing_subscriptions); } + /** + * Show the form for creating a new resource. + * + * @param CreateBillingSubscriptionRequest $request The request + * + * @return Response + * + * + * @OA\Get( + * path="/api/v1/billing_subscriptions/create", + * operationId="getBillingSubscriptionsCreate", + * tags={"billing_subscriptions"}, + * summary="Gets a new blank billing_subscriptions object", + * description="Returns a blank object with default values", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="A blank billing_subscriptions object", + * @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/BillingSubscription"), + * ), + * @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"), + * ), + * ) + */ public function create(CreateBillingSubscriptionRequest $request): \Illuminate\Http\Response { $billing_subscription = BillingSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id); From cd48ae78f9cec2642e6a9c34db53aec030f8fc11 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 9 Mar 2021 21:30:34 +1100 Subject: [PATCH 15/20] Minor fixes --- .../BillingSubscriptionController.php | 241 ++++++++++++++++++ .../OpenAPI/BillingSubscription.php | 4 +- .../RecurringInvoiceController.php | 2 +- app/Http/Controllers/TwoFactorController.php | 2 +- app/Http/Middleware/PasswordProtection.php | 8 +- config/queue.php | 2 +- 6 files changed, 247 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/BillingSubscriptionController.php b/app/Http/Controllers/BillingSubscriptionController.php index 348d0e89b2af..6c858bd1ed59 100644 --- a/app/Http/Controllers/BillingSubscriptionController.php +++ b/app/Http/Controllers/BillingSubscriptionController.php @@ -130,6 +130,45 @@ class BillingSubscriptionController extends BaseController return $this->itemResponse($billing_subscription); } + /** + * Store a newly created resource in storage. + * + * @param StoreBillingSubscriptionRequest $request The request + * + * @return Response + * + * + * @OA\Post( + * path="/api/v1/billing_subscriptions", + * operationId="storeBillingSubscription", + * tags={"billing_subscriptions"}, + * summary="Adds a billing_subscriptions", + * description="Adds an billing_subscriptions to the system", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="Returns the saved billing_subscriptions object", + * @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/BillingSubscription"), + * ), + * @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"), + * ), + * ) + */ public function store(StoreBillingSubscriptionRequest $request): \Illuminate\Http\Response { $billing_subscription = $this->billing_subscription_repo->save($request->all(), BillingSubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id)); @@ -139,16 +178,168 @@ class BillingSubscriptionController extends BaseController return $this->itemResponse($billing_subscription); } + /** + * Display the specified resource. + * + * @param ShowBillingSubscriptionRequest $request The request + * @param Invoice $billing_subscription The invoice + * + * @return Response + * + * + * @OA\Get( + * path="/api/v1/billing_subscriptions/{id}", + * operationId="showBillingSubscription", + * tags={"billing_subscriptions"}, + * summary="Shows an billing_subscriptions", + * description="Displays an billing_subscriptions by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The BillingSubscription Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the BillingSubscription object", + * @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/BillingSubscription"), + * ), + * @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"), + * ), + * ) + */ public function show(ShowBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response { return $this->itemResponse($billing_subscription); } + /** + * Show the form for editing the specified resource. + * + * @param EditBillingSubscriptionRequest $request The request + * @param Invoice $billing_subscription The invoice + * + * @return Response + * + * @OA\Get( + * path="/api/v1/billing_subscriptions/{id}/edit", + * operationId="editBillingSubscription", + * tags={"billing_subscriptions"}, + * summary="Shows an billing_subscriptions for editting", + * description="Displays an billing_subscriptions by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The BillingSubscription Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the invoice object", + * @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/BillingSubscription"), + * ), + * @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"), + * ), + * ) + */ public function edit(EditBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response { return $this->itemResponse($billing_subscription); } + /** + * Update the specified resource in storage. + * + * @param UpdateBillingSubscriptionRequest $request The request + * @param BillingSubscription $billing_subscription The invoice + * + * @return Response + * + * + * @OA\Put( + * path="/api/v1/billing_subscriptions/{id}", + * operationId="updateBillingSubscription", + * tags={"billing_subscriptions"}, + * summary="Updates an billing_subscriptions", + * description="Handles the updating of an billing_subscriptions by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The BillingSubscription Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the billing_subscriptions object", + * @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/BillingSubscription"), + * ), + * @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"), + * ), + * ) + */ public function update(UpdateBillingSubscriptionRequest $request, BillingSubscription $billing_subscription) { if ($request->entityIsDeleted($billing_subscription)) { @@ -160,6 +351,56 @@ class BillingSubscriptionController extends BaseController return $this->itemResponse($billing_subscription); } + /** + * Remove the specified resource from storage. + * + * @param DestroyBillingSubscriptionRequest $request + * @param BillingSubscription $invoice + * + * @return Response + * + * @throws \Exception + * @OA\Delete( + * path="/api/v1/billing_subscriptions/{id}", + * operationId="deleteBillingSubscription", + * tags={"billing_subscriptions"}, + * summary="Deletes a billing_subscriptions", + * description="Handles the deletion of an billing_subscriptions by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The BillingSubscription Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns a HTTP status", + * @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\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"), + * ), + * ) + */ public function destroy(DestroyBillingSubscriptionRequest $request, BillingSubscription $billing_subscription): \Illuminate\Http\Response { $this->billing_subscription_repo->delete($billing_subscription); diff --git a/app/Http/Controllers/OpenAPI/BillingSubscription.php b/app/Http/Controllers/OpenAPI/BillingSubscription.php index bde43b87c618..848a318542e6 100644 --- a/app/Http/Controllers/OpenAPI/BillingSubscription.php +++ b/app/Http/Controllers/OpenAPI/BillingSubscription.php @@ -11,8 +11,8 @@ * @OA\Property(property="is_recurring", type="boolean", example="true", description="______"), * @OA\Property(property="frequency_id", type="string", example="1", description="integer const representation of the frequency"), * @OA\Property(property="auto_bill", type="string", example="always", description="enum setting"), - * @OA\Property(property="promo_code", type="string", example="100OFF", description="______"), - * @OA\Property(property="promo_discount", type="float", example=10, description="______"), + * @OA\Property(property="promo_code", type="string", example="PROMOCODE4U", description="______"), + * @OA\Property(property="promo_discount", type="number", example=10, description="______"), * @OA\Property(property="is_amount_discount", type="boolean", example="true", description="______"), * @OA\Property(property="allow_cancellation", type="boolean", example="true", description="______"), * @OA\Property(property="per_seat_enabled", type="boolean", example="true", description="______"), diff --git a/app/Http/Controllers/RecurringInvoiceController.php b/app/Http/Controllers/RecurringInvoiceController.php index e28a59c715df..e36a8f624924 100644 --- a/app/Http/Controllers/RecurringInvoiceController.php +++ b/app/Http/Controllers/RecurringInvoiceController.php @@ -439,7 +439,7 @@ class RecurringInvoiceController extends BaseController /** * @OA\Get( * path="/api/v1/recurring_invoice/{invitation_key}/download", - * operationId="downloadInvoice", + * operationId="downloadRecurringInvoice", * tags={"invoices"}, * summary="Download a specific invoice by invitation key", * description="Downloads a specific invoice", diff --git a/app/Http/Controllers/TwoFactorController.php b/app/Http/Controllers/TwoFactorController.php index cbee6da80188..8ffca0c5fc9e 100644 --- a/app/Http/Controllers/TwoFactorController.php +++ b/app/Http/Controllers/TwoFactorController.php @@ -38,7 +38,7 @@ class TwoFactorController extends BaseController $data = [ 'secret' => $secret, - 'qrCode' => $qrCode, + 'qrCode' => $qr_code, ]; return response()->json(['data' => $data], 200); diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 3c0275d68b57..7dc70f9ee53d 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -31,18 +31,12 @@ class PasswordProtection */ public function handle($request, Closure $next) { - // {nlog($request->headers->all()); - // nlog($request->all()); $error = [ 'message' => 'Invalid Password', 'errors' => new stdClass, ]; - nlog(Cache::get(auth()->user()->hashed_id.'_logged_in')); - nlog($request->header('X-API-OAUTH-PASSWORD')); - - if (Cache::get(auth()->user()->hashed_id.'_logged_in')) { Cache::pull(auth()->user()->hashed_id.'_logged_in'); @@ -94,4 +88,4 @@ class PasswordProtection } -} +} \ No newline at end of file diff --git a/config/queue.php b/config/queue.php index 6fb44cc4f7a1..dc2d4665ea47 100644 --- a/config/queue.php +++ b/config/queue.php @@ -13,7 +13,7 @@ return [ | */ - 'default' => env('QUEUE_CONNECTION', 'database'), + 'default' => env('QUEUE_CONNECTION', 'sync'), /* |-------------------------------------------------------------------------- From 868b6efd8e05d50c3b9b7007118cf35cff7d35ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 9 Mar 2021 14:24:31 +0100 Subject: [PATCH 16/20] Add currency_id to billing_subscriptions table --- app/Models/BillingSubscription.php | 1 + ...ency_id_to_billing_subscriptions_table.php | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 database/migrations/2021_03_09_132242_add_currency_id_to_billing_subscriptions_table.php diff --git a/app/Models/BillingSubscription.php b/app/Models/BillingSubscription.php index 7e3da4135bba..486e3134d607 100644 --- a/app/Models/BillingSubscription.php +++ b/app/Models/BillingSubscription.php @@ -41,6 +41,7 @@ class BillingSubscription extends BaseModel 'plan_map', 'refund_period', 'webhook_configuration', + 'currency_id', ]; protected $casts = [ diff --git a/database/migrations/2021_03_09_132242_add_currency_id_to_billing_subscriptions_table.php b/database/migrations/2021_03_09_132242_add_currency_id_to_billing_subscriptions_table.php new file mode 100644 index 000000000000..bc687317a449 --- /dev/null +++ b/database/migrations/2021_03_09_132242_add_currency_id_to_billing_subscriptions_table.php @@ -0,0 +1,29 @@ +unsignedInteger('currency_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + } +} From 83b23956f6d34e9f9c7c937593b6bc9f363276a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 9 Mar 2021 14:29:57 +0100 Subject: [PATCH 17/20] Update authorize methods on billing subscription requests --- .../BillingSubscription/CreateBillingSubscriptionRequest.php | 4 ++-- .../BillingSubscription/DestroyBillingSubscriptionRequest.php | 2 +- .../BillingSubscription/EditBillingSubscriptionRequest.php | 4 +--- .../BillingSubscription/ShowBillingSubscriptionRequest.php | 3 +-- .../BillingSubscription/StoreBillingSubscriptionRequest.php | 3 ++- .../BillingSubscription/UpdateBillingSubscriptionRequest.php | 2 +- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php index f7f0a907343d..57bde4545018 100644 --- a/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php +++ b/app/Http/Requests/BillingSubscription/CreateBillingSubscriptionRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests\BillingSubscription; use App\Http\Requests\Request; +use App\Models\BillingSubscription; class CreateBillingSubscriptionRequest extends Request { @@ -13,8 +14,7 @@ class CreateBillingSubscriptionRequest extends Request */ public function authorize(): bool { - return true; -// return auth()->user()->can('create', BillingSubscription::class); // TODO + return auth()->user()->can('create', BillingSubscription::class); } /** diff --git a/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php index c263836d9442..2abde3a9b43d 100644 --- a/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php +++ b/app/Http/Requests/BillingSubscription/DestroyBillingSubscriptionRequest.php @@ -14,7 +14,7 @@ class DestroyBillingSubscriptionRequest extends Request */ public function authorize() { - return true; // TODO + return auth()->user()->can('edit', $this->billing_subscription); } /** diff --git a/app/Http/Requests/BillingSubscription/EditBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/EditBillingSubscriptionRequest.php index dcb3f966bcd5..74538a8a79f9 100644 --- a/app/Http/Requests/BillingSubscription/EditBillingSubscriptionRequest.php +++ b/app/Http/Requests/BillingSubscription/EditBillingSubscriptionRequest.php @@ -14,9 +14,7 @@ class EditBillingSubscriptionRequest extends Request */ public function authorize() { - return true; - - // return auth()->user()->can('view', $this->billing_subscription); // TODO + return auth()->user()->can('edit', $this->billing_subscription); } /** diff --git a/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php index f5d08bec9445..e5ee4828f3da 100644 --- a/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php +++ b/app/Http/Requests/BillingSubscription/ShowBillingSubscriptionRequest.php @@ -14,8 +14,7 @@ class ShowBillingSubscriptionRequest extends Request */ public function authorize() : bool { - return true; -// return auth()->user()->can('view', $this->billing_subscription); // TODO + return auth()->user()->can('view', $this->billing_subscription); } /** diff --git a/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php index c897edc5b299..58d99d06d1f5 100644 --- a/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php +++ b/app/Http/Requests/BillingSubscription/StoreBillingSubscriptionRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests\BillingSubscription; use App\Http\Requests\Request; +use App\Models\BillingSubscription; class StoreBillingSubscriptionRequest extends Request { @@ -13,7 +14,7 @@ class StoreBillingSubscriptionRequest extends Request */ public function authorize() { - return true; // TODO + return auth()->user()->can('create', BillingSubscription::class); } /** diff --git a/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php b/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php index a92d14ff0b33..2436def991ed 100644 --- a/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php +++ b/app/Http/Requests/BillingSubscription/UpdateBillingSubscriptionRequest.php @@ -16,7 +16,7 @@ class UpdateBillingSubscriptionRequest extends Request */ public function authorize() { - return true; // TODO + return auth()->user()->can('edit', $this->billing_subscription); } /** From 737020eeedcbed8a861d97f847eae52c867e96be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 9 Mar 2021 15:25:11 +0100 Subject: [PATCH 18/20] Fix wrong variable in the BillingSubscriptionTransformer.php --- app/Transformers/BillingSubscriptionTransformer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Transformers/BillingSubscriptionTransformer.php b/app/Transformers/BillingSubscriptionTransformer.php index 8b2f376f9762..8adbd2568ac2 100644 --- a/app/Transformers/BillingSubscriptionTransformer.php +++ b/app/Transformers/BillingSubscriptionTransformer.php @@ -58,9 +58,9 @@ class BillingSubscriptionTransformer extends EntityTransformer 'refund_period' => (int)$billing_subscription->refund_period, 'webhook_configuration' => (string)$billing_subscription->webhook_configuration, 'is_deleted' => (bool)$billing_subscription->is_deleted, - 'created_at' => (int) $credit->created_at, - 'updated_at' => (int) $credit->updated_at, - 'archived_at' => (int) $credit->deleted_at, + 'created_at' => (int)$billing_subscription->created_at, + 'updated_at' => (int)$billing_subscription->updated_at, + 'archived_at' => (int)$billing_subscription->deleted_at, ]; } From fa5fa7d527276db842bc3253c08106ad31474fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Tue, 9 Mar 2021 15:25:50 +0100 Subject: [PATCH 19/20] Feature tests for billing subscription API --- .../factories/BillingSubscriptionFactory.php | 38 ++++++ tests/Feature/BillingSubscriptionApiTest.php | 123 ++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 database/factories/BillingSubscriptionFactory.php create mode 100644 tests/Feature/BillingSubscriptionApiTest.php diff --git a/database/factories/BillingSubscriptionFactory.php b/database/factories/BillingSubscriptionFactory.php new file mode 100644 index 000000000000..2fdf320cd5f4 --- /dev/null +++ b/database/factories/BillingSubscriptionFactory.php @@ -0,0 +1,38 @@ +makeTestData(); + + Session::start(); + + $this->faker = \Faker\Factory::create(); + + Model::reguard(); + } + + public function testExpenseGet() + { + $product = Product::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + + $billing_subscription = BillingSubscription::factory()->create([ + 'product_id' => $product->id, + 'company_id' => $this->company->id, + ]); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/billing_subscriptions/' . $this->encodePrimaryKey($billing_subscription->id)); + + $response->assertStatus(200); + } + + public function testBillingSubscriptionsPost() + { + $product = Product::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/billing_subscriptions', ['product_id' => $product->id, 'allow_cancellation' => true]); + + $response->assertStatus(200); + } + + public function testBillingSubscriptionPut() + { + $product = Product::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + + $response1 = $this + ->withHeaders(['X-API-SECRET' => config('ninja.api_secret'),'X-API-TOKEN' => $this->token]) + ->post('/api/v1/billing_subscriptions', ['product_id' => $product->id]) + ->assertStatus(200) + ->json(); + + $response2 = $this + ->withHeaders(['X-API-SECRET' => config('ninja.api_secret'),'X-API-TOKEN' => $this->token]) + ->put('/api/v1/billing_subscriptions/' . $response1['data']['id'], ['allow_cancellation' => true]) + ->assertStatus(200) + ->json(); + + $this->assertNotEquals($response1['data']['allow_cancellation'], $response2['data']['allow_cancellation']); + } + + /* + TypeError : Argument 1 passed to App\Transformers\BillingSubscriptionTransformer::transform() must be an instance of App\Models\BillingSubscription, bool given, called in /var/www/html/vendor/league/fractal/src/Scope.php on line 407 + /var/www/html/app/Transformers/BillingSubscriptionTransformer.php:35 + /var/www/html/vendor/league/fractal/src/Scope.php:407 + /var/www/html/vendor/league/fractal/src/Scope.php:349 + /var/www/html/vendor/league/fractal/src/Scope.php:235 + /var/www/html/app/Http/Controllers/BaseController.php:395 + /var/www/html/app/Http/Controllers/BillingSubscriptionController.php:408 + */ + public function testBillingSubscriptionDeleted() + { + $this->markTestSkipped(); + + $product = Product::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + + $billing_subscription = BillingSubscription::factory()->create([ + 'product_id' => $product->id, + 'company_id' => $this->company->id, + ]); + + $response = $this + ->withHeaders(['X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token]) + ->delete('/api/v1/billing_subscriptions/' . $this->encodePrimaryKey($billing_subscription->id)) + ->assertStatus(200) + ->json(); + + dd($response); + } +} From 90b3b25849333cc7d3e62b0a6c319f2306fa2931 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 10 Mar 2021 09:23:13 +1100 Subject: [PATCH 20/20] Fixes for tests - changed listResponse to itemResponse --- .../Controllers/BillingSubscriptionController.php | 2 +- tests/Feature/BillingSubscriptionApiTest.php | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/BillingSubscriptionController.php b/app/Http/Controllers/BillingSubscriptionController.php index 6c858bd1ed59..199d51005114 100644 --- a/app/Http/Controllers/BillingSubscriptionController.php +++ b/app/Http/Controllers/BillingSubscriptionController.php @@ -405,6 +405,6 @@ class BillingSubscriptionController extends BaseController { $this->billing_subscription_repo->delete($billing_subscription); - return $this->listResponse($billing_subscription->fresh()); + return $this->itemResponse($billing_subscription->fresh()); } } diff --git a/tests/Feature/BillingSubscriptionApiTest.php b/tests/Feature/BillingSubscriptionApiTest.php index c185968ba1e4..cd66f20f063a 100644 --- a/tests/Feature/BillingSubscriptionApiTest.php +++ b/tests/Feature/BillingSubscriptionApiTest.php @@ -1,4 +1,13 @@ markTestSkipped(); $product = Product::factory()->create([ 'company_id' => $this->company->id, @@ -118,6 +130,5 @@ class BillingSubscriptionApiTest extends TestCase ->assertStatus(200) ->json(); - dd($response); } }