Unable to integrate Stripe checkout into Vue JS app - javascript

I am trying to redirect to stripe checkout using a click event that calls the payStripe function
<button #click="payStripe" class="btn btn-primary" type="button">Place Order</button>
I have imported stripe into my Vue component like so;
import { loadStripe } from "#stripe/stripe-js";
const stripe = loadStripe('MY-KEY');
I am using Firebase cloud functions and axois to fetch the session and store this to a data property, this works fine.
But, the payStripe method, when called, gives the following error;
Error in v-on handler: "TypeError: stripe.redirectToCheckout is not a function"
Here is the function i am using which, from all accounts is similar to the Stripe API docs;
data() {
return {
sessionId: null,
}
},
methods: {
//This function sends us to the stripe checkout
payStripe() {
stripe.redirectToCheckout({
sessionId: this.sessionId
})
.then(function(result) {
console.log(result);
}).catch(function(error) {
console.log(error);
});
}
},
I had original issues with babel-core, so i updated to #babel/core to get rid of rest operator issues when compling code, but faced with this new issue. Any advice would be great. Thank you

According to the documentation here, loadStripe returns a promise that you must wait to resolve.
Try something like
import { loadStripe } from "#stripe/stripe-js"
const stripeInit = loadStripe('MY-KEY') // returns a promise
and when you want to use stripe
methods: {
//This function sends us to the stripe checkout
payStripe() {
// wait for the promise to resolve first
stripeInit.then(stripe => {
stripe.redirectToCheckout({
sessionId: this.sessionId
}).then(function(result) {
console.log(result);
}).catch(function(error) {
console.error(error);
});
})
}
}

Using Laravel Homestead as a vm, I turned ssl: true in Homestead.yaml, and entered https://site.test manually in the URL. I didn't get a valid https certificate, but the vue-stripe components load and it works

Related

Uncaught (in promise) TypeError: (intermediate value).findOne is not a function

When trying to call getStocks from a Vue component I get the error stated above.
smileCalc:
import User from "../models/user.js";
let userID = "62e6d96a51186be0ad2864f9";
let userStocks;
async function getUserStocks() {
await User.findOne({ _id: userID }, (err, user) => {
if (user != null || user != undefined) {
userStocks = user.stocks;
}
}).clone();
};
export async function getStocks() {
await getUserStocks();
return userStocks;
}
Vue Component:
<script>
import { getStocks } from "../../../backend/scripts/smileCalc.js";
export default {
methods: {
getStocks,
},
};
</script>
<template>
<h1>User Stocks: {{ getStocks() }}</h1>
</template>
I know that the Schema is defined, exported, and imported correctly because I do not get the error when I execute the script and it works normally. The solution that I have found for TypeErrors similar to mine is to add a semicolon to the end of the function, but that did not fix my issue. I also tried using findById rather than findOne and got the same error.
Update: The issue I was having was trying to access my mongoose schema on the client side which was hosted on port 3000 while my backend was being hosted on 4000. I needed to implement an HTTP request to interact between the two.

NMI gateway Updating Subscription

