Shopify orders/create webhook doesn't work - shopify

I'm trying to build a shopify app which users(customers) get automatically notified with e-mail, to achive this I'm trying to use orders/create webhook and test it by buying an item for free in the development store that my app is installed. But it doesn't work without giving an error in the console.
Here's my related code in the main index.js file;
import nodemailer from "nodemailer";
const smtpTransport = nodemailer.createTransport({
service: "gmail",
auth: {
user: "*****#gmail.com",
pass: "*******"
}
});
async function sendEmail(to, subject, html) {
const mailOptions = {
from: "*****#gmail.com",
to,
subject,
html
};
return smtpTransport.sendMail(mailOptions, (err, data)=>{
if(err) console.log(err)
else console.log("mail sent.")
});
}
import { Shopify } from "#shopify/shopify-api";
Shopify.Webhooks.Registry.addHandler("ORDERS_CREATE", {
path: "/api/webhooks",
webhookHandler: async (_topic, _shop, body) => {
// Parse the request body
const payload = JSON.parse(body);
// Get the email and name of the customer who made the purchase
const { email, customer: { first_name, last_name } } = payload.customer;
// Get the shipping address of the order
const {
address1,
address2,
city,
province,
country,
zip
} = payload.shipping_address;
// Compose the email message
const html = `
<p>Dear ${first_name} ${last_name},</p>
<p>Thank you for your purchase! Your order will be shipped to the following address:</p>
<p>
${address1}<br>
${address2 ? address2 + "<br>" : ""}
${city}, ${province} ${zip}<br>
${country}
</p>
<p>We hope you enjoy your purchase!</p>
`;
// Send the email
await sendEmail(
email,
"Thank you for your purchase!",
html
);
},
});
app.post("/api/webhooks", async (req, res) => {
try {
await Shopify.Webhooks.Registry.process(req, res);
console.log(`Webhook processed, returned status code 200`);
} catch (e) {
console.log(`Failed to process webhook: ${e.message}`);
if (!res.headersSent) {
res.status(500).send(e.message);
}
}
});

Related

"You must provide card details, token or paymentMethodId " error when calling confirmPayment function in stripe

I am using stripe in expo app, when calling confirmPayment method it gives error "You must provide card details, token or paymentMethodId " , here is my code
const handlePayPress = async () => {
//1.Gather the customer's billing information (e.g., email)
if (!cardDetails?.complete || !email) {
Alert.alert("Please enter Complete card details and Email");
return;
}
const billingDetails = {
email: email,
};
//2.Fetch the intent client secret from the backend
try {
const { clientSecret, error } = await fetchPaymentIntentClientSecret();
//2. confirm the payment
if (error) {
console.log("Unable to process payment");
} else {
const { paymentIntent, error } = await confirmPayment(clientSecret, {
type: "Card",
billingDetails: billingDetails,
});
if (error) {
alert(`Payment Confirmation Error ${error.message}`);
} else if (paymentIntent) {
alert("Payment Successful");
console.log("Payment successful ", paymentIntent);
}
}
} catch (e) {
console.log(e);
}
//3.Confirm the payment with the card details
};
You need to integrate either the Payment Sheet or the CardField to collect the payment details for the payment. Unlike the web/Stripe.js versions of this, the reference is not needed explicitly during the confirm call, but you do still need to have them in your app.

How to get gender and country details from google sign in?

In my react native project I've implemented Google signin functionality with firebase. When I signin using Google I get successfull response.
GoogleSignInMethods
export const googleConfigure = () =>
GoogleSignin.configure({
webClientId: FIREBASE_WEB_CLIENT_ID,
offlineAccess: true,
forceCodeForRefreshToken: true,
accountName: '',
});
export const signIn = async () => {
try {
await GoogleSignin.hasPlayServices();
const info = await GoogleSignin.signIn();
console.warn({userInfo: info});
// set user into state
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
console.log('Sign in cancelled');
} else if (error.code === statusCodes.IN_PROGRESS) {
console.log('Sign in inprogress');
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
console.log('Play service not available in this device');
} else {
console.log('Something error happened', error);
}
}
};
userInfo
user {
email: "data comes here"
familyName: "data comes here"
givenName: "data comes here"
id: "data comes here"
name: "data comes here"
photo: "data comes here"
}
for my project I need user's gender and country details. How can I retrieve it?
You cannot retrieve users' gender using google sign in for react-native as the package does not have such functionality implemented. What you can do is call another google api with the token you received from calling the sign in function
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
if (userInfo) {
const { accessToken } = await GoogleSignin.getTokens();
const response = await axios({
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
url: `https://people.googleapis.com/v1/people/${userInfo.user.id}?personFields=genders`,
});
console.log(response); // You should have the info here
}

