How to integrate paypal Payment Button Vuejs3 Composition API (setup function) - vue.js

I'm trying to integrate PayPal buttons with my Vuejs3 project using Composition API (setup ) but all what i get is errors i try to integrate it without using setup and its working fine i leave the working script down
the esseu is i couldent pass data from data to methodes
<script>
import { inject, onMounted, ref } from "vue";
export default {
data() {
return {
loaded: false,
paidFor: false,
product: {
price: 15.22,
description: "leg lamp from that one movie",
img: "./assets/lamp.jpg",
},
};
},
setup() {
const store = inject("store");
console.log(store.state.prodects_in_cart);
return { store };
},methods:{
setLoaded: function() {
this.loaded = true;
paypal_sdk
.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.data;
this.paidFor = true;
console.log(order);
},
onError: err => {
console.log(err);
}
})
.render(this.$refs.paypal);
}
},
mounted: function() {
const script = document.createElement("script");
script.setAttribute('data-namespace',"paypal_sdk");
script.src ="https://www.paypal.com/sdk/js?client-id=Here i pute my Client Id";
script.addEventListener("load", this.setLoaded);
document.body.appendChild(script);
},
};
</script>
the error i get when i use setup() is
The error image
my script using setup()
setup() {
const store = inject("store");
const paypal = ref(null);
let loaded = ref(false);
let paidFor = ref(false);
const product = {
price: 15.22,
description: "leg lamp from that one movie",
img: "./assets/lamp.jpg",
};
onMounted: {
const script = document.createElement("script");
script.setAttribute("data-namespace", "paypal_sdk");
script.src =
"https://www.paypal.com/sdk/js?client-id=AXDJPmFjXpXm9HMXK4uZcW3l9XrCL36AxEeWBa4rhV2-xFcVYJrGKvNowY-xf2PitTSkStVNjabZaihe";
script.addEventListener("load", ()=>{
loaded = true;
console.log('hello adil');
paypal_sdk
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: 'this is product description',
amount: {
currency_code: "USD",
value: 120.00,
},
},
],
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
this.data;
this.paidFor = true;
console.log(order);
},
onError: (err) => {
console.log(err);
},
})
.render(paypal);
});
document.body.appendChild(script);
}
return { store ,paypal};
}

paypal is a ref. You're currently passing to paypal_sdk the ref itself and not the inner value, which would be the template ref's element. To fix this, pass the ref's .value.
Your onMounted code is not properly invoked, as it must be passed a callback.
import { onMounted, ref } from 'vue'
export default {
setup() {
const paypal = ref(null)
onMounted(/* 2 */ () => {
const script = document.createElement('script')
//...
script.addEventListener('load', () => {
paypal_sdk
.Buttons(/*...*/)
.render(paypal.value) /* 1 */
})
})
return {
paypal
}
}
}

The reason why you are getting that error is because you are using option Api onMounted life cycle hook, instead of doing that use the vue 3 life cycle hooks for onMounted.
First you will have to import it from vue like this.
<script>
import {onMounted} from 'vue'
then you are going to use it like this.
return it as a call back function
onMounted(() => {
//all your code should placed inside here and it will work
})
</script>

Here is my answer using the paypal-js npm package
<template>
<div ref="paypalBtn"></div>
</template>
<script>
import { onMounted, ref } from 'vue';
import { loadScript } from '#paypal/paypal-js';
const paypalBtn = ref(null);
onMounted(async () => {
let paypal;
try {
paypal = await loadScript({
'client-id': 'you_client_id_goes_here',
});
} catch (error) {
console.error('failed to load the PayPal JS SDK script', error);
}
if (paypal) {
try {
await paypal.Buttons().render(paypalBtn.value);
} catch (error) {
console.error('failed to render the PayPal Buttons', error);
}
}
});
</script>

Related

Vue 3 / Pinia: how to handle error, loading and data properly

