Refactor context passing between methods and Livewire (#65)

* checkout.com credit card payment for livewire

* implement interface for livewire view

* livewire method interface

* implement interfaces

* assets production build

* checkout.com: credit card

* stripe: credit card

* lift up logic from process payment component

* update stripe payment view logic

* wait fn for mounting existing JS

* credit card: simplify data passing

* stripe: browser pay

* stripe cc: remove getData

* stripe: cc

* stripe: alipay

* checkout :cc

* stripe: apple pay

* stripe: browser pay

* assets production build
This commit is contained in:
Benjamin Beganović 2024-07-10 01:55:36 +02:00 committed by GitHub
parent 18cd647e43
commit 1f7904e317
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 728 additions and 493 deletions

View File

@ -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);
}
}

View File

@ -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 = [

View File

@ -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();
}
}

View File

@ -0,0 +1,22 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\Common;
interface LivewireMethodInterface
{
/**
* Payment page for the gateway method.
*
* @param array $data
*/
public function livewirePaymentView(): string;
}

View File

@ -19,6 +19,7 @@ use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\Common\LivewireMethodInterface;
use App\PaymentDrivers\Common\MethodInterface;
use App\PaymentDrivers\StripePaymentDriver;
use App\Utils\Ninja;
@ -29,7 +30,7 @@ use Stripe\ApplePayDomain;
use Stripe\Exception\ApiErrorException;
use Stripe\PaymentIntent;
class BrowserPay implements MethodInterface
class BrowserPay implements MethodInterface, LivewireMethodInterface
{
protected StripePaymentDriver $stripe;
@ -63,8 +64,9 @@ class BrowserPay implements MethodInterface
{
return redirect()->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';
}
}

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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());

View File

@ -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()});

View File

@ -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()});

9
public/build/assets/wait-d71d9fed.js vendored Normal file
View File

@ -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};

View File

@ -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"
},

View File

@ -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()
);

View File

@ -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();
});

View File

@ -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;
}
}

View File

@ -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())

View File

@ -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());
});
})
});

43
resources/js/clients/wait.js vendored Normal file
View File

@ -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 });
});
}

View File

@ -0,0 +1,78 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4" id="checkout-credit-card-payment">
<meta name="public-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="customer-email" content="{{ $customer_email }}">
<meta name="value" content="{{ $value }}">
<meta name="currency" content="{{ $currency }}">
<meta name="reference" content="{{ $payment_hash }}">
@include('portal.ninja2020.gateways.checkout.credit_card.includes.styles')
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="store_card">
<input type="hidden" name="reference" value="{{ $payment_hash }}">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="value" value="{{ $value }}">
<input type="hidden" name="raw_value" value="{{ $raw_value }}">
<input type="hidden" name="currency" value="{{ $currency }}">
<input type="hidden" name="pay_with_token" value="false">
<input type="hidden" name="token" value="">
</form>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.credit_card') }} (Checkout.com)
@endcomponent
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
@if(count($tokens) > 0)
@foreach($tokens as $token)
<label class="mr-4">
<input type="radio" data-token="{{ $token->hashed_id }}" name="payment-type"
class="form-radio cursor-pointer toggle-payment-with-token" />
<span class="ml-1 cursor-pointer">**** {{ $token->meta?->last4 }}</span>
</label>
@endforeach
@endisset
<label>
<input type="radio" id="toggle-payment-with-credit-card" class="form-radio cursor-pointer" name="payment-type"
checked />
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
</label>
@endcomponent
@include('portal.ninja2020.gateways.includes.save_card')
@component('portal.ninja2020.components.general.card-element-single')
<div id="checkout--container">
<form class="xl:flex xl:justify-center" id="payment-form" method="POST" action="#">
<div class="one-liner">
<div class="card-frame">
<!-- form will be added here -->
</div>
<!-- add submit button -->
<button id="pay-button" disabled>
{{ ctrans('texts.pay') }} {{ App\Utils\Number::formatMoney($total['amount_with_fee'], $client) }}
</button>
</div>
<p class="success-payment-message"></p>
</form>
</div>
@endcomponent
@component('portal.ninja2020.components.general.card-element-single')
<div class="hidden" id="pay-now-with-token--container">
@include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token'])
</div>
@endcomponent
@assets
<script src="https://cdn.checkout.com/js/framesv2.min.js"></script>
@vite('resources/js/clients/payments/checkout-credit-card.js')
@endassets
</div>

View File

@ -0,0 +1,26 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4"
id="stripe-alipay-payment">
@if($gateway->company_gateway->getConfigField('account_id'))
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="stripe-publishable-key" content="{{ config('ninja.ninja_stripe_publishable_key') }}">
@else
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
@endif
<meta name="return_url" content="{{ $return_url }}">
<meta name="ci_intent" content="{{ $ci_intent }}">
@section('gateway_content')
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.alipay') }}
@endcomponent
</div>
@assets
<script src="https://js.stripe.com/v3/"></script>
@vite('resources/js/clients/payments/stripe-alipay.js')
@endassets

