1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 21:22:58 +01:00

Merge pull request #4362 from beganovich/v5-gateways-required-fields

(v5) Support for pre-checking gateway requirements
This commit is contained in:
David Bomba 2020-11-26 06:57:48 +11:00 committed by GitHub
commit b3cdb19b4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 368 additions and 89 deletions

View File

@ -225,6 +225,7 @@ class PaymentController extends Controller
->driver(auth()->user()->client)
->setPaymentMethod($payment_method_id)
->setPaymentHash($payment_hash)
->checkRequirements()
->processPaymentView($data);
}
@ -237,6 +238,7 @@ class PaymentController extends Controller
->driver(auth()->user()->client)
->setPaymentMethod($request->input('payment_method_id'))
->setPaymentHash($payment_hash)
->checkRequirements()
->processPaymentResponse($request);
}

View File

@ -55,6 +55,7 @@ class PaymentMethodController extends Controller
return $gateway
->driver(auth()->user()->client)
->setPaymentMethod($request->query('method'))
->checkRequirements()
->authorizeView($data);
}
@ -71,6 +72,7 @@ class PaymentMethodController extends Controller
return $gateway
->driver(auth()->user()->client)
->setPaymentMethod($request->query('method'))
->checkRequirements()
->authorizeResponse($request);
}

View File

@ -96,6 +96,12 @@ class AuthorizePaymentDriver extends BaseDriver
public function authorizeView($payment_method)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return (new AuthorizePaymentMethod($this))->authorizeView($payment_method);
}
@ -111,11 +117,23 @@ class AuthorizePaymentDriver extends BaseDriver
public function processPaymentView($data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->processPaymentView($data);
}
public function processPaymentResponse($request)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->processPaymentResponse($request);
}

View File

@ -32,6 +32,7 @@ use App\Utils\Traits\SystemLogTrait;
use Checkout\Library\Exceptions\CheckoutHttpException;
use Exception;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
/**
* Class BaseDriver.
@ -69,6 +70,9 @@ class BaseDriver extends AbstractPaymentDriver
public static $methods = [];
/** @var array */
public $required_fields = [];
public function __construct(CompanyGateway $company_gateway, Client $client = null, $invitation = false)
{
$this->company_gateway = $company_gateway;
@ -323,4 +327,118 @@ class BaseDriver extends AbstractPaymentDriver
throw new PaymentFailed($error, $e->getCode());
}
/**
* Wrapper method for checking if resource is good.
*
* @param mixed $resource
* @return bool
*/
public function checkRequiredResource($resource): bool
{
if (is_null($resource) || empty($resource)) {
return true;
}
return false;
}
public function checkRequirements()
{
if ($this->company_gateway->require_billing_address) {
if ($this->checkRequiredResource(auth()->user('contact')->client->address1)) {
$this->required_fields[] = 'billing_address1';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->address2)) {
$this->required_fields[] = 'billing_address2';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->city)) {
$this->required_fields[] = 'billing_city';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->state)) {
$this->required_fields[] = 'billing_state';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->postal_code)) {
$this->required_fields[] = 'billing_postal_code';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->country_id)) {
$this->required_fields[] = 'billing_country';
}
}
if ($this->company_gateway->require_shipping_address) {
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_address1)) {
$this->required_fields[] = 'shipping_address1';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_address2)) {
$this->required_fields[] = 'shipping_address2';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_city)) {
$this->required_fields[] = 'shipping_city';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_state)) {
$this->required_fields[] = 'shipping_state';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_postal_code)) {
$this->required_fields[] = 'shipping_postal_code';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_country_id)) {
$this->required_fields[] = 'shipping_country';
}
}
if ($this->company_gateway->require_client_name) {
if ($this->checkRequiredResource(auth()->user('contact')->client->name)) {
$this->required_fields[] = 'name';
}
}
if ($this->company_gateway->require_client_phone) {
if ($this->checkRequiredResource(auth()->user('contact')->client->phone)) {
$this->required_fields[] = 'phone';
}
}
if ($this->company_gateway->require_contact_email) {
if ($this->checkRequiredResource(auth()->user('contact')->email)) {
$this->required_fields[] = 'contact_email';
}
}
if ($this->company_gateway->require_contact_name) {
if ($this->checkRequiredResource(auth()->user('contact')->first_name)) {
$this->required_fields[] = 'contact_first_name';
}
if ($this->checkRequiredResource(auth()->user('contact')->last_name)) {
$this->required_fields[] = 'contact_last_name';
}
}
if ($this->company_gateway->require_postal_code) {
// In case "require_postal_code" is true, we don't need billing address.
foreach ($this->required_fields as $position => $field) {
if (Str::startsWith($field, 'billing')) {
unset($this->required_fields[$position]);
}
}
if ($this->checkRequiredResource(auth()->user('contact')->client->postal_code)) {
$this->required_fields[] = 'postal_code';
}
}
return $this;
}
}

