mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Braintree: New payment flow (#77)
* pass livewirePaymentView & processPaymentView thru base driver * add paymentData to the interface * braintree
This commit is contained in:
parent
fc80287429
commit
49d96e57bb
@ -20,11 +20,12 @@ use App\Models\Payment;
|
|||||||
use App\Models\PaymentType;
|
use App\Models\PaymentType;
|
||||||
use App\Models\SystemLog;
|
use App\Models\SystemLog;
|
||||||
use App\PaymentDrivers\BraintreePaymentDriver;
|
use App\PaymentDrivers\BraintreePaymentDriver;
|
||||||
|
use App\PaymentDrivers\Common\LivewireMethodInterface;
|
||||||
use App\PaymentDrivers\Common\MethodInterface;
|
use App\PaymentDrivers\Common\MethodInterface;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class ACH implements MethodInterface
|
class ACH implements MethodInterface, LivewireMethodInterface
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
||||||
@ -97,10 +98,7 @@ class ACH implements MethodInterface
|
|||||||
|
|
||||||
public function paymentView(array $data)
|
public function paymentView(array $data)
|
||||||
{
|
{
|
||||||
$data['gateway'] = $this->braintree;
|
$data = $this->paymentData($data);
|
||||||
$data['currency'] = $this->braintree->client->getCurrencyCode();
|
|
||||||
$data['payment_method_id'] = GatewayType::BANK_TRANSFER;
|
|
||||||
$data['amount'] = $this->braintree->payment_hash->data->amount_with_fee;
|
|
||||||
|
|
||||||
return render('gateways.braintree.ach.pay', $data);
|
return render('gateways.braintree.ach.pay', $data);
|
||||||
}
|
}
|
||||||
@ -181,4 +179,24 @@ class ACH implements MethodInterface
|
|||||||
|
|
||||||
throw new PaymentFailed($response->transaction->additionalProcessorResponse, $response->transaction->processorResponseCode);
|
throw new PaymentFailed($response->transaction->additionalProcessorResponse, $response->transaction->processorResponseCode);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function livewirePaymentView(array $data): string
|
||||||
|
{
|
||||||
|
return 'gateways.braintree.ach.pay_livewire';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function paymentData(array $data): array
|
||||||
|
{
|
||||||
|
$data['gateway'] = $this->braintree;
|
||||||
|
$data['currency'] = $this->braintree->client->getCurrencyCode();
|
||||||
|
$data['payment_method_id'] = GatewayType::BANK_TRANSFER;
|
||||||
|
$data['amount'] = $this->braintree->payment_hash->data->amount_with_fee;
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,9 @@ use App\Models\Payment;
|
|||||||
use App\Models\PaymentType;
|
use App\Models\PaymentType;
|
||||||
use App\Models\SystemLog;
|
use App\Models\SystemLog;
|
||||||
use App\PaymentDrivers\BraintreePaymentDriver;
|
use App\PaymentDrivers\BraintreePaymentDriver;
|
||||||
|
use App\PaymentDrivers\Common\LivewireMethodInterface;
|
||||||
|
|
||||||
class CreditCard
|
class CreditCard implements LivewireMethodInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var BraintreePaymentDriver
|
* @var BraintreePaymentDriver
|
||||||
@ -76,17 +77,7 @@ class CreditCard
|
|||||||
|
|
||||||
public function paymentView(array $data)
|
public function paymentView(array $data)
|
||||||
{
|
{
|
||||||
$data['gateway'] = $this->braintree;
|
$data = $this->paymentData($data);
|
||||||
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
|
|
||||||
$data['threeds'] = $this->threeDParameters($data);
|
|
||||||
$data['threeds_enable'] = $this->braintree->company_gateway->getConfigField('threeds') ? "true" : "false";
|
|
||||||
|
|
||||||
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
|
|
||||||
/** https://developer.paypal.com/braintree/docs/reference/request/client-token/generate#merchant_account_id */
|
|
||||||
$data['client_token'] = $this->braintree->gateway->clientToken()->generate([ //@phpstan-ignore-line
|
|
||||||
'merchantAccountId' => $this->braintree->company_gateway->getConfigField('merchantAccountId'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return render('gateways.braintree.credit_card.pay', $data);
|
return render('gateways.braintree.credit_card.pay', $data);
|
||||||
}
|
}
|
||||||
@ -278,4 +269,32 @@ class CreditCard
|
|||||||
return $this->braintree->processInternallyFailedPayment($this->braintree, $e);
|
return $this->braintree->processInternallyFailedPayment($this->braintree, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function livewirePaymentView(array $data): string
|
||||||
|
{
|
||||||
|
return 'gateways.braintree.credit_card.pay_livewire';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function paymentData(array $data): array
|
||||||
|
{
|
||||||
|
$data['gateway'] = $this->braintree;
|
||||||
|
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
|
||||||
|
$data['threeds'] = $this->threeDParameters($data);
|
||||||
|
$data['threeds_enable'] = $this->braintree->company_gateway->getConfigField('threeds') ? "true" : "false";
|
||||||
|
|
||||||
|
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
|
||||||
|
/** https://developer.paypal.com/braintree/docs/reference/request/client-token/generate#merchant_account_id */
|
||||||
|
$data['client_token'] = $this->braintree->gateway->clientToken()->generate([
|
||||||
|
'merchantAccountId' => $this->braintree->company_gateway->getConfigField('merchantAccountId'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,9 @@ use App\Models\Payment;
|
|||||||
use App\Models\PaymentType;
|
use App\Models\PaymentType;
|
||||||
use App\Models\SystemLog;
|
use App\Models\SystemLog;
|
||||||
use App\PaymentDrivers\BraintreePaymentDriver;
|
use App\PaymentDrivers\BraintreePaymentDriver;
|
||||||
|
use App\PaymentDrivers\Common\LivewireMethodInterface;
|
||||||
|
|
||||||
class PayPal
|
class PayPal implements LivewireMethodInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var BraintreePaymentDriver
|
* @var BraintreePaymentDriver
|
||||||
@ -45,8 +46,7 @@ class PayPal
|
|||||||
*/
|
*/
|
||||||
public function paymentView(array $data)
|
public function paymentView(array $data)
|
||||||
{
|
{
|
||||||
$data['gateway'] = $this->braintree;
|
$data = $this->paymentData($data);
|
||||||
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
|
|
||||||
|
|
||||||
return render('gateways.braintree.paypal.pay', $data);
|
return render('gateways.braintree.paypal.pay', $data);
|
||||||
}
|
}
|
||||||
@ -188,4 +188,23 @@ class PayPal
|
|||||||
return $this->braintree->processInternallyFailedPayment($this->braintree, $e);
|
return $this->braintree->processInternallyFailedPayment($this->braintree, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function livewirePaymentView(array $data): string
|
||||||
|
{
|
||||||
|
return 'gateways.braintree.paypal.pay_livewire';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function paymentData(array $data): array
|
||||||
|
{
|
||||||
|
$data['gateway'] = $this->braintree;
|
||||||
|
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/class i{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(e,n){window.braintree.dataCollector.create({client:n,paypal:!0},function(t,a){t||(document.querySelector("input[name=client-data]").value=a.deviceData)})})}mountBraintreePaymentWidget(){window.braintree.dropin.create({authorization:document.querySelector("meta[name=client-token]").content,container:"#dropin-container",threeDSecure:document.querySelector("input[name=threeds_enable]").value.toLowerCase()==="true"},this.handleCallback)}handleCallback(e,n){if(e){console.error(e);return}let t=document.getElementById("pay-now");params=JSON.parse(document.querySelector("input[name=threeds]").value),t.addEventListener("click",()=>{n.requestPaymentMethod({threeDSecure:{challengeRequested:!0,amount:params.amount,email:params.email,billingAddress:{givenName:params.billingAddress.givenName,surname:params.billingAddress.surname,phoneNumber:params.billingAddress.phoneNumber,streetAddress:params.billingAddress.streetAddress,extendedAddress:params.billingAddress.extendedAddress,locality:params.billingAddress.locality,region:params.billingAddress.region,postalCode:params.billingAddress.postalCode,countryCodeAlpha2:params.billingAddress.countryCodeAlpha2}}},function(a,r){if(a){console.log(a),dropin.clearSelectedPaymentMethod(),alert("There was a problem verifying this card, please contact your merchant");return}if(document.querySelector("input[name=threeds_enable]").value==="true"&&!r.liabilityShifted){console.log("Liability did not shift",r),alert("There was a problem verifying this card, please contact your merchant");return}t.disabled=!0,t.querySelector("svg").classList.remove("hidden"),t.querySelector("span").classList.add("hidden"),document.querySelector("input[name=gateway_response]").value=JSON.stringify(r);let d=document.querySelector('input[name="token-billing-checkbox"]:checked');d&&(document.querySelector('input[name="store_card"]').value=d.value),document.getElementById("server-response").submit()})})}handle(){this.initBraintreeDataCollector(),this.mountBraintreePaymentWidget(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(n=>n.addEventListener("click",t=>{document.getElementById("dropin-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",n=>{document.getElementById("dropin-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",document.getElementById("pay-now-with-token").classList.add("hidden"),document.getElementById("pay-now").classList.remove("hidden")});let e=document.getElementById("pay-now-with-token");e.addEventListener("click",n=>{e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}}new i().handle();
|
|
9
public/build/assets/braintree-credit-card-2ba935f9.js
vendored
Normal file
9
public/build/assets/braintree-credit-card-2ba935f9.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import{i as e,w as n}from"./wait-8f4ae121.js";/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/function t(){console.log("boot",document.querySelector("meta[name=client-token]"))}e()?t():n("#braintree-credit-card-payment","meta[name=client-token]").then(()=>t());
|
@ -1,4 +1,4 @@
|
|||||||
/**
|
import{i as l,w as s}from"./wait-8f4ae121.js";/**
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
*
|
*
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
@ -6,4 +6,4 @@
|
|||||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
*
|
*
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
*/class a{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(e,t){window.braintree.dataCollector.create({client:t,paypal:!0},function(n,o){n||(document.querySelector("input[name=client-data]").value=o.deviceData)})})}static getPaymentDetails(){return{flow:"vault"}}static handleErrorMessage(e){let t=document.getElementById("errors");t.innerText=e,t.hidden=!1}handlePaymentWithToken(){Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",n=>{document.getElementById("paypal-button").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")}));let e=document.getElementById("pay-now-with-token");e.addEventListener("click",t=>{e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}handle(){this.initBraintreeDataCollector(),this.handlePaymentWithToken(),braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content}).then(function(e){return braintree.paypalCheckout.create({client:e})}).then(function(e){return e.loadPayPalSDK({vault:!0}).then(function(t){return paypal.Buttons({fundingSource:paypal.FUNDING.PAYPAL,createBillingAgreement:function(){return t.createPayment(a.getPaymentDetails())},onApprove:function(n,o){return t.tokenizePayment(n).then(function(i){let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.querySelector("input[name=gateway_response]").value=JSON.stringify(i),document.getElementById("server-response").submit()})},onCancel:function(n){},onError:function(n){console.log(n.message),a.handleErrorMessage(n.message)}}).render("#paypal-button")})}).catch(function(e){console.log(e.message),a.handleErrorMessage(e.message)})}}new a().handle();
|
*/class a{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(e,t){window.braintree.dataCollector.create({client:t,paypal:!0},function(n,o){n||(document.querySelector("input[name=client-data]").value=o.deviceData)})})}static getPaymentDetails(){return{flow:"vault"}}static handleErrorMessage(e){let t=document.getElementById("errors");t.innerText=e,t.hidden=!1}handlePaymentWithToken(){Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",n=>{document.getElementById("paypal-button").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")}));let e=document.getElementById("pay-now-with-token");e.addEventListener("click",t=>{e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}handle(){this.initBraintreeDataCollector(),this.handlePaymentWithToken(),braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content}).then(function(e){return braintree.paypalCheckout.create({client:e})}).then(function(e){return e.loadPayPalSDK({vault:!0}).then(function(t){return paypal.Buttons({fundingSource:paypal.FUNDING.PAYPAL,createBillingAgreement:function(){return t.createPayment(a.getPaymentDetails())},onApprove:function(n,o){return t.tokenizePayment(n).then(function(c){let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.querySelector("input[name=gateway_response]").value=JSON.stringify(c),document.getElementById("server-response").submit()})},onCancel:function(n){},onError:function(n){console.log(n.message),a.handleErrorMessage(n.message)}}).render("#paypal-button")})}).catch(function(e){console.log(e.message),a.handleErrorMessage(e.message)})}}function i(){new a().handle()}l()?i():s("#braintree-paypal-payment").then(()=>i());
|
@ -75,12 +75,18 @@
|
|||||||
"src": "resources/js/clients/payments/authorize-credit-card-payment.js"
|
"src": "resources/js/clients/payments/authorize-credit-card-payment.js"
|
||||||
},
|
},
|
||||||
"resources/js/clients/payments/braintree-credit-card.js": {
|
"resources/js/clients/payments/braintree-credit-card.js": {
|
||||||
"file": "assets/braintree-credit-card-1c3f3108.js",
|
"file": "assets/braintree-credit-card-2ba935f9.js",
|
||||||
|
"imports": [
|
||||||
|
"_wait-8f4ae121.js"
|
||||||
|
],
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/clients/payments/braintree-credit-card.js"
|
"src": "resources/js/clients/payments/braintree-credit-card.js"
|
||||||
},
|
},
|
||||||
"resources/js/clients/payments/braintree-paypal.js": {
|
"resources/js/clients/payments/braintree-paypal.js": {
|
||||||
"file": "assets/braintree-paypal-45391805.js",
|
"file": "assets/braintree-paypal-16e2f577.js",
|
||||||
|
"imports": [
|
||||||
|
"_wait-8f4ae121.js"
|
||||||
|
],
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/clients/payments/braintree-paypal.js"
|
"src": "resources/js/clients/payments/braintree-paypal.js"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
* @license https://www.elastic.co/licensing/elastic-license
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { wait, instant } from '../wait';
|
||||||
|
|
||||||
class BraintreeCreditCard {
|
class BraintreeCreditCard {
|
||||||
initBraintreeDataCollector() {
|
initBraintreeDataCollector() {
|
||||||
@ -43,8 +44,7 @@ class BraintreeCreditCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let payNow = document.getElementById('pay-now');
|
let payNow = document.getElementById('pay-now');
|
||||||
|
let params = JSON.parse(document.querySelector('input[name=threeds]').value);
|
||||||
params = JSON.parse(document.querySelector('input[name=threeds]').value);
|
|
||||||
|
|
||||||
payNow.addEventListener('click', () => {
|
payNow.addEventListener('click', () => {
|
||||||
dropinInstance.requestPaymentMethod({
|
dropinInstance.requestPaymentMethod({
|
||||||
@ -138,4 +138,8 @@ class BraintreeCreditCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new BraintreeCreditCard().handle();
|
function boot() {
|
||||||
|
new BraintreeCreditCard().handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
instant() ? boot() : wait('#braintree-credit-card-payment', 'meta[name=client-token]').then(() => boot());
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
* @license https://www.elastic.co/licensing/elastic-license
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { wait, instant } from '../wait';
|
||||||
|
|
||||||
class BraintreePayPal {
|
class BraintreePayPal {
|
||||||
initBraintreeDataCollector() {
|
initBraintreeDataCollector() {
|
||||||
window.braintree.client.create({
|
window.braintree.client.create({
|
||||||
@ -119,4 +121,8 @@ class BraintreePayPal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new BraintreePayPal().handle();
|
function boot() {
|
||||||
|
new BraintreePayPal().handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
instant() ? boot() : wait('#braintree-paypal-payment').then(() => boot());
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
@section('gateway_head')
|
@section('gateway_head')
|
||||||
<meta name="client-token" content="{{ $client_token ?? '' }}"/>
|
<meta name="client-token" content="{{ $client_token ?? '' }}"/>
|
||||||
|
<meta name="instant-payment" content="yes" />
|
||||||
|
|
||||||
<script src='https://js.braintreegateway.com/web/dropin/1.33.4/js/dropin.min.js'></script>
|
<script src='https://js.braintreegateway.com/web/dropin/1.33.4/js/dropin.min.js'></script>
|
||||||
{{-- <script src="https://js.braintreegateway.com/web/3.76.2/js/client.min.js"></script> --}}
|
{{-- <script src="https://js.braintreegateway.com/web/3.76.2/js/client.min.js"></script> --}}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
@section('gateway_head')
|
@section('gateway_head')
|
||||||
<meta name="client-token" content="{{ $client_token ?? '' }}"/>
|
<meta name="client-token" content="{{ $client_token ?? '' }}"/>
|
||||||
|
<meta name="instant-payment" content="yes" />
|
||||||
|
|
||||||
<script src="https://js.braintreegateway.com/web/3.76.2/js/client.min.js"></script>
|
<script src="https://js.braintreegateway.com/web/3.76.2/js/client.min.js"></script>
|
||||||
<script src="https://js.braintreegateway.com/web/3.76.2/js/paypal-checkout.min.js"></script>
|
<script src="https://js.braintreegateway.com/web/3.76.2/js/paypal-checkout.min.js"></script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user