Custom member signup with email verification.

hey @Ahmad , i’ve been trying to implement this feature but can’t figure out. https://www.wix.com/corvid/example/members-area

I’ve done all the necessary changes, but can’t figure out how to send the approval code to the user’s email signing up? I even went through the documentation, but still don’t understand. It would be very helpful if you can help me in a personalized way.
Also, how should i define the contactID field in the collection. Becasue it shows an exclamatory symbol and asks to define it.
Thanks in advance!

Hi Roshan :raised_hand_with_fingers_splayed:

Can you show me the code and your website URL.

yeah, here is the website URL: https://rzgancubing.wixsite.com/member
I’m trying to make it work on the free version first, then i’ll implement it in my main website!

  • Approve page code
import wixLocation from 'wix-location';
import wixUsers from 'wix-users';
import { continueRegister, continueLogIn, validateEmailToken } from 'backend/signIn.jsw';

$w.onReady(function () {
 let emailToken = wixLocation.query.tokenRegister;
    register(emailToken);
})

async function register(emailToken) {
 if (await validateEmailToken(emailToken)) {
        $w('#valid').expand();
        $w('#notValid').collapse();
        continueRegister(emailToken)
            .then(async (result) => {
 if (result.approved) {
 await wixUsers.applySessionToken(result.sessionToken);
                    wixLocation.to("/home");
                } else {
                    console.log("Not approved!");
                }
            }).catch(error => {
                console.log("error register");
            })
    } else {
        $w('#valid').collapse();
        $w('#notValid').expand();
    }
}
  • Admin page code
import wixData from 'wix-data';
import { addFile, getAllMembers } from 'backend/data.jsw';

let memberSelectedEmail = "";

$w.onReady(async function () {

    $w('#membersTable').rows = await getAllMembers()
 
    $w('#membersTable').onRowSelect(event => {
        memberSelectedEmail = event.rowData.loginEmail;
        $w("#uploadButton").enable();
    })

    $w('#uploadButton').onChange(async event => {
 try {
 const uploadedFile = await $w("#uploadButton").startUpload();
 const obj = {
 'file': uploadedFile.url,
 'loginEmail': memberSelectedEmail,
            }
 const result = await addFile(obj);
 if (result.insert) {
                $w('#successText').expand();
            } else {
                console.log('Failed to upload');
            }
        } catch (err) {
            console.log("err" + err);
        }

    })
});
  • Register page code
import wixUsers from 'wix-users';
import wixCrm from 'wix-crm';
import wixWindow from 'wix-window';
import { doRegistration, doLogIn } from 'backend/signIn.jsw';

$w.onReady(function () {
    wixUsers.currentUser.loggedIn ? wixWindow.lightbox.close() : init();
});

function init() {
    onClickLogIn();
    onClickSignIn();
    onClickBack()
}

function onClickBack() {
    $w('#backText').onClick(() => {
        presentLogInForm();
    })
}

function onClickLogIn() {
    $w('#emailLogIn').onChange(() => {
        $w('#loginButton').enable();
    })
    $w('#loginButton').onClick(async () => {
        $w('#loginButton').enable();
 const email = $w('#emailLogIn').value;
 const password = $w('#passwordLogin').value;
 const result = await doLogIn(email, password);
 if (result.approved) {
 await wixUsers.applySessionToken(result.sessionToken); //apply active
            wixWindow.lightbox.close();
        } else {
 if (result.reason === "not member") {
                presentSignInForm(email);
            } else {
                $w('#checkPassword').expand();
                console.log("error logIn");
            }
        }
    })
}

async function register() {
 const contactInfo = {
 'email': $w('#email').value,
 'firstName': $w('#firstName').value,
 'lastName': $w('#lastName').value,
 'phone': $w('#mobilePhone').value,
 'password': $w('#password').value,
    }
 const { approved } = await doRegistration(contactInfo);
 if (approved) {
        $w('#checkEmail').expand();
        $w('#signInBottun').disable();
    } else {
        console.log("Registration error");
    }
}