So I'm implementing a payment system with NMI gateway api. I've got everything working except for my update subscription function. To give a break down of how the NMI api works, you put in the required variables for the process you want done, in my case it's updating a subscription on a plan. So I've put the variables and used the POST directly to NMI to update the subscription with the variables that were passed in. I'm getting a response of ok and says it went through and updated subscription, however when I go to my merchant portal to make sure it worked, nothing has changed. To have recurring payments you first set up a plans and then users subscribe to the plans. I've got three different plans that the users should be able to Upgrade or Downgrade to.
Here is my utility function that will update the subscription on a plan
export const updateSubNmi = async ({
subscription_id,
plan_amount,
plan_payments,
day_frequency,
}) => {
const data = {
recurring: 'update_subscription',
subscription_id: subscription_id,
plan_amount: plan_amount,
plan_payments: plan_payments,
day_frequency: day_frequency,
//These are the required variables to read and react with NMI gateway
merchant_id: process.env.NMI_MERCHANT_ID,
merchant_password: process.env.NMI_MERCHANT_PASSWORD,
tokenization_key: process.env.NMI_TOKENIZATION_KEY,
security_key: process.env.NMI_API_KEY,
}
console.log('data to pass in: ', data)
const options = {
method: 'POST',
url: 'https://secure.nmi.com/api/transact.php',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
form: data,
}
return new Promise((resolve, reject) => {
request(options, (err, response, body) => {
if (err) {
reject(err)
} else {
resolve(body, response)
console.log(body)
// console.log(response.body)
}
})
})
}
export default updateSubNmi
Then I've set up a controller that awaits the above function then runs.
import expressAsyncHandler from 'express-async-handler'
import updateSubNmi from '../../Utils/nmiGatewayFunctions/nmiSubUpdate.js'
import Subscription from '../../models/Subscription.js'
export const updateNmiSub = expressAsyncHandler(async (req, res) => {
try {
const { subscription } = req.body
const sub = await Subscription.findById(subscription._id)
if (!sub) {
return res.status(400).json({
message: 'Subscription not found',
})
}
try {
if (sub.plan_id === '') {
console.log(`Free Subscription: ${sub.plan_id}`)
}
} catch (error) {
console.log(error)
return res.status(500).json({
success: false,
message: err.message,
})
}
await updateSubNmi(req.body)
res.status(200).json({ message: `process finished` })
} catch (error) {
console.error(error)
res.status(500).json({ message: `Server Error: ${error.message}` })
}
})
export default updateNmiSub
This is my Postman request to send the body to the gateway. It responds with 200 OK and gives my my custom message I've set up as "process finished"
{
"subscription": {
"_id": "6256f0ab7417d91f8e080aec"
},
"subscription_id": "7146266977",
"plan_amount": "49.99",
"plan_payments": "0",
"day_frequency": "30"
}
Once my route hits it gives me a response from gateway of Subscription Updated as you see below, however when I go the my merchant portal to ensure the subscription was updated to a different plan it's not showing changes.
response=1&responsetext=Subscription Updated&authcode=&transactionid=7146266977&avsresponse=&cvvresponse=&orderid=&type=&response_code=100&subscription_id=7146266977
There is obviously something I'm missing and have been stuck on this for a few days. Any help would be greatly appreciated.
Once a subscription is on a defined plan, the subscription’s plan cannot be updated. You’ll notice that documentation describes the plan_amount, plan_payments, day_frequency variables under the header titled:
Update a Custom Subscription's Plan Details
If the subscription you're attempting to edit uses a plan that's been saved in your account then these values are being ignored. Custom subscriptions are subscriptions that have their own plan that isn’t shared with other subscriptions.
To accommodate your use case, I’d suggest one of two approaches:
When a customer wants to switch plans, you cancel the existing subscription and create a new subscription on the new plan.
Or:
Use custom subscriptions instead of subscriptions on a named plan. To do this in the merchant portal, leave the “Save plan for future use” unchecked when creating subscriptions. If you’re using the Payments API to create subscriptions, use variables described under the “Adding a Custom Subscription” header in the documentation to create subscriptions.

how to return Stripe's createToken tokenId and use it in another function in vuex

so I call Stripe.card.createToken in my api.js file and want to return the token this function generates so I can use it in my vuex, how can I do that?
// api.js
export const stripeToken = async ({ cardInfo }) => {
const { data } = await Stripe.card.createToken({
cardInfo,
});
return data;
};
So I want to use it in my actions in vuex like this. I did this and it doesn't work, it returns undefined:
//vuex
import { stripeToken } from '#src/store/api';
async stripeToken({ dispatch }, { cardInfo }) {
const { data } = await stripeToken({ cardInfo });
console.log('tokenId: ', data.tokenId);
},
I'm not familiar with vuex, but the Stripe.card.createToken method takes two parameters: a JavaScript object containing credit card data entered by the user, and a callback function to handle the response. You can learn more about it in the Stripe documentation here.
Here's how you could display the ID of a token with Stripe.card.createToken:
Stripe.card.createToken(cardInfo, (status, response) => {
if (response.error) {
console.log(response.error);
} else {
console.log(response.id);
}
});
Note that Stripe.card.createToken is an old method from Stripe.js v2 that is now deprecated, so I would recommend upgrading to Stripe.js v3 if possible.

