mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 23:57:33 -05:00 
			
		
		
		
	Merge pull request #4736 from beganovich/v5-2001-gateways-and-new-tokens
(v5) Support for existing tokens and new payment methods
This commit is contained in:
		
						commit
						67e2d692ed
					
				@ -78,7 +78,7 @@ class PaymentController extends Controller
 | 
				
			|||||||
    public function process(Request $request)
 | 
					    public function process(Request $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $is_credit_payment = false;
 | 
					        $is_credit_payment = false;
 | 
				
			||||||
        $token = false;
 | 
					        $tokens = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($request->input('company_gateway_id') == CompanyGateway::GATEWAY_CREDIT) {
 | 
					        if ($request->input('company_gateway_id') == CompanyGateway::GATEWAY_CREDIT) {
 | 
				
			||||||
            $is_credit_payment = true;
 | 
					            $is_credit_payment = true;
 | 
				
			||||||
@ -115,8 +115,7 @@ class PaymentController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        /* This loop checks for under / over payments and returns the user if a check fails */
 | 
					        /* This loop checks for under / over payments and returns the user if a check fails */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach($payable_invoices as $payable_invoice)
 | 
					        foreach ($payable_invoices as $payable_invoice) {
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /*Match the payable invoice to the Model Invoice*/
 | 
					            /*Match the payable invoice to the Model Invoice*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -173,8 +172,7 @@ class PaymentController extends Controller
 | 
				
			|||||||
        //$payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) {
 | 
					        //$payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) {
 | 
				
			||||||
        $payable_invoice_collection = collect();
 | 
					        $payable_invoice_collection = collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach($payable_invoices as $payable_invoice)
 | 
					        foreach ($payable_invoices as $payable_invoice) {
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // nlog($payable_invoice);
 | 
					            // nlog($payable_invoice);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']);
 | 
					            $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']);
 | 
				
			||||||
@ -229,12 +227,15 @@ class PaymentController extends Controller
 | 
				
			|||||||
        $fee_totals = $first_invoice->amount - $starting_invoice_amount;
 | 
					        $fee_totals = $first_invoice->amount - $starting_invoice_amount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($gateway) {
 | 
					        if ($gateway) {
 | 
				
			||||||
            $token = auth()->user()->client->gateway_token($gateway->id, $payment_method_id);
 | 
					            $tokens = auth()->user()->client->gateway_tokens()
 | 
				
			||||||
 | 
					                ->whereCompanyGatewayId($gateway->id)
 | 
				
			||||||
 | 
					                ->whereGatewayTypeId($payment_method_id)
 | 
				
			||||||
 | 
					                ->get();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $payment_hash = new PaymentHash;
 | 
					        $payment_hash = new PaymentHash;
 | 
				
			||||||
        $payment_hash->hash = Str::random(128);
 | 
					        $payment_hash->hash = Str::random(128);
 | 
				
			||||||
        $payment_hash->data = ['invoices' => $payable_invoices->toArray() , 'credits' => $credit_totals];
 | 
					        $payment_hash->data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals];
 | 
				
			||||||
        $payment_hash->fee_total = $fee_totals;
 | 
					        $payment_hash->fee_total = $fee_totals;
 | 
				
			||||||
        $payment_hash->fee_invoice_id = $first_invoice->id;
 | 
					        $payment_hash->fee_invoice_id = $first_invoice->id;
 | 
				
			||||||
        $payment_hash->save();
 | 
					        $payment_hash->save();
 | 
				
			||||||
@ -250,7 +251,7 @@ class PaymentController extends Controller
 | 
				
			|||||||
            'payment_hash' => $payment_hash->hash,
 | 
					            'payment_hash' => $payment_hash->hash,
 | 
				
			||||||
            'total' => $totals,
 | 
					            'total' => $totals,
 | 
				
			||||||
            'invoices' => $payable_invoices,
 | 
					            'invoices' => $payable_invoices,
 | 
				
			||||||
            'token' => $token,
 | 
					            'tokens' => $tokens,
 | 
				
			||||||
            'payment_method_id' => $payment_method_id,
 | 
					            'payment_method_id' => $payment_method_id,
 | 
				
			||||||
            'amount_with_fee' => $invoice_totals + $fee_totals,
 | 
					            'amount_with_fee' => $invoice_totals + $fee_totals,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
@ -266,7 +267,7 @@ class PaymentController extends Controller
 | 
				
			|||||||
                ->setPaymentHash($payment_hash)
 | 
					                ->setPaymentHash($payment_hash)
 | 
				
			||||||
                ->checkRequirements()
 | 
					                ->checkRequirements()
 | 
				
			||||||
                ->processPaymentView($data);
 | 
					                ->processPaymentView($data);
 | 
				
			||||||
        } catch(\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
            SystemLogger::dispatch(
 | 
					            SystemLogger::dispatch(
 | 
				
			||||||
                $e->getMessage(),
 | 
					                $e->getMessage(),
 | 
				
			||||||
                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
					                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
				
			||||||
@ -292,7 +293,7 @@ class PaymentController extends Controller
 | 
				
			|||||||
                ->setPaymentHash($payment_hash)
 | 
					                ->setPaymentHash($payment_hash)
 | 
				
			||||||
                ->checkRequirements()
 | 
					                ->checkRequirements()
 | 
				
			||||||
                ->processPaymentResponse($request);
 | 
					                ->processPaymentResponse($request);
 | 
				
			||||||
        } catch(\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
            SystemLogger::dispatch(
 | 
					            SystemLogger::dispatch(
 | 
				
			||||||
                $e->getMessage(),
 | 
					                $e->getMessage(),
 | 
				
			||||||
                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
					                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
				
			||||||
@ -300,15 +301,13 @@ class PaymentController extends Controller
 | 
				
			|||||||
                SystemLog::TYPE_FAILURE,
 | 
					                SystemLog::TYPE_FAILURE,
 | 
				
			||||||
                auth('contact')->user()->client
 | 
					                auth('contact')->user()->client
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					 | 
				
			||||||
            throw new PaymentFailed($e->getMessage());
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Pay for invoice/s using credits only.
 | 
					     * Pay for invoice/s using credits only.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param  Request $request The request object
 | 
					     * @param Request $request The request object
 | 
				
			||||||
     * @return Response         The response view
 | 
					     * @return Response         The response view
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function credit_response(Request $request)
 | 
					    public function credit_response(Request $request)
 | 
				
			||||||
 | 
				
			|||||||
@ -68,7 +68,7 @@ class AuthorizeCreditCard
 | 
				
			|||||||
        $payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data);
 | 
					        $payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data);
 | 
				
			||||||
        $payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
 | 
					        $payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($request->has('store_card') && $request->input('store_card') === 'true') {
 | 
					        if ($request->has('store_card') && $request->input('store_card') === true) {
 | 
				
			||||||
            $authorise_payment_method->payment_method = GatewayType::CREDIT_CARD;
 | 
					            $authorise_payment_method->payment_method = GatewayType::CREDIT_CARD;
 | 
				
			||||||
            $client_gateway_token = $authorise_payment_method->createClientGatewayToken($payment_profile, $gateway_customer_reference);
 | 
					            $client_gateway_token = $authorise_payment_method->createClientGatewayToken($payment_profile, $gateway_customer_reference);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -80,7 +80,7 @@ class AuthorizeCreditCard
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private function processTokenPayment($request)
 | 
					    private function processTokenPayment($request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $client_gateway_token = ClientGatewayToken::find($this->decodePrimaryKey($request->token));
 | 
					        $client_gateway_token =ClientGatewayToken::where('token', $request->token)->firstOrFail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($client_gateway_token->gateway_customer_reference, $client_gateway_token->token, $request->input('amount_with_fee'));
 | 
					        $data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($client_gateway_token->gateway_customer_reference, $client_gateway_token->token, $request->input('amount_with_fee'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -100,9 +100,10 @@ class AuthorizePaymentMethod
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $gateway_customer_reference = (new AuthorizeCreateCustomer($this->authorize, $this->authorize->client))->create($data);
 | 
					            $gateway_customer_reference = (new AuthorizeCreateCustomer($this->authorize, $this->authorize->client))->create($data);
 | 
				
			||||||
            $payment_profile = $this->addPaymentMethodToClient($gateway_customer_reference, $data);
 | 
					            $payment_profile = $this->addPaymentMethodToClient($gateway_customer_reference, $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->createClientGatewayToken($payment_profile, $gateway_customer_reference);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->createClientGatewayToken($payment_profile, $gateway_customer_reference);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return redirect()->route('client.payment_methods.index');
 | 
					        return redirect()->route('client.payment_methods.index');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -119,6 +120,7 @@ class AuthorizePaymentMethod
 | 
				
			|||||||
        $data['token'] = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
 | 
					        $data['token'] = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
 | 
				
			||||||
        $data['payment_method_id'] = $this->payment_method_id;
 | 
					        $data['payment_method_id'] = $this->payment_method_id;
 | 
				
			||||||
        $data['payment_meta'] = $this->buildPaymentMethod($payment_profile);
 | 
					        $data['payment_meta'] = $this->buildPaymentMethod($payment_profile);
 | 
				
			||||||
 | 
					        $data['payment_method_id'] = GatewayType::CREDIT_CARD;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $additional['gateway_customer_reference'] = $gateway_customer_reference;
 | 
					        $additional['gateway_customer_reference'] = $gateway_customer_reference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,9 @@ class CreditCard
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function authorizeView($data)
 | 
					    public function authorizeView($data)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return render('gateways.checkout.credit_card.authorize');
 | 
					        $data['gateway'] = $this->checkout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return render('gateways.checkout.credit_card.authorize', $data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -91,7 +93,7 @@ class CreditCard
 | 
				
			|||||||
        $this->checkout->payment_hash->data = array_merge((array) $this->checkout->payment_hash->data, $state);
 | 
					        $this->checkout->payment_hash->data = array_merge((array) $this->checkout->payment_hash->data, $state);
 | 
				
			||||||
        $this->checkout->payment_hash->save();
 | 
					        $this->checkout->payment_hash->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($request->has('token') && !is_null($request->token) && $request->pay_with_token) {
 | 
					        if ($request->has('token') && !is_null($request->token) && !empty($request->token)) {
 | 
				
			||||||
            return $this->attemptPaymentUsingToken($request);
 | 
					            return $this->attemptPaymentUsingToken($request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -208,4 +208,9 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
            return $this->processInternallyFailedPayment($this, $e);
 | 
					            return $this->processInternallyFailedPayment($this, $e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function detach(ClientGatewayToken $clientGatewayToken)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Gateway doesn't support this feature.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -39,6 +39,8 @@ class ACH
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function authorizeView(array $data)
 | 
					    public function authorizeView(array $data)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $data['gateway'] = $this->stripe;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return render('gateways.stripe.ach.authorize', array_merge($data));
 | 
					        return render('gateways.stripe.ach.authorize', array_merge($data));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -63,7 +65,12 @@ class ACH
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function verificationView(ClientGatewayToken $token)
 | 
					    public function verificationView(ClientGatewayToken $token)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return render('gateways.stripe.ach.verify', compact('token'));
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'token' => $token,
 | 
				
			||||||
 | 
					            'gateway' => $this->stripe,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return render('gateways.stripe.ach.verify', $data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function processVerification(Request $request, ClientGatewayToken $token)
 | 
					    public function processVerification(Request $request, ClientGatewayToken $token)
 | 
				
			||||||
 | 
				
			|||||||
@ -66,13 +66,7 @@ class CreditCard
 | 
				
			|||||||
            'description' => collect($data['invoices'])->pluck('id'), // TODO: More meaningful description.
 | 
					            'description' => collect($data['invoices'])->pluck('id'), // TODO: More meaningful description.
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($data['token']) {
 | 
					        $payment_intent_data['setup_future_usage'] = 'off_session';
 | 
				
			||||||
            $payment_intent_data['payment_method'] = $data['token']->token;
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            $payment_intent_data['setup_future_usage'] = 'off_session';
 | 
					 | 
				
			||||||
            // $payment_intent_data['save_payment_method'] = true;
 | 
					 | 
				
			||||||
            // $payment_intent_data['confirm'] = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data);
 | 
					        $data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data);
 | 
				
			||||||
        $data['gateway'] = $this->stripe;
 | 
					        $data['gateway'] = $this->stripe;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,2 +1,2 @@
 | 
				
			|||||||
/*! For license information please see authorize-credit-card-payment.js.LICENSE.txt */
 | 
					/*! For license information please see authorize-credit-card-payment.js.LICENSE.txt */
 | 
				
			||||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=2)}({2:function(e,t,n){e.exports=n("hK5p")},hK5p:function(e,t){function n(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0,a=function(){};return{s:a,n:function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var u,c=!0,i=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return c=e.done,e},e:function(e){i=!0,u=e},f:function(){try{c||null==n.return||n.return()}finally{if(i)throw u}}}}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function o(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new(function(){function e(t,r){var o=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),a(this,"handleAuthorization",(function(){var e=$("#my-card"),t={};t.clientKey=o.publicKey,t.apiLoginID=o.loginId;var n={};n.cardNumber=e.CardJs("cardNumber").replace(/[^\d]/g,""),n.month=e.CardJs("expiryMonth"),n.year=e.CardJs("expiryYear"),n.cardCode=document.getElementById("cvv").value;var r={};return r.authData=t,r.cardData=n,document.getElementById("card_button").disabled=!0,document.querySelector("#card_button > svg").classList.remove("hidden"),document.querySelector("#card_button > span").classList.add("hidden"),Accept.dispatchData(r,o.responseHandler),!1})),a(this,"responseHandler",(function(e){if("Error"===e.messages.resultCode){$("#errors").show().html("<p>"+e.messages.message[0].code+": "+e.messages.message[0].text+"</p>"),document.getElementById("card_button").disabled=!1,document.querySelector("#card_button > svg").classList.add("hidden"),document.querySelector("#card_button > span").classList.remove("hidden")}else if("Ok"===e.messages.resultCode){document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue;var t=document.getElementById("store_card_checkbox");t&&(document.getElementById("store_card").value=t.checked),document.getElementById("server_response").submit()}return!1})),a(this,"handle",(function(){if(o.cardButton&&o.cardButton.addEventListener("click",(function(){o.cardButton.disabled=!0,o.handleAuthorization()})),o.payNowButton){var e,t=n(o.payNowButton);try{var r=function(){var t=e.value;t.addEventListener("click",(function(){t.disabled=!0,o.handlePayNowAction(t.dataset.id)}))};for(t.s();!(e=t.n()).done;)r()}catch(e){t.e(e)}finally{t.f()}}return o})),this.publicKey=t,this.loginId=r,this.cardHolderName=document.getElementById("cardholder_name"),this.cardButton=document.getElementById("card_button"),this.payNowButton=document.getElementsByClassName("pay_now_button")}var t,r,u;return t=e,(r=[{key:"handlePayNowAction",value:function(e){document.getElementById("token").value=e,document.getElementById("server_response").submit()}}])&&o(t.prototype,r),u&&o(t,u),e}())(document.querySelector('meta[name="authorize-public-key"]').content,document.querySelector('meta[name="authorize-login-id"]').content).handle()}});
 | 
					!function(e){var t={};function n(r){if(t[r])return t[r].exports;var a=t[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,n),a.l=!0,a.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(r,a,function(t){return e[t]}.bind(null,a));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=2)}({2:function(e,t,n){e.exports=n("hK5p")},hK5p:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new(function(){function e(t,n){var a=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),r(this,"handleAuthorization",(function(){var e=$("#my-card"),t={};t.clientKey=a.publicKey,t.apiLoginID=a.loginId;var n={};n.cardNumber=e.CardJs("cardNumber").replace(/[^\d]/g,""),n.month=e.CardJs("expiryMonth"),n.year=e.CardJs("expiryYear"),n.cardCode=document.getElementById("cvv").value;var r={};return r.authData=t,r.cardData=n,document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),Accept.dispatchData(r,a.responseHandler),!1})),r(this,"responseHandler",(function(e){if("Error"===e.messages.resultCode){$("#errors").show().html("<p>"+e.messages.message[0].code+": "+e.messages.message[0].text+"</p>"),document.getElementById("card_button").disabled=!1,document.querySelector("#card_button > svg").classList.add("hidden"),document.querySelector("#card_button > span").classList.remove("hidden")}else if("Ok"===e.messages.resultCode){document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue;var t=document.querySelector("input[name=token-billing-checkbox]:checked");t&&(document.getElementById("store_card").value=t.value),document.getElementById("server_response").submit()}return!1})),r(this,"handle",(function(){Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach((function(e){return e.addEventListener("click",(function(e){document.getElementById("save-card--container").style.display="none",document.getElementById("authorize--credit-card-container").style.display="none",document.getElementById("token").value=e.target.dataset.token}))}));var e=document.getElementById("toggle-payment-with-credit-card");e&&e.addEventListener("click",(function(){document.getElementById("save-card--container").style.display="grid",document.getElementById("authorize--credit-card-container").style.display="flex",document.getElementById("token").value=null})),a.cardButton&&a.cardButton.addEventListener("click",(function(){a.cardButton.disabled=!0,a.handleAuthorization()}));var t=document.getElementById("pay-now");return t&&t.addEventListener("click",(function(e){var t=document.getElementById("token");t.value?a.handlePayNowAction(t.value):a.handleAuthorization()})),a})),this.publicKey=t,this.loginId=n,this.cardHolderName=document.getElementById("cardholder_name"),this.cardButton=document.getElementById("card_button")}var t,a,o;return t=e,(a=[{key:"handlePayNowAction",value:function(e){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),document.getElementById("token").value=e,document.getElementById("server_response").submit()}}])&&n(t.prototype,a),o&&n(t,o),e}())(document.querySelector('meta[name="authorize-public-key"]').content,document.querySelector('meta[name="authorize-login-id"]').content).handle()}});
 | 
				
			||||||
							
								
								
									
										2
									
								
								public/js/clients/payments/checkout-credit-card.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								public/js/clients/payments/checkout-credit-card.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					/*! For license information please see checkout-credit-card.js.LICENSE.txt */
 | 
				
			||||||
 | 
					!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=8)}({8:function(e,t,n){e.exports=n("fQHp")},fQHp:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.tokens=[]}var t,r,o;return t=e,(r=[{key:"mountFrames",value:function(){console.log("Mount checkout frames..")}},{key:"handlePaymentUsingToken",value:function(e){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.querySelector("input[name=token]").value=e.target.dataset.token}},{key:"handlePaymentUsingCreditCard",value:function(e){var t;document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden");var n=document.getElementById("pay-button"),r=null!==(t=document.querySelector('meta[name="public-key"]').content)&&void 0!==t?t:"",o=document.getElementById("payment-form");Frames.init(r),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,(function(e){n.disabled=!Frames.isCardValid()})),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,(function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()})),o.addEventListener("submit",(function(e){e.preventDefault(),Frames.submitCard()}))}},{key:"completePaymentUsingToken",value:function(e){var t=document.getElementById("pay-now-with-token");t.disabled=!0,t.querySelector("svg").classList.remove("hidden"),t.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}},{key:"handle",value:function(){var e=this;this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach((function(t){return t.addEventListener("click",e.handlePaymentUsingToken)})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}});
 | 
				
			||||||
							
								
								
									
										0
									
								
								public/js/clients/payments/checkout.com.js.LICENSE.txt → public/js/clients/payments/checkout-credit-card.js.LICENSE.txt
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								public/js/clients/payments/checkout.com.js.LICENSE.txt → public/js/clients/payments/checkout-credit-card.js.LICENSE.txt
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,15 +1,15 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "/js/app.js": "/js/app.js?id=a33a5a58bfc6e2174841",
 | 
					    "/js/app.js": "/js/app.js?id=a33a5a58bfc6e2174841",
 | 
				
			||||||
    "/css/app.css": "/css/app.css?id=0bc73459b671b24af838",
 | 
					    "/css/app.css": "/css/app.css?id=599b11149976e86c83a3",
 | 
				
			||||||
    "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
 | 
					    "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
 | 
				
			||||||
    "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
 | 
					    "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
 | 
				
			||||||
    "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=cddcd46c630c71737bda",
 | 
					    "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=cddcd46c630c71737bda",
 | 
				
			||||||
    "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=fe43d5a1ad3ec29387d4",
 | 
					    "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=d7160227f23b73fd1e63",
 | 
				
			||||||
    "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=5469146cd629ea1b5c20",
 | 
					    "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=5469146cd629ea1b5c20",
 | 
				
			||||||
    "/js/clients/payments/checkout.com.js": "/js/clients/payments/checkout.com.js?id=ce184db42e52d403c21b",
 | 
					    "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=935645b176c73b7831f4",
 | 
				
			||||||
    "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad",
 | 
					    "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad",
 | 
				
			||||||
    "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421",
 | 
					    "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421",
 | 
				
			||||||
    "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=f4659d26a26d2f408397",
 | 
					    "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=e2ccaed999d2ae077c1f",
 | 
				
			||||||
    "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=9b9fd56d655ad238f149",
 | 
					    "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=9b9fd56d655ad238f149",
 | 
				
			||||||
    "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa",
 | 
					    "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa",
 | 
				
			||||||
    "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",
 | 
					    "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,6 @@ class AuthorizeAuthorizeCard {
 | 
				
			|||||||
        this.loginId = loginId;
 | 
					        this.loginId = loginId;
 | 
				
			||||||
        this.cardHolderName = document.getElementById("cardholder_name");
 | 
					        this.cardHolderName = document.getElementById("cardholder_name");
 | 
				
			||||||
        this.cardButton = document.getElementById("card_button");
 | 
					        this.cardButton = document.getElementById("card_button");
 | 
				
			||||||
        this.payNowButton = document.getElementsByClassName("pay_now_button");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handleAuthorization = () => {
 | 
					    handleAuthorization = () => {
 | 
				
			||||||
@ -39,9 +38,13 @@ class AuthorizeAuthorizeCard {
 | 
				
			|||||||
        // send the bankData object instead of the cardData object.
 | 
					        // send the bankData object instead of the cardData object.
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
        // secureData.bankData = bankData;
 | 
					        // secureData.bankData = bankData;
 | 
				
			||||||
        document.getElementById('card_button').disabled = true;
 | 
					        let payNowButton = document.getElementById('pay-now');
 | 
				
			||||||
        document.querySelector('#card_button > svg').classList.remove('hidden');
 | 
					
 | 
				
			||||||
        document.querySelector('#card_button > span').classList.add('hidden');
 | 
					        if (payNowButton) {
 | 
				
			||||||
 | 
					            document.getElementById('pay-now').disabled = true;
 | 
				
			||||||
 | 
					            document.querySelector('#pay-now > svg').classList.remove('hidden');
 | 
				
			||||||
 | 
					            document.querySelector('#pay-now > span').classList.add('hidden');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Accept.dispatchData(secureData, this.responseHandler);
 | 
					        Accept.dispatchData(secureData, this.responseHandler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,10 +53,12 @@ class AuthorizeAuthorizeCard {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handlePayNowAction(token_hashed_id) {
 | 
					    handlePayNowAction(token_hashed_id) {
 | 
				
			||||||
 | 
					        document.getElementById('pay-now').disabled = true;
 | 
				
			||||||
 | 
					        document.querySelector('#pay-now > svg').classList.remove('hidden');
 | 
				
			||||||
 | 
					        document.querySelector('#pay-now > span').classList.add('hidden');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document.getElementById("token").value = token_hashed_id;
 | 
					        document.getElementById("token").value = token_hashed_id;
 | 
				
			||||||
        document.getElementById("server_response").submit();
 | 
					        document.getElementById("server_response").submit();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    responseHandler = (response) => {
 | 
					    responseHandler = (response) => {
 | 
				
			||||||
@ -71,10 +76,10 @@ class AuthorizeAuthorizeCard {
 | 
				
			|||||||
            document.getElementById("dataDescriptor").value = response.opaqueData.dataDescriptor;
 | 
					            document.getElementById("dataDescriptor").value = response.opaqueData.dataDescriptor;
 | 
				
			||||||
            document.getElementById("dataValue").value = response.opaqueData.dataValue;
 | 
					            document.getElementById("dataValue").value = response.opaqueData.dataValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let store_card_checkbox = document.getElementById("store_card_checkbox");
 | 
					            let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (store_card_checkbox) {
 | 
					            if (storeCard) {
 | 
				
			||||||
                document.getElementById("store_card").value = store_card_checkbox.checked;
 | 
					                document.getElementById("store_card").value = storeCard.value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            document.getElementById("server_response").submit();
 | 
					            document.getElementById("server_response").submit();
 | 
				
			||||||
@ -85,6 +90,33 @@ class AuthorizeAuthorizeCard {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handle = () => {
 | 
					    handle = () => {
 | 
				
			||||||
 | 
					        Array
 | 
				
			||||||
 | 
					            .from(document.getElementsByClassName('toggle-payment-with-token'))
 | 
				
			||||||
 | 
					            .forEach((element) => element.addEventListener('click', (e) => {
 | 
				
			||||||
 | 
					                document
 | 
				
			||||||
 | 
					                    .getElementById('save-card--container').style.display = 'none';
 | 
				
			||||||
 | 
					                document
 | 
				
			||||||
 | 
					                    .getElementById('authorize--credit-card-container').style.display = 'none';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                document
 | 
				
			||||||
 | 
					                    .getElementById('token').value = e.target.dataset.token;
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (payWithCreditCardToggle) {
 | 
				
			||||||
 | 
					            payWithCreditCardToggle
 | 
				
			||||||
 | 
					                .addEventListener('click', () => {
 | 
				
			||||||
 | 
					                    document
 | 
				
			||||||
 | 
					                        .getElementById('save-card--container').style.display = 'grid';
 | 
				
			||||||
 | 
					                    document
 | 
				
			||||||
 | 
					                        .getElementById('authorize--credit-card-container').style.display = 'flex';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    document
 | 
				
			||||||
 | 
					                        .getElementById('token').value = null;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.cardButton) {
 | 
					        if (this.cardButton) {
 | 
				
			||||||
            this.cardButton.addEventListener("click", () => {
 | 
					            this.cardButton.addEventListener("click", () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -95,18 +127,17 @@ class AuthorizeAuthorizeCard {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.payNowButton) {
 | 
					        let payNowButton = document.getElementById('pay-now');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (let item of this.payNowButton) {
 | 
					        if (payNowButton) {
 | 
				
			||||||
 | 
					            payNowButton
 | 
				
			||||||
                item.addEventListener('click', () => {
 | 
					                .addEventListener('click', (e) => {
 | 
				
			||||||
                    item.disabled = true;
 | 
					                    let token = document.getElementById('token');
 | 
				
			||||||
                    this.handlePayNowAction(item.dataset.id);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    token.value
 | 
				
			||||||
 | 
					                        ? this.handlePayNowAction(token.value)
 | 
				
			||||||
 | 
					                        : this.handleAuthorization();
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										90
									
								
								resources/js/clients/payments/checkout-credit-card.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								resources/js/clients/payments/checkout-credit-card.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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://opensource.org/licenses/AAL
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					            .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');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const payButton = document.getElementById('pay-button');
 | 
				
			||||||
 | 
					        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_TOKENIZED, function (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.getElementById('server-response').submit();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form.addEventListener('submit', function (event) {
 | 
				
			||||||
 | 
					            event.preventDefault();
 | 
				
			||||||
 | 
					            Frames.submitCard();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    completePaymentUsingToken(e) {
 | 
				
			||||||
 | 
					        let btn = document.getElementById('pay-now-with-token');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        btn.disabled = true;
 | 
				
			||||||
 | 
					        btn.querySelector('svg').classList.remove('hidden');
 | 
				
			||||||
 | 
					        btn.querySelector('span').classList.add('hidden');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        document.getElementById('server-response').submit();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    handle() {
 | 
				
			||||||
 | 
					        this.handlePaymentUsingCreditCard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Array
 | 
				
			||||||
 | 
					            .from(document.getElementsByClassName('toggle-payment-with-token'))
 | 
				
			||||||
 | 
					            .forEach((element) => element.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 CheckoutCreditCard().handle();
 | 
				
			||||||
@ -9,9 +9,8 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StripeCreditCard {
 | 
					class StripeCreditCard {
 | 
				
			||||||
    constructor(key, token, secret, onlyAuthorization) {
 | 
					    constructor(key, secret, onlyAuthorization) {
 | 
				
			||||||
        this.key = key;
 | 
					        this.key = key;
 | 
				
			||||||
        this.token = token;
 | 
					 | 
				
			||||||
        this.secret = secret;
 | 
					        this.secret = secret;
 | 
				
			||||||
        this.onlyAuthorization = onlyAuthorization;
 | 
					        this.onlyAuthorization = onlyAuthorization;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -36,9 +35,11 @@ class StripeCreditCard {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    completePaymentUsingToken() {
 | 
					    completePaymentUsingToken() {
 | 
				
			||||||
        let payNowButton = document.getElementById('pay-now-with-token');
 | 
					        let token = document.querySelector('input[name=token]').value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let payNowButton = document.getElementById('pay-now');
 | 
				
			||||||
        this.payNowButton = payNowButton;
 | 
					        this.payNowButton = payNowButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.payNowButton.disabled = true;
 | 
					        this.payNowButton.disabled = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.payNowButton.querySelector('svg').classList.remove('hidden');
 | 
					        this.payNowButton.querySelector('svg').classList.remove('hidden');
 | 
				
			||||||
@ -46,7 +47,7 @@ class StripeCreditCard {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.stripe
 | 
					        this.stripe
 | 
				
			||||||
            .handleCardPayment(this.secret, {
 | 
					            .handleCardPayment(this.secret, {
 | 
				
			||||||
                payment_method: this.token,
 | 
					                payment_method: token,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .then((result) => {
 | 
					            .then((result) => {
 | 
				
			||||||
                if (result.error) {
 | 
					                if (result.error) {
 | 
				
			||||||
@ -71,7 +72,7 @@ class StripeCreditCard {
 | 
				
			|||||||
        this.stripe
 | 
					        this.stripe
 | 
				
			||||||
            .handleCardPayment(this.secret, this.cardElement, {
 | 
					            .handleCardPayment(this.secret, this.cardElement, {
 | 
				
			||||||
                payment_method_data: {
 | 
					                payment_method_data: {
 | 
				
			||||||
                    billing_details: { name: cardHolderName.value },
 | 
					                    billing_details: {name: cardHolderName.value},
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .then((result) => {
 | 
					            .then((result) => {
 | 
				
			||||||
@ -126,7 +127,7 @@ class StripeCreditCard {
 | 
				
			|||||||
        this.stripe
 | 
					        this.stripe
 | 
				
			||||||
            .handleCardSetup(this.secret, this.cardElement, {
 | 
					            .handleCardSetup(this.secret, this.cardElement, {
 | 
				
			||||||
                payment_method_data: {
 | 
					                payment_method_data: {
 | 
				
			||||||
                    billing_details: { name: cardHolderName.value },
 | 
					                    billing_details: {name: cardHolderName.value},
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .then((result) => {
 | 
					            .then((result) => {
 | 
				
			||||||
@ -158,23 +159,33 @@ class StripeCreditCard {
 | 
				
			|||||||
                    return this.handleAuthorization();
 | 
					                    return this.handleAuthorization();
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if (this.token) {
 | 
					            Array
 | 
				
			||||||
                document
 | 
					                .from(document.getElementsByClassName('toggle-payment-with-token'))
 | 
				
			||||||
                    .getElementById('pay-now-with-token')
 | 
					                .forEach((element) => element.addEventListener('click', (element) => {
 | 
				
			||||||
                    .addEventListener('click', () => {
 | 
					                    document.getElementById('stripe--payment-container').classList.add('hidden');
 | 
				
			||||||
 | 
					                    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.querySelector('input[name=token]').value = "";
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.createElement().mountCardElement();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            document
 | 
				
			||||||
 | 
					                .getElementById('pay-now')
 | 
				
			||||||
 | 
					                .addEventListener('click', () => {
 | 
				
			||||||
 | 
					                    let tokenInput = document.querySelector('input[name=token]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (tokenInput.value) {
 | 
				
			||||||
                        return this.completePaymentUsingToken();
 | 
					                        return this.completePaymentUsingToken();
 | 
				
			||||||
                    });
 | 
					                    }
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!this.token) {
 | 
					                    return this.completePaymentWithoutToken();
 | 
				
			||||||
                this.createElement().mountCardElement();
 | 
					                });
 | 
				
			||||||
 | 
					 | 
				
			||||||
                document
 | 
					 | 
				
			||||||
                    .getElementById('pay-now')
 | 
					 | 
				
			||||||
                    .addEventListener('click', () => {
 | 
					 | 
				
			||||||
                        return this.completePaymentWithoutToken();
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -182,12 +193,10 @@ class StripeCreditCard {
 | 
				
			|||||||
const publishableKey =
 | 
					const publishableKey =
 | 
				
			||||||
    document.querySelector('meta[name="stripe-publishable-key"]').content ?? '';
 | 
					    document.querySelector('meta[name="stripe-publishable-key"]').content ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const token = document.querySelector('meta[name="stripe-token"]').content ?? '';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const secret =
 | 
					const secret =
 | 
				
			||||||
    document.querySelector('meta[name="stripe-secret"]').content ?? '';
 | 
					    document.querySelector('meta[name="stripe-secret"]').content ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onlyAuthorization =
 | 
					const onlyAuthorization =
 | 
				
			||||||
    document.querySelector('meta[name="only-authorization"]').content ?? '';
 | 
					    document.querySelector('meta[name="only-authorization"]').content ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
new StripeCreditCard(publishableKey, token, secret, onlyAuthorization).handle();
 | 
					new StripeCreditCard(publishableKey, secret, onlyAuthorization).handle();
 | 
				
			||||||
 | 
				
			|||||||
@ -3324,6 +3324,9 @@ return [
 | 
				
			|||||||
    'required_client_info_save_label' => 'We will save this, so you don\'t have to enter it next time.',
 | 
					    'required_client_info_save_label' => 'We will save this, so you don\'t have to enter it next time.',
 | 
				
			||||||
    'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact. \n :error',
 | 
					    'notification_credit_bounced' => 'We were unable to deliver Credit :invoice to :contact. \n :error',
 | 
				
			||||||
    'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice',
 | 
					    'notification_credit_bounced_subject' => 'Unable to deliver Credit :invoice',
 | 
				
			||||||
 | 
					    'save_payment_method_details' => 'Save payment method details',
 | 
				
			||||||
 | 
					    'new_card' => 'New card',
 | 
				
			||||||
 | 
					    'new_bank_account' => 'New bank account',
 | 
				
			||||||
    'company_limit_reached' => 'Limit of 10 companies per account.',
 | 
					    'company_limit_reached' => 'Limit of 10 companies per account.',
 | 
				
			||||||
    'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
 | 
					    'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
 | 
				
			||||||
    'credit_number_taken' => 'Credit number already taken',
 | 
					    'credit_number_taken' => 'Credit number already taken',
 | 
				
			||||||
 | 
				
			|||||||
@ -32,16 +32,35 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @include('portal.ninja2020.gateways.includes.payment_details')
 | 
					    @include('portal.ninja2020.gateways.includes.payment_details')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @if($token)
 | 
					    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
 | 
				
			||||||
        @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.credit_card')])
 | 
					        @if(count($tokens) > 0)
 | 
				
			||||||
            {{ strtoupper($token->meta->brand) }} - **** {{ $token->meta->last4 }}
 | 
					            @foreach($tokens as $token)
 | 
				
			||||||
        @endcomponent
 | 
					                <label class="mr-4">
 | 
				
			||||||
 | 
					                    <input
 | 
				
			||||||
 | 
					                        type="radio"
 | 
				
			||||||
 | 
					                        data-token="{{ $token->token }}"
 | 
				
			||||||
 | 
					                        name="payment-type"
 | 
				
			||||||
 | 
					                        class="form-radio cursor-pointer toggle-payment-with-token"/>
 | 
				
			||||||
 | 
					                    <span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
 | 
				
			||||||
 | 
					                </label>
 | 
				
			||||||
 | 
					            @endforeach
 | 
				
			||||||
 | 
					        @endisset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'card_button', 'class' => 'pay_now_button', 'data' => ['id' => $token->hashed_id]])
 | 
					        <label>
 | 
				
			||||||
    @else
 | 
					            <input
 | 
				
			||||||
        @include('portal.ninja2020.gateways.authorize.includes.credit_card')
 | 
					                type="radio"
 | 
				
			||||||
        @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'card_button'])
 | 
					                id="toggle-payment-with-credit-card"
 | 
				
			||||||
    @endif
 | 
					                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')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @include('portal.ninja2020.gateways.authorize.includes.credit_card')
 | 
				
			||||||
 | 
					    @include('portal.ninja2020.gateways.includes.pay_now')
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_footer')
 | 
					@section('gateway_footer')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
 | 
					<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
 | 
				
			||||||
     style="display: flex!important; justify-content: center!important;">
 | 
					     style="display: flex!important; justify-content: center!important;" id="authorize--credit-card-container">
 | 
				
			||||||
    <div class="card-js" id="my-card" data-capture-name="true">
 | 
					    <div class="card-js" id="my-card" data-capture-name="true">
 | 
				
			||||||
        <input class="name" id="cardholder_name" name="card-holders-name" placeholder="{{ ctrans('texts.name')}}">
 | 
					        <input class="name" id="cardholder_name" name="card-holders-name" placeholder="{{ ctrans('texts.name')}}">
 | 
				
			||||||
        <input class="card-number my-custom-class" id="card_number" name="card-number">
 | 
					        <input class="card-number my-custom-class" id="card_number" name="card-number">
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,109 @@
 | 
				
			|||||||
    <meta name="currency" content="{{ $currency }}">
 | 
					    <meta name="currency" content="{{ $currency }}">
 | 
				
			||||||
    <meta name="reference" content="{{ $payment_hash }}">
 | 
					    <meta name="reference" content="{{ $payment_hash }}">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <style>*,*::after,*::before{box-sizing:border-box}html{background-color:#FFF;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif}#payment-form{width:31.5rem;margin:0 auto}iframe{width:100%}.one-liner{display:flex;flex-direction:column}#pay-button{border:none;border-radius:3px;color:#FFF;font-weight:500;height:40px;width:100%;background-color:#13395E;box-shadow:0 1px 3px 0 rgba(19,57,94,0.4)}#pay-button:active{background-color:#0B2A49;box-shadow:0 1px 3px 0 rgba(19,57,94,0.4)}#pay-button:hover{background-color:#15406B;box-shadow:0 2px 5px 0 rgba(19,57,94,0.4)}#pay-button:disabled{background-color:#697887;box-shadow:none}#pay-button:not(:disabled){cursor:pointer}.card-frame{border:solid 1px #13395E;border-radius:3px;width:100%;margin-bottom:8px;height:40px;box-shadow:0 1px 3px 0 rgba(19,57,94,0.2)}.card-frame.frame--rendered{opacity:1}.card-frame.frame--rendered.frame--focus{border:solid 1px #13395E;box-shadow:0 2px 5px 0 rgba(19,57,94,0.15)}.card-frame.frame--rendered.frame--invalid{border:solid 1px #D96830;box-shadow:0 2px 5px 0 rgba(217,104,48,0.15)}.success-payment-message{color:#13395E;line-height:1.4}.token{color:#b35e14;font-size:0.9rem;font-family:monospace}@media screen and (min-width: 31rem){.one-liner{flex-direction:row}.card-frame{width:318px;margin-bottom:0}#pay-button{width:175px;margin-left:8px}}</style>
 | 
					    <style>*, *::after, *::before {
 | 
				
			||||||
 | 
					            box-sizing: border-box
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        html {
 | 
				
			||||||
 | 
					            background-color: #FFF;
 | 
				
			||||||
 | 
					            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #payment-form {
 | 
				
			||||||
 | 
					            width: 31.5rem;
 | 
				
			||||||
 | 
					            margin: 0 auto
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        iframe {
 | 
				
			||||||
 | 
					            width: 100%
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .one-liner {
 | 
				
			||||||
 | 
					            display: flex;
 | 
				
			||||||
 | 
					            flex-direction: column
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #pay-button {
 | 
				
			||||||
 | 
					            border: none;
 | 
				
			||||||
 | 
					            border-radius: 3px;
 | 
				
			||||||
 | 
					            color: #FFF;
 | 
				
			||||||
 | 
					            font-weight: 500;
 | 
				
			||||||
 | 
					            height: 40px;
 | 
				
			||||||
 | 
					            width: 100%;
 | 
				
			||||||
 | 
					            background-color: #13395E;
 | 
				
			||||||
 | 
					            box-shadow: 0 1px 3px 0 rgba(19, 57, 94, 0.4)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #pay-button:active {
 | 
				
			||||||
 | 
					            background-color: #0B2A49;
 | 
				
			||||||
 | 
					            box-shadow: 0 1px 3px 0 rgba(19, 57, 94, 0.4)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #pay-button:hover {
 | 
				
			||||||
 | 
					            background-color: #15406B;
 | 
				
			||||||
 | 
					            box-shadow: 0 2px 5px 0 rgba(19, 57, 94, 0.4)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #pay-button:disabled {
 | 
				
			||||||
 | 
					            background-color: #697887;
 | 
				
			||||||
 | 
					            box-shadow: none
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #pay-button:not(:disabled) {
 | 
				
			||||||
 | 
					            cursor: pointer
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .card-frame {
 | 
				
			||||||
 | 
					            border: solid 1px #13395E;
 | 
				
			||||||
 | 
					            border-radius: 3px;
 | 
				
			||||||
 | 
					            width: 100%;
 | 
				
			||||||
 | 
					            margin-bottom: 8px;
 | 
				
			||||||
 | 
					            height: 40px;
 | 
				
			||||||
 | 
					            box-shadow: 0 1px 3px 0 rgba(19, 57, 94, 0.2)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .card-frame.frame--rendered {
 | 
				
			||||||
 | 
					            opacity: 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .card-frame.frame--rendered.frame--focus {
 | 
				
			||||||
 | 
					            border: solid 1px #13395E;
 | 
				
			||||||
 | 
					            box-shadow: 0 2px 5px 0 rgba(19, 57, 94, 0.15)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .card-frame.frame--rendered.frame--invalid {
 | 
				
			||||||
 | 
					            border: solid 1px #D96830;
 | 
				
			||||||
 | 
					            box-shadow: 0 2px 5px 0 rgba(217, 104, 48, 0.15)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .success-payment-message {
 | 
				
			||||||
 | 
					            color: #13395E;
 | 
				
			||||||
 | 
					            line-height: 1.4
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .token {
 | 
				
			||||||
 | 
					            color: #b35e14;
 | 
				
			||||||
 | 
					            font-size: 0.9rem;
 | 
				
			||||||
 | 
					            font-family: monospace
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @media screen and (min-width: 31rem) {
 | 
				
			||||||
 | 
					            .one-liner {
 | 
				
			||||||
 | 
					                flex-direction: row
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .card-frame {
 | 
				
			||||||
 | 
					                width: 318px;
 | 
				
			||||||
 | 
					                margin-bottom: 0
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #pay-button {
 | 
				
			||||||
 | 
					                width: 175px;
 | 
				
			||||||
 | 
					                margin-left: 8px
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <script src="https://cdn.checkout.com/js/framesv2.min.js"></script>
 | 
					    <script src="https://cdn.checkout.com/js/framesv2.min.js"></script>
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,10 +126,7 @@
 | 
				
			|||||||
        <input type="hidden" name="raw_value" value="{{ $raw_value }}">
 | 
					        <input type="hidden" name="raw_value" value="{{ $raw_value }}">
 | 
				
			||||||
        <input type="hidden" name="currency" value="{{ $currency }}">
 | 
					        <input type="hidden" name="currency" value="{{ $currency }}">
 | 
				
			||||||
        <input type="hidden" name="pay_with_token" value="false">
 | 
					        <input type="hidden" name="pay_with_token" value="false">
 | 
				
			||||||
        
 | 
					        <input type="hidden" name="token" value="">
 | 
				
			||||||
        @isset($token)
 | 
					 | 
				
			||||||
            <input type="hidden" name="token" value="{{ $token->token }}">
 | 
					 | 
				
			||||||
        @endisset
 | 
					 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
 | 
					    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
 | 
				
			||||||
@ -37,14 +136,17 @@
 | 
				
			|||||||
    @include('portal.ninja2020.gateways.includes.payment_details')
 | 
					    @include('portal.ninja2020.gateways.includes.payment_details')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
 | 
					    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
 | 
				
			||||||
        @isset($token)
 | 
					        @if(count($tokens) > 0)
 | 
				
			||||||
            <label class="mr-4">
 | 
					            @foreach($tokens as $token)
 | 
				
			||||||
                <input 
 | 
					                <label class="mr-4">
 | 
				
			||||||
                    type="radio" 
 | 
					                    <input
 | 
				
			||||||
                    id="toggle-payment-with-token" 
 | 
					                        type="radio"
 | 
				
			||||||
                    class="form-radio cursor-pointer" name="payment-type" />
 | 
					                        data-token="{{ $token->token }}"
 | 
				
			||||||
                <span class="ml-1 cursor-pointer">**** {{ $token->meta->last4 }}</span>
 | 
					                        name="payment-type"
 | 
				
			||||||
            </label>
 | 
					                        class="form-radio cursor-pointer toggle-payment-with-token"/>
 | 
				
			||||||
 | 
					                    <span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
 | 
				
			||||||
 | 
					                </label>
 | 
				
			||||||
 | 
					            @endforeach
 | 
				
			||||||
        @endisset
 | 
					        @endisset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <label>
 | 
					        <label>
 | 
				
			||||||
@ -54,7 +156,7 @@
 | 
				
			|||||||
                class="form-radio cursor-pointer"
 | 
					                class="form-radio cursor-pointer"
 | 
				
			||||||
                name="payment-type"
 | 
					                name="payment-type"
 | 
				
			||||||
                checked/>
 | 
					                checked/>
 | 
				
			||||||
            <span class="ml-1 cursor-pointer">{{ __('texts.credit_card') }}</span>
 | 
					            <span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
 | 
				
			||||||
        </label>
 | 
					        </label>
 | 
				
			||||||
    @endcomponent
 | 
					    @endcomponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -78,12 +180,12 @@
 | 
				
			|||||||
    @endcomponent
 | 
					    @endcomponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @component('portal.ninja2020.components.general.card-element-single')
 | 
					    @component('portal.ninja2020.components.general.card-element-single')
 | 
				
			||||||
       <div class="hidden" id="pay-now-with-token--container">
 | 
					        <div class="hidden" id="pay-now-with-token--container">
 | 
				
			||||||
            @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token'])
 | 
					            @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token'])
 | 
				
			||||||
       </div>
 | 
					        </div>
 | 
				
			||||||
    @endcomponent
 | 
					    @endcomponent
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_footer')
 | 
					@section('gateway_footer')
 | 
				
			||||||
    <script src="{{ asset('js/clients/payments/checkout.com.js') }}"></script>
 | 
					    <script src="{{ asset('js/clients/payments/checkout-credit-card.js') }}"></script>
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
@unless(isset($show_save) && $show_save == false)
 | 
					@unless(isset($show_save) && $show_save == false)
 | 
				
			||||||
    <div class="{{ ($gateway->token_billing == 'optin' || $gateway->token_billing == 'optout') ? 'sm:grid' : 'hidden' }} px-4 py-5 sm:grid-cols-3 sm:gap-4 sm:px-6" id="save-card--container">
 | 
					    <div class="{{ ($gateway->token_billing == 'optin' || $gateway->token_billing == 'optout') ? 'sm:grid' : 'hidden' }} px-4 py-5 sm:grid-cols-3 sm:gap-4 sm:px-6" id="save-card--container">
 | 
				
			||||||
        <dt class="text-sm leading-5 font-medium text-gray-500">
 | 
					        <dt class="text-sm leading-5 font-medium text-gray-500">
 | 
				
			||||||
            {{ ctrans('texts.token_billing_checkbox') }}
 | 
					            {{ ctrans('texts.save_payment_method_details') }}
 | 
				
			||||||
        </dt>
 | 
					        </dt>
 | 
				
			||||||
        <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
 | 
					        <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
 | 
				
			||||||
            <label class="mr-4">
 | 
					            <label class="mr-4">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
 | 
					@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_head')
 | 
					@section('gateway_head')
 | 
				
			||||||
    <meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
 | 
					    <meta name="stripe-publishable-key" content="{{ $gateway->company_gateway->getPublishableKey() }}">
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_content')
 | 
					@section('gateway_content')
 | 
				
			||||||
@ -14,7 +14,7 @@
 | 
				
			|||||||
    <form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::BANK_TRANSFER]) }}" method="post" id="server_response">
 | 
					    <form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::BANK_TRANSFER]) }}" method="post" id="server_response">
 | 
				
			||||||
        @csrf
 | 
					        @csrf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <input type="hidden" name="company_gateway_id" value="{{ $gateway->id }}">
 | 
					        <input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
 | 
				
			||||||
        <input type="hidden" name="gateway_type_id" value="2">
 | 
					        <input type="hidden" name="gateway_type_id" value="2">
 | 
				
			||||||
        <input type="hidden" name="gateway_response" id="gateway_response">
 | 
					        <input type="hidden" name="gateway_response" id="gateway_response">
 | 
				
			||||||
        <input type="hidden" name="is_default" id="is_default">
 | 
					        <input type="hidden" name="is_default" id="is_default">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
 | 
					@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_content')
 | 
					@section('gateway_content')
 | 
				
			||||||
    @if($token)
 | 
					    @if(count($tokens) > 0)
 | 
				
			||||||
        <div class="alert alert-failure mb-4" hidden id="errors"></div>
 | 
					        <div class="alert alert-failure mb-4" hidden id="errors"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @include('portal.ninja2020.gateways.includes.payment_details')
 | 
					        @include('portal.ninja2020.gateways.includes.payment_details')
 | 
				
			||||||
@ -10,20 +10,33 @@
 | 
				
			|||||||
            @csrf
 | 
					            @csrf
 | 
				
			||||||
            <input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
 | 
					            <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="payment_method_id" value="{{ $payment_method_id }}">
 | 
				
			||||||
            <input type="hidden" name="source" value="{{ $token->token }}">
 | 
					            <input type="hidden" name="source" value="">
 | 
				
			||||||
            <input type="hidden" name="amount" value="{{ $amount }}">
 | 
					            <input type="hidden" name="amount" value="{{ $amount }}">
 | 
				
			||||||
            <input type="hidden" name="currency" value="{{ $currency }}">
 | 
					            <input type="hidden" name="currency" value="{{ $currency }}">
 | 
				
			||||||
            <input type="hidden" name="customer" value="{{ $customer->id }}">
 | 
					            <input type="hidden" name="customer" value="{{ $customer->id }}">
 | 
				
			||||||
            <input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
 | 
					            <input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
 | 
					        @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
 | 
				
			||||||
            {{ ctrans('texts.ach') }} ({{ ctrans('texts.bank_transfer') }}) (**** {{ $token->meta->last4 }})
 | 
					            @if(count($tokens) > 0)
 | 
				
			||||||
 | 
					                @foreach($tokens as $token)
 | 
				
			||||||
 | 
					                    <label class="mr-4">
 | 
				
			||||||
 | 
					                        <input
 | 
				
			||||||
 | 
					                            type="radio"
 | 
				
			||||||
 | 
					                            data-token="{{ $token->token }}"
 | 
				
			||||||
 | 
					                            name="payment-type"
 | 
				
			||||||
 | 
					                            class="form-radio cursor-pointer toggle-payment-with-token"/>
 | 
				
			||||||
 | 
					                        <span class="ml-1 cursor-pointer">{{ ctrans('texts.bank_transfer') }} (*{{ $token->meta->last4 }})</span>
 | 
				
			||||||
 | 
					                    </label>
 | 
				
			||||||
 | 
					                @endforeach
 | 
				
			||||||
 | 
					            @endisset
 | 
				
			||||||
        @endcomponent
 | 
					        @endcomponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @else
 | 
					    @else
 | 
				
			||||||
        @component('portal.ninja2020.components.general.card-element-single', ['title' => 'ACH', 'show_title' => false])
 | 
					        @component('portal.ninja2020.components.general.card-element-single', ['title' => 'ACH', 'show_title' => false])
 | 
				
			||||||
            <span>{{ ctrans('texts.bank_account_not_linked') }}</span>
 | 
					            <span>{{ ctrans('texts.bank_account_not_linked') }}</span>
 | 
				
			||||||
            <a class="button button-link text-primary" href="{{ route('client.payment_methods.index') }}">{{ ctrans('texts.add_payment_method') }}</a>
 | 
					            <a class="button button-link text-primary"
 | 
				
			||||||
 | 
					               href="{{ route('client.payment_methods.index') }}">{{ ctrans('texts.add_payment_method') }}</a>
 | 
				
			||||||
        @endcomponent
 | 
					        @endcomponent
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,7 +45,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@push('footer')
 | 
					@push('footer')
 | 
				
			||||||
    <script>
 | 
					    <script>
 | 
				
			||||||
        document.getElementById('pay-now').addEventListener('click', function() {
 | 
					        Array
 | 
				
			||||||
 | 
					            .from(document.getElementsByClassName('toggle-payment-with-token'))
 | 
				
			||||||
 | 
					            .forEach((element) => element.addEventListener('click', (element) => {
 | 
				
			||||||
 | 
					                document.querySelector('input[name=source]').value = element.target.dataset.token;
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        document.getElementById('pay-now').addEventListener('click', function () {
 | 
				
			||||||
            document.getElementById('server-response').submit();
 | 
					            document.getElementById('server-response').submit();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@
 | 
				
			|||||||
@section('gateway_head')
 | 
					@section('gateway_head')
 | 
				
			||||||
    <meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
 | 
					    <meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
 | 
				
			||||||
    <meta name="stripe-secret" content="{{ $intent->client_secret }}">
 | 
					    <meta name="stripe-secret" content="{{ $intent->client_secret }}">
 | 
				
			||||||
    <meta name="stripe-token" content="{{ optional($token)->token }}">
 | 
					 | 
				
			||||||
    <meta name="only-authorization" content="">
 | 
					    <meta name="only-authorization" content="">
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -16,6 +15,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
 | 
					        <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="payment_method_id" value="{{ $payment_method_id }}">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <input type="hidden" name="token">
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="alert alert-failure mb-4" hidden id="errors"></div>
 | 
					    <div class="alert alert-failure mb-4" hidden id="errors"></div>
 | 
				
			||||||
@ -26,13 +27,33 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @include('portal.ninja2020.gateways.includes.payment_details')
 | 
					    @include('portal.ninja2020.gateways.includes.payment_details')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @if($token)
 | 
					    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
 | 
				
			||||||
        @include('portal.ninja2020.gateways.stripe.includes.pay_with_token')
 | 
					        @if(count($tokens) > 0)
 | 
				
			||||||
        @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token'])
 | 
					            @foreach($tokens as $token)
 | 
				
			||||||
    @else
 | 
					                <label class="mr-4">
 | 
				
			||||||
        @include('portal.ninja2020.gateways.stripe.includes.card_widget')
 | 
					                    <input
 | 
				
			||||||
        @include('portal.ninja2020.gateways.includes.pay_now')
 | 
					                        type="radio"
 | 
				
			||||||
    @endif
 | 
					                        data-token="{{ $token->token }}"
 | 
				
			||||||
 | 
					                        name="payment-type"
 | 
				
			||||||
 | 
					                        class="form-radio cursor-pointer toggle-payment-with-token"/>
 | 
				
			||||||
 | 
					                    <span class="ml-1 cursor-pointer">**** {{ optional($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.stripe.includes.card_widget')
 | 
				
			||||||
 | 
					    @include('portal.ninja2020.gateways.includes.pay_now')
 | 
				
			||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_footer')
 | 
					@section('gateway_footer')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,15 @@
 | 
				
			|||||||
@unless(isset($show_name) && $show_name == false)
 | 
					<div id="stripe--payment-container">
 | 
				
			||||||
    @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.name')])
 | 
					    @unless(isset($show_name) && $show_name == false)
 | 
				
			||||||
        <input class="input w-full" id="cardholder-name" type="text" placeholder="{{ ctrans('texts.name') }}">
 | 
					        @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.name')])
 | 
				
			||||||
    @endcomponent
 | 
					            <input class="input w-full" id="cardholder-name" type="text" placeholder="{{ ctrans('texts.name') }}">
 | 
				
			||||||
@endunless
 | 
					        @endcomponent
 | 
				
			||||||
 | 
					    @endunless
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@unless(isset($show_card_element) && $show_card_element == false)
 | 
					    @unless(isset($show_card_element) && $show_card_element == false)
 | 
				
			||||||
    @component('portal.ninja2020.components.general.card-element-single')
 | 
					        @component('portal.ninja2020.components.general.card-element-single')
 | 
				
			||||||
        <div id="card-element"></div>
 | 
					            <div id="card-element"></div>
 | 
				
			||||||
    @endcomponent
 | 
					        @endcomponent
 | 
				
			||||||
@endunless
 | 
					    @endunless
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@include('portal.ninja2020.gateways.includes.save_card')
 | 
					@include('portal.ninja2020.gateways.includes.save_card')
 | 
				
			||||||
							
								
								
									
										4
									
								
								webpack.mix.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								webpack.mix.js
									
									
									
									
										vendored
									
									
								
							@ -31,8 +31,8 @@ mix.js("resources/js/app.js", "public/js")
 | 
				
			|||||||
        "public/js/clients/payments/stripe-alipay.js"
 | 
					        "public/js/clients/payments/stripe-alipay.js"
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .js(
 | 
					    .js(
 | 
				
			||||||
        "resources/js/clients/payments/checkout.com.js",
 | 
					        "resources/js/clients/payments/checkout-credit-card.js",
 | 
				
			||||||
        "public/js/clients/payments/checkout.com.js"
 | 
					        "public/js/clients/payments/checkout-credit-card.js"
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .js(
 | 
					    .js(
 | 
				
			||||||
        "resources/js/clients/quotes/action-selectors.js",
 | 
					        "resources/js/clients/quotes/action-selectors.js",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user