new payment flow

This commit is contained in:
David Bomba 2024-07-02 14:54:50 +10:00
parent e1be33314e
commit 6f5a08f87d
8 changed files with 181 additions and 41 deletions

View File

@ -25,23 +25,23 @@ class PaymentMethod extends Component
public $methods = []; public $methods = [];
public $isLoading = true; public $isLoading = false;
public function placeholder() // public function placeholder()
{ // {
return <<<'HTML' // return <<<'HTML'
<div class="flex items-center justify-center min-h-screen"> // <div class="flex items-center justify-center min-h-screen">
<svg class="animate-spin h-10 w-10 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> // <svg class="animate-spin h-10 w-10 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> // <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> // <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg> // </svg>
</div> // </div>
HTML; // HTML;
} // }
public function mount() public function mount()
{ {
$this->isLoading = true; // $this->isLoading = true;
$this->invoice = $this->context['invoice']; $this->invoice = $this->context['invoice'];
$this->variables = $this->context['variables']; $this->variables = $this->context['variables'];
@ -49,20 +49,17 @@ class PaymentMethod extends Component
$this->methods = $this->invoice->client->service()->getPaymentMethods($this->invoice->balance); $this->methods = $this->invoice->client->service()->getPaymentMethods($this->invoice->balance);
// $this->isLoading = false;
// $this->dispatch('loadingCompleted');
if(count($this->methods) == 1) { if(count($this->methods) == 1) {
$this->dispatch('payment-method-selected', company_gateway_id: $this->methods[0]['company_gateway_id'], gateway_type_id: $this->methods[0]['gateway_type_id'], amount: $this->invoice->balance); $this->dispatch('singlePaymentMethodFound', company_gateway_id: $this->methods[0]['company_gateway_id'], gateway_type_id: $this->methods[0]['gateway_type_id'], amount: $this->invoice->balance);
}
else {
$this->isLoading = false;
$this->dispatch('loadingCompleted');
} }
} }
public function render() public function render()
{ {
//If there is only one payment method, skip display and push straight to the form!! //If there is only one payment method, skip display and push straight to the form!!
return render('components.livewire.payment_method-flow2', ['methods' => $this->methods, 'amount' => $this->invoice->balance]); return render('components.livewire.payment_method-flow2', ['methods' => $this->methods, 'amount' => $this->invoice->balance]);
} }

View File

@ -31,18 +31,7 @@ class ProcessPayment extends Component
public $isLoading = true; public $isLoading = true;
// public function toJSON()
// {
// nlog("why");
// }
public function mount() public function mount()
{
// $this->loadData();
}
public function boot()
{ {
MultiDB::setDb($this->context['invoice']->company->db); MultiDB::setDb($this->context['invoice']->company->db);
@ -68,8 +57,9 @@ class ProcessPayment extends Component
$this->component_view = ''; $this->component_view = '';
if(!$responder_data['success']) if(!$responder_data['success']) {
throw new PaymentFailed($responder_data['error'], 400); throw new PaymentFailed($responder_data['error'], 400);
}
$driver = $company_gateway $driver = $company_gateway
->driver($invitation->contact->client) ->driver($invitation->contact->client)
@ -116,10 +106,15 @@ class ProcessPayment extends Component
'company_gateway' => $this->payment_data_payload['company_gateway'], 'company_gateway' => $this->payment_data_payload['company_gateway'],
]; ];
// nlog(array_keys($this->payment_data_payload));
$this->isLoading = false; $this->isLoading = false;
}
public function boot()
{
} }
public function render() public function render()

View File