View File

@ -118,11 +118,23 @@ class CheckoutComPaymentDriver extends BaseDriver
public function authorizeView($data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->authorizeView($data);
}
public function authorizeResponse($data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->authorizeResponse($data);
}
@ -134,6 +146,12 @@ class CheckoutComPaymentDriver extends BaseDriver
*/
public function processPaymentView(array $data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->paymentView($data);
}
@ -145,6 +163,12 @@ class CheckoutComPaymentDriver extends BaseDriver
*/
public function processPaymentResponse($request)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->paymentResponse($request);
}

View File

@ -26,44 +26,14 @@ use App\Utils\Traits\MakesHash;
use Exception;
use Omnipay\Common\Item;
use stdClass;
/**
* Response array
* (.
'TOKEN' => 'EC-50V302605X606694D',
'SUCCESSPAGEREDIRECTREQUESTED' => 'false',
'TIMESTAMP' => '2019-09-30T22:21:21Z',
'CORRELATIONID' => '9e0da63193090',
'ACK' => 'SuccessWithWarning',
'VERSION' => '119.0',
'BUILD' => '53688488',
'L_ERRORCODE0' => '11607',
'L_SHORTMESSAGE0' => 'Duplicate Request',
'L_LONGMESSAGE0' => 'A successful transaction has already been completed for this token.',
'L_SEVERITYCODE0' => 'Warning',
'INSURANCEOPTIONSELECTED' => 'false',
'SHIPPINGOPTIONISDEFAULT' => 'false',
'PAYMENTINFO_0_TRANSACTIONID' => '5JE20141KL116573G',
'PAYMENTINFO_0_TRANSACTIONTYPE' => 'expresscheckout',
'PAYMENTINFO_0_PAYMENTTYPE' => 'instant',
'PAYMENTINFO_0_ORDERTIME' => '2019-09-30T22:20:57Z',
'PAYMENTINFO_0_AMT' => '31260.37',
'PAYMENTINFO_0_TAXAMT' => '0.00',
'PAYMENTINFO_0_CURRENCYCODE' => 'USD',
'PAYMENTINFO_0_EXCHANGERATE' => '0.692213615971749',
'PAYMENTINFO_0_PAYMENTSTATUS' => 'Pending',
'PAYMENTINFO_0_PENDINGREASON' => 'unilateral',
'PAYMENTINFO_0_REASONCODE' => 'None',
'PAYMENTINFO_0_PROTECTIONELIGIBILITY' => 'Ineligible',
'PAYMENTINFO_0_PROTECTIONELIGIBILITYTYPE' => 'None',
'PAYMENTINFO_0_ERRORCODE' => '0',
'PAYMENTINFO_0_ACK' => 'Success',
)
*/
class PayPalExpressPaymentDriver extends BasePaymentDriver
{
use MakesHash;
public $payment_hash;
public $required_fields = [];
protected $refundable = true;
protected $token_billing = false;
@ -86,6 +56,120 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL;
public function checkRequirements()
{
if ($this->company_gateway->require_billing_address) {
if ($this->checkRequiredResource(auth()->user('contact')->client->address1)) {
$this->required_fields[] = 'billing_address1';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->address2)) {
$this->required_fields[] = 'billing_address2';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->city)) {
$this->required_fields[] = 'billing_city';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->state)) {
$this->required_fields[] = 'billing_state';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->postal_code)) {
$this->required_fields[] = 'billing_postal_code';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->country_id)) {
$this->required_fields[] = 'billing_country';
}
}
if ($this->company_gateway->require_shipping_address) {
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_address1)) {
$this->required_fields[] = 'shipping_address1';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_address2)) {
$this->required_fields[] = 'shipping_address2';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_city)) {
$this->required_fields[] = 'shipping_city';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_state)) {
$this->required_fields[] = 'shipping_state';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_postal_code)) {
$this->required_fields[] = 'shipping_postal_code';
}
if ($this->checkRequiredResource(auth()->user('contact')->client->shipping_country_id)) {
$this->required_fields[] = 'shipping_country';
}
}
if ($this->company_gateway->require_client_name) {
if ($this->checkRequiredResource(auth()->user('contact')->client->name)) {
$this->required_fields[] = 'name';
}
}
if ($this->company_gateway->require_client_phone) {
if ($this->checkRequiredResource(auth()->user('contact')->client->phone)) {
$this->required_fields[] = 'phone';
}
}
if ($this->company_gateway->require_contact_email) {
if ($this->checkRequiredResource(auth()->user('contact')->email)) {
$this->required_fields[] = 'contact_email';
}
}
if ($this->company_gateway->require_contact_name) {
if ($this->checkRequiredResource(auth()->user('contact')->first_name)) {
$this->required_fields[] = 'contact_first_name';
}
if ($this->checkRequiredResource(auth()->user('contact')->last_name)) {
$this->required_fields[] = 'contact_last_name';
}
}
if ($this->company_gateway->require_postal_code) {
// In case "require_postal_code" is true, we don't need billing address.
foreach ($this->required_fields as $position => $field) {
if (Str::startsWith($field, 'billing')) {
unset($this->required_fields[$position]);
}
}
if ($this->checkRequiredResource(auth()->user('contact')->client->postal_code)) {
$this->required_fields[] = 'postal_code';
}
}
return $this;
}
/**
* Wrapper method for checking if resource is good.
*
* @param mixed $resource
* @return bool
*/
public function checkRequiredResource($resource): bool
{
if (is_null($resource) || empty($resource)) {
return true;
}
return false;
}
/**
* Processes the payment with this gateway.
*
@ -96,6 +180,12 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
*/
public function processPaymentView(array $data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
$response = $this->purchase($this->paymentDetails($data), $this->paymentItems($data));
if ($response->isRedirect()) {
@ -122,8 +212,21 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
}
}
public function setPaymentHash(PaymentHash $payment_hash)
{
$this->payment_hash = $payment_hash;
return $this;
}
public function processPaymentResponse($request)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
$response = $this->completePurchase($request->all());
$transaction_reference = $response->getTransactionReference() ?: $request->input('token');
@ -191,13 +294,11 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
private function buildReturnUrl($input): string
{
$url = $this->client->company->domain().'/client/payments/process/response';
$url .= "?company_gateway_id={$this->company_gateway->id}&gateway_type_id=".GatewayType::PAYPAL;
$url .= '&payment_hash='.$input['payment_hash'];
$url .= '&amount='.$input['amount'];
$url .= '&fee='.$input['fee'];
return $url;
return route('client.payments.response', [
'company_gateway_id' => $this->company_gateway->id,
'payment_hash' => $this->payment_hash->hash,
'payment_method_id' => GatewayType::PAYPAL,
]);
}
private function buildCancelUrl($input): string