How to use google recaptcha in nuxt?

I am using nuxt and would like to use this library: https://github.com/nuxt-community/recaptcha-module. But I don't understand how to verify that the user has passed the check. The example doesn't tell me too much (https://github.com/nuxt-community/recaptcha-module/blob/master/example/v3/pages/index.vue). Could someone show me how to do it correctly?
This example is only half the story. It returns a Recaptcha V3 token on the client-side.
This must then be sent to the serverside and verified using your secret key.
This is done by sending a post to this URL:
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${token}`;
You do not want to allow this secret key on the client side.
To achieve this in Nuxt, assuming version 2.13+, you can utilise privateRuntimeConfig in your nuxt config.
This will allow you to link a .env file to be injected only on the server side.
For this use case, a privateRuntimeConfig like this should suffice:
privateRuntimeConfig: {
secretKey: process.env.GOOGLE_SECRET
}
Once you have done this, you will be able to access these variables as part of this.$config within your Nuxt application - in this case this.$config.secretKey when calling the Recaptcha verify endpoint.
For more information check out the Nuxt blog
Use https://github.com/nuxt-community/recaptcha-module, in your nuxt.config.js
modules: [
'#nuxtjs/recaptcha',
],
recaptcha: {
hideBadge: true,
siteKey: "ABC...", // Better would be from 'process.env.API_KEY' and with '.env' file
version: 2, // Or 3
},
Keep in mind that modules, that's not the same as buildModules (sometimes it might confuse due to similar naming).
Here is a working implementation for ReCaptcha V3:
package.json
"dependencies": {
"#nuxtjs/axios": "^5.13.6",
"#nuxtjs/recaptcha": "^1.0.4",
"h3": "^0.3.9",
},
Note the h3 version. I wasn't able to get it working with a newer version of that because the library is converted to EJS/mjs and TypeScript, which conflicts with Nuxt. Transpiling h3 didn't fix it. It may work with Nuxt V3+.
nuxt.config.js
modules: [
['#nuxtjs/recaptcha', {
siteKey: process.env.RECAPTCHA_SITE_KEY,
version: 3,
}],
],
serverMiddleware: [
{ path: '/api/check-token', handler: '~/middleware/recaptcha' },
],
middleware/recaptcha.js
import { useBody } from 'h3';
import axios from 'axios';
export default async (req, res) => {
res.setHeader('Content-Type', 'application/json');
try {
const { token } = await useBody(req);
if (!token) {
res.end(
JSON.stringify({
success: false,
message: 'Invalid token'
})
);
return;
}
axios.get(`https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`).then((answer) => {
if (answer.status) {
res.end(
JSON.stringify({
success: true,
message: 'Token verified'
})
);
} else {
res.end(
JSON.stringify({
success: false,
message: 'Invalid token'
})
);
}
});
} catch (e) {
console.log('ReCaptcha error:', e);
res.end(
JSON.stringify({
success: false,
message: 'Internal error'
})
);
}
};
.env
RECAPTCHA_SITE_KEY=gosei478htosvei478tvoei478tvge
RECAPTCHA_SECRET_KEY=ios47eos487t6es4897gtv6es487
index.vue
async mounted() {
try {
await this.$recaptcha.init();
} catch (err) {
throw new Error(`index# Problem initializing ReCaptcha: ${err}.`);
}
},
beforeDestroy() {
this.$recaptcha.destroy();
},
methods: {
async submitContactForm() {
try {
const token = await this.$recaptcha.execute('contact')
const formData = {
email: this.contactForm.email,
firstname: name.firstName,
lastname: name.lastName,
phone: this.contactForm.phone,
band_name: this.contactForm.band_name,
initial_message: this.contactForm.message,
}
// note: use POST request
const recaptcha = await this.$axios.post('/api/check-token', { token });
console.log('recaptcha', recaptcha.data);
if (recaptcha.data.success) {
const result = await this.$axios.post(process.env.CONTACT_FORM_API, formData);
// cleanup logic
} else {
// handle error case
}
} catch (err) {
// handle errors
}
},
},
You can read more here: https://www.npmjs.com/package/#nuxtjs/recaptcha
Note the section where it says
Server Side
When you send data + token to the server, you should verify the token on the server side to make sure it does not requested from a bot. You can find out how to verify token on the server side by looking at the server middleware inside v2 example. (The server side is same for both versions)
The above server-side middleware comes from there. It is important to use the version of h3 that I suggest because you need it to access useBody(req). I tried for several hours to find another way to read the request body but it proved too difficult. Your results may vary in a newer version of Nuxt. I suggest trying the newest version of h3 and if that fails with errors when building the application, try the older version.
It is critically important to not expose the ReCaptcha secret key, and this solution keeps it secret in the server-side.
A more optimal solution might be to use your actual server and make an endpoint for validating ReCaptcha tokens. This above solution allows you to do it purely client-side, assuming you are using SSR.

