How to get Paypal Checkout to work with Vue.JS 3 - javascript

I am trying to make Paypal Checkout work with Vue.JS 3 (using the loader)
Right now I got this far:
setPaypal() {
const script = document.createElement('script');
script.src = 'https://www.paypal.com/sdk/js?client-id=AdlvqGHWrrwVpGXreZuf5VHBXjIeUWGLHBJmDzbI44Ib2w1MMN7P-UJysCHFb_W7BWTvpz0ofji0SiYB';
document.body.appendChild(script);
script.addEventListener('load', this.setLoaded())
}
This function is called in the mounted(): and inserts the script tag in my page.
setLoaded() {
window.paypal.Buttons({
createOrder: (actions) => {
return actions.order.create({
purchase_units: [
{
description: this.prestation.courtedescription,
amount: {
currency_code: "EUR",
value: this.total
}
}
]
});
},
onApprove: async () => {
this.paidFor = true;
this.loading = false;
},
onError: err => {
console.log(err)
}
})
.render(this.$refs.paypal)
}
This is the setLoaded() function called when the script is loaded.
Well obviously window.paypal is undefined
I tried using the official docs and same shit, they ask you to
const PayPalButton = paypal.Buttons.driver("vue", window.Vue);
But hey, paypal is not defined
For reference, this is the official docs
Add the SDK <script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID"></script>
Vue integration
<div id="container">
<app></app>
</div>
<script>
const PayPalButton = paypal.Buttons.driver("vue", window.Vue);
Vue.component("app", {
template: `
<paypal-buttons :on-approve="onApprove" :create-order="createOrder" />
`,
components: {
"paypal-buttons": PayPalButton,
},
computed: {
createOrder: function () {
return (data, actions) => {
return actions.order.create({
purchase_units: [
{
amount: {
value: "10",
},
},
],
});
}
},
onApprove: function () {
return (data, actions) => {
return actions.order.capture();
}
},
},
});
const vm = new Vue({
el: "#container",
});
</script>

Instead of using this in your setPaypal() function
script.addEventListener('load', this.setLoaded())
Use this
script.addEventListener('load', () => this.setLoaded())

Related

How to create Paypal Button in Vue 3 script setup?