View File

@ -159,6 +159,12 @@ class StripePaymentDriver extends BaseDriver
*/
public function authorizeView(array $data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->authorizeView($data);
}
@ -170,6 +176,12 @@ class StripePaymentDriver extends BaseDriver
*/
public function authorizeResponse($request)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->authorizeResponse($request);
}
@ -181,41 +193,23 @@ class StripePaymentDriver extends BaseDriver
*/
public function processPaymentView(array $data)
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->paymentView($data);
}
/**
* Payment Intent Reponse looks like this.
* +"id": "pi_1FMR7JKmol8YQE9DuC4zMeN3"
* +"object": "payment_intent"
* +"allowed_source_types": array:1 [
* 0 => "card"
* ]
* +"amount": 2372484
* +"canceled_at": null
* +"cancellation_reason": null
* +"capture_method": "automatic"
* +"client_secret": "pi_1FMR7JKmol8YQE9DuC4zMeN3_secret_J3yseWJG6uV0MmsrAT1FlUklV"
* +"confirmation_method": "automatic"
* +"created": 1569381877
* +"->currency()": "usd"
* +"description": "[3]"
* +"last_payment_error": null
* +"livemode": false
* +"next_action": null
* +"next_source_action": null
* +"payment_method": "pm_1FMR7ZKmol8YQE9DQWqPuyke"
* +"payment_method_types": array:1 []
* +"receipt_email": null
* +"setup_future_usage": "off_session"
* +"shipping": null
* +"source": null
* +"status": "succeeded"
* @param $request
* @return
*/
public function processPaymentResponse($request) //We never have to worry about unsuccessful payments as failures are handled at the front end for this driver.
{
if (count($this->required_fields) > 0) {
return redirect()
->route('client.profile.edit', ['client_contact' => auth()->user()->hashed_id])
->with('missing_required_fields', $this->required_fields);
}
return $this->payment_method->paymentResponse($request);
}

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"/js/app.js": "/js/app.js?id=a33a5a58bfc6e2174841",
"/css/app.css": "/css/app.css?id=e55ed33a5056c9779c03",
"/css/app.css": "/css/app.css?id=2fee89354bd20f89bf73",
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
"/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=cddcd46c630c71737bda",