How to change email in firebase auth?

I am trying to change/update a user's email address using :
firebase.auth().changeEmail({oldEmail, newEmail, password}, cb)
But I am getting ...changeEmail is not a function error. I found the reference here from the old firebase docu.
So how to I do it in the 3.x version? Because I cant find a reference in the new documentation.
You're looking for the updateEmail() method on the firebase.User object: https://firebase.google.com/docs/reference/js/firebase.User#updateEmail
Since this is on the user object, your user will already have to be signed in. Hence it only requires the password.
Simple usage:
firebase.auth()
.signInWithEmailAndPassword('you#domain.example', 'correcthorsebatterystaple')
.then(function(userCredential) {
userCredential.user.updateEmail('newyou#domain.example')
})
If someone is looking for updating a user's email via Firebase Admin, it's documented over here and can be performed with:
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com"
});
FOR FIREBASE V9 (modular) USERS:
The accepted answer will not apply to you. Instead, you can do this, i.e., import { updateEmail } and use it like any other import. The following code was copy/pasted directly from the fb docs at https://firebase.google.com/docs/auth/web/manage-users
Happy coding!
import { getAuth, updateEmail } from "firebase/auth";
const auth = getAuth();
updateEmail(auth.currentUser, "user#example.com").then(() => {
// Email updated!
// ...
}).catch((error) => {
// An error occurred
// ...
});
You can do this directly with AngularFire2, you just need to add "currentUser" to your path.
this.af.auth.currentUser.updateEmail(email)
.then(() => {
...
});
You will also need to reauthenticate the login prior to calling this as Firebase requires a fresh authentication to perform certain account functions such as deleting the account, changing the email or the password.
For the project I just implemented this on, I just included the login as part of the change password/email forms and then called "signInWithEmailAndPassword" just prior to the "updateEmail" call.
To update the password just do the following:
this.af.auth.currentUser.updatePassword(password)
.then(() => {
...
});
updateEmail needs to happen right after sign in due to email being a security sensitive info
Example for Kotlin
// need to sign user in immediately before updating the email
auth.signInWithEmailAndPassword("currentEmail","currentPassword")
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success now update email
auth.currentUser!!.updateEmail(newEmail)
.addOnCompleteListener{ task ->
if (task.isSuccessful) {
// email update completed
}else{
// email update failed
}
}
} else {
// sign in failed
}
}
async updateEmail() {
const auth = firebase.auth();
try {
const usercred = await auth.currentUser.updateEmail(this.email.value);
console.log('Email updated!!')
} catch(err) {
console.log(err)
}
}
You can use this to update email with Firebase.
Firebase v9:
const changeEmail = (userInput) => {
const { newEmail, pass } = userInput
signInWithEmailAndPassword(auth, oldEmail, pass)
.then(cred => updateEmail(cred.user, newEmail))
}

Categories