The paypal developer docs show how to implement the Paypal Button into Vue.js but I don't understand the code. At this point I'm not even sure if this is vue 2 or vue 3 or angular?? code.
1: import script in parent blade:
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID"></script>
2: use button in script tag of component?
paypal.Buttons.driver("vue", window.Vue);
3: this is where I get lost, use this in app.js??:
#ng.core.Component({
selector: 'my-app',
template:
<div id="app">
<paypal-buttons [props]="{
createOrder: createOrder,
onApprove: onApprove
}"></paypal-buttons>
</div>
,
})
class AppComponent {
createOrder(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01'
}
}]
});
}
onApprove(data, actions) {
return actions.order.capture();
}
}
#ng.core.NgModule({
imports: [
ng.platformBrowser.BrowserModule,
paypal.Buttons.driver('angular2', ng.core)
],
declarations: [
AppComponent
],
bootstrap: [
AppComponent
]
})
class AppModule {}
ng.platformBrowserDynamic
.platformBrowserDynamic()
.bootstrapModule(AppModule);
Could it be that this isn't even vue code but angular code?
4: and put this in the vue component??:
<div id="container">
<app></app>
</div>
<script>
const PayPalButton = paypal.Buttons.driver("vue", window.Vue);
Vue.component("app", {
// The style prop for the PayPal button should be passed in as `style-object` or `styleObject` to avoid conflict with Vue's `style` reserved prop.
template: `
<paypal-buttons :on-approve="onApprove" :create-order="createOrder" :on-shipping-change="onShippingChange" :on-error="onError" :style-object="style" />
`,
components: {
"paypal-buttons": PayPalButton,
},
computed: {
createOrder: function () {
return (data, actions) => {
return actions.order.create({
purchase_units: [
{
amount: {
value: "10",
},
},
],
});
}
},
onApprove: function () {
return (data, actions) => {
return actions.order.capture();
}
},
onShippingChange: function () {
return (data, actions) => {
if (data.shipping_address.country_code !== 'US') {
return actions.reject();
}
return actions.resolve();
}
},
onError: function () {
return (err) => {
console.error(err);
window.location.href = "/your-error-page-here";
}
},
style: function () {
return {
shape: 'pill',
color: 'gold',
layout: 'horizontal',
label: 'paypal',
tagline: false
}
},
},
});
const vm = new Vue({
el: "#container",
});
</script>
My question is, how would I create a simple paypal button in Vue 3's script setup? The paypal cdn is imported in the parent blade file.
Something like:
<script setup>
import {onMounted} from "vue";
onMounted(() => {
//create component from -> paypal.Buttons.driver("vue", window.Vue);
})
</script>
<template>
<div id="checkout" class="checkout">
<paypal-buttons></paypal-buttons>
</div>
</template>
I would recommend the following implementation:
Install official paypal-js npm package: npm install #paypal/paypal-js
Then you can write your PaypalButtons Component as follows:
<script setup>
import {Inertia} from '#inertiajs/inertia';
import {loadScript} from '#paypal/paypal-js';
import {onMounted} from 'vue';
const props = defineProps({
// Some kind of reference if you like
reference: Object
});
onMounted(async() => {
try {
const paypal = await loadScript({
'client-id': <your-paypal-client-id>
});
await paypal.Buttons({
createOrder: function() {
function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
// e.g reference.price
value: '<your-price>',
},
}],
});
},
onApprove: function(data) {
return actions.order.capture().then(function(orderData) {
// Successful capture!
// e.g. Inertia.post(route('order.update', reference.orderId)
});
},
style: {
// Adapt to your needs
layout: 'vertical',
color: 'gold',
shape: 'rect',
label: 'paypal',
},
// The following is optional and you can
// limit the buttons to those you want to
// provide
fundingSource: paypal.FUNDING.PAYPAL,
}).render('#paypal-button-container');
} catch (error) {
// Add proper error handling
console.error(error);
}
});
</script>
<template>
<div id="paypal-button-container"></div>
</template>
Now use it as:
<PaypalButtons :reference="reference" />
Paypal documentation is a huge mess. The server integration seems to work like so:
if you're using laravel as backend, import this into app.blade.php/welcome.blade.php file:
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID"></script>
and then your vue component can look like this:
<script setup>
import {onMounted} from "vue";
onMounted(() => {
paypal.Buttons({
// Call your server to set up the transaction
createOrder: function(data, actions) {
return fetch('/demo/checkout/api/paypal/order/create/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
return orderData.id;
});
},
// Call your server to finalize the transaction
onApprove: function(data, actions) {
return fetch('/demo/checkout/api/paypal/order/' + data.orderID + '/capture/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you
// This example reads a v2/checkout/orders capture response, propagated from the server
// You could use a different API or structure for your 'orderData'
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
return alert(msg); // Show a failure message (try to avoid alerts in production environments)
}
// Successful capture! For demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction '+ transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
// Replace the above to show a success message within this page, e.g.
// const element = document.getElementById('paypal-button-container');
// element.innerHTML = '';
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
});
}
}).render('#paypal-button-container');
})
</script>
<template>
<div id="checkout" class="checkout">
<div id="paypal-button-container"></div>
</div>
</template>
Which payment methods are displayed is determined automatically and depends on your IP (???). You can hide payment methods by manipulating the script import like so:
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID&disable-funding=card,giropay,sepa,sofort"></script>

How to dispatch event on Push receive using Workbox?

I have a VueJS component where I listen for pushMessageEvent :
<template>
<div>
<VueBotUI
:options="options"
:is-open="isOpen"
:bot-typing="botTyping"
:input-disable="inputDisable"
:messages="messages"
#msg-send="onSend"
></VueBotUI>
</div>
</template>
<script>
export default {
components: {
VueBotUI
},
data: function () {
return {
options: {botTitle: 'test',},
user: {msg: null,},
msgRegex: /^[a-zA-Z ]+$/,
messages: []
}
},
mounted() {
document.addEventListener('pushMsgEvent', this.printPush);
},
beforeDestroy () {
document.removeEventListener('pushMsgEvent', this.printPush);
},
methods: {
printPush (e) {
console.log(e)
console.log("------------------")
console.log(e.detail)
},
}
}
</script>
And I want to fire this pushMessageEvent when I get a Push event in my service-worker:
/* eslint-disable */
importScripts(
"https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"
);
// Load all ENVERYWHERE enviroment variables
importScripts('./env-vars.js')
const PushMsgEvent = new CustomEvent('pushMsgEvent', { detail: null });
workbox.core.skipWaiting();
workbox.core.clientsClaim();
self.__WB_MANIFEST;
// Listen to push event
self.addEventListener("push", (event) => {
if (event.data) {
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
PushMsgEvent.detail = event.data.text();
//document.dispatchEvent(PushMsgEvent);
}
});
workbox.precaching.precacheAndRoute([]);
but I can't use document.dispatchEvent since I get document is not defined, is it a workaround to fire this event and catch it in my component ?
I have read about workbox-window but I can't figure out how to fire my event from the service-worker in order to catch it in the component
My solution:
service-worker.js:
// Listen to push event
self.addEventListener("push", (event) => {
if (event.data) {
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage(JSON.stringify(event.data.text()));
});
});
}
});
my component.vue :
mounted() {
navigator.serviceWorker.addEventListener('message', event => {
let msg = event.data;
this.printPush(msg);
});
},
beforeDestroy () {
navigator.serviceWorker.removeEventListener('message', event => {
this.printPush(event);
});
},
methods: {
printPush (e) {
console.log(e)
}
}