function presentSignInForm(email) {
    $w("#email").value = email;
    $w('#containerLogIn').collapse();
    $w('#containerRegister').expand();
    $w('#backText').show();
}

function presentLogInForm() {
    $w('#containerLogIn').expand();
    $w('#containerRegister').collapse();
    $w('#backText').hide();
}

function onClickSignIn() {
    $w('#signInBottun').onClick(async () => {
 if (checkValidation()) await register();
 else $w('#textValidation').expand();
    })
}

function checkValidation() { // you can add more validations as you wish
 return $w('#password').value === $w('#secondPassword').value;
}
  • data.jsw
import wixData from 'wix-data';

export async function addFile(obj) {
 try {
 const result = await wixData.query('members_tasks').eq('loginEmail', obj.loginEmail).find({ "suppressAuth": true });
 if (result.items && result.items.length > 0) {
 const itemToUpdate = result.items[0];
            itemToUpdate.file = obj.file;
 await wixData.update('members_tasks', itemToUpdate, { "suppressAuth": true })
        } else {
 await wixData.insert('members_tasks', obj, { "suppressAuth": true })
        }
 return { insert: true };
    } catch (err) {
 return { insert: false };
    }
}

export async function getAllMembers() {
 try {
 let tableArray = [];
 const result = await wixData.query('Members/PrivateMembersData').find({ "suppressAuth": true })
 if (result.items && result.items.length > 0) {
            result.items.forEach(obj => {
 const { loginEmail, firstName, lastName, picture } = obj;
                tableArray.push({ loginEmail, firstName, lastName, picture });
            })
 return tableArray;
        } else {
 throw new Error('No items in members collection')
        }
    } catch (err) {
        console.log(err.message);
    }
}
  • sendEmail.js
import wixCrm from 'wix-crm-backend';
const verificationEmailId = 'verify';

export function sendEmailVerification(contactId, approvalToken){
 const obj ={
 'url': 'https://rzgancubing.wixsite.com/member/approve?tokenRegister=${approvalToken}'
    }
    sendEmail(verificationEmailId, contactId, obj);
} 

function sendEmail(emailId, contactId, variables) {
 try {
        wixCrm.emailContact(emailId, contactId, {
 "variables": variables
        })
        .then(() => {
            console.log('email sent successfully');
        })
        .catch((err) => {
            console.log('err in sendEmail is ', err.toString());
        });
    } catch (err) {
        console.log("err", err.toString())
    }   
}


  • signIn.js
 import wixCrm from 'wix-crm-backend';
 import wixData from 'wix-data';
 import { sendEmailVerification } from 'backend/sendEmail';

 export async function invalidateEmailToken(emailToken) {
 return await wixData.remove("tokens", emailToken, { "suppressAuth": true });
 }

 export async function approveBy3rdParty(contactInfo) {
 const contactId = await wixCrm.createContact({ "emails": [`${contactInfo.email}`] });
 const tokenForEmail = await createToken(contactInfo, contactId);
    sendEmailVerification(contactId, tokenForEmail);
 }

 export async function createToken(contactInfo, contactId) {
 let item;
 const objToInsert = {
 'email': contactInfo.email,
 "firstName": contactInfo.firstName,
 "lastName": contactInfo.lastName,
 "phone": contactInfo.phone,
 "password": contactInfo.password,
 "contactID": contactId,
    }
    item = await wixData.insert('tokens', objToInsert, { "suppressAuth": true });
 return item._id;
 }
  • signIn.jsw
 import wixUsersBackend from 'wix-users-backend';
 import wixData from 'wix-data';
 import { invalidateEmailToken, approveBy3rdParty } from 'backend/signIn.js';

 export const options = { "suppressAuth": true };

 export async function validateEmailToken(emailToken) {
 let result = await wixData.query('tokens').eq('_id', emailToken).find(options);
 return result.items.length > 0;
 }

 export async function doRegistration(contactInfo) {
 try {
 if (contactInfo.email && contactInfo.firstName && contactInfo.lastName && contactInfo.phone && contactInfo.password) {
 await approveBy3rdParty(contactInfo);
 return { 'approved': true };
        } else {
 throw new Error("invalid data")
        }
    } catch (err) {
 return { 'approved': false, 'reason': err.message };
    }
 }

 export async function continueRegister(emailToken) {
 const item = await wixData.get("tokens", emailToken, options);

 const result = await wixUsersBackend.register(item.email, item.password, {
 "contactInfo": {
 "firstName": item.firstName,
 "lastName": item.lastName,
 "phones": [item.phone],
        }
    });
 const check = await invalidateEmailToken(emailToken);
 return check ? { "sessionToken": result.sessionToken, "approved": true } : { "sessionToken": result.sessionToken, "approved": false };

 }

 export async function doLogIn(email, password) {
 try {
 const result = await wixData.query('Members/PrivateMembersData').eq('loginEmail', email).find(options);
 if (result.items && result.items.length > 0) {
 const sessionToken = await wixUsersBackend.login(email, password);
 return { sessionToken, "approved": true };
        } else {
 return { "approved": false, "reason": "not member" };
        }
    } catch (err) {
 return { "approved": false, "reason": 'err' };
    }
 }