undefined jwt token react/express

I am implementing a JWT authentication on a login/registration system. When there is a successful login/registration I am setting a user token in localStorage.
Problem is when I check my localStorage the user key is present but the value is undefined. I think the issue might be in my axios post or in my express file but I can't quite figure it out.
// my action creator
export function login(user, history) {
return async (dispatch) => {
axios.post('/api/login', { user })
.then(res => {
dispatch({ type: AUTHENTICATED });
localStorage.setItem('user', res.data.token);
history.push('/');
})
.catch((error) => {
dispatch({
type: AUTHENTICATION_ERROR,
payload: 'Invalid email or password'
});
});
};
}
The data is reaching my api correctly. The item is being set but the value res.data.token is undefined.. Below is my express file
// login.js (/api/login)
router.post('/', function(req, res) {
var email = req.body.user.email;
var password = req.body.user.password;
// TODO: create db file and import connection
var connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "dbname",
port: 3307
});
connection.connect(function(err) {
if(err) {
console.log(err);
} else {
connection.query("SELECT ID, Password FROM Users WHERE Email = ?", [email], function(err, result) {
if(err) {
console.log('Could not find account');
res.send(err);
} else {
var id = result[0].ID;
bcrypt.compare(password, result[0].Password, function(err, result) {
if(result) {
console.log(id);
res.json({ id });
} else {
console.log('Incorrect password');
}
});
}
});
}
});
});
Since the res.data.token in my action creator is returning undefined does that mean the response in my express file ( res.json([id]) ) is just returning defined?
You are not sending the response properly.
res.json([id]); Its just sending the array of id. That's why res.data.token is undefined. as data does not contain an object.
Send proper object like:
res.json({id});

Using async in express