View File

@ -3305,4 +3305,10 @@ return [
'by_clicking_next_you_accept_terms' => 'By clicking "Next step" you accept terms.',
'not_specified' => 'Not specified',
'before_proceeding_with_payment_warning' => 'Before proceeding with payment, you have to fill following fields',
'after_completing_go_back_to_previous_page' => 'After completing, go back to previous page.',
'billing_country' => 'Billing Country',
'shipping_country' => 'Shipping Country',
];

View File

@ -10,7 +10,7 @@
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-4">
<label for="shipping_address1" class="input-label">{{ ctrans('texts.shipping_address1') }}</label>
<input id="shipping_address1" class="input w-full" name="shipping_address1" />
<input id="shipping_address1" class="input w-full {{ in_array('shipping_address1', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_address1" />
@error('shipping_address1')
<div class="validation validation-fail">
{{ $message }}
@ -19,7 +19,7 @@
</div>
<div class="col-span-6 sm:col-span-3">
<label for="shipping_address2" class="input-label">{{ ctrans('texts.shipping_address2') }}</label>
<input id="shipping_address2" class="input w-full" name="shipping_address2" />
<input id="shipping_address2 {{ in_array('shipping_address2', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" class="input w-full" name="shipping_address2" />
@error('shipping_address2')
<div class="validation validation-fail">
{{ $message }}
@ -28,7 +28,7 @@
</div>
<div class="col-span-6 sm:col-span-3">
<label for="shipping_city" class="input-label">{{ ctrans('texts.shipping_city') }}</label>
<input id="shipping_city" class="input w-full" name="shipping_city" />
<input id="shipping_city" class="input w-full {{ in_array('shipping_city', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_city" />
@error('shipping_city')
<div class="validation validation-fail">
{{ $message }}
@ -37,7 +37,7 @@
</div>
<div class="col-span-6 sm:col-span-2">
<label for="shipping_state" class="input-label">{{ ctrans('texts.shipping_state') }}</label>
<input id="shipping_state" class="input w-full" name="shipping_state" />
<input id="shipping_state" class="input w-ful {{ in_array('shipping_state', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}l" name="shipping_state" />
@error('shipping_state')
<div class="validation validation-fail">
{{ $message }}
@ -46,7 +46,7 @@
</div>
<div class="col-span-6 sm:col-span-2">
<label for="shipping_postal_code" class="input-label">{{ ctrans('texts.shipping_postal_code') }}</label>
<input id="shipping_postal_code" class="input w-full" name="shipping_postal_code" />
<input id="shipping_postal_code" class="input w-full {{ in_array('shipping_postal_code', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_postal_code" />
@error('shipping_postal_code')
<div class="validation validation-fail">
{{ $message }}
@ -55,7 +55,7 @@
</div>
<div class="col-span-4 sm:col-span-2">
<label for="shipping_country" class="input-label">{{ ctrans('texts.shipping_country') }}</label>
<select id="shipping_country" class="input w-full form-select" name="shipping_country">
<select id="shipping_country" class="input w-full form-select {{ in_array('shipping_country', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_country">
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
<option {{ $country == isset(auth()->user()->client->shipping_country->id) ? 'selected' : null }} value="{{ $country->id }}">
{{ $country->iso_3166_2 }}

View File

@ -7,6 +7,20 @@
@endsection
@section('body')
@if(session()->has('missing_required_fields'))
<div class="validation validation-fail">
<p class="mb-3 font-semibold">{{ ctrans('texts.before_proceeding_with_payment_warning') }}:</p>
<ul>
@foreach(session()->get('missing_required_fields') as $field)
<li class="block"> {{ ctrans("texts.{$field}") }}</li>
@endforeach
</ul>
<button onclick="window.history.back();" class="block mt-3 button button-link pl-0 ml-0 underline">{{ ctrans('texts.after_completing_go_back_to_previous_page') }}</button>
</div>
@endif
<!-- Basic information: first & last name, e-mail address etc. -->
@livewire('profile.settings.general')

