Merge pull request #5225 from beganovich/v5-2203-billing-portal

(v5) 2203: Billing portal
This commit is contained in:
Benjamin Beganović 2021-03-23 16:24:04 +01:00 committed by GitHub
commit 7b89f1cd1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 45 deletions

View File

@ -0,0 +1,47 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\DataMapper\Billing;
class BillingContextMapper
{
/**
* @var int
*/
public $billing_subscription_id;
/**
* @var string
*/
public $email;
/**
* @var int
*/
public $client_id;
/**
* @var int
*/
public $invoice_id;
/**
* @var string[]
*/
public $casts = [
'billing_subscription_id' => 'integer',
'email' => 'string',
'client_id' => 'integer',
'invoice_id' => 'integer',
];
}

View File

@ -16,6 +16,7 @@ use App\Http\Controllers\Controller;
use App\Models\BillingSubscription; use App\Models\BillingSubscription;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -41,7 +42,9 @@ class BillingSubscriptionPurchaseController extends Controller
*/ */
private function setLocale(string $locale): void private function setLocale(string $locale): void
{ {
$record = DB::table('languages')->where('locale', $locale)->first(); $record = Cache::get('languages')->filter(function ($item) use ($locale) {
return $item->locale == $locale;
})->first();
if ($record) { if ($record) {
App::setLocale($record->locale); App::setLocale($record->locale);

View File

@ -17,6 +17,7 @@ use App\Models\ClientContact;
use App\Models\Invoice; use App\Models\Invoice;
use App\Repositories\ClientContactRepository; use App\Repositories\ClientContactRepository;
use App\Repositories\ClientRepository; use App\Repositories\ClientRepository;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -138,6 +139,13 @@ class BillingPortalPurchase extends Component
*/ */
public $request_data; public $request_data;
/**
* Price of product.
*
* @var string
*/
public $price;
/** /**
* Handle user authentication * Handle user authentication
* *
@ -192,7 +200,11 @@ class BillingPortalPurchase extends Component
]; ];
if (array_key_exists('locale', $this->request_data)) { if (array_key_exists('locale', $this->request_data)) {
$record = DB::table('languages')->where('locale', $this->request_data['locale'])->first(); $request = $this->request_data;
$record = Cache::get('languages')->filter(function ($item) use ($request) {
return $item->locale == $request['locale'];
})->first();
if ($record) { if ($record) {
$data['settings']['language_id'] = (string)$record->id; $data['settings']['language_id'] = (string)$record->id;
@ -262,7 +274,7 @@ class BillingPortalPurchase extends Component
'client_contact_id' => $this->contact->hashed_id, 'client_contact_id' => $this->contact->hashed_id,
]], ]],
'user_input_promo_code' => $this->coupon, 'user_input_promo_code' => $this->coupon,
'coupon' => $this->coupon, 'coupon' => empty($this->billing_subscription->promo_code) ? '' : $this->coupon,
'quantity' => $this->quantity, 'quantity' => $this->quantity,
]; ];
@ -280,7 +292,7 @@ class BillingPortalPurchase extends Component
'client_id' => $this->contact->client->id, 'client_id' => $this->contact->client->id,
'invoice_id' => $this->invoice->id, 'invoice_id' => $this->invoice->id,
'quantity' => $this->quantity, 'quantity' => $this->quantity,
'subscription_id' => $this->billing_subscription->id], 'subscription_id' => $this->billing_subscription->id,
now()->addMinutes(60) now()->addMinutes(60)
); );
@ -296,6 +308,8 @@ class BillingPortalPurchase extends Component
{ {
return $this->billing_subscription->service()->startTrial([ return $this->billing_subscription->service()->startTrial([
'email' => $this->email ?? $this->contact->email, 'email' => $this->email ?? $this->contact->email,
'quantity' => $this->quantity,
'contact_id' => $this->contact->id,
]); ]);
} }
@ -311,14 +325,19 @@ class BillingPortalPurchase extends Component
return $this->quantity; return $this->quantity;
} }
// TODO: Dave review. if ($this->quantity >= $this->billing_subscription->max_seats_limit && $option == 'increment') {
if ($this->quantity >= $this->billing_subscription->max_seats_limit) {
return $this->quantity; return $this->quantity;
} }
return $option == 'increment' if ($option == 'increment') {
? $this->quantity++ $this->quantity++;
: $this->quantity--; return $this->price = (int)$this->price + $this->billing_subscription->product->price;
}
$this->quantity--;
$this->price = (int)$this->price - $this->billing_subscription->product->price;
return 0;
} }
public function render() public function render()

View File

@ -2,7 +2,7 @@
@section('meta_title', $billing_subscription->product->product_key) @section('meta_title', $billing_subscription->product->product_key)
@section('body') @section('body')
@livewire('billing-portal-purchase', ['billing_subscription' => $billing_subscription, 'contact' => auth('contact')->user(), 'hash' => $hash, 'request_data' => $request_data]) @livewire('billing-portal-purchase', ['billing_subscription' => $billing_subscription, 'contact' => auth('contact')->user(), 'hash' => $hash, 'request_data' => $request_data, 'price' => $billing_subscription->product->price])
@stop @stop
@push('footer') @push('footer')

View File

@ -13,32 +13,35 @@
<span class="text-sm uppercase font-bold">{{ ctrans('texts.price') }}:</span> <span class="text-sm uppercase font-bold">{{ ctrans('texts.price') }}:</span>
<div class="flex space-x-2"> <div class="flex space-x-2">
<h1 class="text-2xl font-bold tracking-wide">{{ App\Utils\Number::formatMoney($billing_subscription->product->price, $billing_subscription->company) }}</h1> <h1 class="text-2xl font-bold tracking-wide">{{ App\Utils\Number::formatMoney($price, $billing_subscription->company) }}</h1>
@if($billing_subscription->per_seat_enabled) @if($billing_subscription->is_recurring)
<span class="text-sm">/unit</span> <span class="text-xs uppercase">/ {{ \App\Models\RecurringInvoice::frequencyForKey($billing_subscription->frequency_id) }}</span>
@endif @endif
</div> </div>
<div class="flex mt-4 space-x-4 items-center">
<span class="text-sm">{{ ctrans('texts.qty') }}</span> @if($billing_subscription->per_seat_enabled && $billing_subscription->max_seats_limit > 1)
<button wire:click="updateQuantity('decrement')" class="bg-gray-100 border rounded p-1"> <div class="flex mt-4 space-x-4 items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" <span class="text-sm">{{ ctrans('texts.qty') }}</span>
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" <button wire:click="updateQuantity('decrement')" class="bg-gray-100 border rounded p-1">
class="feather feather-minus"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
<line x1="5" y1="12" x2="19" y2="12"></line> stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
</svg> class="feather feather-minus">
</button> <line x1="5" y1="12" x2="19" y2="12"></line>
<button>{{ $quantity }}</button> </svg>
<button wire:click="updateQuantity('increment')" class="bg-gray-100 border rounded p-1"> </button>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" <button>{{ $quantity }}</button>
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" <button wire:click="updateQuantity('increment')" class="bg-gray-100 border rounded p-1">
class="feather feather-plus"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
<line x1="12" y1="5" x2="12" y2="19"></line> stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
<line x1="5" y1="12" x2="19" y2="12"></line> class="feather feather-plus">
</svg> <line x1="12" y1="5" x2="12" y2="19"></line>
</button> <line x1="5" y1="12" x2="19" y2="12"></line>
</div> </svg>
</button>
</div>
@endif
@if(auth('contact')->user()) @if(auth('contact')->user())
<a href="{{ route('client.invoices.index') }}" class="block mt-16 inline-flex items-center space-x-2"> <a href="{{ route('client.invoices.index') }}" class="block mt-16 inline-flex items-center space-x-2">
@ -137,22 +140,24 @@
</form> </form>
@endif @endif
<div class="relative mt-8"> @if(!empty($billing_subscription->promo_code) && !$billing_subscription->trial_enabled)
<div class="absolute inset-0 flex items-center"> <div class="relative mt-8">
<div class="w-full border-t border-gray-300"></div> <div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm leading-5">
<span class="px-2 text-gray-700 bg-white">Have a coupon code?</span>
</div>
</div> </div>
<div class="relative flex justify-center text-sm leading-5"> <div class="flex items-center mt-4">
<span class="px-2 text-gray-700 bg-white">Have a coupon code?</span> <label class="w-full mr-2">
<input type="text" wire:model.lazy="coupon" class="input w-full m-0"/>
<small class="block text-gray-900 mt-2">{{ ctrans('texts.billing_coupon_notice') }}</small>
</label>
</div> </div>
</div> @endif
<div class="flex items-center mt-4">
<label class="w-full mr-2">
<input type="text" wire:model.lazy="coupon" class="input w-full m-0"/>
<small class="block text-gray-900 mt-2">{{ ctrans('texts.billing_coupon_notice') }}</small>
</label>
</div>
</div> </div>
</div> </div>
</div> </div>