Below is my rest API endpoint /signup. The problem I'm having now is that the endpoint does not stop after validateEmail. Even after it failed email form-validation and res.send() is done, the endpoint continues. So I'm keep getting the error 'Error: Can't set headers after they are sent.'. I would like to be able to finish the endpoint inside its functions like validateEmail , checkEmailInUse, makeUser, and so on.
router.post("/signup", async (req, res, next) => {
const { email, password } = req.body;
const users = req.app.get("users");
validateEmail(res, email);
await checkEmailInUse(res, users, email);
const user = await makeUser(res, users, email, password);
res.send({ message: "POST signup request OK", user });
});
function validateEmail(res, email) {
const isEmail = emailFilter.test(email);
if (!isEmail) {
res.status(400).send({
error: {
message: "Requested email is not email type",
type: "FormatValidation",
location: "validateEmail"
}
});
return;
}
}
async function checkEmailInUse(res, users, email) {
const query = { email };
try {
const user = await users.findOne(query);
if (user) {
res.send({ message: "The email is already used" });
}
} catch (err) {
res.status(400).send({
error: {
message: "Failed to find user",
type: "DatabaseError",
location: "checkEmailInUse"
}
});
return;
}
}
The code keeps going after a failed validate because you call:
validateEmail(res, email);
and then your code just keeps going. This is normal flow of control in Javascript. Your function keeps executing lines of code until you return in the function. The same issue is true for checkEmailInUse(). If you want to sometimes send the response inside those functions and be done, then you need a return value from those functions that you can check and then use if statements to determine whether your code should do more or not.
Following your style of sending the error response inside the validation functions (which is not how I would probably structure things), you could return values from those functions and test those return values in the request handler like this:
router.post("/signup", async (req, res, next) => {
const { email, password } = req.body;
const users = req.app.get("users");
if (validateEmail(res, email)) {
if (await checkEmailInUse(res, users, email)) {
const user = await makeUser(res, users, email, password);
res.send({ message: "POST signup request OK", user });
}
}
});
function validateEmail(res, email) {
const isEmail = emailFilter.test(email);
if (!isEmail) {
res.status(400).send({
error: {
message: "Requested email is not email type",
type: "FormatValidation",
location: "validateEmail"
}
});
return false;
}
return true;
}
async function checkEmailInUse(res, users, email) {
const query = { email };
try {
const user = await users.findOne(query);
if (user) {
res.send({ message: "The email is already used" });
return false;
} else {
return true;
}
} catch (err) {
res.status(400).send({
error: {
message: "Failed to find user",
type: "DatabaseError",
location: "checkEmailInUse"
}
});
return false;
}
}
}
But, I think you might find this is simpler if you get rid of the local functions because then when you send a response, you can just directly return from the main function and be done. Here's how that could look:
router.post("/signup", async (req, res, next) => {
function err(res, message, type, location) {
res.status(400).send({error: {message, type, location}});
}
const { email, password } = req.body;
if (!emailFilter.test(email)) {
err(res, "Requested email is not email type", "FormatValidation", "validateEmail");
return;
}
const users = req.app.get("users");
try {
const user = await users.findOne({email});
if (user) {
res.send({ message: "The email is already used" });
return;
}
} catch(e) {
err(res, "Failed to find user", "DatabaseError", "checkEmailInUse");
return;
}
try {
const user = await makeUser(res, users, email, password);
res.send({ message: "POST signup request OK", user });
} catch(e) {
err(res, "Failed to make user", "DatabaseError", "makeUser");
}
}

mocha : testing a signup process with email-verification framework

I try to test my signup router post function which use email-verification framework,but mocha shows me the error message below :
TypeError: Cannot read property 'findOne' of null
This my code :
function that invoke the error:
nev.resendVerificationEmail(email,(err,userExist) =>{
if(err){
return res.status(404).send('Error : resending verification email failed');}
if(userExist){
res.json({message : 'An email has been sent to you , again. Please check it to verify your account'});
}else{
res.json({message : 'Your verification code has expired . Please sign up again'});
and this is the implementation of resendVerificationEmail function
var resendVerificationEmail = function(email, callback) {
var query = {};
query[options.emailFieldName] = email;
options.tempUserModel.findOne(query, function(err, tempUser) { //this the error handler I guess
if (err) {
return callback(err, null);
}
// user found (i.e. user re-requested verification email before expiration)
if (tempUser) {
// generate new user token
tempUser[options.URLFieldName] = randtoken.generate(options.URLLength);
tempUser.save(function(err) {
if (err) {
return callback(err, null);
}
sendVerificationEmail(getNestedValue(tempUser, options.emailFieldName), tempUser[options.URLFieldName], function(err) {
if (err) {
return callback(err, null);
}
return callback(null, true);
});
});
} else {
return callback(null, false);
}
});
};
and this is my spec
describe(' SignUp : /POST Test with fake client request : ', () => {
let req, res, statusCode, sendData,user;
beforeEach((done) => {
SignupModal.remove({}, (err) => {
done();
});
user = {
firstName : 'ben',
secondName : 'wissem',
username : 'wiss',
email : 'xy#zt.sq',
password : 'wissem'
};
res = {
json: function (code, data) {
statusCode = code;
sendData = data;
}
};
});
it('should send 200 code', () => {
chai.request(server)
.post('/user/signup')
.send(user)
.end((err, res) => {
res.statusCode.should.equal(200);
});
});
Can any one help me please ?!
if you want to end-to-end test email verification try EmailE2E.com, it's a free API that let's you send and receive emails from randomly generated inboxes. It's perfect for Firebase, Amazon Cognito, or other OAuth providers that use email verification codes during sign up. It also has a Javascript client you can use with Mocha.