diff --git a/app/Livewire/Flow2/ProcessPayment.php b/app/Livewire/Flow2/ProcessPayment.php index 0ad4f42f139f..8e012b713fb5 100644 --- a/app/Livewire/Flow2/ProcessPayment.php +++ b/app/Livewire/Flow2/ProcessPayment.php @@ -18,14 +18,13 @@ use Livewire\Component; use App\Libraries\MultiDB; use App\Models\CompanyGateway; use App\Models\InvoiceInvitation; -use App\Services\ClientPortal\InstantPayment; use App\Services\ClientPortal\LivewireInstantPayment; class ProcessPayment extends Component { use WithSecureContext; - private string $component_view = ''; + private ?string $payment_view; private array $payment_data_payload = []; @@ -55,8 +54,6 @@ class ProcessPayment extends Component $company_gateway = CompanyGateway::find($this->getContext()['company_gateway_id']); - $this->component_view = ''; - if(!$responder_data['success']) { throw new PaymentFailed($responder_data['error'], 400); } @@ -66,46 +63,8 @@ class ProcessPayment extends Component ->setPaymentMethod($data['payment_method_id']) ->setPaymentHash($responder_data['payload']['ph']); - $payment_data = $driver->processPaymentViewData($responder_data['payload']); - $payment_data['client_secret'] = $payment_data['intent']->client_secret; - - unset($payment_data['intent']); - - $token_billing_string = 'true'; - - if($company_gateway->token_billing == 'off' || $company_gateway->token_billing == 'optin') { - $token_billing_string = 'false'; - } - - if (isset($data['pre_payment']) && $data['pre_payment'] == '1' && isset($data['is_recurring']) && $data['is_recurring'] == '1') { - $token_billing_string = 'true'; - } - - $payment_data['token_billing_string'] = $token_billing_string; - - $this->payment_data_payload = $payment_data; - // $this->payment_data_payload['company_gateway'] = $company_gateway; - - $this->payment_data_payload = - [ - 'stripe_account_id' => $this->payment_data_payload['company_gateway']->getConfigField('account_id'), - 'publishable_key' => $this->payment_data_payload['company_gateway']->getPublishableKey(), - 'require_postal_code' => $this->payment_data_payload['company_gateway']->require_postal_code, - 'gateway' => $this->payment_data_payload['gateway'], - 'client' => $this->payment_data_payload['client'], - 'payment_method_id' => $this->payment_data_payload['payment_method_id'], - 'token_billing_string' => $this->payment_data_payload['token_billing_string'], - 'tokens' => $this->payment_data_payload['tokens'], - 'client_secret' => $this->payment_data_payload['client_secret'], - 'payment_hash' => $this->payment_data_payload['payment_hash'], - 'total' => $this->payment_data_payload['total'], - 'invoices' => $this->payment_data_payload['invoices'], - 'amount_with_fee' => $this->payment_data_payload['amount_with_fee'], - 'pre_payment' => $this->payment_data_payload['pre_payment'], - 'is_recurring' => $this->payment_data_payload['is_recurring'], - 'company_gateway' => $this->payment_data_payload['company_gateway'], - ]; - + $this->payment_view = $driver->livewirePaymentView(); + $this->payment_data_payload = $driver->processPaymentViewData($responder_data['payload']); $this->isLoading = false; @@ -119,6 +78,6 @@ class ProcessPayment extends Component HTML; } - return render('gateways.stripe.credit_card.livewire_pay', $this->payment_data_payload); + return render($this->payment_view, $this->payment_data_payload); } } diff --git a/app/PaymentDrivers/CheckoutCom/CreditCard.php b/app/PaymentDrivers/CheckoutCom/CreditCard.php index feee99b93ad8..fb75e0c3f35f 100644 --- a/app/PaymentDrivers/CheckoutCom/CreditCard.php +++ b/app/PaymentDrivers/CheckoutCom/CreditCard.php @@ -19,6 +19,7 @@ use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\Models\SystemLog; use App\PaymentDrivers\CheckoutComPaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\Utils\Traits\MakesHash; use Checkout\CheckoutApiException; @@ -32,7 +33,7 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\View\View; -class CreditCard implements MethodInterface +class CreditCard implements MethodInterface, LivewireMethodInterface { use Utilities; use MakesHash; @@ -140,7 +141,7 @@ class CreditCard implements MethodInterface } } - public function paymentView($data) + private function paymentData(array $data): array { $data['gateway'] = $this->checkout; $data['company_gateway'] = $this->checkout->company_gateway; @@ -150,9 +151,25 @@ class CreditCard implements MethodInterface $data['raw_value'] = $data['total']['amount_with_fee']; $data['customer_email'] = $this->checkout->client->present()->email(); + return $data; + } + + public function paymentView($data, $livewire = false) + { + $data = $this->paymentData($data); + + if ($livewire) { + return render('gateways.checkout.credit_card.pay_livewire', $data); + } + return render('gateways.checkout.credit_card.pay', $data); } + public function livewirePaymentView(): string + { + return 'gateways.checkout.credit_card.livewire_pay'; + } + public function paymentResponse(PaymentResponseRequest $request) { $state = [ diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index 03b161d076af..156d7a7cb0e8 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -12,6 +12,7 @@ namespace App\PaymentDrivers; +use App\PaymentDrivers\Common\LivewireMethodInterface; use Exception; use App\Models\Company; use App\Models\Invoice; @@ -45,7 +46,7 @@ use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use Checkout\Payments\Previous\PaymentRequest as PreviousPaymentRequest; use Checkout\Payments\Previous\Source\RequestIdSource as SourceRequestIdSource; -class CheckoutComPaymentDriver extends BaseDriver +class CheckoutComPaymentDriver extends BaseDriver implements LivewireMethodInterface { use SystemLogTrait; use Utilities; @@ -189,6 +190,17 @@ class CheckoutComPaymentDriver extends BaseDriver return $this->payment_method->paymentView($data); } + /** + * Process payment view for the Livewire payments. + * + * @param array $data + * @return array + */ + public function processPaymentViewData(array $data): array + { + return $this->payment_method->paymentData($data); + } + /** * Process the payment response * @@ -617,4 +629,9 @@ class CheckoutComPaymentDriver extends BaseDriver }); } + + public function livewirePaymentView(): string + { + return $this->payment_method->livewirePaymentView(); + } } diff --git a/app/PaymentDrivers/Common/LivewireMethodInterface.php b/app/PaymentDrivers/Common/LivewireMethodInterface.php new file mode 100644 index 000000000000..ef8fa395bf6d --- /dev/null +++ b/app/PaymentDrivers/Common/LivewireMethodInterface.php @@ -0,0 +1,22 @@ +route('client.payment_methods.index'); } + - public function paymentView(array $data): View + public function paymentData(array $data): array { $payment_intent_data = [ 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), @@ -93,6 +95,13 @@ class BrowserPay implements MethodInterface 'requestPayerEmail' => true, ]; + return $data; + } + + public function paymentView(array $data): View + { + $data = $this->paymentData($data); + return render('gateways.stripe.browser_pay.pay', $data); } @@ -231,4 +240,11 @@ class BrowserPay implements MethodInterface return str_replace(['https://', '/public'], '', $domain); } + /** + * @inheritDoc + */ + public function livewirePaymentView(): string + { + return 'gateways.stripe.browser_pay.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/CreditCard.php b/app/PaymentDrivers/Stripe/CreditCard.php index 4ba138f7ad69..b40ddc1051ab 100644 --- a/app/PaymentDrivers/Stripe/CreditCard.php +++ b/app/PaymentDrivers/Stripe/CreditCard.php @@ -19,12 +19,13 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer; use App\PaymentDrivers\StripePaymentDriver; use Stripe\PaymentIntent; use Stripe\PaymentMethod; -class CreditCard +class CreditCard implements LivewireMethodInterface { public $stripe; @@ -57,16 +58,8 @@ class CreditCard return redirect()->route('client.payment_methods.index'); } - public function paymentData(array $data) + public function paymentData(array $data): array { - $data = $this->getData($data); - - return $data; - } - - private function getData(array $data): array - { - $description = $this->stripe->getDescription(false); $payment_intent_data = [ @@ -90,11 +83,16 @@ class CreditCard public function paymentView(array $data) { - $data = $this->getData($data); + $data = $this->paymentData($data); return render('gateways.stripe.credit_card.pay', $data); } + public function livewirePaymentView(): string + { + return 'gateways.stripe.credit_card.pay_livewire'; + } + public function paymentResponse(PaymentResponseRequest $request) { $this->stripe->init(); diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index 17eb08c9acf9..42869afba180 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -23,6 +23,7 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentHash; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Stripe\ACH; use App\PaymentDrivers\Stripe\ACSS; use App\PaymentDrivers\Stripe\Alipay; @@ -61,7 +62,7 @@ use Stripe\SetupIntent; use Stripe\Stripe; use Stripe\StripeClient; -class StripePaymentDriver extends BaseDriver +class StripePaymentDriver extends BaseDriver implements LivewireMethodInterface { use MakesHash; use Utilities; @@ -420,7 +421,29 @@ class StripePaymentDriver extends BaseDriver public function processPaymentViewData(array $data): array { - return $this->payment_method->paymentData($data); + $data = $this->payment_method->paymentData($data); + + $data['stripe_account_id'] = $this->company_gateway->getConfigField('account_id'); + + if (array_key_exists('intent', $data)) { + $data['client_secret'] = $data['intent']->client_secret; + } + + unset($data['intent']); + + $token_billing_string = 'true'; + + if($this->company_gateway->token_billing == 'off' || $this->company_gateway->token_billing == 'optin') { + $token_billing_string = 'false'; + } + + if (isset($data['pre_payment']) && $data['pre_payment'] == '1' && isset($data['is_recurring']) && $data['is_recurring'] == '1') { + $token_billing_string = 'true'; + } + + $data['token_billing_string'] = $token_billing_string; + + return $data; } public function processPaymentResponse($request) @@ -1018,4 +1041,9 @@ class StripePaymentDriver extends BaseDriver return false; } + + public function livewirePaymentView(): string + { + return $this->payment_method->livewirePaymentView(); + } } diff --git a/public/build/assets/checkout-credit-card-8a04938c.js b/public/build/assets/checkout-credit-card-8a04938c.js deleted file mode 100644 index 0125ca29bec6..000000000000 --- a/public/build/assets/checkout-credit-card-8a04938c.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class o{constructor(){this.tokens=[]}mountFrames(){console.log("Mount checkout frames..")}handlePaymentUsingToken(t){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token}handlePaymentUsingCreditCard(t){document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="";const e=document.getElementById("pay-button"),d=document.querySelector('meta[name="public-key"]').content??"",a=document.getElementById("payment-form");Frames.init(d),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,function(n){e.disabled=!Frames.isCardValid()}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED,function(n){e.disabled=!1}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,function(n){e.disabled=!0,document.querySelector('input[name="gateway_response"]').value=JSON.stringify(n),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}),a.addEventListener("submit",function(n){n.preventDefault(),e.disabled=!0,Frames.submitCard()})}completePaymentUsingToken(t){let e=document.getElementById("pay-now-with-token");e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}handle(){this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",this.handlePaymentUsingToken)),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}new o().handle(); diff --git a/public/build/assets/checkout-credit-card-ba005c24.js b/public/build/assets/checkout-credit-card-ba005c24.js new file mode 100644 index 000000000000..0b28ba71e3e1 --- /dev/null +++ b/public/build/assets/checkout-credit-card-ba005c24.js @@ -0,0 +1,9 @@ +import{w as o}from"./wait-d71d9fed.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class s{constructor(){this.tokens=[]}handlePaymentUsingToken(t){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token}handlePaymentUsingCreditCard(t){document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="";const e=document.getElementById("pay-button"),d=document.querySelector('meta[name="public-key"]').content??"",a=document.getElementById("payment-form");Frames.init(d),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,function(n){e.disabled=!Frames.isCardValid()}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED,function(n){e.disabled=!1}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,function(n){e.disabled=!0,document.querySelector('input[name="gateway_response"]').value=JSON.stringify(n),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}),a.addEventListener("submit",function(n){n.preventDefault(),e.disabled=!0,Frames.submitCard()})}completePaymentUsingToken(t){let e=document.getElementById("pay-now-with-token");e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}handle(){this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",this.handlePaymentUsingToken)),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}o("#checkout-credit-card-payment").then(()=>new s().handle()); diff --git a/public/build/assets/stripe-alipay-00a4a19f.js b/public/build/assets/stripe-alipay-010d5388.js similarity index 64% rename from public/build/assets/stripe-alipay-00a4a19f.js rename to public/build/assets/stripe-alipay-010d5388.js index 1aa7a164b732..0c8522442897 100644 --- a/public/build/assets/stripe-alipay-00a4a19f.js +++ b/public/build/assets/stripe-alipay-010d5388.js @@ -1,9 +1,9 @@ -var i=Object.defineProperty;var c=(n,e,t)=>e in n?i(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(c(n,typeof e!="symbol"?e+"":e,t),t);/** +var i=Object.defineProperty;var o=(n,e,t)=>e in n?i(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var s=(n,e,t)=>(o(n,typeof e!="symbol"?e+"":e,t),t);/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license - */class a{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));this.key=e,this.stripeConnect=t,this.errors=document.getElementById("errors")}async handle(){document.getElementById("pay-now").addEventListener("click",async e=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");const{error:t}=await this.stripe.confirmAlipayPayment(document.querySelector("meta[name=ci_intent]").content,{return_url:`${document.querySelector("meta[name=return_url]").content}`});document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),t&&(this.errors.textContent="",this.errors.textContent=result.error.message,this.errors.hidden=!1)})}}var s;const d=((s=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:s.content)??"";var o;const l=((o=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:o.content)??"";new a(d,l).setupStripe().handle(); + * @license https://www.elastic.co/licensing/elastic-license + */class c{constructor(e,t){s(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));this.key=e,this.stripeConnect=t,this.errors=document.getElementById("errors")}async handle(){document.getElementById("pay-now").addEventListener("click",async e=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");const{error:t}=await this.stripe.confirmAlipayPayment(document.querySelector("meta[name=ci_intent]").content,{return_url:`${document.querySelector("meta[name=return_url]").content}`});document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),t&&(this.errors.textContent="",this.errors.textContent=result.error.message,this.errors.hidden=!1)})}}wait("#stripe-alipay-payment").then(()=>{var t,r;const n=((t=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:t.content)??"",e=((r=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:r.content)??"";new c(n,e).setupStripe().handle()}); diff --git a/public/build/assets/stripe-credit-card-c690d3d4.js b/public/build/assets/stripe-credit-card-ce33996a.js similarity index 83% rename from public/build/assets/stripe-credit-card-c690d3d4.js rename to public/build/assets/stripe-credit-card-ce33996a.js index 4174085eb4a0..2b636374f5fd 100644 --- a/public/build/assets/stripe-credit-card-c690d3d4.js +++ b/public/build/assets/stripe-credit-card-ce33996a.js @@ -1,4 +1,4 @@ -/** +import{w as l}from"./wait-d71d9fed.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class l{constructor(e,t,n,o){this.key=e,this.secret=t,this.onlyAuthorization=n,this.stripeConnect=o}setupStripe(){return this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this.elements=this.stripe.elements(),this}createElement(){var e;return this.cardElement=this.elements.create("card",{hidePostalCode:((e=document.querySelector("meta[name=stripe-require-postal-code]"))==null?void 0:e.content)==="0",value:{postalCode:document.querySelector("meta[name=client-postal-code]").content},hideIcon:!1}),this}mountCardElement(){return this.cardElement.mount("#card-element"),this}completePaymentUsingToken(){let e=document.querySelector("input[name=token]").value,t=document.getElementById("pay-now");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardPayment(this.secret,{payment_method:e}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}completePaymentWithoutToken(){let e=document.getElementById("pay-now");this.payNowButton=e,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden");let t=document.getElementById("cardholder-name");this.stripe.handleCardPayment(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:t.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,this.payNowButton.disabled=!1,this.payNowButton.querySelector("svg").classList.add("hidden"),this.payNowButton.querySelector("span").classList.remove("hidden")}handleAuthorization(){let e=document.getElementById("cardholder-name"),t=document.getElementById("authorize-card");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardSetup(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:e.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccessfulAuthorization(n))}handleSuccessfulAuthorization(e){document.getElementById("gateway_response").value=JSON.stringify(e.setupIntent),document.getElementById("server_response").submit()}handle(){this.setupStripe(),this.onlyAuthorization?(this.createElement().mountCardElement(),document.getElementById("authorize-card").addEventListener("click",()=>this.handleAuthorization())):(Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.getElementById("stripe--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",e=>{document.getElementById("stripe--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),this.createElement().mountCardElement(),document.getElementById("pay-now").addEventListener("click",()=>{try{return document.querySelector("input[name=token]").value?this.completePaymentUsingToken():this.completePaymentWithoutToken()}catch(e){console.log(e.message)}}))}}Livewire.hook("component.init",()=>{var a,i,s,d;console.log("running now");const r=((a=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:a.content)??"",e=((i=document.querySelector('meta[name="stripe-secret"]'))==null?void 0:i.content)??"",t=((s=document.querySelector('meta[name="only-authorization"]'))==null?void 0:s.content)??"",n=((d=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:d.content)??"";let o=new l(r,e,t,n);o.handle(),document.addEventListener("livewire:init",()=>{Livewire.on("passed-required-fields-check",()=>o.handle())})}); + */class c{constructor(e,t,n,r){this.key=e,this.secret=t,this.onlyAuthorization=n,this.stripeConnect=r}setupStripe(){return this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this.elements=this.stripe.elements(),this}createElement(){var e;return this.cardElement=this.elements.create("card",{hidePostalCode:((e=document.querySelector("meta[name=stripe-require-postal-code]"))==null?void 0:e.content)==="0",value:{postalCode:document.querySelector("meta[name=client-postal-code]").content},hideIcon:!1}),this}mountCardElement(){return this.cardElement.mount("#card-element"),this}completePaymentUsingToken(){let e=document.querySelector("input[name=token]").value,t=document.getElementById("pay-now");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardPayment(this.secret,{payment_method:e}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}completePaymentWithoutToken(){let e=document.getElementById("pay-now");this.payNowButton=e,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden");let t=document.getElementById("cardholder-name");this.stripe.handleCardPayment(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:t.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,this.payNowButton.disabled=!1,this.payNowButton.querySelector("svg").classList.add("hidden"),this.payNowButton.querySelector("span").classList.remove("hidden")}handleAuthorization(){let e=document.getElementById("cardholder-name"),t=document.getElementById("authorize-card");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardSetup(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:e.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccessfulAuthorization(n))}handleSuccessfulAuthorization(e){document.getElementById("gateway_response").value=JSON.stringify(e.setupIntent),document.getElementById("server_response").submit()}handle(){this.setupStripe(),this.onlyAuthorization?(this.createElement().mountCardElement(),document.getElementById("authorize-card").addEventListener("click",()=>this.handleAuthorization())):(Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.getElementById("stripe--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",e=>{document.getElementById("stripe--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),this.createElement().mountCardElement(),document.getElementById("pay-now").addEventListener("click",()=>{try{return document.querySelector("input[name=token]").value?this.completePaymentUsingToken():this.completePaymentWithoutToken()}catch(e){console.log(e.message)}}))}}l("#stripe-credit-card-payment").then(()=>{var a,s,i,d;const o=((a=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:a.content)??"",e=((s=document.querySelector('meta[name="stripe-secret"]'))==null?void 0:s.content)??"",t=((i=document.querySelector('meta[name="only-authorization"]'))==null?void 0:i.content)??"",n=((d=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:d.content)??"";new c(o,e,t,n).handle()}); diff --git a/public/build/assets/wait-d71d9fed.js b/public/build/assets/wait-d71d9fed.js new file mode 100644 index 000000000000..1bc4888178d9 --- /dev/null +++ b/public/build/assets/wait-d71d9fed.js @@ -0,0 +1,9 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */function i(...e){return new Promise(t=>{if(!e.length){t([]);return}const r=e.map(n=>document.querySelector(n)).filter(Boolean);if(r.length===e.length){t(r);return}const o=new MutationObserver(()=>{const n=e.map(u=>document.querySelector(u)).filter(Boolean);n.length===e.length&&(o.disconnect(),t(n))});o.observe(document.body,{childList:!0,subtree:!0})})}export{i as w}; diff --git a/public/build/manifest.json b/public/build/manifest.json index 1e53806fc209..efd8c8201849 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -8,6 +8,9 @@ "__commonjsHelpers-725317a4.js" ] }, + "_wait-d71d9fed.js": { + "file": "assets/wait-d71d9fed.js" + }, "resources/js/app.js": { "file": "assets/app-234e3402.js", "imports": [ @@ -71,7 +74,10 @@ "src": "resources/js/clients/payments/braintree-paypal.js" }, "resources/js/clients/payments/checkout-credit-card.js": { - "file": "assets/checkout-credit-card-8a04938c.js", + "file": "assets/checkout-credit-card-ba005c24.js", + "imports": [ + "_wait-d71d9fed.js" + ], "isEntry": true, "src": "resources/js/clients/payments/checkout-credit-card.js" }, @@ -121,7 +127,7 @@ "src": "resources/js/clients/payments/stripe-acss.js" }, "resources/js/clients/payments/stripe-alipay.js": { - "file": "assets/stripe-alipay-00a4a19f.js", + "file": "assets/stripe-alipay-010d5388.js", "isEntry": true, "src": "resources/js/clients/payments/stripe-alipay.js" }, @@ -146,7 +152,10 @@ "src": "resources/js/clients/payments/stripe-browserpay.js" }, "resources/js/clients/payments/stripe-credit-card.js": { - "file": "assets/stripe-credit-card-c690d3d4.js", + "file": "assets/stripe-credit-card-ce33996a.js", + "imports": [ + "_wait-d71d9fed.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-credit-card.js" }, diff --git a/resources/js/clients/payments/checkout-credit-card.js b/resources/js/clients/payments/checkout-credit-card.js index bb32fa7337ce..ea0a6df7d7bd 100644 --- a/resources/js/clients/payments/checkout-credit-card.js +++ b/resources/js/clients/payments/checkout-credit-card.js @@ -5,64 +5,70 @@ * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license + * @license https://www.elastic.co/licensing/elastic-license */ +import { wait } from '../wait'; + class CheckoutCreditCard { constructor() { this.tokens = []; } - mountFrames() { - console.log('Mount checkout frames..'); - } - handlePaymentUsingToken(e) { document.getElementById('checkout--container').classList.add('hidden'); - document.getElementById('pay-now-with-token--container').classList.remove('hidden'); + document + .getElementById('pay-now-with-token--container') + .classList.remove('hidden'); document.getElementById('save-card--container').style.display = 'none'; - document - .querySelector('input[name=token]') - .value = e.target.dataset.token; + document.querySelector('input[name=token]').value = + e.target.dataset.token; } handlePaymentUsingCreditCard(e) { - document.getElementById('checkout--container').classList.remove('hidden'); - document.getElementById('pay-now-with-token--container').classList.add('hidden'); + document + .getElementById('checkout--container') + .classList.remove('hidden'); + document + .getElementById('pay-now-with-token--container') + .classList.add('hidden'); document.getElementById('save-card--container').style.display = 'grid'; - document - .querySelector('input[name=token]') - .value = ''; + document.querySelector('input[name=token]').value = ''; const payButton = document.getElementById('pay-button'); - - const publicKey = document.querySelector('meta[name="public-key"]').content ?? ''; + + const publicKey = + document.querySelector('meta[name="public-key"]').content ?? ''; const form = document.getElementById('payment-form'); Frames.init(publicKey); - Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED, function (event) { - payButton.disabled = !Frames.isCardValid(); - }); + Frames.addEventHandler( + Frames.Events.CARD_VALIDATION_CHANGED, + function (event) { + payButton.disabled = !Frames.isCardValid(); + } + ); - Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED, function (event) { - payButton.disabled = false; - }); + Frames.addEventHandler( + Frames.Events.CARD_TOKENIZATION_FAILED, + function (event) { + payButton.disabled = false; + } + ); Frames.addEventHandler(Frames.Events.CARD_TOKENIZED, function (event) { payButton.disabled = true; - document.querySelector( - 'input[name="gateway_response"]' - ).value = JSON.stringify(event); + document.querySelector('input[name="gateway_response"]').value = + JSON.stringify(event); - document.querySelector( - 'input[name="store_card"]' - ).value = document.querySelector( - 'input[name=token-billing-checkbox]:checked' - ).value; + document.querySelector('input[name="store_card"]').value = + document.querySelector( + 'input[name=token-billing-checkbox]:checked' + ).value; document.getElementById('server-response').submit(); }); @@ -87,9 +93,11 @@ class CheckoutCreditCard { handle() { this.handlePaymentUsingCreditCard(); - Array - .from(document.getElementsByClassName('toggle-payment-with-token')) - .forEach((element) => element.addEventListener('click', this.handlePaymentUsingToken)); + Array.from( + document.getElementsByClassName('toggle-payment-with-token') + ).forEach((element) => + element.addEventListener('click', this.handlePaymentUsingToken) + ); document .getElementById('toggle-payment-with-credit-card') @@ -101,4 +109,6 @@ class CheckoutCreditCard { } } -new CheckoutCreditCard().handle(); +wait('#checkout-credit-card-payment').then(() => + new CheckoutCreditCard().handle() +); diff --git a/resources/js/clients/payments/stripe-alipay.js b/resources/js/clients/payments/stripe-alipay.js index 9b902a7dee35..a806b4c06da8 100644 --- a/resources/js/clients/payments/stripe-alipay.js +++ b/resources/js/clients/payments/stripe-alipay.js @@ -5,7 +5,7 @@ * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license + * @license https://www.elastic.co/licensing/elastic-license */ class ProcessAlipay { @@ -17,57 +17,66 @@ class ProcessAlipay { } setupStripe = () => { + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - - } - else { + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); } - return this; }; async handle() { + document + .getElementById('pay-now') + .addEventListener('click', async (e) => { + document.getElementById('pay-now').disabled = true; + document + .querySelector('#pay-now > svg') + .classList.add('hidden'); + document + .querySelector('#pay-now > span') + .classList.remove('hidden'); - document.getElementById('pay-now').addEventListener('click', async (e) => { - document.getElementById('pay-now').disabled = true; - document.querySelector('#pay-now > svg').classList.add('hidden'); - document.querySelector('#pay-now > span').classList.remove('hidden'); - - const { error } = await this.stripe.confirmAlipayPayment(document.querySelector('meta[name=ci_intent]').content, { - // Return URL where the customer should be redirected after the authorization - return_url: `${document.querySelector('meta[name=return_url]').content}`, - }); + const { error } = await this.stripe.confirmAlipayPayment( + document.querySelector('meta[name=ci_intent]').content, + { + // Return URL where the customer should be redirected after the authorization + return_url: `${ + document.querySelector('meta[name=return_url]') + .content + }`, + } + ); document.getElementById('pay-now').disabled = false; - document.querySelector('#pay-now > svg').classList.remove('hidden'); - document.querySelector('#pay-now > span').classList.add('hidden'); + document + .querySelector('#pay-now > svg') + .classList.remove('hidden'); + document + .querySelector('#pay-now > span') + .classList.add('hidden'); if (error) { - this.errors.textContent = ''; this.errors.textContent = result.error.message; this.errors.hidden = false; - } }); - } } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +wait('#stripe-alipay-payment').then(() => { + const publishableKey = + document.querySelector('meta[name="stripe-publishable-key"]') + ?.content ?? ''; -const stripeConnect = document.querySelector( - 'meta[name="stripe-account-id"]' -)?.content ?? ''; + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; -new ProcessAlipay(publishableKey, stripeConnect).setupStripe().handle(); + new ProcessAlipay(publishableKey, stripeConnect).setupStripe().handle(); +}); diff --git a/resources/js/clients/payments/stripe-applepay.js b/resources/js/clients/payments/stripe-applepay.js new file mode 100644 index 000000000000..944c7ed879d8 --- /dev/null +++ b/resources/js/clients/payments/stripe-applepay.js @@ -0,0 +1,142 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +import { wait } from '../wait'; + +/** + * @typedef {Object} ApplePayOptions + * @property {string} publishable_key + * @property {string|null} account_id + * @property {string} country + * @property {string} currency + * @property {string} total_label + * @property {string} total_amount + * @property {string} client_secret + */ + +wait('#stripe-applepay-payment', () => { + applePay({ + publishable_key: document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content, + account_id: + document.querySelector('meta[name="stripe-account-id"]')?.content ?? + null, + country: document.querySelector('meta[name="stripe-country"]')?.content, + currency: document.querySelector('meta[name="stripe-currency"]') + ?.content, + total_label: document.querySelector('meta[name="stripe-total-label"]') + ?.content, + total_amount: document.querySelector('meta[name="stripe-total-amount"]') + ?.content, + client_secret: document.querySelector( + 'meta[name="stripe-client-secret"]' + )?.content, + }); +}); + +/** + * @param {ApplePayOptions} options + */ +function applePay(options) { + let $options = { + apiVersion: '2018-05-21', + }; + + if (options.account_id) { + $options.stripeAccount = options.account_id; + } + + const stripe = Stripe(options.publishable_key, $options); + + const paymentRequest = stripe.paymentRequest({ + country: options.country, + currency: options.currency, + total: { + label: options.total_label, + amount: options.total_amount, + }, + requestPayerName: true, + requestPayerEmail: true, + }); + + const elements = stripe.elements(); + const prButton = elements.create('paymentRequestButton', { + paymentRequest: paymentRequest, + }); + + // Check the availability of the Payment Request API first. + paymentRequest.canMakePayment().then(function (result) { + if (result) { + prButton.mount('#payment-request-button'); + } else { + document.getElementById('payment-request-button').style.display = + 'none'; + } + }); + + paymentRequest.on('paymentmethod', function (ev) { + // Confirm the PaymentIntent without handling potential next actions (yet). + stripe + .confirmCardPayment( + options.client_secret, + { payment_method: ev.paymentMethod.id }, + { handleActions: false } + ) + .then(function (confirmResult) { + if (confirmResult.error) { + // Report to the browser that the payment failed, prompting it to + // re-show the payment interface, or show an error message and close + // the payment interface. + ev.complete('fail'); + } else { + // Report to the browser that the confirmation was successful, prompting + // it to close the browser payment method collection interface. + ev.complete('success'); + // Check if the PaymentIntent requires any actions and if so let Stripe.js + // handle the flow. If using an API version older than "2019-02-11" + // instead check for: `paymentIntent.status === "requires_source_action"`. + if ( + confirmResult.paymentIntent.status === 'requires_action' + ) { + // Let Stripe.js handle the rest of the payment flow. + stripe + .confirmCardPayment(clientSecret) + .then(function (result) { + if (result.error) { + // The payment failed -- ask your customer for a new payment method. + handleFailure(result.error); + } else { + // The payment has succeeded. + handleSuccess(result); + } + }); + } else { + // The payment has succeeded. + } + } + }); + }); + + function handleSuccess(result) { + document.querySelector('input[name="gateway_response"]').value = + JSON.stringify(result.paymentIntent); + + document.getElementById('server-response').submit(); + } + + function handleFailure(message) { + let errors = document.getElementById('errors'); + + errors.textContent = ''; + errors.textContent = message; + errors.hidden = false; + } +} diff --git a/resources/js/clients/payments/stripe-browserpay.js b/resources/js/clients/payments/stripe-browserpay.js index dff741ddc63a..afdec08afb42 100644 --- a/resources/js/clients/payments/stripe-browserpay.js +++ b/resources/js/clients/payments/stripe-browserpay.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait } from '../wait'; + class StripeBrowserPay { constructor() { this.clientSecret = document.querySelector( @@ -142,4 +144,4 @@ class StripeBrowserPay { } } -new StripeBrowserPay().handle(); +wait('#stripe-browserpay-payment').then(() => new StripeBrowserPay().handle()) diff --git a/resources/js/clients/payments/stripe-credit-card.js b/resources/js/clients/payments/stripe-credit-card.js index cf330538d896..659067200fbe 100644 --- a/resources/js/clients/payments/stripe-credit-card.js +++ b/resources/js/clients/payments/stripe-credit-card.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait } from '../wait'; + class StripeCreditCard { constructor(key, secret, onlyAuthorization, stripeConnect) { this.key = key; @@ -17,15 +19,11 @@ class StripeCreditCard { } setupStripe() { - if (this.stripeConnect) { - this.stripe = Stripe(this.key, { stripeAccount: this.stripeConnect, }); - - } - else { + } else { this.stripe = Stripe(this.key); } @@ -36,9 +34,13 @@ class StripeCreditCard { createElement() { this.cardElement = this.elements.create('card', { - hidePostalCode: document.querySelector('meta[name=stripe-require-postal-code]')?.content === "0", + hidePostalCode: + document.querySelector('meta[name=stripe-require-postal-code]') + ?.content === '0', value: { - postalCode: document.querySelector('meta[name=client-postal-code]').content, + postalCode: document.querySelector( + 'meta[name=client-postal-code]' + ).content, }, hideIcon: false, }); @@ -103,9 +105,8 @@ class StripeCreditCard { } handleSuccess(result) { - document.querySelector( - 'input[name="gateway_response"]' - ).value = JSON.stringify(result.paymentIntent); + document.querySelector('input[name="gateway_response"]').value = + JSON.stringify(result.paymentIntent); let tokenBillingCheckbox = document.querySelector( 'input[name="token-billing-checkbox"]:checked' @@ -177,69 +178,74 @@ class StripeCreditCard { return this.handleAuthorization(); }); } else { - Array - .from(document.getElementsByClassName('toggle-payment-with-token')) - .forEach((element) => element.addEventListener('click', (element) => { - document.getElementById('stripe--payment-container').classList.add('hidden'); - document.getElementById('save-card--container').style.display = 'none'; - document.querySelector('input[name=token]').value = element.target.dataset.token; - })); + Array.from( + document.getElementsByClassName('toggle-payment-with-token') + ).forEach((element) => + element.addEventListener('click', (element) => { + document + .getElementById('stripe--payment-container') + .classList.add('hidden'); + document.getElementById( + 'save-card--container' + ).style.display = 'none'; + document.querySelector('input[name=token]').value = + element.target.dataset.token; + }) + ); document .getElementById('toggle-payment-with-credit-card') .addEventListener('click', (element) => { - document.getElementById('stripe--payment-container').classList.remove('hidden'); - document.getElementById('save-card--container').style.display = 'grid'; - document.querySelector('input[name=token]').value = ""; + document + .getElementById('stripe--payment-container') + .classList.remove('hidden'); + document.getElementById( + 'save-card--container' + ).style.display = 'grid'; + document.querySelector('input[name=token]').value = ''; }); this.createElement().mountCardElement(); - document - .getElementById('pay-now') - .addEventListener('click', () => { + document.getElementById('pay-now').addEventListener('click', () => { + try { + let tokenInput = + document.querySelector('input[name=token]'); - try { - let tokenInput = document.querySelector('input[name=token]'); - - if (tokenInput.value) { - return this.completePaymentUsingToken(); - } - - return this.completePaymentWithoutToken(); - } catch (error) { - console.log(error.message); + if (tokenInput.value) { + return this.completePaymentUsingToken(); } - }); + return this.completePaymentWithoutToken(); + } catch (error) { + console.log(error.message); + } + }); } } } - -Livewire.hook('component.init', () => { - - console.log("running now"); - +wait('#stripe-credit-card-payment').then(() => { const publishableKey = - document.querySelector('meta[name="stripe-publishable-key"]')?.content ?? ''; + document.querySelector('meta[name="stripe-publishable-key"]') + ?.content ?? ''; const secret = document.querySelector('meta[name="stripe-secret"]')?.content ?? ''; const onlyAuthorization = - document.querySelector('meta[name="only-authorization"]')?.content ?? ''; + document.querySelector('meta[name="only-authorization"]')?.content ?? + ''; const stripeConnect = document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; - let s = new StripeCreditCard(publishableKey, secret, onlyAuthorization, stripeConnect); + let s = new StripeCreditCard( + publishableKey, + secret, + onlyAuthorization, + stripeConnect + ); s.handle(); - - document.addEventListener('livewire:init', () => { - - Livewire.on('passed-required-fields-check', () => s.handle()); - - }); -}) \ No newline at end of file +}); diff --git a/resources/js/clients/wait.js b/resources/js/clients/wait.js new file mode 100644 index 000000000000..ac83b278bbd8 --- /dev/null +++ b/resources/js/clients/wait.js @@ -0,0 +1,43 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +/** + * @param {...string} selectors + */ +export function wait(...selectors) { + return new Promise((resolve) => { + if (!selectors.length) { + resolve([]); + return; + } + + const elements = selectors + .map((selector) => document.querySelector(selector)) + .filter(Boolean); + + if (elements.length === selectors.length) { + resolve(elements); + return; + } + + const observer = new MutationObserver(() => { + const foundElements = selectors + .map((selector) => document.querySelector(selector)) + .filter(Boolean); + + if (foundElements.length === selectors.length) { + observer.disconnect(); + resolve(foundElements); + } + }); + + observer.observe(document.body, { childList: true, subtree: true }); + }); +} diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay_livewire.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay_livewire.blade.php new file mode 100644 index 000000000000..1276e501ac99 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay_livewire.blade.php @@ -0,0 +1,78 @@ +