View File

@ -0,0 +1,31 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4"
id="stripe-applepay-payment">
<meta name="stripe-publishable-key" content="{{ config('ninja.ninja_stripe_publishable_key') }}" />
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}" />
<meta name="stripe-country" content="{{ $country->iso_3166_2 }}" />
<meta name="stripe-currency" content="{{ $currency }}" />
<meta name="stripe-total-label" content="{{ ctrans('texts.payment_amount') }}" />
<meta name="stripe-total-amount" content="{{ $stripe_amount }}" />
<meta name="stripe-client-secret" content="{{ $intent->client_secret }}" />
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@include('portal.ninja2020.gateways.includes.payment_details')
<div id="payment-request-button">
<!-- A Stripe Element will be inserted here. -->
</div>
</div>
@assets
<script src="https://js.stripe.com/v3/"></script>
@vite('resources/js/clients/payments/stripe-applepay.js')
@endassets

View File

@ -0,0 +1,36 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4"
id="stripe-browserpay-payment">
@if($gateway->company_gateway->getConfigField('account_id'))
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="stripe-publishable-key" content="{{ config('ninja.ninja_stripe_publishable_key') }}">
@else
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
@endif
<meta name="stripe-pi-client-secret" content="{{ $pi_client_secret }}">
<meta name="no-available-methods" content="{{ json_encode(ctrans('texts.no_available_methods')) }}">
<meta name="payment-request-data" content="{{ json_encode($payment_request_data) }}">
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="store_card">
<input type="hidden" name="token">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element-single')
<div id="payment-request-button"></div>
@endcomponent
</div>
@assets
<script src="https://js.stripe.com/v3/"></script>
@vite('resources/js/clients/payments/stripe-browserpay.js')
@endassets

View File

@ -1,297 +0,0 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4">
@if($stripe_account_id)
<meta name="stripe-account-id" content="{{ $stripe_account_id }}">
<meta name="stripe-publishable-key" content="{{ config('ninja.ninja_stripe_publishable_key') }}">
@else
<meta name="stripe-publishable-key" content="{{ $company_gateway->getPublishableKey() }}">
@endif
<meta name="stripe-secret" content="{{ $client_secret }}">
<meta name="only-authorization" content="">
<meta name="client-postal-code" content="{{ $client->postal_code ?? '' }}">
<meta name="stripe-require-postal-code" content="{{ $company_gateway->require_postal_code }}">
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="store_card" value="{{ $token_billing_string }}">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="token">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.credit_card') }}
@endcomponent
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
<ul class="list-none">
@if(count($tokens) > 0)
@foreach($tokens as $token)
<li class="py-2 hover:text-white hover:bg-blue-600">
<label class="mr-4">
<input
type="radio"
data-token="{{ $token->token }}"
name="payment-type"
class="form-check-input text-indigo-600 rounded-full cursor-pointer toggle-payment-with-token toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">**** {{ $token->meta?->last4 }}</span>
</label>
</li>
@endforeach
@endisset
<li class="py-2 hover:text-white hover:bg-blue-600">
<label>
<input
type="radio"
id="toggle-payment-with-credit-card"
class="form-check-input text-indigo-600 rounded-full cursor-pointer"
name="payment-type"
checked/>
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
</label>
</li>
</ul>
@endcomponent
@include('portal.ninja2020.gateways.stripe.includes.card_widget')
@include('portal.ninja2020.gateways.includes.pay_now')
@assets
<script src="https://js.stripe.com/v3/"></script>
@endassets
@script
<script>
const publishableKey =
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 ?? '';
const stripeConnect =
document.querySelector('meta[name="stripe-account-id"]')?.content ?? '';
var cardElement;
var stripe;
var elements;
function setupStripe() {
if (stripeConnect) {
stripe = Stripe(publishableKey, {
stripeAccount: stripeConnect,
});
}
else {
stripe = Stripe(publishableKey);
}
elements = stripe.elements();
}
function createElement() {
cardElement = elements.create('card', {
hidePostalCode: document.querySelector('meta[name=stripe-require-postal-code]')?.content === "0",
value: {
postalCode: document.querySelector('meta[name=client-postal-code]').content,
},
hideIcon: false,
});
}
function mountCardElement() {
cardElement.mount('#card-element');
}
function completePaymentUsingToken() {
let token = document.querySelector('input[name=token]').value;
let payNowButton = document.getElementById('pay-now');
payNowButton = payNowButton;
payNowButton.disabled = true;
payNowButton.querySelector('svg').classList.remove('hidden');
payNowButton.querySelector('span').classList.add('hidden');
stripe
.handleCardPayment(secret, {
payment_method: token,
})
.then((result) => {
if (result.error) {
return handleFailure(result.error.message);
}
return handleSuccess(result);
});
}
function completePaymentWithoutToken() {
let payNowButton = document.getElementById('pay-now');
payNowButton = payNowButton;
payNowButton.disabled = true;
payNowButton.querySelector('svg').classList.remove('hidden');
payNowButton.querySelector('span').classList.add('hidden');
let cardHolderName = document.getElementById('cardholder-name');
stripe
.handleCardPayment(secret, cardElement, {
payment_method_data: {
billing_details: { name: cardHolderName.value },
},
})
.then((result) => {
if (result.error) {
return handleFailure(result.error.message);
}
return handleSuccess(result);
});
}
function handleSuccess(result) {
document.querySelector(
'input[name="gateway_response"]'
).value = JSON.stringify(result.paymentIntent);
let tokenBillingCheckbox = document.querySelector(
'input[name="token-billing-checkbox"]:checked'
);
if (tokenBillingCheckbox) {
document.querySelector('input[name="store_card"]').value =
tokenBillingCheckbox.value;
}
document.getElementById('server-response').submit();
}
function handleFailure(message) {
let payNowButton = document.getElementById('pay-now');
let errors = document.getElementById('errors');
errors.textContent = '';
errors.textContent = message;
errors.hidden = false;
payNowButton.disabled = false;
payNowButton.querySelector('svg').classList.add('hidden');
payNowButton.querySelector('span').classList.remove('hidden');
}
function handleAuthorization() {
let cardHolderName = document.getElementById('cardholder-name');
let payNowButton = document.getElementById('authorize-card');
payNowButton = payNowButton;
payNowButton.disabled = true;
payNowButton.querySelector('svg').classList.remove('hidden');
payNowButton.querySelector('span').classList.add('hidden');
stripe
.handleCardSetup(secret, cardElement, {
payment_method_data: {
billing_details: { name: cardHolderName.value },
},
})
.then((result) => {
if (result.error) {
return handleFailure(result.error.message);
}
return handleSuccessfulAuthorization(result);
});
}
function handleSuccessfulAuthorization(result) {
document.getElementById('gateway_response').value = JSON.stringify(
result.setupIntent
);
document.getElementById('server_response').submit();
}
setupStripe();
if (onlyAuthorization) {
createElement();
mountCardElement();
document
.getElementById('authorize-card')
.addEventListener('click', () => {
return 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;
}));
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 = "";
});
createElement();
mountCardElement();
document
.getElementById('pay-now')
.addEventListener('click', () => {
try {
let tokenInput = document.querySelector('input[name=token]');
if (tokenInput.value) {
return completePaymentUsingToken();
}
return completePaymentWithoutToken();
} catch (error) {
console.log(error.message);
}
});
}
</script>
@endscript
</div>