Using Fetch, and not axios, I wanna receive data in my components like that:
const userStore = useAuthStore();
const { user, error, loading } = storeToRefs(userStore);
userStore.getMe();
But I don't know how to do that.
I want that to have directly the error, data and loading state in one line because I think it's better.
But I don't wanna declare a loading like this in the store:
export const useAuthStore = defineStore({
id: "auth",
state: () => ({
user: {} as User,
loading: false,
}),
Because if I call another method related to this store (User), it will be the same Loading state. So this loading state (even the error state) will be in conflict.
If I would use Javascript and no Typescript, I would definetly replace this.user like this when fetching or when error (in the store):
async getMe() {
this.user = { loading: true };
try {
return await AuthService.getMe();
} catch (error) {
if (error) {
his.user = { error };
}
}
},
Because it's TypeScript, I can't replace the "user" state like that as I have set an Interface.
All I want, is to return a Data, Erros, Loading related to an unique action (not related to a state).
auth store:
import { defineStore } from "pinia";
import AuthService from "#/api/modules/auth";
interface User {
email: string;
first_name: string;
last_name: string;
force_password_change: boolean;
groups: string[];
has_2fa_enabled: boolean;
is_staff: boolean;
lang: string;
last_password_change: string;
permissions: string[];
session_expiry_date: string;
uid: string;
}
export const useAuthStore = defineStore({
id: "auth",
state: () => ({
user: {} as User,
loading: false,
}),
actions: {
async getMe() {
// this.user = { loading: true };
try {
return await AuthService.getMe();
} catch (error) {
if (error) {
// this.user = { error };
}
}
},
},
});
service:
import { Api } from "../apiSettings";
class AuthService {
async getMe(): Promise<any> {
return await Api.get("api/auth/me/");
}
}
export default new AuthService();
App.vue:
<script setup lang="ts">
import { useAuthStore } from "#/stores";
import { storeToRefs } from "pinia";
const userStore = useAuthStore();
const { user } = storeToRefs(userStore);
userStore.getMe();
console.log(user.value);
</script>
You can define a separate object for loading and error states and return it from the store's action along with the actual data. You can then use object destructuring to extract the loading, error, and data states in your component. Here's an example:
Auth store:
import { defineStore } from "pinia";
import AuthService from "#/api/modules/auth";
interface RequestState {
loading: boolean;
error: Error | null;
}
export const useAuthStore = defineStore({
id: "auth",
state: () => ({
user: {} as User,
}),
actions: {
async getMe(): Promise<{ data: User; requestState: RequestState }> {
const requestState: RequestState = {
loading: true,
error: null,
};
try {
const data = await AuthService.getMe();
requestState.loading = false;
return { data, requestState };
} catch (error) {
requestState.loading = false;
requestState.error = error;
return { data: {} as User, requestState };
}
},
},
});
Component:
<script setup lang="ts">
import { useAuthStore } from "#/stores";
import { storeToRefs } from "pinia";
const userStore = useAuthStore();
const { user, requestState } = storeToRefs(userStore);
// Call the action to get user data
userStore.getMe();
console.log(user.value); // {} - initial value of user
console.log(requestState.value); // { loading: true, error: null }
// Use object destructuring to get the actual user data, loading and error states
const { data, loading, error } = requestState.value;
if (loading) {
// Render a loading state
} else if (error) {
// Render an error state
} else {
// Render the user data
console.log(data);
}
</script>

TypeError when rendering property of Vue-test setData object

I'm running into a strange situation and can't figure out why. Basically in my HTML, if I render 'actor[0]', the test runs fine and the console log shows the entire 'actor' object present in setData
However, if I try to access a property of the 'actor' object, like actor[0].firstname, the test throws a TypeError-can't-read-property-of-undefined.
The weird part is console logging 'wrapper.vm.actor[0].firstname' works fine so it doesn't seem like an async issue.
myapps.spec.js
import { mount } from "#vue/test-utils";
import MyApps from "#/pages/myapps.vue";
import Vuetify from "vuetify";
describe("Testing Myapps", () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify();
});
it("Checks SideBarComponent is rendered", async () => {
const wrapper = mount(MyApps, {
// localVue,
vuetify,
mocks: {
$vuetify: { breakpoint: {} }
},
stubs: {
SideBarComponent: true,
FooterComponent: true
}
});
await wrapper.setData({
actor: [
{
firstname: "bob",
lastname: "bob",
group: "actors"
}
]
});
console.log(wrapper.html()); // TypeError: Cannot read property 'first name' of undefined
console.log(wrapper.vm.actor[0].firstname); // "bob" if I set the template back to actor[0] so the test runs
});
});
myapps.vue
<template>
<div>
<v-app>
<v-col cols="3">
<v-btn
text
#click="getAcceptedApplications"
elevation="0"
block
>Accepted {{actor[0].firstname}}</v-btn>
</v-col>
</v-app>
</div>
</template>
<script>
export default {
async asyncData({ params, $axios, store }) {
try {
const body = store.getters.loggedInUser.id;
const [applications, actor] = await Promise.all([
$axios.$get(`/api/v1/apps/`, {
params: {
user: body
}
}),
$axios.$get(`/api/v1/actors/`, {
params: {
user: body
}
})
]);
return { applications, actor };
if (applications.length == 0) {
const hasApps = false;
}
} catch (error) {
if (error.response.status === 403) {
const hasPermission = false;
console.log(hasPermission, "perm");
console.error(error);
return { hasPermission };
}
}
},
data() {
return {
actor: []
};
}
};
</script>
Try not to use setData method, pass data while mounting the component like that:
const wrapper = mount(MyApps, {
vuetify,
mocks: {
$vuetify: { breakpoint: {} }
},
stubs: {
SideBarComponent: true,
FooterComponent: true
}
data: () => ({
actor: [
{
firstname: "bob",
lastname: "bob",
group: "actors"
}
]
})
})