Vue Can Paypal Sandbox Accounts Make Real Transactions on New Website

I finished my app and I am planning to host it online. Can my clients use sandbox accounts to make real transactions? This is my first time developing an app and making it have real online transactions using Paypal. Does it have something to do with coding or I have to change a setting in Paypal itself?
Payments.vue
<template>
<div>
<div ref="paypal"></div>
</div>
</template>
mounted()
{
const script = document.createElement("script");
script.src =
"https://www.paypal.com/sdk/js?client-MY-CLIENT-ID";
script.addEventListener("load", this.setLoaded);
document.body.appendChild(script);
}
methods: {
setLoaded: function () {
this.loaded = true;
window.paypal
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
// description: this.product.description,
amount: {
currency_code: "USD",
value: this.product.price,
},
},
],
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
this.$q.notify({
message: "Transaction Successful!",
color: "green",
actions: [
{
label: "Dismiss",
color: "white",
handler: () => {
/* ... */
},
},
],
});
let dateObj = new Date();
let month = dateObj.getMonth();
let day = dateObj.getDate();
let year = dateObj.getFullYear();
let output = month + "" + day + "" + year;
this.$store
.dispatch("SAVE_ENTRY", {
username: this.username,
password: this.password,
confirmPass: this.confirmPass,
access_id: output + this.newAccData,
chosenSchoolId: this.chosenSchoolId,
})
.then((res) => {
if (res.data === "Success1") {
this.$q.notify({
message:
"Transaction Successful! Please complete your registration process inside the website.",
color: "green",
actions: [
{
label: "Dismiss",
color: "white",
handler: () => {
/* ... */
},
},
],
});
}
});
console.log(order);
},
onError: (err) => {
console.log(err);
},
})
.render(this.$refs.paypal);
},
}
I believe there's a sandbox environment, on https://sandbox.paypal.com/ . You have to change the API endpoint to something like that. The sandbox uses separate accounts that can be made in the developer environment on PayPal API pages.
You should be able to set up sandbox accounts here:
https://developer.paypal.com/developer/accounts/
However I dont think this is supposed to be a Javascript/Vue question, you'd want your PayPal API calls to take place on the server side as much as possible.

Why show this error,",Uncaught ReferenceError: c3 is not defined"

I have created one calculator in that calculator i have use pie chart to show data dynamically,but chart throw error.The code is given below,
custom.min.js in this js file i have usen c3 chart,
var chart = c3.generate({
data: {
columns: [
["Interest Payable", 3900519],
["Principal", 3e6]
],
type: "pie"
},
axis: { x: { label: "Sepal.Width" }, y: { label: "Petal.Width" } },
legend: { show: !1 },
tooltip: { format: { title: function(t) { return "" }, value: function(t, e, n) { return t } }, contents: tooltip_contents }
});
I have usen this plugin to done this : c3.min.js,c3_renderers.min.js,d3.min.js,d3.v3.min.js,pivot.min.js.
I have downloaded this plugin and put inside the asset folder and I am call this js file by scriptloaderService, and this scriptloaderService imported inside component.
DynamicScriptLoaderServiceService this is scriptloaderServise code.
import { Injectable } from '#angular/core';
interface Scripts {
name: string;
src: string;
}
export const ScriptStore: Scripts[] = [
{ name: 'd3.v3.min.js', src: './assets/Scripts/d3.v3.min.js' },
{ name: 'd3.min.js', src: './assets/Scripts/d3.min.js'},
{ name: 'c3.min.js', src: './assets/Scripts/c3.min.js' },
{ name: 'custom.min.js', src: './assets/Scripts/custom.min.js' },
];
declare var document: any;
#Injectable()
export class DynamicScriptLoaderServiceService {
private scripts: any = {};
constructor() {
ScriptStore.forEach((script: any) => {
this.scripts[script.name] = {
loaded: false,
src: script.src
};
});
}
load(...scripts: string[]) {
const promises: any[] = [];
scripts.forEach((script) => promises.push(this.loadScript(script)));
return Promise.all(promises);
}
loadScript(name: string) {
return new Promise((resolve, reject) => {
if (!this.scripts[name].loaded) {
//load script
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = this.scripts[name].src;
if (script.readyState) { //IE
script.onreadystatechange = () => {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
this.scripts[name].loaded = true;
resolve({script: name, loaded: true, status: 'Loaded'});
}
};
} else { //Others
script.onload = () => {
this.scripts[name].loaded = true;
resolve({script: name, loaded: true, status: 'Loaded'});
};
}
script.onerror = (error: any) => resolve({script: name, loaded: false, status: 'Loaded'});
document.getElementsByTagName('head')[0].appendChild(script);
} else {
resolve({ script: name, loaded: true, status: 'Already Loaded' });
}
});
}
}
CalcyComponent this is component in which service and JS file was called to show pie chart.
import { Component, OnInit, AfterViewInit } from '#angular/core';
import { DynamicScriptLoaderServiceService } from '../../../ScriptLoaderService/dynamic-script-loader-service.service';
#Component({
selector: 'app-calcy',
templateUrl: './calcy.component.html',
styleUrls: ['./calcy.component.css']
})
export class CalcyComponent implements OnInit, AfterViewInit {
constructor(private dynamicScriptLoader:DynamicScriptLoaderServiceService) { }
ngOnInit() {
this.loadScripts();
}
ngAfterViewInit() { }
loadScripts() {
// You can load multiple scripts by just providing the key as argument into load method of the service
this.dynamicScriptLoader.load(
'd3.v3.min.js','d3.min.js','c3.min.js','custom.min.js',).then(data => {
// Script Loaded Successfully
}).catch(error => console.log(error));
}
}
calcy.component.html this is template where chart should be show, I have given small code of that.
<div class="abc">
<div id="chart" class="chart" style="width: 320px; height: 285px;"></div>
<div style="display: none;">
<div title="" style="width: 454px;
height: 319px;">
<!-- <div id="Graph"></div> -->
</div>
</div>
</div>
Main error shown by browser is this. "Uncaught ReferenceError: c3 is not defined"
any one can help in this code????

