mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge pull request #5225 from beganovich/v5-2203-billing-portal
(v5) 2203: Billing portal
This commit is contained in:
commit
7b89f1cd1e
47
app/DataMapper/Billing/BillingContextMapper.php
Normal file
47
app/DataMapper/Billing/BillingContextMapper.php
Normal 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',
|
||||||
|
];
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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()
|
||||||
|
@ -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')
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user