diff --git a/app/DataMapper/Analytics/DbQuery.php b/app/DataMapper/Analytics/DbQuery.php index c5718278dab0..ac65c29b36c0 100644 --- a/app/DataMapper/Analytics/DbQuery.php +++ b/app/DataMapper/Analytics/DbQuery.php @@ -52,6 +52,10 @@ class DbQuery extends GenericMixedMetric public $string_metric7 = 'ip_address'; + public $string_metric8 = 'client_version'; + + public $string_metric9 = 'platform'; + /** * The counter * set to 1. diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index d3b1d56c6da1..079030b16670 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -32,7 +32,6 @@ use App\Models\BankIntegration; use App\Models\BankTransaction; use App\Models\ExpenseCategory; use League\Fractal\Resource\Item; -use App\DataMapper\EDoc\Schema\RO; use App\Models\BankTransactionRule; use Illuminate\Support\Facades\Auth; use App\Transformers\ArraySerializer; @@ -42,6 +41,7 @@ use Illuminate\Database\Eloquent\Builder; use League\Fractal\Serializer\JsonApiSerializer; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use Illuminate\Contracts\Container\BindingResolutionException; +use Invoiceninja\Einvoice\Decoder\Schema; /** * Class BaseController. @@ -890,7 +890,6 @@ class BaseController extends Controller /** @phpstan-ignore-next-line **/ $query = $paginator->getCollection();// @phpstan-ignore-line - $resource = new Collection($query, $transformer, $this->entity_type); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); } @@ -998,8 +997,8 @@ class BaseController extends Controller if(request()->has('einvoice')){ - $ro = new RO(); - $response_data['einvoice_schema'] = $ro(); + $ro = new Schema(); + $response_data['einvoice_schema'] = $ro('FACT1'); } diff --git a/app/Http/Middleware/QueryLogging.php b/app/Http/Middleware/QueryLogging.php index 106d498cb55f..efe0ef707702 100644 --- a/app/Http/Middleware/QueryLogging.php +++ b/app/Http/Middleware/QueryLogging.php @@ -73,7 +73,22 @@ class QueryLogging $ip = $request->ip(); } - LightLogs::create(new DbQuery($request->method(), substr(urldecode($request->url()), 0, 180), $count, $time, $ip)) + $client_version = $request->server('HTTP_USER_AGENT'); + $platform = ''; + + if ($request->hasHeader('X-CLIENT-PLATFORM')) { + $platform = $request->header('X-CLIENT-PLATFORM'); + } + elseif($request->hasHeader('X-React')){ + $platform = 'react'; + } + + if ($request->hasHeader('X-CLIENT-VERSION')) + { + $client_version = $request->header('X-CLIENT-VERSION'); + } + + LightLogs::create(new DbQuery($request->method(), substr(urldecode($request->url()), 0, 180), $count, $time, $ip, $client_version, $platform)) ->batch(); } diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php index 4cdd76bddcf6..f415386a0889 100644 --- a/app/Http/Requests/Company/UpdateCompanyRequest.php +++ b/app/Http/Requests/Company/UpdateCompanyRequest.php @@ -17,6 +17,7 @@ use App\Http\ValidationRules\Company\ValidSubdomain; use App\Http\ValidationRules\ValidSettingsRule; use App\Utils\Ninja; use App\Utils\Traits\MakesHash; +use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronica; class UpdateCompanyRequest extends Request { @@ -113,6 +114,11 @@ class UpdateCompanyRequest extends Request $input['smtp_verify_peer'] == 'true' ? true : false; } + // if(isset($input['e_invoice'])){ + // nlog("am i set?"); + // $r = FatturaElettronica::validate($input['e_invoice']); + // } + $this->replace($input); } diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index eb288bcf31f0..c6a1054f2eaa 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -241,6 +241,32 @@ class CompanyImport implements ShouldQueue CompanyGateway::class => [ 'always_show_required_fields', ] + ], + '5.8.57' => [ + Company::class => [ + 'einvoice', + 'e_invoice', + ], + Invoice::class => [ + 'einvoice', + 'e_invoice', + ], + Quote::class => [ + 'einvoice', + 'e_invoice', + ], + Credit::class => [ + 'einvoice', + 'e_invoice', + ], + PurchaseOrder::class => [ + 'einvoice', + 'e_invoice', + ], + Expense::class => [ + 'einvoice', + 'e_invoice', + ], ] ]; diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php index 7abfdbc29c12..b1b1c94690eb 100644 --- a/app/Jobs/Mail/NinjaMailerJob.php +++ b/app/Jobs/Mail/NinjaMailerJob.php @@ -136,8 +136,8 @@ class NinjaMailerJob implements ShouldQueue $mailable = $this->nmo->mailable; - /** May need to re-build it here */ - if (Ninja::isHosted() && method_exists($mailable, 'build')) { + /** May need to re-build it here @todo explain why we need this? */ + if (Ninja::isHosted() && method_exists($mailable, 'build') && $this->nmo->settings->email_style != "custom") { $mailable->build(); } diff --git a/app/Jobs/Payment/EmailPayment.php b/app/Jobs/Payment/EmailPayment.php index 9e3428ccf9f5..1d1c88313e02 100644 --- a/app/Jobs/Payment/EmailPayment.php +++ b/app/Jobs/Payment/EmailPayment.php @@ -116,13 +116,17 @@ class EmailPayment implements ShouldQueue $invoice->invitations->each(function ($invite) use ($email_builder) { + + $cloned_mailable = unserialize(serialize($email_builder)); + $nmo = new NinjaMailerObject(); - $nmo->mailable = new TemplateEmail($email_builder, $invite->contact, $invite); + $nmo->mailable = new TemplateEmail($cloned_mailable, $invite->contact, $invite); $nmo->to_user = $invite->contact; $nmo->settings = $this->settings; $nmo->company = $this->company; $nmo->entity = $this->payment; (new NinjaMailerJob($nmo))->handle(); + $nmo = null; event(new PaymentWasEmailed($this->payment, $this->payment->company, $invite->contact, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 64c0ebce9270..f5f0a7ef871b 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -126,7 +126,7 @@ class ReminderJob implements ShouldQueue } $reminder_template = $invoice->calculateTemplate('invoice'); - nrlog("reminder template = {$reminder_template}"); + nrlog("#{$invoice->number} => reminder template = {$reminder_template}"); $invoice->service()->touchReminder($reminder_template)->save(); $fees = $this->calcLateFee($invoice, $reminder_template); diff --git a/app/Mail/Engine/InvoiceEmailEngine.php b/app/Mail/Engine/InvoiceEmailEngine.php index 9e66b4524fe1..8f9e15f8dbf0 100644 --- a/app/Mail/Engine/InvoiceEmailEngine.php +++ b/app/Mail/Engine/InvoiceEmailEngine.php @@ -134,10 +134,6 @@ class InvoiceEmailEngine extends BaseEmailEngine $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->invoice->numberFormatter().'.pdf']]); } - // $hash = Str::uuid(); - // $url = \Illuminate\Support\Facades\URL::temporarySignedRoute('protected_download', now()->addHour(), ['hash' => $hash]); - // Cache::put($hash, $url, now()->addHour()); - //attach third party documents if ($this->client->getSetting('document_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { if ($this->invoice->recurring_invoice()->exists()) { diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php index 32613b80f192..c2d68aef570f 100644 --- a/app/Mail/Engine/PaymentEmailEngine.php +++ b/app/Mail/Engine/PaymentEmailEngine.php @@ -99,7 +99,6 @@ class PaymentEmailEngine extends BaseEmailEngine ->setViewLink('') ->setViewText(''); - if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { $template_in_use = false; diff --git a/app/Models/Account.php b/app/Models/Account.php index c2b54f25211e..81d262454b79 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -31,6 +31,7 @@ use Laracasts\Presenter\PresentableTrait; * App\Models\Account * * @property int $id + * @property int $email_quota * @property string|null $plan * @property string|null $plan_term * @property string|null $plan_started diff --git a/app/Models/Company.php b/app/Models/Company.php index 0538ddddf5df..e83c465cef79 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -364,6 +364,7 @@ class Company extends BaseModel 'smtp_encryption', 'smtp_local_domain', 'smtp_verify_peer', + 'e_invoice', ]; protected $hidden = [ @@ -388,6 +389,7 @@ class Company extends BaseModel 'e_invoice_certificate_passphrase' => EncryptedCast::class, 'smtp_username' => 'encrypted', 'smtp_password' => 'encrypted', + 'e_invoice' => 'object', ]; protected $with = []; diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 530c0bb46196..44f3c606ea6e 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -155,6 +155,7 @@ class CompanyGateway extends BaseModel 'b9886f9257f0c6ee7c302f1c74475f6c' => 321, //GoCardless 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9' => 322, '80af24a6a691230bbec33e930ab40666' => 323, + 'vpyfbmdrkqcicpkjqdusgjfluebftuva' => 324, //BTPay ]; protected $touches = []; diff --git a/app/Models/Credit.php b/app/Models/Credit.php index d73b497b9381..b20000e8ff44 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -28,6 +28,7 @@ use Laracasts\Presenter\PresentableTrait; * App\Models\Credit * * @property int $id + * @property object|null $e_invoice * @property int $client_id * @property int $user_id * @property int|null $assigned_user_id @@ -179,6 +180,7 @@ class Credit extends BaseModel 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', 'is_amount_discount' => 'bool', + 'e_invoice' => 'object', ]; diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 22032fafc152..eb9ee205ca6f 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * App\Models\Expense * * @property int $id + * @property object|null $e_invoice * @property int|null $created_at * @property int|null $updated_at * @property int|null $deleted_at @@ -141,6 +142,7 @@ class Expense extends BaseModel 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', + 'e_invoice' => 'object', ]; protected $touches = []; diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 78cd6ba231c5..135f420ed8f5 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -32,6 +32,7 @@ use Laracasts\Presenter\PresentableTrait; * App\Models\Invoice * * @property int $id + * @property object|null $e_invoice * @property int $client_id * @property int $user_id * @property int|null $assigned_user_id @@ -209,6 +210,7 @@ class Invoice extends BaseModel 'custom_surcharge_tax2' => 'bool', 'custom_surcharge_tax3' => 'bool', 'custom_surcharge_tax4' => 'bool', + 'e_invoice' => 'object', ]; protected $with = []; diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index 085fc2806dd9..00fd2ccbc2dc 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -83,9 +83,9 @@ class CompanyPresenter extends EntityPresenter ]; if (strlen($settings->company_logo) >= 1 && (strpos($settings->company_logo, 'http') !== false)) { - return "data:image/png;base64, ". base64_encode(@file_get_contents($settings->company_logo, false, stream_context_create($context_options))); + return "data:image/png;base64,". base64_encode(@file_get_contents($settings->company_logo, false, stream_context_create($context_options))); } elseif (strlen($settings->company_logo) >= 1) { - return "data:image/png;base64, ". base64_encode(@file_get_contents(url('') . $settings->company_logo, false, stream_context_create($context_options))); + return "data:image/png;base64,". base64_encode(@file_get_contents(url('') . $settings->company_logo, false, stream_context_create($context_options))); } else { return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="; } diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index 3d89be54aa07..63912a90ca91 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -22,6 +22,7 @@ use Illuminate\Support\Carbon; * App\Models\PurchaseOrder * * @property int $id + * @property object|null $e_invoice * @property int|null $client_id * @property int $user_id * @property int|null $assigned_user_id @@ -187,7 +188,7 @@ class PurchaseOrder extends BaseModel 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', 'is_amount_discount' => 'bool', - + 'e_invoice' => 'object', ]; public const STATUS_DRAFT = 1; diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 5a1d73391ab8..71770a531f8a 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -27,6 +27,7 @@ use Laracasts\Presenter\PresentableTrait; * App\Models\Quote * * @property int $id + * @property object|null $e_invoice * @property int $client_id * @property int $user_id * @property int|null $assigned_user_id @@ -174,6 +175,7 @@ class Quote extends BaseModel 'deleted_at' => 'timestamp', 'is_deleted' => 'boolean', 'is_amount_discount' => 'bool', + 'e_invoice' => 'object', ]; public const STATUS_DRAFT = 1; diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 344c8d832c05..5ef3305d525b 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -150,6 +150,8 @@ class SystemLog extends Model public const TYPE_PAYPAL_PPCP = 323; + public const TYPE_BTC_PAY = 324; + public const TYPE_QUOTA_EXCEEDED = 400; public const TYPE_UPSTREAM_FAILURE = 401; diff --git a/app/PaymentDrivers/AuthorizePaymentDriver.php b/app/PaymentDrivers/AuthorizePaymentDriver.php index bd1ee2274e79..465474a5d595 100644 --- a/app/PaymentDrivers/AuthorizePaymentDriver.php +++ b/app/PaymentDrivers/AuthorizePaymentDriver.php @@ -67,7 +67,7 @@ class AuthorizePaymentDriver extends BaseDriver public function getClientRequiredFields(): array { $data = [ - ['name' => 'client_name', 'label' => ctrans('texts.name'), 'type' => 'text', 'validation' => 'required|min:2'], + // ['name' => 'client_name', 'label' => ctrans('texts.name'), 'type' => 'text', 'validation' => 'required|min:2'], ['name' => 'client_phone', 'label' => ctrans('texts.phone'), 'type' => 'text', 'validation' => 'required'], ['name' => 'contact_email', 'label' => ctrans('texts.email'), 'type' => 'text', 'validation' => 'required|email:rfc'], ['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required'], diff --git a/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php b/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php new file mode 100644 index 000000000000..a3059c20cdcd --- /dev/null +++ b/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php @@ -0,0 +1,426 @@ + 'paypal', + 1 => 'card', + 25 => 'venmo', + 29 => 'paypal_advanced_cards', + // 9 => 'sepa', + // 12 => 'bancontact', + // 17 => 'eps', + // 15 => 'giropay', + // 13 => 'ideal', + // 26 => 'mercadopago', + // 27 => 'mybank', + 28 => 'paylater', + // 16 => 'p24', + // 7 => 'sofort' + ]; + + public function gatewayTypes() + { + + $funding_options = + + collect($this->company_gateway->fees_and_limits) + ->filter(function ($fee) { + return $fee->is_enabled; + })->map(function ($fee, $key) { + return (int)$key; + })->toArray(); + + /** Parse funding options and remove card option if advanced cards is enabled. */ + if(in_array(1, $funding_options) && in_array(29, $funding_options)){ + + if (($key = array_search(1, $funding_options)) !== false) + unset($funding_options[$key]); + + } + + return $funding_options; + + } + + public function getPaymentMethod($gateway_type_id): int + { + $method = PaymentType::PAYPAL; + + match($gateway_type_id) { + "1" => $method = PaymentType::CREDIT_CARD_OTHER, + "3" => $method = PaymentType::PAYPAL, + "25" => $method = PaymentType::VENMO, + "28" => $method = PaymentType::PAY_LATER, + "29" => $method = PaymentType::CREDIT_CARD_OTHER, + }; + + return $method; + } + + public function init() + { + + $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; + + $secret = $this->company_gateway->getConfigField('secret'); + $client_id = $this->company_gateway->getConfigField('clientId'); + + if($this->access_token && $this->token_expiry && $this->token_expiry->isFuture()) { + return $this; + } + + $response = Http::withBasicAuth($client_id, $secret) + ->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']) + ->withQueryParameters(['grant_type' => 'client_credentials']) + ->post("{$this->api_endpoint_url}/v1/oauth2/token"); + + if($response->successful()) { + $this->access_token = $response->json()['access_token']; + $this->token_expiry = now()->addSeconds($response->json()['expires_in'] - 60); + } else { + throw new PaymentFailed('Unable to gain access token from Paypal. Check your configuration', 401); + } + + return $this; + + } + + + /** + * getFundingOptions + * + * Hosted fields requires this. + * + * @return string + */ + public function getFundingOptions(): string + { + + $enums = [ + 1 => 'card', + 3 => 'paypal', + 25 => 'venmo', + 28 => 'paylater', + // 9 => 'sepa', + // 12 => 'bancontact', + // 17 => 'eps', + // 15 => 'giropay', + // 13 => 'ideal', + // 26 => 'mercadopago', + // 27 => 'mybank', + // 28 => 'paylater', + // 16 => 'p24', + // 7 => 'sofort' + ]; + + $funding_options = ''; + + foreach($this->company_gateway->fees_and_limits as $key => $value) { + + if($value->is_enabled) { + + $funding_options .= $enums[$key].','; + + } + + } + + return rtrim($funding_options, ','); + + } + + public function getShippingAddress(): ?array + { + return $this->company_gateway->require_shipping_address ? + [ + "address" => + [ + "address_line_1" => strlen($this->client->shipping_address1) > 1 ? $this->client->shipping_address1 : $this->client->address1, + "address_line_2" => $this->client->shipping_address2, + "admin_area_2" => strlen($this->client->shipping_city) > 1 ? $this->client->shipping_city : $this->client->city, + "admin_area_1" => strlen($this->client->shipping_state) > 1 ? $this->client->shipping_state : $this->client->state, + "postal_code" => strlen($this->client->shipping_postal_code) > 1 ? $this->client->shipping_postal_code : $this->client->postal_code, + "country_code" => $this->client->present()->shipping_country_code(), + ], + ] + + : [ + "name" => [ + "full_name" => $this->client->present()->name() + ] + ]; + + } + + public function getBillingAddress(): array + { + return + [ + "address_line_1" => $this->client->address1, + "address_line_2" => $this->client->address2, + "admin_area_2" => $this->client->city, + "admin_area_1" => $this->client->state, + "postal_code" => $this->client->postal_code, + "country_code" => $this->client->country->iso_3166_2, + ]; + } + + public function getPaymentSource(): array + { + //@todo - roll back here for advanced payments vs hosted card fields. + if($this->gateway_type_id == GatewayType::PAYPAL_ADVANCED_CARDS) { + + return [ + "card" => [ + "attributes" => [ + "verification" => [ + "method" => "SCA_WHEN_REQUIRED", //SCA_ALWAYS + // "method" => "SCA_ALWAYS", //SCA_ALWAYS + ], + "vault" => [ + "store_in_vault" => "ON_SUCCESS", //must listen to this webhook - VAULT.PAYMENT-TOKEN.CREATED webhook. + ], + ], + "experience_context" => [ + "shipping_preference" => "SET_PROVIDED_ADDRESS" + ], + "stored_credential" => [ + // "payment_initiator" => "MERCHANT", //"CUSTOMER" who initiated the transaction? + "payment_initiator" => "CUSTOMER", //"" who initiated the transaction? + "payment_type" => "UNSCHEDULED", //UNSCHEDULED + "usage"=> "DERIVED", + ], + ], + ]; + + } + + $order = [ + "paypal" => [ + "name" => [ + "given_name" => $this->client->present()->first_name(), + "surname" => $this->client->present()->last_name(), + ], + "email_address" => $this->client->present()->email(), + "experience_context" => [ + "user_action" => "PAY_NOW" + ], + ], + ]; + + /** If we have a complete address, add it to the order, otherwise leave it blank! */ + if( + strlen($this->client->shipping_address1 ?? '') > 2 && + strlen($this->client->shipping_city ?? '') > 2 && + strlen($this->client->shipping_state ?? '') >= 2 && + strlen($this->client->shipping_postal_code ?? '') > 2 && + strlen($this->client->shipping_country->iso_3166_2 ?? '') >= 2 + ) { + $order['paypal']['address'] = [ + "address_line_1" => $this->client->shipping_address1, + "address_line_2" => $this->client->shipping_address2, + "admin_area_2" => $this->client->shipping_city, + "admin_area_1" => $this->client->shipping_state, + "postal_code" => $this->client->shipping_postal_code, + "country_code" => $this->client->present()->shipping_country_code(), + ]; + } + elseif( + strlen($this->client->address1 ?? '') > 2 && + strlen($this->client->city ?? '') > 2 && + strlen($this->client->state ?? '') >= 2 && + strlen($this->client->postal_code ?? '') > 2 && + strlen($this->client->country->iso_3166_2 ?? '') >= 2 + ) + { + $order['paypal']['address'] = [ + "address_line_1" => $this->client->address1, + "address_line_2" => $this->client->address2, + "admin_area_2" => $this->client->city, + "admin_area_1" => $this->client->state, + "postal_code" => $this->client->postal_code, + "country_code" => $this->client->country->iso_3166_2, + ]; + } + + return $order; + + } + + /** + * Payment method setter + * + * @param mixed $payment_method_id + * @return self + */ + public function setPaymentMethod($payment_method_id): self + { + if(!$payment_method_id) { + return $this; + } + + $this->gateway_type_id = $payment_method_id; + + $this->paypal_payment_method = $this->funding_options[$payment_method_id]; + + return $this; + } + + public function authorizeView($payment_method) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + public function authorizeResponse($request) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + /** + * Generates the gateway request + * + * @param string $uri + * @param string $verb + * @param array $data + * @param ?array $headers + * @return \Illuminate\Http\Client\Response + */ + public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) + { + $this->init(); + + $r = Http::withToken($this->access_token) + ->withHeaders($this->getHeaders($headers)) + ->{$verb}("{$this->api_endpoint_url}{$uri}", $data); + + if($r->successful()) { + return $r; + } + + SystemLogger::dispatch( + ['response' => $r->body()], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company ?? $this->company_gateway->company, + ); + + throw new PaymentFailed("Gateway failure - {$r->body()}", 401); + + } + + /** + * Generates the request headers + * + * @param array $headers + * @return array + */ + public function getHeaders(array $headers = []): array + { + return array_merge([ + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + 'Accept-Language' => 'en_US', + 'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP', + 'PayPal-Request-Id' => Str::uuid()->toString(), + ], $headers); + } + + /** + * Generates a client token for the payment form. + * + * @return string + */ + public function getClientToken(): string + { + + $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']); + + if($r->successful()) { + return $r->json()['client_token']; + } + + throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401); + + } + + public function auth(): bool + { + + try { + $this->init()->getClientToken(); + return true; + } + catch(\Exception $e) { + + } + + return false; + } + + public function importCustomers() + { + return true; + } + + public function processWebhookRequest(Request $request) + { + + $this->init(); + + PayPalWebhook::dispatch($request->all(), $request->headers->all(), $this->access_token); + } + +} \ No newline at end of file diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php index 12758fa10171..055e41d1587e 100644 --- a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php +++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php @@ -21,188 +21,34 @@ use Illuminate\Http\Request; use App\Jobs\Util\SystemLogger; use App\Utils\Traits\MakesHash; use App\Exceptions\PaymentFailed; +use App\Models\ClientGatewayToken; use Illuminate\Support\Facades\Http; use App\PaymentDrivers\PayPal\PayPalWebhook; +use App\PaymentDrivers\PayPal\PayPalBasePaymentDriver; -class PayPalPPCPPaymentDriver extends BaseDriver +class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver { use MakesHash; - - public $token_billing = false; - - public $can_authorise_credit_card = false; - - private $omnipay_gateway; - - private float $fee = 0; + +///v1/customer/partners/merchant-accounts/{merchant_id}/capabilities - test if advanced cards is available. +// { +// "capabilities": [ +// { +// "name": "ADVANCED_CARD_PAYMENTS", +// "status": "ENABLED" +// }, +// { +// "name": "VAULTING", +// "status": "ENABLED" +// } +// ] +// } public const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL_PPCP; - - private string $api_endpoint_url = ''; - - private string $paypal_payment_method = ''; - - private ?int $gateway_type_id = null; - - protected mixed $access_token = null; - - protected ?Carbon $token_expiry = null; - - private array $funding_options = [ - 3 => 'paypal', - 1 => 'card', - 25 => 'venmo', - // 9 => 'sepa', - // 12 => 'bancontact', - // 17 => 'eps', - // 15 => 'giropay', - // 13 => 'ideal', - // 26 => 'mercadopago', - // 27 => 'mybank', - 28 => 'paylater', - // 16 => 'p24', - // 7 => 'sofort' - ]; - + /** - * Return an array of - * enabled gateway payment methods - * - * @return array - */ - public function gatewayTypes(): array - { - - return collect($this->company_gateway->fees_and_limits) - ->filter(function ($fee) { - return $fee->is_enabled; - })->map(function ($fee, $key) { - return (int)$key; - })->toArray(); - - } - - private function getPaymentMethod($gateway_type_id): int - { - $method = PaymentType::PAYPAL; - - match($gateway_type_id) { - "1" => $method = PaymentType::CREDIT_CARD_OTHER, - "3" => $method = PaymentType::PAYPAL, - "25" => $method = PaymentType::VENMO, - "28" => $method = PaymentType::PAY_LATER, - }; - - return $method; - } - - private function getFundingOptions(): string - { - - $enums = [ - 1 => 'card', - 3 => 'paypal', - 25 => 'venmo', - 28 => 'paylater', - // 9 => 'sepa', - // 12 => 'bancontact', - // 17 => 'eps', - // 15 => 'giropay', - // 13 => 'ideal', - // 26 => 'mercadopago', - // 27 => 'mybank', - // 28 => 'paylater', - // 16 => 'p24', - // 7 => 'sofort' - ]; - - $funding_options = ''; - - foreach($this->company_gateway->fees_and_limits as $key => $value) { - - if($value->is_enabled) { - - $funding_options .= $enums[$key].','; - - } - - } - - return rtrim($funding_options, ','); - - } - - /** - * Initialize the Paypal gateway. - * - * Attempt to generate and return the access token. - * - * @return self - */ - public function init(): self - { - - $this->api_endpoint_url = 'https://api-m.paypal.com'; - // $this->api_endpoint_url = 'https://api-m.sandbox.paypal.com'; - $secret = config('ninja.paypal.secret'); - $client_id = config('ninja.paypal.client_id'); - - if($this->access_token && $this->token_expiry && $this->token_expiry->isFuture()) { - return $this; - } - - $response = Http::withBasicAuth($client_id, $secret) - ->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']) - ->withQueryParameters(['grant_type' => 'client_credentials']) - ->post("{$this->api_endpoint_url}/v1/oauth2/token"); - - if($response->successful()) { - $this->access_token = $response->json()['access_token']; - $this->token_expiry = now()->addSeconds($response->json()['expires_in'] - 60); - } else { - throw new PaymentFailed('Unable to gain access token from Paypal. Check your configuration', 401); - } - - return $this; - - } - - /** - * Payment method setter - * - * @param mixed $payment_method_id - * @return self - */ - public function setPaymentMethod($payment_method_id): self - { - if(!$payment_method_id) { - return $this; - } - - $this->gateway_type_id = $payment_method_id; - - $this->paypal_payment_method = $this->funding_options[$payment_method_id]; - - return $this; - } - - public function authorizeView($payment_method) - { - // PayPal doesn't support direct authorization. - - return $this; - } - - public function authorizeResponse($request) - { - // PayPal doesn't support direct authorization. - - return $this; - } - - /** - * Checks whether payments are enabled on the account - * + * Checks whether payments are enabled on the merchant account + * * @return self */ private function checkPaymentsReceivable(): self @@ -252,7 +98,10 @@ class PayPalPPCPPaymentDriver extends BaseDriver $data['merchantId'] = $this->company_gateway->getConfigField('merchantId'); $data['currency'] = $this->client->currency()->code; - return render('gateways.paypal.ppcp.pay', $data); + if($this->gateway_type_id == 29) + return render('gateways.paypal.ppcp.card', $data); + else + return render('gateways.paypal.ppcp.pay', $data); } @@ -268,6 +117,10 @@ class PayPalPPCPPaymentDriver extends BaseDriver $request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']); $response = json_decode($request['gateway_response'], true); + if($request->has('token') && strlen($request->input('token')) > 2) { + return $this->processTokenPayment($request, $response); + } + //capture $orderID = $response['orderID']; @@ -335,7 +188,7 @@ class PayPalPPCPPaymentDriver extends BaseDriver ['response' => $response, 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, - SystemLog::TYPE_PAYPAL, + SystemLog::TYPE_PAYPAL_PPCP, $this->client, $this->client->company, ); @@ -352,7 +205,7 @@ class PayPalPPCPPaymentDriver extends BaseDriver ['response' => $response], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, - SystemLog::TYPE_PAYPAL, + SystemLog::TYPE_PAYPAL_PPCP, $this->client, $this->client->company, ); @@ -372,74 +225,13 @@ class PayPalPPCPPaymentDriver extends BaseDriver return $r->json(); } - /** - * Generates a client token for the payment form. - * - * @return string - */ - private function getClientToken(): string - { - - $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']); - - if($r->successful()) { - return $r->json()['client_token']; - } - - throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401); - - } - - /** - * Builds the payment request. - * - * @return array - */ - private function paymentSource(): array - { - /** we only need to support paypal as payment source until as we are only using hosted payment buttons */ - return $this->injectPayPalPaymentSource(); - - } - - private function injectPayPalPaymentSource(): array - { - - $order = [ - "paypal" => [ - "name" => [ - "given_name" => $this->client->present()->first_name(), - "surname" => $this->client->present()->last_name(), - ], - "email_address" => $this->client->present()->email(), - "experience_context" => [ - "user_action" => "PAY_NOW" - ], - ], - ]; - - if( - strlen($this->client->address1 ?? '') > 2 && - strlen($this->client->city ?? '') > 2 && - strlen($this->client->state ?? '') >= 2 && - strlen($this->client->postal_code ?? '') > 2 && - strlen($this->client->country->iso_3166_2 ?? '') >= 2 - ) - { - $order["paypal"]["address"] = $this->getBillingAddress(); - } - - return $order; - - } - /** * Creates the PayPal Order object * * @param array $data * @return string */ - private function createOrder(array $data): string + public function createOrder(array $data): string { $_invoice = collect($this->payment_hash->data->invoices)->first(); @@ -453,7 +245,7 @@ class PayPalPPCPPaymentDriver extends BaseDriver $order = [ "intent" => "CAPTURE", - "payment_source" => $this->paymentSource(), + "payment_source" => $this->getPaymentSource(), "purchase_units" => [ [ "custom_id" => $this->payment_hash->hash, @@ -465,7 +257,6 @@ class PayPalPPCPPaymentDriver extends BaseDriver "payment_instruction" => [ "disbursement_mode" => "INSTANT", ], - $this->getShippingAddress(), "amount" => [ "value" => (string)$data['amount_with_fee'], "currency_code" => $this->client->currency()->code, @@ -496,125 +287,76 @@ class PayPalPPCPPaymentDriver extends BaseDriver $order['purchase_units'][0]["shipping"] = $shipping; } + if(isset($data['payment_source'])) { + $order['payment_source'] = $data['payment_source']; + } + $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); return $r->json()['id']; } - private function getBillingAddress(): array - { - return - [ - "address_line_1" => $this->client->address1, - "address_line_2" => $this->client->address2, - "admin_area_2" => $this->client->city, - "admin_area_1" => $this->client->state, - "postal_code" => $this->client->postal_code, - "country_code" => $this->client->country->iso_3166_2, - ]; - } - - private function getShippingAddress(): ?array - { - return $this->company_gateway->require_shipping_address ? - [ - "address" => - [ - "address_line_1" => strlen($this->client->shipping_address1 ?? '') > 1 ? $this->client->shipping_address1 : $this->client->address1, - "address_line_2" => $this->client->shipping_address2, - "admin_area_2" => strlen($this->client->shipping_city ?? '') > 1 ? $this->client->shipping_city : $this->client->city, - "admin_area_1" => strlen($this->client->shipping_state ?? '') > 1 ? $this->client->shipping_state : $this->client->state, - "postal_code" => strlen($this->client->shipping_postal_code ?? '') > 1 ? $this->client->shipping_postal_code : $this->client->postal_code, - "country_code" => $this->client->present()->shipping_country_code(), - ], - ] - - : null; - - } - /** - * Generates the gateway request + * processTokenPayment * - * @param string $uri - * @param string $verb - * @param array $data - * @param ?array $headers - * @return \Illuminate\Http\Client\Response + * With PayPal and token payments, the order needs to be + * deleted and then created with the payment source that + * has been selected by the client. + * + * This method handle the deletion of the current paypal order, + * and the automatic payment of the order with the selected payment source. + * + * @param mixed $request + * @param array $response + * @return void */ - public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) - { - $this->init(); + public function processTokenPayment($request, array $response) { - $r = Http::withToken($this->access_token) - ->withHeaders($this->getHeaders($headers)) - ->{$verb}("{$this->api_endpoint_url}{$uri}", $data); + $cgt = ClientGatewayToken::where('client_id', $this->client->id) + ->where('token', $request['token']) + ->firstOrFail(); - if($r->successful()) { - return $r; - } + $orderId = $response['orderID']; + $r = $this->gatewayRequest("/v1/checkout/orders/{$orderId}/", 'delete', ['body' => '']); + + $data['amount_with_fee'] = $this->payment_hash->data->amount_with_fee; + $data["payment_source"] = [ + "card" => [ + "vault_id" => $cgt->token, + "stored_credential" => [ + "payment_initiator" => "MERCHANT", + "payment_type" => "UNSCHEDULED", + "usage" => "SUBSEQUENT", + ], + ], + ]; + + $orderId = $this->createOrder($data); + + $r = $this->gatewayRequest("/v2/checkout/orders/{$orderId}", 'get', ['body' => '']); + + $response = $r->json(); + + $data = [ + 'payment_type' => $this->getPaymentMethod($request->gateway_type_id), + 'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'], + 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'], + 'gateway_type_id' => $this->gateway_type_id, + ]; + + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); SystemLogger::dispatch( - ['response' => $r->body()], + ['response' => $response, 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_FAILURE, - SystemLog::TYPE_PAYPAL, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_PAYPAL_PPCP, $this->client, $this->client->company, ); - throw new PaymentFailed("Gateway failure - {$r->body()}", 401); + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); } - - /** - * Generates the request headers - * - * @param array $headers - * @return array - */ - private function getHeaders(array $headers = []): array - { - return array_merge([ - 'Accept' => 'application/json', - 'Content-type' => 'application/json', - 'Accept-Language' => 'en_US', - 'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP', - 'PayPal-Request-Id' => Str::uuid()->toString(), - ], $headers); - } - - public function processWebhookRequest(Request $request) - { - - // nlog(json_encode($request->all())); - $this->init(); - - PayPalWebhook::dispatch($request->all(), $request->headers->all(), $this->access_token); - } - - public function auth(): bool - { - - try { - $this->init()->getClientToken(); - return true; - } - catch(\Exception $e) { - - } - - return false; - } - - public function importCustomers() - { - - // $response = $this->gatewayRequest('/v1/reporting/transactions', 'get', ['fields' => 'all','page_size' => 500,'start_date' => '2024-02-01T00:00:00-0000', 'end_date' => '2024-03-01T00:00:00-0000']); - - // nlog($response->json()); - - return true; - } -} +} \ No newline at end of file diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index 5745ab86d725..24f44b127487 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -12,147 +12,22 @@ namespace App\PaymentDrivers; -use Carbon\Carbon; -use Omnipay\Omnipay; use App\Models\Invoice; use App\Models\SystemLog; use App\Models\GatewayType; -use App\Models\PaymentType; use Illuminate\Support\Str; use App\Jobs\Util\SystemLogger; use App\Utils\Traits\MakesHash; use App\Exceptions\PaymentFailed; use App\Models\ClientGatewayToken; -use Illuminate\Support\Facades\Http; +use App\PaymentDrivers\PayPal\PayPalBasePaymentDriver; -class PayPalRestPaymentDriver extends BaseDriver +class PayPalRestPaymentDriver extends PayPalBasePaymentDriver { use MakesHash; - public $token_billing = false; - - public $can_authorise_credit_card = false; - - private $omnipay_gateway; - - private float $fee = 0; - public const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL; - private string $api_endpoint_url = ''; - - private string $paypal_payment_method = ''; - - private ?int $gateway_type_id = null; - - protected mixed $access_token = null; - - protected ?Carbon $token_expiry = null; - - private array $funding_options = [ - 3 => 'paypal', - 1 => 'card', - 25 => 'venmo', - 29 => 'paypal_advanced_cards', - // 9 => 'sepa', - // 12 => 'bancontact', - // 17 => 'eps', - // 15 => 'giropay', - // 13 => 'ideal', - // 26 => 'mercadopago', - // 27 => 'mybank', - 28 => 'paylater', - // 16 => 'p24', - // 7 => 'sofort' - ]; - - - public function gatewayTypes() - { - - $funding_options = []; - - foreach ($this->company_gateway->fees_and_limits as $key => $value) { - if ($value->is_enabled) { - $funding_options[] = $key; - } - } - - return $funding_options; - - } - - public function init() - { - - $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; - - $secret = $this->company_gateway->getConfigField('secret'); - $client_id = $this->company_gateway->getConfigField('clientId'); - - if($this->access_token && $this->token_expiry && $this->token_expiry->isFuture()) { - return $this; - } - - $response = Http::withBasicAuth($client_id, $secret) - ->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']) - ->withQueryParameters(['grant_type' => 'client_credentials']) - ->post("{$this->api_endpoint_url}/v1/oauth2/token"); - - if($response->successful()) { - $this->access_token = $response->json()['access_token']; - $this->token_expiry = now()->addSeconds($response->json()['expires_in'] - 60); - } else { - throw new PaymentFailed('Unable to gain access token from Paypal. Check your configuration', 401); - } - - return $this; - - } - - private function getPaymentMethod($gateway_type_id): int - { - $method = PaymentType::PAYPAL; - - match($gateway_type_id) { - "1" => $method = PaymentType::CREDIT_CARD_OTHER, - "3" => $method = PaymentType::PAYPAL, - "25" => $method = PaymentType::VENMO, - "28" => $method = PaymentType::PAY_LATER, - "29" => $method = PaymentType::CREDIT_CARD_OTHER, - }; - - return $method; - } - - public function setPaymentMethod($payment_method_id): self - { - if(!$payment_method_id) { - return $this; - } - - $this->gateway_type_id = $payment_method_id; - - $this->paypal_payment_method = $this->funding_options[$payment_method_id]; - - - return $this; - } - - public function authorizeView($payment_method) - { - // PayPal doesn't support direct authorization. - - return $this; - } - - public function authorizeResponse($request) - { - // PayPal doesn't support direct authorization. - - return $this; - } - public function processPaymentView($data) { $this->init(); @@ -169,110 +44,20 @@ class PayPalRestPaymentDriver extends BaseDriver $data['gateway_type_id'] = $this->gateway_type_id; $data['currency'] = $this->client->currency()->code; - -// return render('gateways.paypal.ppcp.card', $data); - -return render('gateways.paypal.pay', $data); + if($this->gateway_type_id == 29) + return render('gateways.paypal.ppcp.card', $data); + else + return render('gateways.paypal.pay', $data); } - - private function getFundingOptions(): string - { - - $enums = [ - 3 => 'paypal', - 1 => 'card', - 25 => 'venmo', - // 9 => 'sepa', - // 12 => 'bancontact', - // 17 => 'eps', - // 15 => 'giropay', - // 13 => 'ideal', - // 26 => 'mercadopago', - // 27 => 'mybank', - // 28 => 'paylater', - // 16 => 'p24', - // 7 => 'sofort' - ]; - - $funding_options = ''; - - foreach($this->company_gateway->fees_and_limits as $key => $value) { - - if($value->is_enabled) { - - $funding_options .= $enums[$key].','; - - } - - } - - return rtrim($funding_options, ','); - - } - - public function processTokenPayment($request, array $response) { - - $cgt = ClientGatewayToken::where('client_id', $this->client->id) - ->where('token', $request['token']) - ->firstOrFail(); - nlog("process token"); - - nlog($request->all()); - nlog($response); - - $orderId = $response['orderID']; - $r = $this->gatewayRequest("/v1/checkout/orders/{$orderId}/", 'delete', ['body' => '']); - - nlog($r); - - $data['amount_with_fee'] = $this->payment_hash->data->amount_with_fee; - $data["payment_source"] = [ - "card" => [ - "vault_id" => $cgt->token, - "stored_credential" => [ - "payment_initiator" => "MERCHANT", - "payment_type" => "UNSCHEDULED", - "usage" => "SUBSEQUENT", - // "previous_transaction_reference" => $cgt->gateway_customer_reference, - ], - ], - ]; - - $orderId = $this->createOrder($data); - - nlog("post order creation"); - nlog($orderId); - - - $r = $this->gatewayRequest("/v2/checkout/orders/{$orderId}", 'get', ['body' => '']); - nlog($r); - - $response = $r->json(); - nlog($response); - - $data = [ - 'payment_type' => $this->getPaymentMethod($request->gateway_type_id), - 'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'], - 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'], - 'gateway_type_id' => $this->gateway_type_id, - ]; - - $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); - - SystemLogger::dispatch( - ['response' => $response, 'data' => $data], - SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_SUCCESS, - SystemLog::TYPE_PAYPAL, - $this->client, - $this->client->company, - ); - - return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); - - } - + + + /** + * processPaymentResponse + * + * @param mixed $request + * @return void + */ public function processPaymentResponse($request) { @@ -280,12 +65,10 @@ return render('gateways.paypal.pay', $data); $request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']); $response = json_decode($request['gateway_response'], true); - nlog($request->all()); if($request->has('token') && strlen($request->input('token')) > 2) return $this->processTokenPayment($request, $response); - // nlog($response); //capture $orderID = $response['orderID']; @@ -339,6 +122,9 @@ return render('gateways.paypal.pay', $data); $response = $r; + nlog("Process response =>"); + nlog($response->json()); + if(isset($response['status']) && $response['status'] == 'COMPLETED' && isset($response['purchase_units'])) { return $this->createNinjaPayment($request, $response); @@ -367,8 +153,6 @@ return render('gateways.paypal.pay', $data); private function createNinjaPayment($request, $response) { - nlog($response->json()); - $data = [ 'payment_type' => $this->getPaymentMethod($request->gateway_type_id), 'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'], @@ -401,7 +185,6 @@ return render('gateways.paypal.pay', $data); $data['token'] = $token; $data['payment_method_id'] = GatewayType::PAYPAL_ADVANCED_CARDS; $data['payment_meta'] = $payment_meta; - // $data['payment_method_id'] = GatewayType::CREDIT_CARD; $additional['gateway_customer_reference'] = $gateway_customer_reference; @@ -423,116 +206,7 @@ return render('gateways.paypal.pay', $data); } - private function getClientToken(): string - { - - $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']); - - if($r->successful()) { - return $r->json()['client_token']; - } - - throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401); - - } - - private function getPaymentSource(): array - { - //@todo - roll back here for advanced payments vs hosted card fields. - if($this->gateway_type_id == GatewayType::PAYPAL_ADVANCED_CARDS) { - - return [ - "card" => [ - "attributes" => [ - "verification" => [ - "method" => "SCA_WHEN_REQUIRED", //SCA_ALWAYS - // "method" => "SCA_ALWAYS", //SCA_ALWAYS - ], - "vault" => [ - "store_in_vault" => "ON_SUCCESS", //must listen to this webhook - VAULT.PAYMENT-TOKEN.CREATED webhook. - ], - ], - "experience_context" => [ - "shipping_preference" => "SET_PROVIDED_ADDRESS" - ], - // "name" => $this->client->present()->primary_contact_name(), - // "email_address" => $this->client->present()->email(), - // "address" => [ - // "address_line_1" => $this->client->address1, - // "address_line_2" => $this->client->address2, - // "admin_area_2" => $this->client->city, - // "admin_area_1" => $this->client->state, - // "postal_code" => $this->client->postal_code, - // "country_code" => $this->client->country->iso_3166_2, - // ], - // "experience_context" => [ - // "user_action" => "PAY_NOW" - // ], - "stored_credential" => [ - // "payment_initiator" => "MERCHANT", //"CUSTOMER" who initiated the transaction? - "payment_initiator" => "CUSTOMER", //"" who initiated the transaction? - "payment_type" => "UNSCHEDULED", //UNSCHEDULED - "usage"=> "DERIVED", - ], - ], - ]; - - } - - $order = [ - "paypal" => [ - "name" => [ - "given_name" => $this->client->present()->first_name(), - "surname" => $this->client->present()->last_name(), - ], - "email_address" => $this->client->present()->email(), - "experience_context" => [ - "user_action" => "PAY_NOW" - ], - ], - ]; - - /** If we have a complete address, add it to the order, otherwise leave it blank! */ - if( - strlen($this->client->shipping_address1 ?? '') > 2 && - strlen($this->client->shipping_city ?? '') > 2 && - strlen($this->client->shipping_state ?? '') >= 2 && - strlen($this->client->shipping_postal_code ?? '') > 2 && - strlen($this->client->shipping_country->iso_3166_2 ?? '') >= 2 - ) { - $order['paypal']['address'] = [ - "address_line_1" => $this->client->shipping_address1, - "address_line_2" => $this->client->shipping_address2, - "admin_area_2" => $this->client->shipping_city, - "admin_area_1" => $this->client->shipping_state, - "postal_code" => $this->client->shipping_postal_code, - "country_code" => $this->client->present()->shipping_country_code(), - ]; - } - elseif( - strlen($this->client->address1 ?? '') > 2 && - strlen($this->client->city ?? '') > 2 && - strlen($this->client->state ?? '') >= 2 && - strlen($this->client->postal_code ?? '') > 2 && - strlen($this->client->country->iso_3166_2 ?? '') >= 2 - ) - { - $order['paypal']['address'] = [ - "address_line_1" => $this->client->address1, - "address_line_2" => $this->client->address2, - "admin_area_2" => $this->client->city, - "admin_area_1" => $this->client->state, - "postal_code" => $this->client->postal_code, - "country_code" => $this->client->country->iso_3166_2, - ]; - } - - return $order; - - } - - - private function createOrder(array $data): string + public function createOrder(array $data): string { $_invoice = collect($this->payment_hash->data->invoices)->first(); @@ -576,126 +250,81 @@ return render('gateways.paypal.pay', $data); ] ]; - if($shipping = $this->getShippingAddress()) { $order['purchase_units'][0]["shipping"] = $shipping; } if(isset($data['payment_source'])) $order['payment_source'] = $data['payment_source']; - + + + $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); return $r->json()['id']; } - private function getShippingAddress(): ?array - { - return $this->company_gateway->require_shipping_address ? - [ - "address" => - [ - "address_line_1" => strlen($this->client->shipping_address1) > 1 ? $this->client->shipping_address1 : $this->client->address1, - "address_line_2" => $this->client->shipping_address2, - "admin_area_2" => strlen($this->client->shipping_city) > 1 ? $this->client->shipping_city : $this->client->city, - "admin_area_1" => strlen($this->client->shipping_state) > 1 ? $this->client->shipping_state : $this->client->state, - "postal_code" => strlen($this->client->shipping_postal_code) > 1 ? $this->client->shipping_postal_code : $this->client->postal_code, - "country_code" => $this->client->present()->shipping_country_code(), - ], - ] + /** + * processTokenPayment + * + * With PayPal and token payments, the order needs to be + * deleted and then created with the payment source that + * has been selected by the client. + * + * This method handle the deletion of the current paypal order, + * and the automatic payment of the order with the selected payment source. + * + * @param mixed $request + * @param array $response + * @return void + */ + public function processTokenPayment($request, array $response) { - : [ - "name" => [ - "full_name" => $this->client->present()->name() - ] + $cgt = ClientGatewayToken::where('client_id', $this->client->id) + ->where('token', $request['token']) + ->firstOrFail(); + + $orderId = $response['orderID']; + $r = $this->gatewayRequest("/v1/checkout/orders/{$orderId}/", 'delete', ['body' => '']); + + $data['amount_with_fee'] = $this->payment_hash->data->amount_with_fee; + $data["payment_source"] = [ + "card" => [ + "vault_id" => $cgt->token, + "stored_credential" => [ + "payment_initiator" => "MERCHANT", + "payment_type" => "UNSCHEDULED", + "usage" => "SUBSEQUENT", + ], + ], + ]; + + $orderId = $this->createOrder($data); + + $r = $this->gatewayRequest("/v2/checkout/orders/{$orderId}", 'get', ['body' => '']); + + $response = $r->json(); + + $data = [ + 'payment_type' => $this->getPaymentMethod($request->gateway_type_id), + 'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'], + 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'], + 'gateway_type_id' => $this->gateway_type_id, ]; - } - - /** - * Generates the gateway request - * - * @param string $uri - * @param string $verb - * @param array $data - * @param ?array $headers - * @return \Illuminate\Http\Client\Response - */ - public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) - { - $this->init(); - - $r = Http::withToken($this->access_token) - ->withHeaders($this->getHeaders($headers)) - ->{$verb}("{$this->api_endpoint_url}{$uri}", $data); - - if($r->successful()) { - return $r; - } + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); SystemLogger::dispatch( - ['response' => $r->body()], + ['response' => $response, 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_PAYPAL, $this->client, - $this->client->company ?? $this->company_gateway->company, + $this->client->company, ); - throw new PaymentFailed("Gateway failure - {$r->body()}", 401); + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); } - - private function getHeaders(array $headers = []): array - { - return array_merge([ - 'Accept' => 'application/json', - 'Content-type' => 'application/json', - 'Accept-Language' => 'en_US', - 'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP', - 'PayPal-Request-Id' => Str::uuid()->toString(), - ], $headers); - } - - private function feeCalc($invoice, $invoice_total) - { - $invoice->service()->removeUnpaidGatewayFees(); - $invoice = $invoice->fresh(); - - $balance = floatval($invoice->balance); - - $_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save(); - - if (floatval($_updated_invoice->balance) > $balance) { - $fee = floatval($_updated_invoice->balance) - $balance; - - $this->payment_hash->fee_total = $fee; - $this->payment_hash->save(); - - return $fee; - } - - return 0; - } - - public function auth(): bool - { - - try { - $this->init()->getClientToken(); - return true; - } - catch(\Exception $e) { - - } - - return false; - } - - public function importCustomers() - { - return true; - } - } diff --git a/app/Repositories/TaskRepository.php b/app/Repositories/TaskRepository.php index c95207c25a1b..7d19a122665f 100644 --- a/app/Repositories/TaskRepository.php +++ b/app/Repositories/TaskRepository.php @@ -150,7 +150,7 @@ class TaskRepository extends BaseRepository { if(isset($time_log[0][0])) { - return \Carbon\Carbon::createFromTimestamp($time_log[0][0]); + return \Carbon\Carbon::createFromTimestamp($time_log[0][0])->addSeconds($task->company->utc_offset()); } return null; diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index 661b8aef52c4..e56091c909d5 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -314,7 +314,7 @@ class Email implements ShouldQueue $this->logMailError($e->getMessage(), $this->company->clients()->first()); $this->cleanUpMailers(); -$this->entityEmailFailed($message); + $this->entityEmailFailed($message); return; } diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index 7c94978d7260..f13090bb63de 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -211,6 +211,7 @@ class CompanyTransformer extends EntityTransformer 'smtp_password' => $company->smtp_password ? '********' : '', 'smtp_local_domain' => (string)$company->smtp_local_domain ?? '', 'smtp_verify_peer' => (bool)$company->smtp_verify_peer, + 'e_invoice' => $company->e_invoice ?: new \stdClass(), ]; } diff --git a/app/Transformers/CreditTransformer.php b/app/Transformers/CreditTransformer.php index 63b6053bdf21..4e9f143516c2 100644 --- a/app/Transformers/CreditTransformer.php +++ b/app/Transformers/CreditTransformer.php @@ -133,6 +133,7 @@ class CreditTransformer extends EntityTransformer 'subscription_id' => $this->encodePrimaryKey($credit->subscription_id), 'invoice_id' => $credit->invoice_id ? $this->encodePrimaryKey($credit->invoice_id) : '', 'tax_info' => $credit->tax_data ?: new \stdClass(), + 'e_invoice' => $credit->e_invoice ?: new \stdClass(), ]; } diff --git a/app/Transformers/ExpenseTransformer.php b/app/Transformers/ExpenseTransformer.php index 1f825ba81796..e8e130e2ec10 100644 --- a/app/Transformers/ExpenseTransformer.php +++ b/app/Transformers/ExpenseTransformer.php @@ -148,6 +148,8 @@ class ExpenseTransformer extends EntityTransformer 'uses_inclusive_taxes' => (bool) $expense->uses_inclusive_taxes, 'calculate_tax_by_amount' => (bool) $expense->calculate_tax_by_amount, 'entity_type' => 'expense', + 'e_invoice' => $expense->e_invoice ?: new \stdClass(), + ]; } } diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index fd18eb49a0c5..415f6f5b7092 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -158,6 +158,8 @@ class InvoiceTransformer extends EntityTransformer 'subscription_id' => $this->encodePrimaryKey($invoice->subscription_id), 'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled, 'tax_info' => $invoice->tax_data ?: new \stdClass(), + 'e_invoice' => $invoice->e_invoice ?: new \stdClass(), + ]; if (request()->has('reminder_schedule') && request()->query('reminder_schedule') == 'true') { diff --git a/app/Transformers/PurchaseOrderTransformer.php b/app/Transformers/PurchaseOrderTransformer.php index 6df9bfed3647..4d1a2469beeb 100644 --- a/app/Transformers/PurchaseOrderTransformer.php +++ b/app/Transformers/PurchaseOrderTransformer.php @@ -150,6 +150,8 @@ class PurchaseOrderTransformer extends EntityTransformer 'expense_id' => $this->encodePrimaryKey($purchase_order->expense_id), 'currency_id' => $purchase_order->currency_id ? (string) $purchase_order->currency_id : '', 'tax_info' => $purchase_order->tax_data ?: new \stdClass(), + 'e_invoice' => $purchase_order->e_invoice ?: new \stdClass(), + ]; } } diff --git a/app/Transformers/QuoteTransformer.php b/app/Transformers/QuoteTransformer.php index f918a90653e8..c981568043ae 100644 --- a/app/Transformers/QuoteTransformer.php +++ b/app/Transformers/QuoteTransformer.php @@ -149,6 +149,8 @@ class QuoteTransformer extends EntityTransformer 'project_id' => $this->encodePrimaryKey($quote->project_id), 'subscription_id' => $this->encodePrimaryKey($quote->subscription_id), 'tax_info' => $quote->tax_data ?: new \stdClass(), + 'e_invoice' => $quote->e_invoice ?: new \stdClass(), + ]; } } diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index c3946d619309..111ec0f4f74a 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -561,10 +561,19 @@ class HtmlEngine $data['$spc_qr_code'] = ['value' => $this->company->present()->getSpcQrCode($this->client->currency()->code, $this->entity->number, $this->entity->balance, $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client)), 'label' => '']; - $logo = $this->company->present()->logo_base64($this->settings); + if(Ninja::isHosted()) + $logo = $this->company->present()->logo($this->settings); + else + $logo = $this->company->present()->logo_base64($this->settings); + + $logo_url = $this->company->present()->logo($this->settings); + $data['$company.logo'] = ['value' => $logo ?: ' ', 'label' => ctrans('texts.logo')]; $data['$company_logo'] = &$data['$company.logo']; + + $data['$company.logo_url'] = ['value' => $logo_url ?: ' ', 'label' => ctrans('texts.logo')]; + $data['$company1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')]; $data['$company2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company2', $this->settings->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company2')]; $data['$company3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company3', $this->settings->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company3')]; diff --git a/composer.lock b/composer.lock index a9fb7f986e18..f572fe345741 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "da485c7cec773404ffe59d450b6505cf", + "content-hash": "1356155e46e797b140685c105df97b8e", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -3221,26 +3221,26 @@ }, { "name": "firebase/php-jwt", - "version": "v6.10.0", + "version": "v6.10.1", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" + "reference": "500501c2ce893c824c801da135d02661199f60c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", - "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5", + "reference": "500501c2ce893c824c801da135d02661199f60c5", "shasum": "" }, "require": { - "php": "^7.4||^8.0" + "php": "^8.0" }, "require-dev": { - "guzzlehttp/guzzle": "^6.5||^7.4", + "guzzlehttp/guzzle": "^7.4", "phpspec/prophecy-phpunit": "^2.0", "phpunit/phpunit": "^9.5", - "psr/cache": "^1.0||^2.0", + "psr/cache": "^2.0||^3.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" }, @@ -3278,9 +3278,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" + "source": "https://github.com/firebase/php-jwt/tree/v6.10.1" }, - "time": "2023-12-01T16:26:39+00:00" + "time": "2024-05-18T18:05:11+00:00" }, { "name": "fruitcake/php-cors", @@ -3602,23 +3602,23 @@ }, { "name": "google/apiclient-services", - "version": "v0.355.0", + "version": "v0.356.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "235e6a45ecafd77accc102b5ab6d529aab54da23" + "reference": "8e22b0a6f661f2db3f99abb6ee5a1dcf28d370e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/235e6a45ecafd77accc102b5ab6d529aab54da23", - "reference": "235e6a45ecafd77accc102b5ab6d529aab54da23", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/8e22b0a6f661f2db3f99abb6ee5a1dcf28d370e7", + "reference": "8e22b0a6f661f2db3f99abb6ee5a1dcf28d370e7", "shasum": "" }, "require": { - "php": "^7.4||^8.0" + "php": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7||^8.5.13" + "phpunit/phpunit": "^9.6" }, "type": "library", "autoload": { @@ -3640,9 +3640,9 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client-services/issues", - "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.355.0" + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.356.0" }, - "time": "2024-05-11T01:02:11+00:00" + "time": "2024-05-18T01:10:18+00:00" }, { "name": "google/auth", @@ -5034,12 +5034,12 @@ "source": { "type": "git", "url": "https://github.com/invoiceninja/einvoice.git", - "reference": "6028038ff94e6c0090ba5c444bd0be56a65bc0bc" + "reference": "39aec367c528ba66d923dc1d9e5c96f673bb46c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/invoiceninja/einvoice/zipball/6028038ff94e6c0090ba5c444bd0be56a65bc0bc", - "reference": "6028038ff94e6c0090ba5c444bd0be56a65bc0bc", + "url": "https://api.github.com/repos/invoiceninja/einvoice/zipball/39aec367c528ba66d923dc1d9e5c96f673bb46c7", + "reference": "39aec367c528ba66d923dc1d9e5c96f673bb46c7", "shasum": "" }, "require": { @@ -5075,7 +5075,7 @@ "source": "https://github.com/invoiceninja/einvoice/tree/main", "issues": "https://github.com/invoiceninja/einvoice/issues" }, - "time": "2024-05-18T12:35:18+00:00" + "time": "2024-05-20T11:42:32+00:00" }, { "name": "invoiceninja/inspector", diff --git a/database/factories/AccountFactory.php b/database/factories/AccountFactory.php index 371c2a3f9193..17cb13d7e89e 100644 --- a/database/factories/AccountFactory.php +++ b/database/factories/AccountFactory.php @@ -27,6 +27,7 @@ class AccountFactory extends Factory 'default_company_id' => 1, 'key' => Str::random(32), 'report_errors' => 1, + 'referral_code' => Str::lower(Str::random(32)), ]; } } diff --git a/database/migrations/2024_05_03_145535_btcpay_gateway.php b/database/migrations/2024_05_03_145535_btcpay_gateway.php index 5f952ce4ff76..b618db1f5679 100644 --- a/database/migrations/2024_05_03_145535_btcpay_gateway.php +++ b/database/migrations/2024_05_03_145535_btcpay_gateway.php @@ -12,24 +12,28 @@ return new class extends Migration */ public function up(): void { - $gateway = new Gateway; - $gateway->name = 'BTCPay'; - $gateway->key = 'vpyfbmdrkqcicpkjqdusgjfluebftuva'; - $gateway->provider = 'BTCPay'; - $gateway->is_offsite = true; + if(!Gateway::find(62)) + { + $gateway = new Gateway; + $gateway->id = 62; + $gateway->name = 'BTCPay'; + $gateway->key = 'vpyfbmdrkqcicpkjqdusgjfluebftuva'; + $gateway->provider = 'BTCPay'; + $gateway->is_offsite = true; - $btcpayFieds = new \stdClass; - $btcpayFieds->btcpayUrl = ""; - $btcpayFieds->apiKey = ""; - $btcpayFieds->storeId = ""; - $btcpayFieds->webhookSecret = ""; - $gateway->fields = \json_encode($btcpayFieds); + $btcpayFieds = new \stdClass; + $btcpayFieds->btcpayUrl = ""; + $btcpayFieds->apiKey = ""; + $btcpayFieds->storeId = ""; + $btcpayFieds->webhookSecret = ""; + $gateway->fields = \json_encode($btcpayFieds); - $gateway->visible = true; - $gateway->site_url = 'https://btcpayserver.org'; - $gateway->default_gateway_type_id = GatewayType::CRYPTO; - $gateway->save(); + $gateway->visible = true; + $gateway->site_url = 'https://btcpayserver.org'; + $gateway->default_gateway_type_id = GatewayType::CRYPTO; + $gateway->save(); + } } /** @@ -39,4 +43,4 @@ return new class extends Migration { // } -}; +}; \ No newline at end of file diff --git a/database/migrations/2024_05_19_215103_2024_05_20_einvoice_columns.php b/database/migrations/2024_05_19_215103_2024_05_20_einvoice_columns.php new file mode 100644 index 000000000000..5fca9a9feb95 --- /dev/null +++ b/database/migrations/2024_05_19_215103_2024_05_20_einvoice_columns.php @@ -0,0 +1,53 @@ +mediumText('e_invoice')->nullable(); + }); + + Schema::table('invoices', function (Blueprint $table) { + $table->mediumText('e_invoice')->nullable(); + }); + + Schema::table('quotes', function (Blueprint $table) { + $table->mediumText('e_invoice')->nullable(); + }); + + Schema::table('credits', function (Blueprint $table) { + $table->mediumText('e_invoice')->nullable(); + }); + + Schema::table('purchase_orders', function (Blueprint $table) { + $table->mediumText('e_invoice')->nullable(); + }); + + Schema::table('expenses', function (Blueprint $table) { + $table->mediumText('e_invoice')->nullable(); + }); + + + Schema::table('accounts', function (Blueprint $table) { + $table->integer('email_quota')->default(20)->nullable(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/database/seeders/RandomDataSeeder.php b/database/seeders/RandomDataSeeder.php index 2e08f0171fec..b747764d0113 100644 --- a/database/seeders/RandomDataSeeder.php +++ b/database/seeders/RandomDataSeeder.php @@ -252,7 +252,7 @@ class RandomDataSeeder extends Seeder $invoice->service()->createInvitations()->markSent()->save(); - $invoice->ledger()->updateInvoiceBalance($invoice->balance); + $invoice->ledger()->update_invoiceBalance($invoice->balance); if (rand(0, 1)) { $payment = Payment::create([ @@ -277,7 +277,7 @@ class RandomDataSeeder extends Seeder event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); - // $payment->service()->updateInvoicePayment($payment_hash); + // $payment->service()->update_invoicePayment($payment_hash); } }); diff --git a/lang/en/texts.php b/lang/en/texts.php index 63654d484928..ed11493f87b1 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5320,6 +5320,11 @@ $lang = array( 'task_round_to_nearest' => 'Round To Nearest', 'bulk_updated' => 'Successfully updated data', 'bulk_update' => 'Bulk Update', + 'calculate' => 'Calculate', + 'sum' => 'Sum', + 'money' => 'Money', + 'web_app' => 'Web App', + 'desktop_app' => 'Desktop App', ); return $lang; diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index adecee4bb1b7..ee826a75d2c0 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -5297,6 +5297,26 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'local_domain_help' => 'Domaine EHLO (facultatif)', 'port_help' => 'ex. 25,587,465', 'host_help' => 'ex. smtp.gmail.com', + 'always_show_required_fields' => 'Permet l\'affichage des champs requis d\'un formulaire', + 'always_show_required_fields_help' => 'Affiche toujours les champs requis d\'un formulaire au paiement', + 'advanced_cards' => 'Cartes avancées', + 'activity_140' => 'État de compte envoyé à :client', + 'invoice_net_amount' => 'Montant net de la facture', + 'round_to_minutes' => 'Arrondir aux minutes', + '1_minute' => '1 minute', + '5_minutes' => '5 minutes', + '15_minutes' => '15 minutes', + '30_minutes' => '30 minutes', + '1_hour' => '1 heure', + '1_day' => '1 jour', + 'round_tasks' => 'Arrondir les tâches', + 'round_tasks_help' => 'Arrondir les intervales à la sauvegarde des tâches', + 'direction' => 'Direction', + 'round_up' => 'Arrondir à hausse', + 'round_down' => 'Arrondir à la baisse', + 'task_round_to_nearest' => 'Arrondir au plus près', + 'bulk_updated' => 'Les données ont été mises à jour', + 'bulk_update' => 'Mise à jour groupée', ); return $lang; diff --git a/lang/ja/texts.php b/lang/ja/texts.php index 9f046c23ceb4..dfec27c63c5c 100644 --- a/lang/ja/texts.php +++ b/lang/ja/texts.php @@ -700,7 +700,7 @@ $lang = array( 'total_invoiced' => 'Total Invoiced', 'open_balance' => 'Open Balance', 'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.', - 'basic_settings' => 'Basic Settings', + 'basic_settings' => '基本設定', 'pro' => 'Pro', 'gateways' => 'ペイメントゲートウェイ', 'next_send_on' => 'Send Next: :date', @@ -1344,7 +1344,7 @@ $lang = array( 'auto_bill_payment_method_credit_card' => 'クレジットカード', 'auto_bill_payment_method_paypal' => 'PayPal アカウント', 'auto_bill_notification_placeholder' => 'この請求書は、期日にて登録されているクレジットカードに自動的に請求されます。', - 'payment_settings' => 'Payment Settings', + 'payment_settings' => '支払い設定', 'on_send_date' => '発送日', 'on_due_date' => '支払期日', @@ -5300,6 +5300,26 @@ $lang = array( 'local_domain_help' => 'EHLO domain (optional)', 'port_help' => 'ie. 25,587,465', 'host_help' => 'ie. smtp.gmail.com', + 'always_show_required_fields' => 'Allows show required fields form', + 'always_show_required_fields_help' => 'Displays the required fields form always at checkout', + 'advanced_cards' => 'Advanced Cards', + 'activity_140' => 'Statement sent to :client', + 'invoice_net_amount' => 'Invoice Net Amount', + 'round_to_minutes' => 'Round To Minutes', + '1_minute' => '1 Minute', + '5_minutes' => '5 Minutes', + '15_minutes' => '15 Minutes', + '30_minutes' => '30 Minutes', + '1_hour' => '1 Hour', + '1_day' => '1 Day', + 'round_tasks' => 'Round Tasks', + 'round_tasks_help' => 'Round time intervals when saving tasks', + 'direction' => 'Direction', + 'round_up' => 'Round Up', + 'round_down' => 'Round Down', + 'task_round_to_nearest' => 'Round To Nearest', + 'bulk_updated' => 'Successfully updated data', + 'bulk_update' => 'Bulk Update', ); return $lang; diff --git a/public/build/assets/app-bfac6a32.js b/public/build/assets/app-a52d5f77.js similarity index 96% rename from public/build/assets/app-bfac6a32.js rename to public/build/assets/app-a52d5f77.js index d1ce4cff3984..ce2f066a6600 100644 --- a/public/build/assets/app-bfac6a32.js +++ b/public/build/assets/app-a52d5f77.js @@ -83,7 +83,7 @@ Would you like to refresh the page?`)&&window.location.reload()}function ec(e){X 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } - `;let r=Du();r&&(e.nonce=r),document.head.appendChild(e)}var ea=[],ss=["data-csrf","aria-hidden"];function Eo(e,r){let n=new DOMParser().parseFromString(e,"text/html"),a=document.adoptNode(n.body),s=document.adoptNode(n.head);ea=ea.concat(Array.from(document.body.querySelectorAll("script")).map(m=>fs(ds(m.outerHTML,ss))));let l=()=>{};Bc(s).finally(()=>{l()}),Fc(a,ea);let v=document.body;document.body.replaceWith(a),Alpine.destroyTree(v),r(m=>l=m)}function Fc(e,r){e.querySelectorAll("script").forEach(n=>{if(n.hasAttribute("data-navigate-once")){let a=fs(ds(n.outerHTML,ss));if(r.includes(a))return}n.replaceWith(ls(n))})}function Bc(e){let r=Array.from(document.head.children),n=r.map(l=>l.outerHTML),a=document.createDocumentFragment(),s=[];for(let l of Array.from(e.children))if(Co(l))if(n.includes(l.outerHTML))a.appendChild(l);else if(us(l)&&Hc(l,r)&&setTimeout(()=>window.location.reload()),cs(l))try{s.push(Uc(ls(l)))}catch{}else document.head.appendChild(l);for(let l of Array.from(document.head.children))Co(l)||l.remove();for(let l of Array.from(e.children))document.head.appendChild(l);return Promise.all(s)}async function Uc(e){return new Promise((r,n)=>{e.src?(e.onload=()=>r(),e.onerror=()=>n()):r(),document.head.appendChild(e)})}function ls(e){let r=document.createElement("script");r.textContent=e.textContent,r.async=e.async;for(let n of e.attributes)r.setAttribute(n.name,n.value);return r}function us(e){return e.hasAttribute("data-navigate-track")}function Hc(e,r){let[n,a]=Oo(e);return r.some(s=>{if(!us(s))return!1;let[l,v]=Oo(s);if(l===n&&a!==v)return!0})}function Oo(e){return(cs(e)?e.src:e.href).split("?")}function Co(e){return e.tagName.toLowerCase()==="link"&&e.getAttribute("rel").toLowerCase()==="stylesheet"||e.tagName.toLowerCase()==="style"||e.tagName.toLowerCase()==="script"}function cs(e){return e.tagName.toLowerCase()==="script"}function fs(e){return e.split("").reduce((r,n)=>(r=(r<<5)-r+n.charCodeAt(0),r&r),0)}function ds(e,r){let n=e;return r.forEach(a=>{const s=new RegExp(`${a}="[^"]*"|${a}='[^']*'`,"g");n=n.replace(s,"")}),n=n.replaceAll(" ",""),n.trim()}var ta=!0;function qc(e){e.navigate=n=>{let a=Yr(n);Yt("alpine:navigate",{url:a,history:!1,cached:!1})||r(a)},e.navigate.disableProgressBar=()=>{ta=!1},e.addInitSelector(()=>`[${e.prefixed("navigate")}]`),e.directive("navigate",(n,{modifiers:a})=>{a.includes("hover")&&Rc(n,60,()=>{let l=ho(n);go(l,(v,m)=>{mo(v,l,m)})}),Pc(n,l=>{let v=ho(n);go(v,(m,R)=>{mo(m,v,R)}),l(()=>{Yt("alpine:navigate",{url:v,history:!1,cached:!1})||r(v)})})});function r(n,a=!0){ta&&jc(),Vc(n,(s,l)=>{Yt("alpine:navigating"),_o(),ta&&Ic(),zc(),Ec(),Ao(e,v=>{xo(m=>{vo(m)}),a?Ac(s,l):is(l,s),Eo(s,m=>{yo(document.body),So((R,B)=>{bo(R)}),wo(),m(()=>{v(()=>{setTimeout(()=>{}),To(e),Yt("alpine:navigated")})})})})})}Cc(n=>{n(a=>{let s=Yr(a);if(Yt("alpine:navigate",{url:s,history:!0,cached:!1}))return;r(s,!1)})},(n,a,s,l)=>{let v=Yr(a);Yt("alpine:navigate",{url:v,history:!0,cached:!0})||(_o(),Yt("alpine:navigating"),Oc(s,l),Ao(e,R=>{xo(B=>{vo(B)}),Eo(n,()=>{Dc(),yo(document.body),So((B,ue)=>{bo(B)}),wo(),R(()=>{To(e),Yt("alpine:navigated")})})}))}),setTimeout(()=>{Yt("alpine:navigated")})}function Vc(e,r){Mc(e,r,()=>{kc(e,r)})}function Ao(e,r){e.stopObservingMutations(),r(n=>{e.startObservingMutations(),queueMicrotask(()=>{n()})})}function Yt(e,r){let n=new CustomEvent(e,{cancelable:!0,bubbles:!0,detail:r});return document.dispatchEvent(n),n.defaultPrevented}function To(e){e.initTree(document.body,void 0,(r,n)=>{r._x_wasPersisted&&n()})}function zc(){let e=function(r,n){Alpine.walk(r,(a,s)=>{Lc(a)&&s(),Nc(a)?s():n(a,s)})};Alpine.destroyTree(document.body,e)}function Wc(e){e.magic("queryString",(r,{interceptor:n})=>{let a,s=!1,l=!1;return n((v,m,R,B,ue)=>{let ne=a||B,{initial:z,replace:Y,push:S,pop:y}=ua(ne,v,s);return R(z),l?(e.effect(()=>S(m())),y(async _=>{R(_),await(()=>Promise.resolve())()})):e.effect(()=>Y(m())),z},v=>{v.alwaysShow=()=>(s=!0,v),v.usePush=()=>(l=!0,v),v.as=m=>(a=m,v)})}),e.history={track:ua}}function ua(e,r,n=!1){let{has:a,get:s,set:l,remove:v}=Jc(),m=new URL(window.location.href),R=a(m,e),B=R?s(m,e):r,ue=JSON.stringify(B),ne=S=>JSON.stringify(S)===ue;n&&(m=l(m,e,B)),Po(m,e,{value:B});let z=!1,Y=(S,y)=>{if(z)return;let _=new URL(window.location.href);!n&&!R&&ne(y)||y===void 0?_=v(_,e):_=l(_,e,y),S(_,e,{value:y})};return{initial:B,replace(S){Y(Po,S)},push(S){Y(Kc,S)},pop(S){let y=_=>{!_.state||!_.state.alpine||Object.entries(_.state.alpine).forEach(([O,{value:P}])=>{if(O!==e)return;z=!0;let I=S(P);I instanceof Promise?I.finally(()=>z=!1):z=!1})};return window.addEventListener("popstate",y),()=>window.removeEventListener("popstate",y)}}}function Po(e,r,n){let a=window.history.state||{};a.alpine||(a.alpine={}),a.alpine[r]=Sa(n),window.history.replaceState(a,"",e.toString())}function Kc(e,r,n){let a=window.history.state||{};a.alpine||(a.alpine={}),a={alpine:{...a.alpine,[r]:Sa(n)}},window.history.pushState(a,"",e.toString())}function Sa(e){if(e!==void 0)return JSON.parse(JSON.stringify(e))}function Jc(){return{has(e,r){let n=e.search;if(!n)return!1;let a=ei(n);return Object.keys(a).includes(r)},get(e,r){let n=e.search;return n?ei(n)[r]:!1},set(e,r,n){let a=ei(e.search);return a[r]=ps(Sa(n)),e.search=Ro(a),e},remove(e,r){let n=ei(e.search);return delete n[r],e.search=Ro(n),e}}}function ps(e){if(!va(e))return e;for(let r in e)e[r]===null?delete e[r]:e[r]=ps(e[r]);return e}function Ro(e){let r=s=>typeof s=="object"&&s!==null,n=(s,l={},v="")=>(Object.entries(s).forEach(([m,R])=>{let B=v===""?m:`${v}[${m}]`;R===null?l[B]="":r(R)?l={...l,...n(R,l,B)}:l[B]=encodeURIComponent(R).replaceAll("%20","+").replaceAll("%2C",",")}),l),a=n(e);return Object.entries(a).map(([s,l])=>`${s}=${l}`).join("&")}function ei(e){if(e=e.replace("?",""),e==="")return{};let r=(s,l,v)=>{let[m,R,...B]=s.split(".");if(!R)return v[s]=l;v[m]===void 0&&(v[m]=isNaN(R)?{}:[]),r([R,...B].join("."),l,v[m])},n=e.split("&").map(s=>s.split("=")),a=Object.create(null);return n.forEach(([s,l])=>{if(!(typeof l>"u"))if(l=decodeURIComponent(l.replaceAll("+","%20")),!s.includes("["))a[s]=l;else{let v=s.replaceAll("[",".").replaceAll("]","");r(v,l,a)}}),a}var Gc=tt(Lu()),Yc=tt(ju()),Et=tt(Ot());function Xc(){setTimeout(()=>Qc()),Gi(document,"livewire:init"),Gi(document,"livewire:initializing"),Et.default.plugin(Gc.default),Et.default.plugin(Wc),Et.default.plugin(xc.default),Et.default.plugin(bc.default),Et.default.plugin(Sc.default),Et.default.plugin(_c.default),Et.default.plugin(wc.default),Et.default.plugin(qc),Et.default.plugin(Yc.default),Et.default.addRootSelector(()=>"[wire\\:id]"),Et.default.onAttributesAdded((e,r)=>{if(!Array.from(r).some(a=>_n(a.name)))return;let n=or(e,!1);n&&r.forEach(a=>{if(!_n(a.name))return;let s=aa(e,a.name);lt("directive.init",{el:e,component:n,directive:s,cleanup:l=>{Et.default.onAttributeRemoved(e,s.raw,l)}})})}),Et.default.interceptInit(Et.default.skipDuringClone(e=>{if(!Array.from(e.attributes).some(n=>_n(n.name)))return;if(e.hasAttribute("wire:id")){let n=oc(e);Et.default.onAttributeRemoved(e,"wire:id",()=>{sc(n.id)})}let r=or(e,!1);r&&(lt("element.init",{el:e,component:r}),Array.from(e.getAttributeNames()).filter(a=>_n(a)).map(a=>aa(e,a)).forEach(a=>{lt("directive.init",{el:e,component:r,directive:a,cleanup:s=>{Et.default.onAttributeRemoved(e,a.raw,s)}})}))})),Et.default.start(),setTimeout(()=>window.Livewire.initialRenderIsFinished=!0),Gi(document,"livewire:initialized")}function Qc(){let e=document.querySelector("script[data-update-uri][data-csrf]");if(!e)return;let r=e.closest("[wire\\:id]");r&&console.warn("Livewire: missing closing tags found. Ensure your template elements contain matching closing tags.",r)}var Ea=tt(Ot());ze("effect",({component:e,effects:r})=>{Zc(e,r.listeners||[])});function Zc(e,r){r.forEach(n=>{let a=s=>{s.__livewire&&s.__livewire.receivedBy.push(e),e.$wire.call("__dispatch",n,s.detail||{})};window.addEventListener(n,a),e.addCleanup(()=>window.removeEventListener(n,a)),e.el.addEventListener(n,s=>{s.__livewire&&(s.bubbles||(s.__livewire&&s.__livewire.receivedBy.push(e.id),e.$wire.call("__dispatch",n,s.detail||{})))})})}var ko=tt(Ot()),Vr=new WeakMap,ri=new Set;ze("payload.intercept",async({assets:e})=>{if(e)for(let[r,n]of Object.entries(e))await rf(r,async()=>{await nf(n)})});ze("component.init",({component:e})=>{let r=e.snapshot.memo.assets;r&&r.forEach(n=>{ri.has(n)||ri.add(n)})});ze("effect",({component:e,effects:r})=>{let n=r.scripts;n&&Object.entries(n).forEach(([a,s])=>{ef(e,a,()=>{let l=tf(s);ko.default.dontAutoEvaluateFunctions(()=>{ko.default.evaluate(e.el,l,{$wire:e.$wire})})})})});function ef(e,r,n){if(Vr.has(e)&&Vr.get(e).includes(r))return;n(),Vr.has(e)||Vr.set(e,[]);let a=Vr.get(e);a.push(r),Vr.set(e,a)}function tf(e){let n=/]*>([\s\S]*?)<\/script>/gm.exec(e);return n&&n[1]?n[1].trim():""}async function rf(e,r){ri.has(e)||(await r(),ri.add(e))}async function nf(e){let r=new DOMParser().parseFromString(e,"text/html"),n=document.adoptNode(r.head);for(let a of n.children)try{await af(a)}catch{}}async function af(e){return new Promise((r,n)=>{if(of(e)){let a=sf(e);a.src?(a.onload=()=>r(),a.onerror=()=>n()):r(),document.head.appendChild(a)}else document.head.appendChild(e),r()})}function of(e){return e.tagName.toLowerCase()==="script"}function sf(e){let r=document.createElement("script");r.textContent=e.textContent,r.async=e.async;for(let n of e.attributes)r.setAttribute(n.name,n.value);return r}var Mo=tt(Ot());ze("effect",({component:e,effects:r})=>{let n=r.js,a=r.xjs;n&&Object.entries(n).forEach(([s,l])=>{ic(e,s,()=>{Mo.default.evaluate(e.el,l)})}),a&&a.forEach(s=>{Mo.default.evaluate(e.el,s)})});var lf=tt(Ot());function uf(e,r,n){let a=r.parentElement?r.parentElement.tagName.toLowerCase():"div",s=document.createElement(a);s.innerHTML=n;let l;try{l=or(r.parentElement)}catch{}l&&(s.__livewire=l);let v=s.firstElementChild;v.__livewire=e,lt("morph",{el:r,toEl:v,component:e}),lf.default.morph(r,v,{updating:(m,R,B,ue)=>{if(!zr(m)){if(lt("morph.updating",{el:m,toEl:R,component:e,skip:ue,childrenOnly:B}),m.__livewire_ignore===!0||(m.__livewire_ignore_self===!0&&B(),No(m)&&m.getAttribute("wire:id")!==e.id))return ue();No(m)&&(R.__livewire=e)}},updated:m=>{zr(m)||lt("morph.updated",{el:m,component:e})},removing:(m,R)=>{zr(m)||lt("morph.removing",{el:m,component:e,skip:R})},removed:m=>{zr(m)||lt("morph.removed",{el:m,component:e})},adding:m=>{lt("morph.adding",{el:m,component:e})},added:m=>{zr(m)||(or(m).id,lt("morph.added",{el:m}))},key:m=>{if(!zr(m))return m.hasAttribute("wire:key")?m.getAttribute("wire:key"):m.hasAttribute("wire:id")?m.getAttribute("wire:id"):m.id},lookahead:!1})}function zr(e){return typeof e.hasAttribute!="function"}function No(e){return e.hasAttribute("wire:id")}ze("effect",({component:e,effects:r})=>{let n=r.html;n&&queueMicrotask(()=>{queueMicrotask(()=>{uf(e,e.el,n)})})});ze("effect",({component:e,effects:r})=>{cf(e,r.dispatches||[])});function cf(e,r){r.forEach(({name:n,params:a={},self:s=!1,to:l})=>{s?Jr(e,n,a):l?_a(l,n,a):ns(e,n,a)})}var ff=tt(Ot()),ca=new Iu;ze("directive.init",({el:e,directive:r,cleanup:n,component:a})=>setTimeout(()=>{r.value==="submit"&&e.addEventListener("submit",()=>{let s=r.expression.startsWith("$parent")?a.parent.id:a.id,l=df(e);window.yo=l,ca.add(s,l)})}));ze("commit",({component:e,respond:r})=>{r(()=>{ca.each(e.id,n=>n()),ca.remove(e.id)})});function df(e){let r=[];return ff.default.walk(e,(n,a)=>{if(e.contains(n)){if(n.hasAttribute("wire:ignore"))return a();pf(n)?r.push(gf(n)):hf(n)&&r.push(mf(n))}}),()=>{for(;r.length>0;)r.shift()()}}function pf(e){let r=e.tagName.toLowerCase();return r==="select"||r==="button"&&e.type==="submit"||r==="input"&&(e.type==="checkbox"||e.type==="radio")}function hf(e){return["input","textarea"].includes(e.tagName.toLowerCase())}function gf(e){let r=e.disabled?()=>{}:()=>e.disabled=!1;return e.disabled=!0,r}function mf(e){let r=e.readOnly?()=>{}:()=>e.readOnly=!1;return e.readOnly=!0,r}ze("commit.pooling",({commits:e})=>{e.forEach(r=>{let n=r.component;hs(n,a=>{a.$wire.$commit()})})});ze("commit.pooled",({pools:e})=>{vf(e).forEach(n=>{let a=n.component;hs(a,s=>{yf(e,a,s)})})});function vf(e){let r=[];return e.forEach(n=>{n.commits.forEach(a=>{r.push(a)})}),r}function yf(e,r,n){let a=Lo(e,r),s=Lo(e,n),l=s.findCommitByComponent(n);s.delete(l),a.add(l),e.forEach(v=>{v.empty()&&e.delete(v)})}function Lo(e,r){for(let[n,a]of e.entries())if(a.hasCommitFor(r))return a}function hs(e,r){gs(e,n=>{(bf(n)||_f(n))&&r(n)})}function bf(e){return!!e.snapshot.memo.props}function _f(e){return!!e.snapshot.memo.bindings}function gs(e,r){e.children.forEach(n=>{r(n),gs(n,r)})}ze("commit",({succeed:e})=>{e(({effects:r})=>{let n=r.download;if(!n)return;let a=window.webkitURL||window.URL,s=a.createObjectURL(wf(n.content,n.contentType)),l=document.createElement("a");l.style.display="none",l.href=s,l.download=n.name,document.body.appendChild(l),l.click(),setTimeout(function(){a.revokeObjectURL(s)},0)})});function wf(e,r="",n=512){const a=atob(e),s=[];r===null&&(r="");for(let l=0;l{let r=e.snapshot.memo;r.lazyLoaded!==void 0&&(da.add(e),r.lazyIsolated!==void 0&&r.lazyIsolated===!1&&fa.add(e))});ze("commit.pooling",({commits:e})=>{e.forEach(r=>{da.has(r.component)&&(fa.has(r.component)?(r.isolate=!1,fa.delete(r.component)):r.isolate=!0,da.delete(r.component))})});var jo=tt(Ot());ze("effect",({component:e,effects:r,cleanup:n})=>{let a=r.url;a&&Object.entries(a).forEach(([s,l])=>{let{name:v,as:m,use:R,alwaysShow:B,except:ue}=xf(s,l);m||(m=v);let ne=[!1,null,void 0].includes(ue)?Xt(e.ephemeral,v):ue,{replace:z,push:Y,pop:S}=ua(m,ne,B);if(R==="replace"){let y=jo.default.effect(()=>{z(Xt(e.reactive,v))});n(()=>jo.default.release(y))}else if(R==="push"){let y=ze("commit",({component:O,succeed:P})=>{if(e!==O)return;let I=Xt(e.canonical,v);P(()=>{let H=Xt(e.canonical,v);JSON.stringify(I)!==JSON.stringify(H)&&Y(H)})}),_=S(async O=>{await e.$wire.set(v,O),document.querySelectorAll("input").forEach(P=>{P._x_forceModelUpdate&&P._x_forceModelUpdate(P._x_model.get())})});n(()=>{y(),_()})}})});function xf(e,r){let n={use:"replace",alwaysShow:!1};return typeof r=="string"?{...n,name:r,as:r}:{...{...n,name:e,as:e},...r}}ze("request",({options:e})=>{window.Echo&&(e.headers["X-Socket-ID"]=window.Echo.socketId())});ze("effect",({component:e,effects:r})=>{(r.listeners||[]).forEach(a=>{if(a.startsWith("echo")){if(typeof window.Echo>"u"){console.warn("Laravel Echo cannot be found");return}let s=a.split(/(echo:|echo-)|:|,/);s[1]=="echo:"&&s.splice(2,0,"channel",void 0),s[2]=="notification"&&s.push(void 0,void 0);let[l,v,m,R,B,ue,ne]=s;if(["channel","private","encryptedPrivate"].includes(m)){let z=Y=>Jr(e,a,[Y]);window.Echo[m](B).listen(ne,z),e.addCleanup(()=>{window.Echo[m](B).stopListening(ne,z)})}else if(m=="presence")if(["here","joining","leaving"].includes(ne))window.Echo.join(B)[ne](z=>{Jr(e,a,[z])});else{let z=Y=>Jr(e,a,[Y]);window.Echo.join(B).listen(ne,z),e.addCleanup(()=>{window.Echo.leaveChannel(B)})}else m=="notification"?window.Echo.private(B).notification(z=>{Jr(e,a,[z])}):console.warn("Echo channel type not yet supported")}})});var ms=new WeakSet;ze("component.init",({component:e})=>{e.snapshot.memo.isolate===!0&&ms.add(e)});ze("commit.pooling",({commits:e})=>{e.forEach(r=>{ms.has(r.component)&&(r.isolate=!0)})});Ef()&&Alpine.navigate.disableProgressBar();document.addEventListener("alpine:navigate",e=>Oa("livewire:navigate",e));document.addEventListener("alpine:navigating",e=>Oa("livewire:navigating",e));document.addEventListener("alpine:navigated",e=>Oa("livewire:navigated",e));function Oa(e,r){let n=new CustomEvent(e,{cancelable:!0,bubbles:!0,detail:r.detail});document.dispatchEvent(n),n.defaultPrevented&&r.preventDefault()}function Sf(e,r,n){e.redirectUsingNavigate?Alpine.navigate(r):n()}function Ef(){return!!(document.querySelector("[data-no-progress-bar]")||window.livewireScriptConfig&&window.livewireScriptConfig.progressBar===!1)}ze("effect",({effects:e})=>{if(!e.redirect)return;let r=e.redirect;Sf(e,r,()=>{window.location.href=r})});var Io=tt(Ot());ze("morph.added",({el:e})=>{e.__addedByMorph=!0});Ft("transition",({el:e,directive:r,component:n,cleanup:a})=>{let s=Io.default.reactive({state:!e.__addedByMorph});Io.default.bind(e,{[r.rawName.replace("wire:","x-")]:"","x-show"(){return s.state}}),e.__addedByMorph&&setTimeout(()=>s.state=!0);let l=[];l.push(ze("morph.removing",({el:v,skip:m})=>{m(),v.addEventListener("transitionend",()=>{v.remove()}),s.state=!1,l.push(ze("morph",({component:R})=>{R===n&&(v.remove(),l.forEach(B=>B()))}))})),a(()=>l.forEach(v=>v()))});var Of=new Vo;function Cf(e,r){Of.each(e,n=>{n.callback(),n.callback=()=>{}}),r()}var Do=tt(Ot());ze("directive.init",({el:e,directive:r,cleanup:n,component:a})=>{if(["snapshot","effects","model","init","loading","poll","ignore","id","data","key","target","dirty"].includes(r.value)||mc(r.value))return;let s=r.rawName.replace("wire:","x-on:");r.value==="submit"&&!r.modifiers.includes("prevent")&&(s=s+".prevent");let l=Do.default.bind(e,{[s](v){let m=()=>{Cf(a,()=>{Do.default.evaluate(e,"$wire."+r.expression,{scope:{$event:v}})})};e.__livewire_confirm?e.__livewire_confirm(()=>{m()},()=>{v.stopImmediatePropagation()}):m()}});n(l)});var Gr=tt(Ot());Gr.default.addInitSelector(()=>"[wire\\:navigate]");Gr.default.addInitSelector(()=>"[wire\\:navigate\\.hover]");Gr.default.interceptInit(Gr.default.skipDuringClone(e=>{e.hasAttribute("wire:navigate")?Gr.default.bind(e,{"x-navigate":!0}):e.hasAttribute("wire:navigate.hover")&&Gr.default.bind(e,{"x-navigate.hover":!0})}));document.addEventListener("alpine:navigating",()=>{Livewire.all().forEach(e=>{e.inscribeSnapshotAndEffectsOnElement()})});Ft("confirm",({el:e,directive:r})=>{let n=r.expression,a=r.modifiers.includes("prompt");n=n.replaceAll("\\n",` + `;let r=Du();r&&(e.nonce=r),document.head.appendChild(e)}var ea=[],ss=["data-csrf","aria-hidden"];function Eo(e,r){let n=new DOMParser().parseFromString(e,"text/html"),a=document.adoptNode(n.body),s=document.adoptNode(n.head);ea=ea.concat(Array.from(document.body.querySelectorAll("script")).map(m=>fs(ds(m.outerHTML,ss))));let l=()=>{};Bc(s).finally(()=>{l()}),Fc(a,ea);let v=document.body;document.body.replaceWith(a),Alpine.destroyTree(v),r(m=>l=m)}function Fc(e,r){e.querySelectorAll("script").forEach(n=>{if(n.hasAttribute("data-navigate-once")){let a=fs(ds(n.outerHTML,ss));if(r.includes(a))return}n.replaceWith(ls(n))})}function Bc(e){let r=Array.from(document.head.children),n=r.map(l=>l.outerHTML),a=document.createDocumentFragment(),s=[];for(let l of Array.from(e.children))if(Co(l))if(n.includes(l.outerHTML))a.appendChild(l);else if(us(l)&&Hc(l,r)&&setTimeout(()=>window.location.reload()),cs(l))try{s.push(Uc(ls(l)))}catch{}else document.head.appendChild(l);for(let l of Array.from(document.head.children))Co(l)||l.remove();for(let l of Array.from(e.children))document.head.appendChild(l);return Promise.all(s)}async function Uc(e){return new Promise((r,n)=>{e.src?(e.onload=()=>r(),e.onerror=()=>n()):r(),document.head.appendChild(e)})}function ls(e){let r=document.createElement("script");r.textContent=e.textContent,r.async=e.async;for(let n of e.attributes)r.setAttribute(n.name,n.value);return r}function us(e){return e.hasAttribute("data-navigate-track")}function Hc(e,r){let[n,a]=Oo(e);return r.some(s=>{if(!us(s))return!1;let[l,v]=Oo(s);if(l===n&&a!==v)return!0})}function Oo(e){return(cs(e)?e.src:e.href).split("?")}function Co(e){return e.tagName.toLowerCase()==="link"&&e.getAttribute("rel").toLowerCase()==="stylesheet"||e.tagName.toLowerCase()==="style"||e.tagName.toLowerCase()==="script"}function cs(e){return e.tagName.toLowerCase()==="script"}function fs(e){return e.split("").reduce((r,n)=>(r=(r<<5)-r+n.charCodeAt(0),r&r),0)}function ds(e,r){let n=e;return r.forEach(a=>{const s=new RegExp(`${a}="[^"]*"|${a}='[^']*'`,"g");n=n.replace(s,"")}),n=n.replaceAll(" ",""),n.trim()}var ta=!0;function qc(e){e.navigate=n=>{let a=Yr(n);Yt("alpine:navigate",{url:a,history:!1,cached:!1})||r(a)},e.navigate.disableProgressBar=()=>{ta=!1},e.addInitSelector(()=>`[${e.prefixed("navigate")}]`),e.directive("navigate",(n,{modifiers:a})=>{a.includes("hover")&&Rc(n,60,()=>{let l=ho(n);go(l,(v,m)=>{mo(v,l,m)})}),Pc(n,l=>{let v=ho(n);go(v,(m,R)=>{mo(m,v,R)}),l(()=>{Yt("alpine:navigate",{url:v,history:!1,cached:!1})||r(v)})})});function r(n,a=!0){ta&&jc(),Vc(n,(s,l)=>{Yt("alpine:navigating"),_o(),ta&&Ic(),zc(),Ec(),Ao(e,v=>{xo(m=>{vo(m)}),a?Ac(s,l):is(l,s),Eo(s,m=>{yo(document.body),So((R,B)=>{bo(R)}),wo(),m(()=>{v(()=>{setTimeout(()=>{}),To(e),Yt("alpine:navigated")})})})})})}Cc(n=>{n(a=>{let s=Yr(a);if(Yt("alpine:navigate",{url:s,history:!0,cached:!1}))return;r(s,!1)})},(n,a,s,l)=>{let v=Yr(a);Yt("alpine:navigate",{url:v,history:!0,cached:!0})||(_o(),Yt("alpine:navigating"),Oc(s,l),Ao(e,R=>{xo(B=>{vo(B)}),Eo(n,()=>{Dc(),yo(document.body),So((B,ue)=>{bo(B)}),wo(),R(()=>{To(e),Yt("alpine:navigated")})})}))}),setTimeout(()=>{Yt("alpine:navigated")})}function Vc(e,r){Mc(e,r,()=>{kc(e,r)})}function Ao(e,r){e.stopObservingMutations(),r(n=>{e.startObservingMutations(),queueMicrotask(()=>{n()})})}function Yt(e,r){let n=new CustomEvent(e,{cancelable:!0,bubbles:!0,detail:r});return document.dispatchEvent(n),n.defaultPrevented}function To(e){e.initTree(document.body,void 0,(r,n)=>{r._x_wasPersisted&&n()})}function zc(){let e=function(r,n){Alpine.walk(r,(a,s)=>{Lc(a)&&s(),Nc(a)?s():n(a,s)})};Alpine.destroyTree(document.body,e)}function Wc(e){e.magic("queryString",(r,{interceptor:n})=>{let a,s=!1,l=!1;return n((v,m,R,B,ue)=>{let ne=a||B,{initial:z,replace:Y,push:S,pop:y}=ua(ne,v,s);return R(z),l?(e.effect(()=>S(m())),y(async _=>{R(_),await(()=>Promise.resolve())()})):e.effect(()=>Y(m())),z},v=>{v.alwaysShow=()=>(s=!0,v),v.usePush=()=>(l=!0,v),v.as=m=>(a=m,v)})}),e.history={track:ua}}function ua(e,r,n=!1){let{has:a,get:s,set:l,remove:v}=Jc(),m=new URL(window.location.href),R=a(m,e),B=R?s(m,e):r,ue=JSON.stringify(B),ne=S=>JSON.stringify(S)===ue;n&&(m=l(m,e,B)),Po(m,e,{value:B});let z=!1,Y=(S,y)=>{if(z)return;let _=new URL(window.location.href);!n&&!R&&ne(y)||y===void 0?_=v(_,e):_=l(_,e,y),S(_,e,{value:y})};return{initial:B,replace(S){Y(Po,S)},push(S){Y(Kc,S)},pop(S){let y=_=>{!_.state||!_.state.alpine||Object.entries(_.state.alpine).forEach(([O,{value:P}])=>{if(O!==e)return;z=!0;let I=S(P);I instanceof Promise?I.finally(()=>z=!1):z=!1})};return window.addEventListener("popstate",y),()=>window.removeEventListener("popstate",y)}}}function Po(e,r,n){let a=window.history.state||{};a.alpine||(a.alpine={}),a.alpine[r]=Sa(n),window.history.replaceState(a,"",e.toString())}function Kc(e,r,n){let a=window.history.state||{};a.alpine||(a.alpine={}),a={alpine:{...a.alpine,[r]:Sa(n)}},window.history.pushState(a,"",e.toString())}function Sa(e){if(e!==void 0)return JSON.parse(JSON.stringify(e))}function Jc(){return{has(e,r){let n=e.search;if(!n)return!1;let a=ei(n);return Object.keys(a).includes(r)},get(e,r){let n=e.search;return n?ei(n)[r]:!1},set(e,r,n){let a=ei(e.search);return a[r]=ps(Sa(n)),e.search=Ro(a),e},remove(e,r){let n=ei(e.search);return delete n[r],e.search=Ro(n),e}}}function ps(e){if(!va(e))return e;for(let r in e)e[r]===null?delete e[r]:e[r]=ps(e[r]);return e}function Ro(e){let r=s=>typeof s=="object"&&s!==null,n=(s,l={},v="")=>(Object.entries(s).forEach(([m,R])=>{let B=v===""?m:`${v}[${m}]`;R===null?l[B]="":r(R)?l={...l,...n(R,l,B)}:l[B]=encodeURIComponent(R).replaceAll("%20","+").replaceAll("%2C",",")}),l),a=n(e);return Object.entries(a).map(([s,l])=>`${s}=${l}`).join("&")}function ei(e){if(e=e.replace("?",""),e==="")return{};let r=(s,l,v)=>{let[m,R,...B]=s.split(".");if(!R)return v[s]=l;v[m]===void 0&&(v[m]=isNaN(R)?{}:[]),r([R,...B].join("."),l,v[m])},n=e.split("&").map(s=>s.split("=")),a=Object.create(null);return n.forEach(([s,l])=>{if(!(typeof l>"u"))if(l=decodeURIComponent(l.replaceAll("+","%20")),!s.includes("["))a[s]=l;else{let v=s.replaceAll("[",".").replaceAll("]","");r(v,l,a)}}),a}var Gc=tt(Lu()),Yc=tt(ju()),Et=tt(Ot());function Xc(){setTimeout(()=>Qc()),Gi(document,"livewire:init"),Gi(document,"livewire:initializing"),Et.default.plugin(Gc.default),Et.default.plugin(Wc),Et.default.plugin(xc.default),Et.default.plugin(bc.default),Et.default.plugin(Sc.default),Et.default.plugin(_c.default),Et.default.plugin(wc.default),Et.default.plugin(qc),Et.default.plugin(Yc.default),Et.default.addRootSelector(()=>"[wire\\:id]"),Et.default.onAttributesAdded((e,r)=>{if(!Array.from(r).some(a=>_n(a.name)))return;let n=or(e,!1);n&&r.forEach(a=>{if(!_n(a.name))return;let s=aa(e,a.name);lt("directive.init",{el:e,component:n,directive:s,cleanup:l=>{Et.default.onAttributeRemoved(e,s.raw,l)}})})}),Et.default.interceptInit(Et.default.skipDuringClone(e=>{if(!Array.from(e.attributes).some(n=>_n(n.name)))return;if(e.hasAttribute("wire:id")){let n=oc(e);Et.default.onAttributeRemoved(e,"wire:id",()=>{sc(n.id)})}let r=or(e,!1);r&&(lt("element.init",{el:e,component:r}),Array.from(e.getAttributeNames()).filter(a=>_n(a)).map(a=>aa(e,a)).forEach(a=>{lt("directive.init",{el:e,component:r,directive:a,cleanup:s=>{Et.default.onAttributeRemoved(e,a.raw,s)}})}))})),Et.default.start(),setTimeout(()=>window.Livewire.initialRenderIsFinished=!0),Gi(document,"livewire:initialized")}function Qc(){let e=document.querySelector("script[data-update-uri][data-csrf]");if(!e)return;let r=e.closest("[wire\\:id]");r&&console.warn("Livewire: missing closing tags found. Ensure your template elements contain matching closing tags.",r)}var Ea=tt(Ot());ze("effect",({component:e,effects:r})=>{Zc(e,r.listeners||[])});function Zc(e,r){r.forEach(n=>{let a=s=>{s.__livewire&&s.__livewire.receivedBy.push(e),e.$wire.call("__dispatch",n,s.detail||{})};window.addEventListener(n,a),e.addCleanup(()=>window.removeEventListener(n,a)),e.el.addEventListener(n,s=>{s.__livewire&&(s.bubbles||(s.__livewire&&s.__livewire.receivedBy.push(e.id),e.$wire.call("__dispatch",n,s.detail||{})))})})}var ko=tt(Ot()),Vr=new WeakMap,ri=new Set;ze("payload.intercept",async({assets:e})=>{if(e)for(let[r,n]of Object.entries(e))await rf(r,async()=>{await nf(n)})});ze("component.init",({component:e})=>{let r=e.snapshot.memo.assets;r&&r.forEach(n=>{ri.has(n)||ri.add(n)})});ze("effect",({component:e,effects:r})=>{let n=r.scripts;n&&Object.entries(n).forEach(([a,s])=>{ef(e,a,()=>{let l=tf(s);ko.default.dontAutoEvaluateFunctions(()=>{ko.default.evaluate(e.el,l,{$wire:e.$wire})})})})});function ef(e,r,n){if(Vr.has(e)&&Vr.get(e).includes(r))return;n(),Vr.has(e)||Vr.set(e,[]);let a=Vr.get(e);a.push(r),Vr.set(e,a)}function tf(e){let n=/]*>([\s\S]*?)<\/script>/gm.exec(e);return n&&n[1]?n[1].trim():""}async function rf(e,r){ri.has(e)||(await r(),ri.add(e))}async function nf(e){let r=new DOMParser().parseFromString(e,"text/html"),n=document.adoptNode(r.head);for(let a of n.children)try{await af(a)}catch{}}async function af(e){return new Promise((r,n)=>{if(of(e)){let a=sf(e);a.src?(a.onload=()=>r(),a.onerror=()=>n()):r(),document.head.appendChild(a)}else document.head.appendChild(e),r()})}function of(e){return e.tagName.toLowerCase()==="script"}function sf(e){let r=document.createElement("script");r.textContent=e.textContent,r.async=e.async;for(let n of e.attributes)r.setAttribute(n.name,n.value);return r}var Mo=tt(Ot());ze("effect",({component:e,effects:r})=>{let n=r.js,a=r.xjs;n&&Object.entries(n).forEach(([s,l])=>{ic(e,s,()=>{Mo.default.evaluate(e.el,l)})}),a&&a.forEach(s=>{Mo.default.evaluate(e.el,s)})});var lf=tt(Ot());function uf(e,r,n){let a=r.parentElement?r.parentElement.tagName.toLowerCase():"div",s=document.createElement(a);s.innerHTML=n;let l;try{l=or(r.parentElement)}catch{}l&&(s.__livewire=l);let v=s.firstElementChild;v.__livewire=e,lt("morph",{el:r,toEl:v,component:e}),lf.default.morph(r,v,{updating:(m,R,B,ue)=>{if(!zr(m)){if(lt("morph.updating",{el:m,toEl:R,component:e,skip:ue,childrenOnly:B}),m.__livewire_ignore===!0||(m.__livewire_ignore_self===!0&&B(),No(m)&&m.getAttribute("wire:id")!==e.id))return ue();No(m)&&(R.__livewire=e)}},updated:m=>{zr(m)||lt("morph.updated",{el:m,component:e})},removing:(m,R)=>{zr(m)||lt("morph.removing",{el:m,component:e,skip:R})},removed:m=>{zr(m)||lt("morph.removed",{el:m,component:e})},adding:m=>{lt("morph.adding",{el:m,component:e})},added:m=>{zr(m)||(or(m).id,lt("morph.added",{el:m}))},key:m=>{if(!zr(m))return m.hasAttribute("wire:key")?m.getAttribute("wire:key"):m.hasAttribute("wire:id")?m.getAttribute("wire:id"):m.id},lookahead:!1})}function zr(e){return typeof e.hasAttribute!="function"}function No(e){return e.hasAttribute("wire:id")}ze("effect",({component:e,effects:r})=>{let n=r.html;n&&queueMicrotask(()=>{queueMicrotask(()=>{uf(e,e.el,n)})})});ze("effect",({component:e,effects:r})=>{cf(e,r.dispatches||[])});function cf(e,r){r.forEach(({name:n,params:a={},self:s=!1,to:l})=>{s?Jr(e,n,a):l?_a(l,n,a):ns(e,n,a)})}var ff=tt(Ot()),ca=new Iu;ze("directive.init",({el:e,directive:r,cleanup:n,component:a})=>setTimeout(()=>{r.value==="submit"&&e.addEventListener("submit",()=>{let s=r.expression.startsWith("$parent")?a.parent.id:a.id,l=df(e);ca.add(s,l)})}));ze("commit",({component:e,respond:r})=>{r(()=>{ca.each(e.id,n=>n()),ca.remove(e.id)})});function df(e){let r=[];return ff.default.walk(e,(n,a)=>{if(e.contains(n)){if(n.hasAttribute("wire:ignore"))return a();pf(n)?r.push(gf(n)):hf(n)&&r.push(mf(n))}}),()=>{for(;r.length>0;)r.shift()()}}function pf(e){let r=e.tagName.toLowerCase();return r==="select"||r==="button"&&e.type==="submit"||r==="input"&&(e.type==="checkbox"||e.type==="radio")}function hf(e){return["input","textarea"].includes(e.tagName.toLowerCase())}function gf(e){let r=e.disabled?()=>{}:()=>e.disabled=!1;return e.disabled=!0,r}function mf(e){let r=e.readOnly?()=>{}:()=>e.readOnly=!1;return e.readOnly=!0,r}ze("commit.pooling",({commits:e})=>{e.forEach(r=>{let n=r.component;hs(n,a=>{a.$wire.$commit()})})});ze("commit.pooled",({pools:e})=>{vf(e).forEach(n=>{let a=n.component;hs(a,s=>{yf(e,a,s)})})});function vf(e){let r=[];return e.forEach(n=>{n.commits.forEach(a=>{r.push(a)})}),r}function yf(e,r,n){let a=Lo(e,r),s=Lo(e,n),l=s.findCommitByComponent(n);s.delete(l),a.add(l),e.forEach(v=>{v.empty()&&e.delete(v)})}function Lo(e,r){for(let[n,a]of e.entries())if(a.hasCommitFor(r))return a}function hs(e,r){gs(e,n=>{(bf(n)||_f(n))&&r(n)})}function bf(e){return!!e.snapshot.memo.props}function _f(e){return!!e.snapshot.memo.bindings}function gs(e,r){e.children.forEach(n=>{r(n),gs(n,r)})}ze("commit",({succeed:e})=>{e(({effects:r})=>{let n=r.download;if(!n)return;let a=window.webkitURL||window.URL,s=a.createObjectURL(wf(n.content,n.contentType)),l=document.createElement("a");l.style.display="none",l.href=s,l.download=n.name,document.body.appendChild(l),l.click(),setTimeout(function(){a.revokeObjectURL(s)},0)})});function wf(e,r="",n=512){const a=atob(e),s=[];r===null&&(r="");for(let l=0;l{let r=e.snapshot.memo;r.lazyLoaded!==void 0&&(da.add(e),r.lazyIsolated!==void 0&&r.lazyIsolated===!1&&fa.add(e))});ze("commit.pooling",({commits:e})=>{e.forEach(r=>{da.has(r.component)&&(fa.has(r.component)?(r.isolate=!1,fa.delete(r.component)):r.isolate=!0,da.delete(r.component))})});var jo=tt(Ot());ze("effect",({component:e,effects:r,cleanup:n})=>{let a=r.url;a&&Object.entries(a).forEach(([s,l])=>{let{name:v,as:m,use:R,alwaysShow:B,except:ue}=xf(s,l);m||(m=v);let ne=[!1,null,void 0].includes(ue)?Xt(e.ephemeral,v):ue,{replace:z,push:Y,pop:S}=ua(m,ne,B);if(R==="replace"){let y=jo.default.effect(()=>{z(Xt(e.reactive,v))});n(()=>jo.default.release(y))}else if(R==="push"){let y=ze("commit",({component:O,succeed:P})=>{if(e!==O)return;let I=Xt(e.canonical,v);P(()=>{let H=Xt(e.canonical,v);JSON.stringify(I)!==JSON.stringify(H)&&Y(H)})}),_=S(async O=>{await e.$wire.set(v,O),document.querySelectorAll("input").forEach(P=>{P._x_forceModelUpdate&&P._x_forceModelUpdate(P._x_model.get())})});n(()=>{y(),_()})}})});function xf(e,r){let n={use:"replace",alwaysShow:!1};return typeof r=="string"?{...n,name:r,as:r}:{...{...n,name:e,as:e},...r}}ze("request",({options:e})=>{window.Echo&&(e.headers["X-Socket-ID"]=window.Echo.socketId())});ze("effect",({component:e,effects:r})=>{(r.listeners||[]).forEach(a=>{if(a.startsWith("echo")){if(typeof window.Echo>"u"){console.warn("Laravel Echo cannot be found");return}let s=a.split(/(echo:|echo-)|:|,/);s[1]=="echo:"&&s.splice(2,0,"channel",void 0),s[2]=="notification"&&s.push(void 0,void 0);let[l,v,m,R,B,ue,ne]=s;if(["channel","private","encryptedPrivate"].includes(m)){let z=Y=>Jr(e,a,[Y]);window.Echo[m](B).listen(ne,z),e.addCleanup(()=>{window.Echo[m](B).stopListening(ne,z)})}else if(m=="presence")if(["here","joining","leaving"].includes(ne))window.Echo.join(B)[ne](z=>{Jr(e,a,[z])});else{let z=Y=>Jr(e,a,[Y]);window.Echo.join(B).listen(ne,z),e.addCleanup(()=>{window.Echo.leaveChannel(B)})}else m=="notification"?window.Echo.private(B).notification(z=>{Jr(e,a,[z])}):console.warn("Echo channel type not yet supported")}})});var ms=new WeakSet;ze("component.init",({component:e})=>{e.snapshot.memo.isolate===!0&&ms.add(e)});ze("commit.pooling",({commits:e})=>{e.forEach(r=>{ms.has(r.component)&&(r.isolate=!0)})});Ef()&&Alpine.navigate.disableProgressBar();document.addEventListener("alpine:navigate",e=>Oa("livewire:navigate",e));document.addEventListener("alpine:navigating",e=>Oa("livewire:navigating",e));document.addEventListener("alpine:navigated",e=>Oa("livewire:navigated",e));function Oa(e,r){let n=new CustomEvent(e,{cancelable:!0,bubbles:!0,detail:r.detail});document.dispatchEvent(n),n.defaultPrevented&&r.preventDefault()}function Sf(e,r,n){e.redirectUsingNavigate?Alpine.navigate(r):n()}function Ef(){return!!(document.querySelector("[data-no-progress-bar]")||window.livewireScriptConfig&&window.livewireScriptConfig.progressBar===!1)}ze("effect",({effects:e})=>{if(!e.redirect)return;let r=e.redirect;Sf(e,r,()=>{window.location.href=r})});var Io=tt(Ot());ze("morph.added",({el:e})=>{e.__addedByMorph=!0});Ft("transition",({el:e,directive:r,component:n,cleanup:a})=>{let s=Io.default.reactive({state:!e.__addedByMorph});Io.default.bind(e,{[r.rawName.replace("wire:","x-")]:"","x-show"(){return s.state}}),e.__addedByMorph&&setTimeout(()=>s.state=!0);let l=[];l.push(ze("morph.removing",({el:v,skip:m})=>{m(),v.addEventListener("transitionend",()=>{v.remove()}),s.state=!1,l.push(ze("morph",({component:R})=>{R===n&&(v.remove(),l.forEach(B=>B()))}))})),a(()=>l.forEach(v=>v()))});var Of=new Vo;function Cf(e,r){Of.each(e,n=>{n.callback(),n.callback=()=>{}}),r()}var Do=tt(Ot());ze("directive.init",({el:e,directive:r,cleanup:n,component:a})=>{if(["snapshot","effects","model","init","loading","poll","ignore","id","data","key","target","dirty"].includes(r.value)||mc(r.value))return;let s=r.rawName.replace("wire:","x-on:");r.value==="submit"&&!r.modifiers.includes("prevent")&&(s=s+".prevent");let l=Do.default.bind(e,{[s](v){let m=()=>{Cf(a,()=>{Do.default.evaluate(e,"$wire."+r.expression,{scope:{$event:v}})})};e.__livewire_confirm?e.__livewire_confirm(()=>{m()},()=>{v.stopImmediatePropagation()}):m()}});n(l)});var Gr=tt(Ot());Gr.default.addInitSelector(()=>"[wire\\:navigate]");Gr.default.addInitSelector(()=>"[wire\\:navigate\\.hover]");Gr.default.interceptInit(Gr.default.skipDuringClone(e=>{e.hasAttribute("wire:navigate")?Gr.default.bind(e,{"x-navigate":!0}):e.hasAttribute("wire:navigate.hover")&&Gr.default.bind(e,{"x-navigate.hover":!0})}));document.addEventListener("alpine:navigating",()=>{Livewire.all().forEach(e=>{e.inscribeSnapshotAndEffectsOnElement()})});Ft("confirm",({el:e,directive:r})=>{let n=r.expression,a=r.modifiers.includes("prompt");n=n.replaceAll("\\n",` `),n===""&&(n="Are you sure?"),e.__livewire_confirm=(s,l)=>{if(a){let[v,m]=n.split("|");m?prompt(v)===m?s():l():console.warn("Livewire: Must provide expectation with wire:confirm.prompt")}else confirm(n)?s():l()}});function Or(e,r,n,a=null){if(n=r.modifiers.includes("remove")?!n:n,r.modifiers.includes("class")){let s=r.expression.split(" ").filter(String);n?e.classList.add(...s):e.classList.remove(...s)}else if(r.modifiers.includes("attr"))n?e.setAttribute(r.expression,!0):e.removeAttribute(r.expression);else{let s=a??window.getComputedStyle(e,null).getPropertyValue("display"),l=["inline","block","table","flex","grid","inline-flex"].filter(v=>r.modifiers.includes(v))[0]||"inline-block";l=r.modifiers.includes("remove")&&!n?s:l,e.style.display=n?l:"none"}}var pa=new Set,ha=new Set;window.addEventListener("offline",()=>pa.forEach(e=>e()));window.addEventListener("online",()=>ha.forEach(e=>e()));Ft("offline",({el:e,directive:r,cleanup:n})=>{let a=()=>Or(e,r,!0),s=()=>Or(e,r,!1);pa.add(a),ha.add(s),n(()=>{pa.delete(a),ha.delete(s)})});Ft("loading",({el:e,directive:r,component:n,cleanup:a})=>{let{targets:s,inverted:l}=kf(e),[v,m]=Af(r),R=Tf(n,s,l,[()=>v(()=>Or(e,r,!0)),()=>m(()=>Or(e,r,!1))]),B=Pf(n,s,[()=>v(()=>Or(e,r,!0)),()=>m(()=>Or(e,r,!1))]);a(()=>{R(),B()})});function Af(e){if(!e.modifiers.includes("delay")||e.modifiers.includes("none"))return[l=>l(),l=>l()];let r=200,n={shortest:50,shorter:100,short:150,default:200,long:300,longer:500,longest:1e3};Object.keys(n).some(l=>{if(e.modifiers.includes(l))return r=n[l],!0});let a,s=!1;return[l=>{a=setTimeout(()=>{l(),s=!0},r)},async l=>{s?(await l(),s=!1):clearTimeout(a)}]}function Tf(e,r,n,[a,s]){return ze("commit",({component:l,commit:v,respond:m})=>{l===e&&(r.length>0&&Rf(v,r)===n||(a(),m(()=>{s()})))})}function Pf(e,r,[n,a]){let s=R=>{let{id:B,property:ue}=R.detail;return B!==e.id||r.length>0&&!r.map(ne=>ne.target).includes(ue)},l=Yi(window,"livewire-upload-start",R=>{s(R)||n()}),v=Yi(window,"livewire-upload-finish",R=>{s(R)||a()}),m=Yi(window,"livewire-upload-error",R=>{s(R)||a()});return()=>{l(),v(),m()}}function Rf(e,r){let{updates:n,calls:a}=e;return r.some(({target:s,params:l})=>{if(l)return a.some(({method:m,params:R})=>s===m&&l===vs(JSON.stringify(R)));if(Object.keys(n).some(m=>m.includes(".")&&m.split(".")[0]===s?!0:m===s)||a.map(m=>m.method).includes(s))return!0})}function kf(e){let r=wa(e),n=[],a=!1;if(r.has("target")){let s=r.get("target"),l=s.expression;s.modifiers.includes("except")&&(a=!0),l.includes("(")&&l.includes(")")?n.push({target:s.method,params:vs(JSON.stringify(s.params))}):l.includes(",")?l.split(",").map(v=>v.trim()).forEach(v=>{n.push({target:v})}):n.push({target:l})}else{let s=["init","dirty","offline","target","loading","poll","ignore","key","id"];r.all().filter(l=>!s.includes(l.value)).map(l=>l.expression.split("(")[0]).forEach(l=>n.push({target:l}))}return{targets:n,inverted:a}}function vs(e){return btoa(encodeURIComponent(e))}Ft("stream",({el:e,directive:r,cleanup:n})=>{let{expression:a,modifiers:s}=r,l=ze("stream",({name:v,content:m,replace:R})=>{v===a&&(s.includes("replace")||R?e.innerHTML=m:e.innerHTML=e.innerHTML+m)});n(l)});ze("request",({respond:e})=>{e(r=>{let n=r.response;n.headers.has("X-Livewire-Stream")&&(r.response={ok:!0,redirected:!1,status:200,async text(){let a=await Mf(n,s=>{lt("stream",s)});return Ko(a)&&(this.ok=!1),a}})})});async function Mf(e,r){let n=e.body.getReader(),a="";for(;;){let{done:s,value:l}=await n.read(),m=new TextDecoder().decode(l),[R,B]=Nf(a+m);if(R.forEach(ue=>{r(ue)}),a=B,s)return a}}function Nf(e){let r=/({"stream":true.*?"endStream":true})/g,n=e.match(r),a=[];if(n)for(let l=0;l{r.modifiers.includes("self")?e.__livewire_ignore_self=!0:e.__livewire_ignore=!0});var ys=new Vo;ze("commit",({component:e,respond:r})=>{r(()=>{setTimeout(()=>{ys.each(e,n=>n(!1))})})});Ft("dirty",({el:e,directive:r,component:n})=>{let a=Lf(e);Alpine.reactive({state:!1});let s=!1,l=e.style.display,v=m=>{Or(e,r,m,l),s=m};ys.add(n,v),Alpine.effect(()=>{let m=!1;if(a.length===0)m=JSON.stringify(n.canonical)!==JSON.stringify(n.reactive);else for(let R=0;Ra.trim()))),n}var jf=tt(Ot());Ft("model",({el:e,directive:r,component:n,cleanup:a})=>{let{expression:s,modifiers:l}=r;if(!s)return console.warn("Livewire: [wire:model] is missing a value.",e);if(bs(n,s))return console.warn('Livewire: [wire:model="'+s+'"] property does not exist on component: ['+n.name+"]",e);if(e.type&&e.type.toLowerCase()==="file")return Bu(e,s,n,a);let v=l.includes("live"),m=l.includes("lazy")||l.includes("change"),R=l.includes("blur"),B=l.includes("debounce"),ue=s.startsWith("$parent")?()=>n.$wire.$parent.$commit():()=>n.$wire.$commit(),ne=Df(e)&&!B&&v?$f(ue,150):ue;jf.default.bind(e,{"@change"(){m&&ue()},"@blur"(){R&&ue()},["x-model"+If(l)](){return{get(){return Xt(n.$wire,s)},set(z){hi(n.$wire,s,z),v&&!m&&!R&&ne()}}}})});function If(e){return e=e.filter(r=>!["lazy","defer"].includes(r)),e.length===0?"":"."+e.join(".")}function Df(e){return["INPUT","TEXTAREA"].includes(e.tagName.toUpperCase())&&!["checkbox","radio"].includes(e.type)}function bs(e,r){if(r.startsWith("$parent")){let a=or(e.el.parentElement,!1);return a?bs(a,r.split("$parent.")[1]):!0}let n=r.split(".")[0];return!Object.keys(e.canonical).includes(n)}function $f(e,r){var n;return function(){var a=this,s=arguments,l=function(){n=null,e.apply(a,s)};clearTimeout(n),n=setTimeout(l,r)}}var Ff=tt(Ot());Ft("init",({el:e,directive:r})=>{let n=r.expression??"$refresh";Ff.default.evaluate(e,`$wire.${n}`)});var Bf=tt(Ot());Ft("poll",({el:e,directive:r})=>{let n=Xf(r.modifiers,2e3),{start:a,pauseWhile:s,throttleWhile:l,stopWhen:v}=Hf(()=>{Uf(e,r)},n);a(),l(()=>zf()&&Kf(r)),s(()=>Jf(r)&&Gf(e)),s(()=>Wf(e)),s(()=>Vf()),v(()=>Yf(e))});function Uf(e,r){Bf.default.evaluate(e,r.expression?"$wire."+r.expression:"$wire.$commit()")}function Hf(e,r=2e3){let n=[],a=[],s=[];return{start(){let l=qf(r,()=>{if(s.some(v=>v()))return l();n.some(v=>v())||a.some(v=>v())&&Math.random()<.95||e()})},pauseWhile(l){n.push(l)},throttleWhile(l){a.push(l)},stopWhen(l){s.push(l)}}}var Sr=[];function qf(e,r){if(!Sr[e]){let n={timer:setInterval(()=>n.callbacks.forEach(a=>a()),e),callbacks:new Set};Sr[e]=n}return Sr[e].callbacks.add(r),()=>{Sr[e].callbacks.delete(r),Sr[e].callbacks.size===0&&(clearInterval(Sr[e].timer),delete Sr[e])}}var Ca=!1;window.addEventListener("offline",()=>Ca=!0);window.addEventListener("online",()=>Ca=!1);function Vf(){return Ca}var _s=!1;document.addEventListener("visibilitychange",()=>{_s=document.hidden},!1);function zf(){return _s}function Wf(e){return!wa(e).has("poll")}function Kf(e){return!e.modifiers.includes("keep-alive")}function Jf(e){return e.modifiers.includes("visible")}function Gf(e){let r=e.getBoundingClientRect();return!(r.top<(window.innerHeight||document.documentElement.clientHeight)&&r.left<(window.innerWidth||document.documentElement.clientWidth)&&r.bottom>0&&r.right>0)}function Yf(e){return e.isConnected===!1}function Xf(e,r){let n,a=e.find(l=>l.match(/([0-9]+)ms/)),s=e.find(l=>l.match(/([0-9]+)s/));return a?n=Number(a.replace("ms","")):s&&(n=Number(s.replace("s",""))*1e3),n||r}var Aa={directive:Ft,dispatchTo:_a,start:Xc,first:fc,find:cc,getByName:uc,all:dc,hook:ze,trigger:lt,triggerAsync:Go,dispatch:pc,on:gc,get navigate(){return Ea.default.navigate}},Ta=e=>console.warn(`Detected multiple instances of ${e} running`);window.Livewire&&Ta("Livewire");window.Alpine&&Ta("Alpine");window.Livewire=Aa;window.Alpine=Ea.default;window.livewireScriptConfig===void 0&&(window.Alpine.__fromLivewire=!0,document.addEventListener("DOMContentLoaded",()=>{window.Alpine.__fromLivewire===void 0&&Ta("Alpine"),Aa.start()}));Ea.default;/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress * @license MIT *//*! Bundled license information: diff --git a/public/build/manifest.json b/public/build/manifest.json index 135c7957a661..04835f81a223 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -9,7 +9,7 @@ ] }, "resources/js/app.js": { - "file": "assets/app-bfac6a32.js", + "file": "assets/app-a52d5f77.js", "imports": [ "_index-08e160a7.js", "__commonjsHelpers-725317a4.js" diff --git a/resources/js/clients/payments/square-credit-card.js b/resources/js/clients/payments/square-credit-card.js index ca8a153e9359..4242eae5d651 100644 --- a/resources/js/clients/payments/square-credit-card.js +++ b/resources/js/clients/payments/square-credit-card.js @@ -171,15 +171,10 @@ class SquareCreditCard { document.querySelector('input[name=token]').value = ''; }); - // let toggleWithToken = document.querySelector( - // '.toggle-payment-with-token' - // ); - - // if (!toggleWithToken) { document.getElementById('loader').classList.add('hidden'); document.getElementById('payment-list').classList.remove('hidden'); document.getElementById('toggle-payment-with-credit-card')?.click(); - // } + }); } diff --git a/resources/views/portal/ninja2020/gateways/paypal/ppcp/card.blade.php b/resources/views/portal/ninja2020/gateways/paypal/ppcp/card.blade.php index d28545083011..37cdf78dd3e5 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/ppcp/card.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/ppcp/card.blade.php @@ -1,5 +1,18 @@ @extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => '']) +@php + $gateway_instance = $gateway instanceof \App\Models\CompanyGateway ? $gateway : $gateway->company_gateway; + $token_billing_string = 'true'; + if($gateway_instance->token_billing == 'off' || $gateway_instance->token_billing == 'optin'){ + $token_billing_string = 'false'; + } + + if (isset($pre_payment) && $pre_payment == '1' && isset($is_recurring) && $is_recurring == '1') { + $token_billing_string = 'true'; + } + + +@endphp @section('gateway_head') @endsection @@ -12,7 +25,7 @@ - + @@ -44,6 +57,7 @@
+
@@ -62,8 +76,12 @@ @push('footer') - +@if(isset($merchantId)) + +@else + +@endif