Vue JS Custom directive data binding

I'm creating a custom directive to make a form submit via ajax - however I can't seem to get validation errors to bind to the Vue instance.
I am adding my directive here:
<form action="{{ route('user.settings.update.security', [$user->uuid]) }}" method="POST"
enctype="multipart/form-data" v-ajax errors="formErrors.security" data="formData.security">
My directive looks like:
Vue.directive('ajax', {
twoWay: true,
params: ['errors', 'data'],
bind: function () {
this.el.addEventListener('submit', this.onSubmit.bind(this));
},
update: function (value) {
},
onSubmit: function (e) {
e.preventDefault();
this.vm
.$http[this.getRequestType()](this.el.action, vm[this.params.data])
.then(this.onComplete.bind(this))
.catch(this.onError.bind(this));
},
onComplete: function () {
swal({
title: 'Success!',
text: this.params.success,
type: 'success',
confirmButtonText: 'Back'
});
},
onError: function (response) {
swal({
title: 'Error!',
text: response.data.message,
type: 'error',
confirmButtonText: 'Back'
});
this.set(this.vm, this.params.errors, response.data);
},
getRequestType: function () {
var method = this.el.querySelector('input[name="_method"]');
return (method ? method.value : this.el.method).toLowerCase();
},
});
And my VUE instance looks like:
var vm = new Vue({
el: '#settings',
data: function () {
return {
active: 'profile',
updatedSettings: getSharedData('updated'),
formData: {
security: {
current_password: ''
}
},
formErrors: {
security: []
},
}
},
methods: {
setActive: function (name) {
this.active = name;
},
isActive: function (name) {
if (this.active == name) {
return true;
} else {
return false;
}
},
hasError: function (item, array, sub) {
if (this[array][sub][item]) {
return true;
} else {
return false;
}
},
isInArray: function (value, array) {
return array.indexOf(value) > -1;
},
showNotification: function () {
if (this.updatedSettings) {
$.iGrowl({
title: 'Updated',
message: 'Your settings have been updated successfully.',
type: 'success',
});
}
},
}
});
However, when I output the data, the value for formErrors.security is empty
Any idea why?
The issue is with the this.set(/* ... */) line. this.set doesn't work the same as Vue.set or vm.$set.
this.set attempts to set the path that you passed to the directive: v-my-directive="a.b.c". So running this.set('foo') will attempt to set $vm.a.b.c to 'foo'.
What you want to do is this:
(ES6)
this.params.errors.splice(0, this.params.errors.length, ...response.data)
(Vanilla JS)
this.params.errors.splice.apply(this.params.errors, [0,this.params.errors.length].concat(response.data))
That will update whatever Object is tied to the errors param on the DOM Node. Make sure that you do v-bind:errors="formErrors.security" or :errors="formErrors.security".

Categories

Resources