@ -0,0 +1,30 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Livewire\Flow2;
use Livewire\Component;
class RequiredFields extends Component
{
public $context;
public function mount()
{
}
public function render()
{
return render('components.livewire.required-fields');
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Livewire\Flow2;
use App\Utils\Number;
use Livewire\Component;
class UnderOverPayment extends Component
{
public $context;
public $payableAmount;
public $currency;
public function mount()
{
$invoice = $this->context['invoice'];
$invoice_amount = $invoice->partial > 0 ? $invoice->partial : $invoice->balance;
$this->currency = $invoice->client->currency();
$this->payableAmount = Number::formatValue($invoice_amount, $this->currency);
}
public function render()
{
return render('components.livewire.under-over-payments',[
'settings' => $this->context['settings'],
]);
}
}

View File

@ -22,6 +22,8 @@ use Livewire\Attributes\Computed;
use Livewire\Attributes\Reactive; use Livewire\Attributes\Reactive;
use App\Livewire\Flow2\PaymentMethod; use App\Livewire\Flow2\PaymentMethod;
use App\Livewire\Flow2\ProcessPayment; use App\Livewire\Flow2\ProcessPayment;
use App\Livewire\Flow2\UnderOverPayment;
use App\Utils\Number;
class InvoicePay extends Component class InvoicePay extends Component
{ {
@ -37,6 +39,12 @@ class InvoicePay extends Component
public $payment_method_accepted = false; public $payment_method_accepted = false;
public $under_over_payment = false;
public $required_fields = false;
public $ready = true;
public array $context = []; public array $context = [];
#[On('update.context')] #[On('update.context')]
@ -70,9 +78,17 @@ class InvoicePay extends Component
} }
#[On('payable-amount')]
public function payableAmount($payable_amount)
{
$this->context['payable_invoices'][0]['amount'] = Number::parseFloat($payable_amount);
$this->under_over_payment = false;
}
#[On('payment-method-selected')] #[On('payment-method-selected')]
public function paymentMethodSelected($company_gateway_id, $gateway_type_id, $amount) public function paymentMethodSelected($company_gateway_id, $gateway_type_id, $amount)
{ {
$this->context['company_gateway_id'] = $company_gateway_id; $this->context['company_gateway_id'] = $company_gateway_id;
$this->context['gateway_type_id'] = $gateway_type_id; $this->context['gateway_type_id'] = $gateway_type_id;
$this->context['amount'] = $amount; $this->context['amount'] = $amount;
@ -83,6 +99,8 @@ class InvoicePay extends Component
// $this->invite = \App\Models\InvoiceInvitation::withTrashed()->find($this->invitation_id)->withoutRelations(); // $this->invite = \App\Models\InvoiceInvitation::withTrashed()->find($this->invitation_id)->withoutRelations();
$this->payment_method_accepted =true; $this->payment_method_accepted =true;
} }
#[Computed()] #[Computed()]
@ -94,9 +112,13 @@ class InvoicePay extends Component
if(!$this->signature_accepted) if(!$this->signature_accepted)
return Signature::class; return Signature::class;
if($this->under_over_payment)
return UnderOverPayment::class;
if(!$this->payment_method_accepted) if(!$this->payment_method_accepted)
return PaymentMethod::class; return PaymentMethod::class;
// if($this->ready)
return ProcessPayment::class; return ProcessPayment::class;
} }
@ -116,13 +138,23 @@ class InvoicePay extends Component
$client = $invite->contact->client; $client = $invite->contact->client;
$variables = ($invite && auth()->guard('contact')->user()->client->getSetting('show_accept_invoice_terms')) ? (new HtmlEngine($invite))->generateLabelsAndValues() : false; $variables = ($invite && auth()->guard('contact')->user()->client->getSetting('show_accept_invoice_terms')) ? (new HtmlEngine($invite))->generateLabelsAndValues() : false;
$settings = $client->getMergedSettings(); $settings = $client->getMergedSettings();
$this->context['settings'] = $settings;
//under-over / payment
//required fields
$this->terms_accepted = !$settings->show_accept_invoice_terms; $this->terms_accepted = !$settings->show_accept_invoice_terms;
$this->signature_accepted = !$settings->require_invoice_signature; $this->signature_accepted = !$settings->require_invoice_signature;
$this->under_over_payment = $settings->client_portal_allow_over_payment || $settings->client_portal_allow_under_payment;
$this->required_fields = false;
$this->context['variables'] = $variables; $this->context['variables'] = $variables;
$this->context['invoice'] = $invite->invoice; $this->context['invoice'] = $invite->invoice;
$this->context['settings'] = $settings; $this->context['settings'] = $settings;
$this->context['payable_invoices'] = ['invoice_id' => $this->context['invoice']->hashed_id, 'amount' => $invite->invoice->partial > 0 ? $invite->invoice->partial : $invite->invoice->balance];
} }
public function render() public function render()

View File

@ -29,6 +29,10 @@
isLoading = false; isLoading = false;
}); });
Livewire.on('singlePaymentMethodFound', (event) => {
$wire.dispatch('payment-method-selected', {company_gateway_id: event.company_gateway_id, gateway_type_id: event.gateway_type_id, amount: event.amount })
});
const buttons = document.querySelectorAll('.payment-method'); const buttons = document.querySelectorAll('.payment-method');
buttons.forEach(button => { buttons.forEach(button => {

View File

@ -0,0 +1,3 @@
<div>
</div>

View File

@ -0,0 +1,37 @@
<div x-data="{ payableAmount: '{{ $payableAmount }}' }" class="px-4 py-5 bg-white sm:gap-4 sm:px-6">
<dt class="text-sm font-medium leading-5 text-gray-500">
{{ ctrans('texts.payment_amount') }}
</dt>
<dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2 flex flex-col">
<div class="flex items-center">
<label>
<span class="mt-2">{{ $currency->code }} ({{ $currency->symbol }})</span>
<input
type="text"
class="input mt-0 mr-4 relative"
name="payable_amount"
x-model="payableAmount"
value="{{ $payableAmount }}"/>
</label>
</div>
@if($settings->client_portal_allow_under_payment)
<span class="mt-1 text-sm text-gray-800">{{ ctrans('texts.minimum_payment') }}: {{ $settings->client_portal_under_payment_minimum }}</span>
@endif
</dd>
<div class="bg-white px-4 py-5 flex w-full justify-end">
<button
class="button button-primary bg-primary payment-method flex items-center justify-center relative py-4"
@click="$wire.dispatch('payable-amount', { payable_amount: payableAmount })">
<svg class="animate-spin h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>{{ ctrans('texts.next') }}</span>
</button>
</div>
</div>