And this is how my tokens collection looks:

only the contact id field has the exclamation mark.

And this is the triggered email, i’ve setted up:

Please let me know, if you want me to send anything else, Thanks in advance!

Hi Roshan :raised_hand_with_fingers_splayed:

I’ve checked your website and your code, turns out that the approval token isn’t being sent to the user after the registration, and I think I know what causing your problem.

The issue is in your sendEmail.js file, where you’re defining the value of the ( url ), obviously it’s the field ID in your email, you’re defining the url value as string, all of it using the single quotation ( ’ ’ ) like this:

'https://rzgancubing.wixsite.com/member/approve?tokenRegister=${approvalToken}'

Which basically telling the code to be set the URL to be :
https://rzgancubing.wixsite.com/member/approve?tokenRegister=${approvalToken}

While it should be:
https://rzgancubing.wixsite.com/member/approve?tokenRegister=sdfjkhs324halflsdf

To do so, you need to change the quotations from (’ ') to ( ) , notice how they differ from each other.

`https://rzgancubing.wixsite.com/member/approve?tokenRegister=${approvalToken}`

There might be other issues as well, but fix this issue first then we’ll see, I’ll keep update my answer, not sure though if the example has this issue.

Hope this solves your issue~!
Ahmad

Yeah, i’ve made that change now. Still not getting the token ID in triggered email.

In your signIn.js file, the function createToken() is not returning the token value, you’re NOT returning the value to sendEmail.js , you’re returning the value to the variable item , you need to return item after that.

How do i return it?

Hi Roshan :raised_hand_with_fingers_splayed:

In your signIn.js file, the last function " createToken() " after the insertion, add this:

return item._id;

So the insert become like this:

return wixData.insert('tokens', objToInsert, { "suppressAuth": true }).then((item) => {
        return item._id;
}) 

Hope that solves the problem.

Hello, Ahmad. return item._id is already added

Hey Roshan :raised_hand_with_fingers_splayed:

The login token is not being returned somehow, I’s hard me spot the issue just by looking at the code, I need to work with it by hand, edit the code, manipulate it and debug it, if you’re interested, send me a message on my profile.

Also i have a doubt, maybe as the contactID field is not defined in the collection. Would that be a problem?

Of course, how would you know that this user opened the approval link in a new tab or browser, the login token must be connected to its owner, however, it’s not what causing this issue.

As i took this feature from the corvid. I guess there will be only few changes which are stopping from making this work.

That’s what I’m trying to say, and the longer the code, the harder it gets to discover the issue.

so how do we resolve this, is there anyway i can send you the website with code etc. What do u say?

You’ll find links on my profile, choose the platform you want and send them a message and one of the developers will reply back to you.

Tell them that you was told to contact them by me, and give them the link of this post so they can verify it’s true.

ok thanks, just want to confirm. Is it free?

No, but their charge is reasonable for debugging.

Hey Ahmad, i need help integrating custom map with locations and it should show real time distance from current location to saved location. Please help me with this! Thanks in advance!