View File

@ -0,0 +1,74 @@
<div class="rounded-lg border bg-card text-card-foreground shadow-sm overflow-hidden py-5 bg-white sm:gap-4" id="stripe-credit-card-payment">
@if($stripe_account_id)
<meta name="stripe-account-id" content="{{ $stripe_account_id }}">
<meta name="stripe-publishable-key" content="{{ config('ninja.ninja_stripe_publishable_key') }}">
@else
<meta name="stripe-publishable-key" content="{{ $company_gateway->getPublishableKey() }}">
@endif
<meta name="stripe-secret" content="{{ $client_secret }}">
<meta name="only-authorization" content="">
<meta name="client-postal-code" content="{{ $client->postal_code ?? '' }}">
<meta name="stripe-require-postal-code" content="{{ $company_gateway->require_postal_code }}">
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
@csrf
<input type="hidden" name="gateway_response">
<input type="hidden" name="store_card" value="{{ $token_billing_string }}">
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
<input type="hidden" name="token">
</form>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.credit_card') }}
@endcomponent
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
<ul class="list-none">
@if(count($tokens) > 0)
@foreach($tokens as $token)
<li class="py-2 hover:text-white hover:bg-blue-600">
<label class="mr-4">
<input
type="radio"
data-token="{{ $token->token }}"
name="payment-type"
class="form-check-input text-indigo-600 rounded-full cursor-pointer toggle-payment-with-token toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">**** {{ $token->meta?->last4 }}</span>
</label>
</li>
@endforeach
@endisset
<li class="py-2 hover:text-white hover:bg-blue-600">
<label>
<input
type="radio"
id="toggle-payment-with-credit-card"
class="form-check-input text-indigo-600 rounded-full cursor-pointer"
name="payment-type"
checked/>
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
</label>
</li>
</ul>
@endcomponent
@include('portal.ninja2020.gateways.stripe.includes.card_widget')
@include('portal.ninja2020.gateways.includes.pay_now')
@assets
<script src="https://js.stripe.com/v3/"></script>
@vite('resources/js/clients/payments/stripe-credit-card.js')
@endassets
</div>