TypeError: Cannot read property 'push' of undefined using Vue router and Vue 3

Guys I have a problem in Vue 3 and Vite, I'm trying to use the router but I have a problem because they didn't find it.
props: ["usuario", "senha"],
setup(props) {
const mensagemErro = ref("");
async function login() {
try {
const { data } = await services.post("auth/login", {
userName: props.usuario,
password: props.senha,
});
console.log(data);
const { token, userName } = data;
window.localStorage.setItem("token", token);
this.$router.push("/home");
} catch (error) {
console.log(error);
}
}
return {
login,
mensagemErro,
};
},
You should use the composable function useRouter to get the instance router :
import {useRouter} from 'vue-router'
export default{
props: ["usuario", "senha"],
setup(props) {
const mensagemErro = ref("");
const router=userRouter();
async function login() {
try {
const { data } = await services.post("auth/login", {
userName: props.usuario,
password: props.senha,
});
console.log(data);
const { token, userName } = data;
window.localStorage.setItem("token", token);
router.push("/home");
} catch (error) {
console.log(error);
}
}
return {
login,
mensagemErro,
};
},

How to add a module getters in composition api?

I am using vue 2, installed composition api. How can I add Getters?
Usually:
computed: {
...mapGetters("Auth", ["isLogged"])}
..........................................................................
setup() {
const title_app = ref("Name App");
const logout = () => {
store
.dispatch("Auth/logout")
.then(() => {
router.push({ name: "About" });
})
.catch((err) => {
console.log(err);
});
};
return {
title_app,
logout,
};
},
You can do something like this:
import { computed } from 'vue'
export default {
setup (props, { root }) {
const isLogged = computed(() => root.$store.Auth.getters.isLogged)
return {
isLogged
}
}
}

Why can't I pass my user_name value into my component? (Auth)

I am trying to pass the name of the user after authentication into a Vue component, but I get a name: undefined value after load.
Here is my AuthService.js:
//config details taken from OAUTH JS doc: https://github.com/andreassolberg/jso
import { JSO, Fetcher } from 'jso';
const client = new JSO({
providerID: '<my-provider>',
default_lifetime: 1800,
client_id: '<my-client-id>',
redirect_uri: 'http://localhost:8080/',
authorization:'<my-auth-server>/oauth/authorize'
//scopes: { request: ['https://www.googleapis.com/auth/userinfo.profile'] }
});
export default {
getProfile() {
// JSO plugin provides a simple wrapper around the fetch API to handle headers
let f = new Fetcher(client);
let url = 'https://www.googleapis.com/auth/userinfo.profile';
f.fetch(url, {})
.then(data => {
return data.json();
})
.then(data => {
return data.user_name;
})
.catch(err => {
console.error('Error from fetcher', err);
});
}
};
Then, in my single file component named MainNav, I have:
import AuthService from "#/AuthService";
export default {
name: "MainNav",
data() {
return {
name: ""
};
},
created() {
this.name = AuthService.getProfile();
}
};
</script>
Anyone have any tips on how I can get the user_name value from the AuthService to my component? I will then need to then display the name in my nav template. Doing a console.log test works fine, just can't return it to my SFC. Also, the JSO library is here: https://github.com/andreassolberg/jso#fetching-data-from-a-oauth-protected-endpoint
Because getProfile returns nothing (undefined). I see you use es6 then you can use async functions
//config details taken from OAUTH JS doc: https://github.com/andreassolberg/jso
import { JSO, Fetcher } from 'jso';
const client = new JSO({
providerID: '<my-provider>',
default_lifetime: 1800,
client_id: '<my-client-id>',
redirect_uri: 'http://localhost:8080/',
authorization:'<my-auth-server>/oauth/authorize'
//scopes: { request: ['https://www.googleapis.com/auth/userinfo.profile'] }
});
export default {
getProfile() {
// JSO plugin provides a simple wrapper around the fetch API to handle headers
let f = new Fetcher(client);
let url = 'https://www.googleapis.com/auth/userinfo.profile';
return f.fetch(url, {}) // return promise here
.then(data => {
return data.json();
})
.then(data => {
return data.user_name;
})
.catch(err => {
console.error('Error from fetcher', err);
});
}
};
And
import AuthService from "#/AuthService";
export default {
name: "MainNav",
data() {
return {
name: ""
};
},
async created() {
try {
this.name = await AuthService.getProfile();
} catch(error) {
// handle
}
}
};
</script>
Or without async (add one more then)
import AuthService from "#/AuthService";
export default {
name: "MainNav",
data() {
return {
name: ""
};
},
created() {
AuthService.getProfile().then((userName) => this.name = userName))
.catch((error) => { /* handle */ })
}
};
</script>