View File

@ -18,7 +18,7 @@
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="first_name" class="input-label">@lang('texts.first_name')</label>
<input id="first_name" class="input w-full" name="first_name" wire:model.defer="first_name" />
<input id="first_name" class="input w-full {{ in_array('contact_first_name', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="first_name" wire:model.defer="first_name" />
@error('first_name')
<div class="validation validation-fail">
{{ $message }}
@ -28,7 +28,7 @@
<div class="col-span-6 sm:col-span-3">
<label for="last_name" class="input-label">@lang('texts.last_name')</label>
<input id="last_name" class="input w-full" name="last_name" wire:model.defer="last_name" />
<input id="last_name" class="input w-full {{ in_array('contact_last_name', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="last_name" wire:model.defer="last_name" />
@error('last_name')
<div class="validation validation-fail">
{{ $message }}
@ -38,7 +38,7 @@
<div class="col-span-6 sm:col-span-4">
<label for="email_address" class="input-label">@lang('texts.email_address')</label>
<input id="email_address" class="input w-full" type="email" name="email" wire:model.defer="email" />
<input id="email_address" class="input w-full {{ in_array('contact_email', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" type="email" name="email" wire:model.defer="email" />
@error('email')
<div class="validation validation-fail">
{{ $message }}

View File

@ -17,7 +17,7 @@
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="street" class="input-label">{{ ctrans('texts.name') }}</label>
<input id="name" class="input w-full" name="name" wire:model.defer="name" />
<input id="name" class="input w-full {{ in_array('name', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="name" wire:model.defer="name" />
@error('name')
<div class="validation validation-fail">
{{ $message }}

View File

@ -15,7 +15,7 @@
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-4">
<label for="address1" class="input-label">{{ ctrans('texts.address1') }}</label>
<input id="address1" class="input w-full" name="address1" wire:model.defer="address1" />
<input id="address1" class="input w-full {{ in_array('billing_address1', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="address1" wire:model.defer="address1" />
@error('address1')
<div class="validation validation-fail">
{{ $message }}
@ -24,7 +24,7 @@
</div>
<div class="col-span-6 sm:col-span-3">
<label for="address2" class="input-label">{{ ctrans('texts.address2') }}</label>
<input id="address2" class="input w-full" name="address2" wire:model.defer="address2" />
<input id="address2" class="input w-full {{ in_array('billing_address2', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="address2" wire:model.defer="address2" />
@error('address2')
<div class="validation validation-fail">
{{ $message }}
@ -33,7 +33,7 @@
</div>
<div class="col-span-6 sm:col-span-3">
<label for="city" class="input-label">{{ ctrans('texts.city') }}</label>
<input id="city" class="input w-full" name="city" wire:model.defer="city" />
<input id="city" class="input w-full {{ in_array('billing_city', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="city" wire:model.defer="city" />
@error('city')
<div class="validation validation-fail">
{{ $message }}
@ -42,7 +42,7 @@
</div>
<div class="col-span-6 sm:col-span-2">
<label for="state" class="input-label">{{ ctrans('texts.state') }}</label>
<input id="state" class="input w-full" name="state" wire:model.defer="state" />
<input id="state" class="input w-full {{ in_array('billing_state', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="state" wire:model.defer="state" />
@error('state')
<div class="validation validation-fail">
{{ $message }}
@ -51,7 +51,7 @@
</div>
<div class="col-span-6 sm:col-span-2">
<label for="postal_code" class="input-label">{{ ctrans('texts.postal_code') }}</label>
<input id="postal_code" class="input w-full" name="postal_code" wire:model.defer="postal_code" />
<input id="postal_code" class="input w-full {{ in_array('billing_postal_code', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="postal_code" wire:model.defer="postal_code" />
@error('postal_code')
<div class="validation validation-fail">
{{ $message }}
@ -60,7 +60,7 @@
</div>
<div class="col-span-6 sm:col-span-2">
<label for="country" class="input-label">@lang('texts.country')</label>
<select id="country" class="input w-full form-select" wire:model.defer="country_id">
<select id="country" class="input w-full form-select {{ in_array('billing_country', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" wire:model.defer="country_id">
<option value="none"></option>
@foreach($countries as $country)
<option value="{{ $country->id }}">