Support for "Start Free Trial"

This commit is contained in:
Benjamin Beganović 2021-03-18 14:14:10 +01:00
parent 85c09ecf11
commit 415ea81eec
3 changed files with 141 additions and 8 deletions

View File

@ -12,7 +12,9 @@
namespace App\Http\Livewire; namespace App\Http\Livewire;
use App\Factory\ClientFactory; use App\Factory\ClientFactory;
use App\Models\BillingSubscription;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\Invoice;
use App\Repositories\ClientContactRepository; use App\Repositories\ClientContactRepository;
use App\Repositories\ClientRepository; use App\Repositories\ClientRepository;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
@ -21,39 +23,111 @@ use Livewire\Component;
class BillingPortalPurchase extends Component class BillingPortalPurchase extends Component
{ {
/**
* Random hash generated by backend to handle the tracking of state.
*
* @var string
*/
public $hash; public $hash;
public $heading_text = 'Log in'; /**
* Top level text on the left side of billing page.
*
* @var string
*/
public $heading_text;
/**
* E-mail address model for user input.
*
* @var string
*/
public $email; public $email;
/**
* Password model for user input.
*
* @var string
*/
public $password; public $password;
/**
* Instance of billing subscription.
*
* @var BillingSubscription
*/
public $billing_subscription; public $billing_subscription;
/**
* Instance of client contact.
*
* @var null|ClientContact
*/
public $contact; public $contact;
/**
* Rules for validating the form.
*
* @var \string[][]
*/
protected $rules = [ protected $rules = [
'email' => ['required', 'email'], 'email' => ['required', 'email'],
]; ];
/**
* Id for CompanyGateway record.
*
* @var string|integer
*/
public $company_gateway_id; public $company_gateway_id;
/**
* Id for GatewayType.
*
* @var string|integer
*/
public $payment_method_id; public $payment_method_id;
/**
* List of steps that frontend form follows.
*
* @var array
*/
public $steps = [ public $steps = [
'passed_email' => false, 'passed_email' => false,
'existing_user' => false, 'existing_user' => false,
'fetched_payment_methods' => false, 'fetched_payment_methods' => false,
'fetched_client' => false, 'fetched_client' => false,
'show_start_trial' => false,
]; ];
/**
* List of payment methods fetched from client.
*
* @var array
*/
public $methods = []; public $methods = [];
/**
* Instance of \App\Models\Invoice
*
* @var Invoice
*/
public $invoice; public $invoice;
/**
* Coupon model for user input
*
* @var string
*/
public $coupon; public $coupon;
/**
* Handle user authentication
*
* @return $this|bool|void
*/
public function authenticate() public function authenticate()
{ {
$this->validate(); $this->validate();
@ -81,6 +155,12 @@ class BillingPortalPurchase extends Component
} }
} }
/**
* Create a blank client. Used for new customers purchasing.
*
* @return mixed
* @throws \Laracasts\Presenter\Exceptions\PresenterException
*/
protected function createBlankClient() protected function createBlankClient()
{ {
$company = $this->billing_subscription->company; $company = $this->billing_subscription->company;
@ -98,13 +178,26 @@ class BillingPortalPurchase extends Component
return $client->contacts->first(); return $client->contacts->first();
} }
/**
* Fetching payment methods from the client.
*
* @param ClientContact $contact
* @return $this
*/
protected function getPaymentMethods(ClientContact $contact): self protected function getPaymentMethods(ClientContact $contact): self
{ {
if ($this->billing_subscription->trial_enabled) {
$this->heading_text = ctrans('texts.plan_trial');
$this->steps['show_start_trial'] = true;
return $this;
}
$this->steps['fetched_payment_methods'] = true; $this->steps['fetched_payment_methods'] = true;
$this->methods = $contact->client->service()->getPaymentMethods(1000); $this->methods = $contact->client->service()->getPaymentMethods(1000);
$this->heading_text = 'Pick a payment method'; $this->heading_text = ctrans('texts.payment_methods');
Auth::guard('contact')->login($contact); Auth::guard('contact')->login($contact);
@ -113,6 +206,13 @@ class BillingPortalPurchase extends Component
return $this; return $this;
} }
/**
* Middle method between selecting payment method &
* submitting the from to the backend.
*
* @param $company_gateway_id
* @param $gateway_type_id
*/
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id) public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
{ {
$this->company_gateway_id = $company_gateway_id; $this->company_gateway_id = $company_gateway_id;
@ -121,6 +221,11 @@ class BillingPortalPurchase extends Component
$this->handleBeforePaymentEvents(); $this->handleBeforePaymentEvents();
} }
/**
* Method to handle events before payments.
*
* @return void
*/
public function handleBeforePaymentEvents() public function handleBeforePaymentEvents()
{ {
$data = [ $data = [
@ -144,13 +249,26 @@ class BillingPortalPurchase extends Component
Cache::put($this->hash, [ Cache::put($this->hash, [
'email' => $this->email ?? $this->contact->email, 'email' => $this->email ?? $this->contact->email,
'client_id' => $this->contact->client->id, 'client_id' => $this->contact->client->id,
'invoice_id' => $this->invoice->id], 'invoice_id' => $this->invoice->id,
'subscription_id' => $this->billing_subscription->id],
now()->addMinutes(60) now()->addMinutes(60)
); );
$this->emit('beforePaymentEventsCompleted'); $this->emit('beforePaymentEventsCompleted');
} }
/**
* Proxy method for starting the trial.
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function handleTrial()
{
return $this->billing_subscription->service()->startTrial([
'email' => $this->email ?? $this->contact->email,
]);
}
public function render() public function render()
{ {
if ($this->contact instanceof ClientContact) { if ($this->contact instanceof ClientContact) {

View File

@ -38,18 +38,22 @@ class BillingSubscriptionService
// At this point we have some state carried from the billing page // At this point we have some state carried from the billing page
// to this, available as $payment_hash->data->billing_context. Make something awesome ⭐ // to this, available as $payment_hash->data->billing_context. Make something awesome ⭐
// create client subscription record // create client subscription record
// //
// create recurring invoice if is_recurring // create recurring invoice if is_recurring
// //
} }
public function startTrial(array $data) public function startTrial(array $data)
{ {
// Redirects from here work just fine. Livewire will respect it.
// Some magic here..
return redirect('/trial-started');
} }
public function createInvoice($data): ?\App\Models\Invoice public function createInvoice($data): ?\App\Models\Invoice

View File

@ -32,14 +32,14 @@
<div class="col-span-12 lg:col-span-6 bg-white lg:shadow-lg lg:h-screen"> <div class="col-span-12 lg:col-span-6 bg-white lg:shadow-lg lg:h-screen">
<div class="grid grid-cols-12 flex flex-col p-10 lg:mt-48 lg:ml-16"> <div class="grid grid-cols-12 flex flex-col p-10 lg:mt-48 lg:ml-16">
<div class="col-span-12 w-full lg:col-span-6"> <div class="col-span-12 w-full lg:col-span-6">
<h2 class="text-2xl font-bold tracking-wide">{{ $heading_text }}</h2> <h2 class="text-2xl font-bold tracking-wide">{{ $heading_text ?? ctrans('texts.login') }}</h2>
@if (session()->has('message')) @if (session()->has('message'))
@component('portal.ninja2020.components.message') @component('portal.ninja2020.components.message')
{{ session('message') }} {{ session('message') }}
@endcomponent @endcomponent
@endif @endif
@if($this->steps['fetched_payment_methods']) @if($steps['fetched_payment_methods'])
<div class="flex items-center mt-4 text-sm"> <div class="flex items-center mt-4 text-sm">
<form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}" <form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}"
method="post" method="post"
@ -67,6 +67,17 @@
</button> </button>
@endforeach @endforeach
</div> </div>
@elseif($steps['show_start_trial'])
<form wire:submit.prevent="handleTrial" class="mt-8">
@csrf
<p class="mb-4">Some text about the trial goes here. Details about the days, etc.</p>
<button class="px-3 py-2 border rounded mr-4 hover:border-blue-600">
{{ ctrans('texts.trial_call_to_action') }}
</button>
</form>
@else @else
<form wire:submit.prevent="authenticate" class="mt-8"> <form wire:submit.prevent="authenticate" class="mt-8">
@csrf @csrf