Nahuel Sanchez

Check if a user has made a purchase on a VTEX store

There's no a simple built in functionality on VTEX that allows to check if the current user browsing the store has already made a purchase there, but there's a twisted workaround to get this information.

Before actually going through the guide, this is the idea: we need to create a new attribute in the CL Data Entity that retains this information and this attribute is going to be updated on the Order Placed page doing an HTTP request to the VTEX Master Data using JavaScript.

Sounds simple? Weeell...

Creating the new attribute

Thanks to the VTEX Master Data API we can get a lot of information about the clients, but if he or she has made a purchase is not there anywhere, that's why we need to create a new attribute that will hold this information.

The new attribute, let's name it buyer, needs to be Boolean, and we need to grant it permissions to be read it and edit it using the API.

Attribute configuration on the Client Data Entity

All existing registered users on our store will have it in null or false until we changed it to true after a user performs a full checkout process.

Updating the attribute

After the user performs a purchase he will end on the Order Placed page that says something like "Em breve você receberá um e-mail no endereço john [email protected] com todos os detalhes do pedido". We need to get that email from the source code of the page and using jQuery AJAX performs a PATCH in order to set the buyer attribute in true.

The problem is that we can't directly insert any piece of JavaScript code in the VTEX Smart Checkout. If you're thinking on adding the JavaScript code into any of the orderplaced-* templates, it won't work, the code won't be displayed because of security reasons.

VTEX Portal section

Here's the fun part: we need to use Google Tag Manager to insert JavaScript on that page.

GTM as our Trojan Horse

Since the VTEX Smart Checkout, as any other checkout process from any other platform, is a sensible part of the site and that's the reason why we don't have, directly on VTEX, a way to insert JavaScript code.

If we have Google Tag Manager integrated into our VTEX store it's our lucky day because that tool can add JavaScript on the page, and the idea is to give to GTM our jQuery AJAX request code.

To accomplish this we need to create a new Custom HTML Tag where instead of HTML we are going to add our script. This script.

$(window).load(function() {
    if ($('.orderplaced-sending-email strong').text().trim()) {
        var urlProtocol = window.location.protocol;
        var apiUrl = urlProtocol + '//api.vtexcrm.com.br/storename/dataentities/CL/documents';

        $.ajax({
            "headers": {
                "Accept": "application/vnd.vtex.masterdata.v10+json",
                "Content-Type": "application/json"
            },
            "url": apiUrl,
            "async" : false,
            "crossDomain": true,
            "type": "PATCH",
            "data": JSON.stringify({
                "email" : $('.orderplaced-sending-email strong').text().trim(),
                "buyer" : true
            })
        });
    }
});

Next, on the Fire on step, click on More in order to create a new trigger. The new trigger needs to be a personalized event that will be fired on "orderPlaced".

GTM new Tag

Save the tag and publish the changes on Google Tag Manager. This is not affected by VTEX cache so we should see the changes immediately.

At this point every time a user hits that page, meaning every time an user finish a purchase, the buyer attribute will be set to true. So now in any other page of the store you can use the VTEX Master Data API to check if buyer is true. If so, then the user browsing the site already made a purchase and you can do whatever you want with that information.

Waaaait

This is a delicate move. You are inserting JavaScript into the checkout process so test it very well, you don't want to screw this part of the VTEX implementation.

Discover on what commit a bug was introduced using git bisect

The git bisect command allows us to performs a binary search to find out what commit causes a bug. It's the fast version of going through each commit from the first one to the last one until you finds out that "Oh, after this commit the bug can be reproduced".

Basically what you do is tell Git where things were working (meaning on what commit you remember the bug didn't appear), and then you tell Git where things are not working (meaning a commit where the bug can be reproduced, that can be the last one).

It's easy with an example

In theory sounds great but it's better if we go through an example to learn how to use the git bisect command.

I have a small fake project with a series of commits and I introduced a bug in one of them. Of course, the commit messages are self explanatory but we need that to see if the git bisect really works.

We are going to find out on what commit a typo was introduced on the content of my example.txt file.

Finding the commits where things works and doesn't

This is the easy part and you probably know how to do it.

Checkout to an old commit, see if the bug disappeared, write down the commit hash, and come back to the HEAD. And you can use the newest commit hash as the bad point.

Here's the list of my commits and the one I choose for the good and bad points.

commit 9cc1e21a4422a3e1365bd04b616ddafd98475c3e  
Improved ways to contact us.  
(here things are bad)

commit 785361159507faa755dfb4a1aaaac8cb7ad18c7e  
Added more text to encourage people.

commit 5603f08930ad4aaf84843f6205f25cdd0f684f4f  
Added a signature.

commit 45493f69eba7153d7d954f0eeb7e4973e32d9f3a  
Added a way to contact us in case he/she finds a typo.

commit 9487c88848e056f120166e0a5844822cb0f6b8a4  
Added even more text to the example.txt file to encourage the user to report any typo.

commit c04e336490796f22423b9685c0ffe745ebcddcbb  
A wild typo appears...

commit 2fec0d56c80fd1f7f000ab31ecba643d4c12533d  
Improved example.txt file by adding more text to it.

commit 979ba26a9e0eaeae35e0e236bc14126708a942a6  
Updated example.txt file with some text without a typo.  
(here things are good)

commit 27c9d5ec2bcae6f1aa8ee295483ac9696e579296  
Initial commit with example.txt empty.  

Indicating the commits where things works and doesn't

You have the good and the bad, let's transfer that knowledge to Git.

git bisect start  
git bisect good 979ba26a9e0eaeae35e0e236bc14126708a942a6  
git bisect bad 9cc1e21a4422a3e1365bd04b616ddafd98475c3e  

Bisecting

Git will move you to any of the commits trap in the middle of the good and bad points. And the only thing you need to do is to tell Git if that commit is a good or a bad one, meaning if you can reproduce the bug or not.

Here's what I get:

➜  Bisect_Project git:(master) git bisect start
➜  Bisect_Project git:(master) git bisect good 979ba26a9e0eaeae35e0e236bc14126708a942a6
➜  Bisect_Project git:(master) git bisect bad 9cc1e21a4422a3e1365bd04b616ddafd98475c3e
Bisecting: 3 revisions left to test after this (roughly 2 steps)  
[9487c88848e056f120166e0a5844822cb0f6b8a4] Added even more text to the example.txt file to encourage the user to report any typo.
➜  Bisect_Project git:(9487c88)

I check the example.txt file to see if the typo is there or not.

This test hat no typo. Please, feel free to check it.

If you find a problem, please report it.  

Is there (it says hat instead of has), so it's a bad commit. I tell Git about it and he move me to anothe commit.

➜  Bisect_Project git:(9487c88) git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)  
[c04e336490796f22423b9685c0ffe745ebcddcbb] A wild typo appears...

I check the example.txt file again.

This test hat no typo. Please, feel free to check it.  

The typo is there, I tell Git about it and he'll move me again.

➜  Bisect_Project git:(c04e336) git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)  
[2fec0d56c80fd1f7f000ab31ecba643d4c12533d] Improved example.txt file by adding more text to it.

I check the example.txt file one more time.

This test has no typo. Please, feel free to check it.  

No typo! It's a good commit.

➜  Bisect_Project git:(2fec0d5) git bisect good
c04e336490796f22423b9685c0ffe745ebcddcbb is the first bad commit  
commit c04e336490796f22423b9685c0ffe745ebcddcbb  
Author: nahuelsanchez <[email protected]>  
Date:   Fri Feb 19 22:52:04 2016 -0300

    A wild typo appears...

:100644 100644 5aebed1a496b67a06fe93b31d9590eae6fb258ad 657a182a496be92d26c948ece9cd9fa0061e03bb M    example.txt
➜  Bisect_Project git:(2fec0d5)

And there we have it. Git is telling us on what commit the bug appeared for the first time. It's up to your skills to check the code and fix it.

When you want to return to the HEAD just do a git bisect reset and you'll be back.

Code to GET, POST, PATCH and PUT data in VTEX Master Data

We can manipulate the documents existing in the VTEX Master Data using HTTP request, and it's like the most important thing we should know to dominate this platform's tool.

The official Master Data API documentation breaks down the different HTTP request we can use and explains each part of the request but this information wasn't enought for me to create a working code.

Using and old technique known as "try and failure" I came up with four working jQuery AJAX requests ready to be used to GET, POST, PATCH and PUT data in a specific Data Entity.

My goal was to simplify the interaction with the VTEX Master Data during the implementation of a VTEX store. The code is far from perfect but it's a working start.

GET

function getFromMasterData(name, where, fields) {  
    var store = 'storeName';
    var urlProtocol = window.location.protocol;
    var apiUrl = urlProtocol + '//api.vtex.com/' + store + '/dataentities/' + name + '/search?_where=' + where + '&_fields='+ fields;
    var response;

    $.ajax({
        "headers": {
            "Accept": "application/vnd.vtex.masterdata.v10.profileSchema+json"
        },
        "url": apiUrl,
        "async" : false,
        "crossDomain": true,
        "type": "GET"
    }).success(function(data) {
        response = data[0];
    }).fail(function(data) {
        response = data;
    });

    return response;
}

The first parameter here, name, refers to the Data Entity's acronym (for example, CL for the Client's Data Entity) and it's the same for the next HTTP requests.

The second parameter, where allows us to define the filter to use while requesting the documents from the Master Data. For example, [email protected].

Finally, fields is there to specify what attributes we want to retrieve from the results.

Here's an example to retrieve the first name and last name from a client knowing its email address.

getFromMasterData('CL', [email protected]', 'firstName,lastName')  

POST

function postInMasterData(name, email, fields) {  
    var store = 'storeName';
    var urlProtocol = window.location.protocol;
    var apiUrl = urlProtocol + '//api.vtexcrm.com.br/' + store + '/dataentities/' + name + '/documents';
    var response;

    var who = {
        "email": email
    };

    var data = $.extend(who, fields);

    $.ajax({
        "headers": {
            "Accept": "application/vnd.vtex.ds.v10+json",
            "Content-Type": "application/json"
        },
        "url": apiUrl,
        "async" : false,
        "crossDomain": true,
        "type": "POST",
        "data": JSON.stringify(data)
    }).success(function(data) {
        response = data;
    }).fail(function(data) {
        response = data;
    });

    return response;
}

In this case, email needs to be an actual email address that the code is going to use as an unique identifier of the document that is going to be created on the Master Data, and fields is a JSON object with the attributes and values to send in the request.

Life is better with examples.

var someAttributes = {  
     firstName : 'John',
     lastName : 'Doe'
};

postInMasterData('CL', [email protected]', someAttributes);  

PATCH

function patchInMasterData(name, email, fields) {  
    var store = 'storeName';
    var urlProtocol = window.location.protocol;
    var apiUrl = urlProtocol + '//api.vtexcrm.com.br/' + store + '/dataentities/' + name + '/documents';
    var response;

    var who = {
        "email": email
    };

    var data = $.extend(who, fields);

    $.ajax({
        "headers": {
            "Accept": "application/vnd.vtex.masterdata.v10+json",
            "Content-Type": "application/json"
        },
        "url": apiUrl,
        "async" : false,
        "crossDomain": true,
        "type": "PATCH",
        "data": JSON.stringify(data)
    }).success(function(data) {
        response = data;
    }).fail(function(data) {
        response = data;
    });

    return response;
}

The parameters here works just as the POST's example, where email is the way to identify the document is going to be edited and fields the JSON object with the attributes and values, as show in the following example.

var someAttributes = {  
     firstName : 'John'
};

patchInMasterData('CL', [email protected]', someAttributes);  

PUT

function putInMasterData(name, email, fields) {  
    var store = 'storeName';
    var urlProtocol = window.location.protocol;
    var apiUrl = urlProtocol + '//api.vtexcrm.com.br/' + store + '/dataentities/' + name + '/documents';
    var response;

    var who = {
        "email": email
    };

    var data = $.extend(who, fields);

    $.ajax({
        "headers": {
            "Accept": "application/vnd.vtex.masterdata.v10+json",
            "Content-Type": "application/json"
        },
        "url": apiUrl,
        "async" : false,
        "crossDomain": true,
        "type": "PUT",
        "data": JSON.stringify(data)
    }).success(function(data) {
        response = data;
    }).fail(function(data) {
        response = data;
    });

    return response;
}

More of the same, just like the last two codes. And a similar example.

var someAttributes = {  
     lastName : 'Doe'
};

putInMasterData('CL', [email protected]', someAttributes);  

This is on GitHub

I decided to put this snippets on a Git repository you can find in https://github.com/nahuelsanchez/vtexmasterdataapiconnection.

Feel free to send any improvement you think this code desperately needs (thanks in advance).

Configure a Ghost blog using a custom domain with www and non-www using CloudFlare

Ghost is a blogging platform that you can host in your own web server or allow the company to host it for yourself to save time and stress (and to just focus on writing post). Think about it as a WordPress blog that has been tear down to the basic functionality like write post, tag them and no more like that.

"Ghost is a platform dedicated to one thing: Publishing." Ghost's About page

But if you're reading this it means that you already knows Ghost, you already have a blog hosted by them under an URL like yourname.ghost.io, and you already know it can cause you a big headache to configure your custom domain to point to your blog using both the www and non-www version of your domain and not just one as Ghost seems to allow.

I fought with this problem when I created this blog, so probably what I experience can help somebody.

CloudFlare DNS and Page Rules configuration

Let's assume you have the yourdomain.com domain and you want to point it to yourname.ghost.io. Also, let's go a even forward and assume you already add your custom domain to CloudFlare because the main problem here is to make both www and non-www work (because Ghost already explain how to get only one of them working).

DNS settings

Access your domain DNS setting in order to create a new CNAME record that points your subdomain www to your Ghost blog URL.

  • Type: CNAME
  • Name: www
  • Value / Domain name: yourname.ghost.io

You can take a look at my configuration in the image below. The first row is the one that matters for this tutorial, and the others are used to make Google Apps works (so I can have my email hosted by Google).

DNS settings to point www and non-www to Ghost using CloudFlare

It's very important that the configured CNAME goes through the CloudFlare servers, which means that the cloud icon at the last column should be orange as the image shows.

Page Rules setting

Lucky us CloudFlare provides a way to configure page rules to redirect somebody who types some URL to another desire URL. In this case we want to redirect the non-www version of our domain (yourdomain.com) to the www version of it (www.yourdomain.com).

Add a new rule for the URL pattern http://yourdomain.com/*, set the Forwarding option to ON, enter http://www.yourdomain.com/$1 as URL destination, and finally set the Forwarding type as Permanent - 301.

Look at my page rule for this blog to get things even more clear.

Page Rule configuration to redirect non-www URLs to www version

Probably you're wondering about the * and $1 on the URLs. They are there to match whatever comes after the domain without www in order to add the same to the www sub-domain once the redirection takes place.

For example, with this page rule as described, when somebody types yourdomain.com/this-is-a-blog-post/ it's going to be translated to www.yourdomain.com/this-is-a-blog-post/. No visitor is left behind with this configuration.

Ghost Custom Domain configuration

Finally, this whole CloudFlare setup will make sense if you go back to your Ghost blog configuration in order to set the www version of your domain as the Custom Domain of your blog.

Ghost blog configuration using www as Custom Domain


That's all it takes to have both yourdomain.com and www.yourdomain.com working with your Ghost blog.

If you can't see the results, meaning the redirection is not working for you, first take a break because it could take some minutes to take effect, or blame the cache of your browser (clean your browser cache and try again, or simple change the browser you are using).

Differences between POST, PATCH and PUT in VTEX Master Data

We can thrown five different HTTP requests at the VTEX Master Data: GET, POST, PATCH, PUTCH and DELETE. GET and DELETE are very simple to understand (once for get documents and the other one to delete them), but POST, PATCH and PUT seems like they all do the same thing.

There are small differences between POST, PATCH and PUT that justify the existence of all of them, and knowing this could improve our VTEX implementation and how we interact with the Master Data.

POST, PATCH or PUT?

The first one, POST, allows us to create a new document in a specific Data Entity. If the document already exists we won't be able to introduce the new data into the Data Entity.

For example, if we perform a POST request to create a new client with the email [email protected] and name John, but the Data Entity CL already has a client with that email address a new document won't be created.

It doesn't matter if the existing document with the email [email protected] has a different name for the client. Since the email needs to be unique the request will fail and the other attributes like the name will be discards.

On the other hand, PATCH works just like POST in terms that it will create a new document, but if the document already exists then the attributes from the existing document are updated with the new values that we are sending in the request.

Take into consideration that PATCH, in the scenario where the document already exists, will update only the attributes that we are sending in the request. The attributes that exists on the document but are not mentioned in the request won't be affected.

Finally, PUT works as POST too when the document doesn't exists: it creates a new one with the values we're sending in the request.

When using PUT in an scenario where the document already exists it will cause the existing document in the Data Entity to be completed overwritten. In other words, the existing document will be deleted, a new one will be created with the attributes mentioned in the request, and the ignored attributes in the request will be empty in the final created document.

POST, PATCH and PUT in VTEX Master Data documentation

Let me recap. POST to create a new document, PATCH to create a new document if it doesn't exist or to update the values of the attributes in an existing document, and finally PUT to create a new document if it doesn't exist or to overwrite an existing document.

A friendly example

Given an empty CL Data Entity, we perform a POST to create a new document with the following JSON in the request.

{
     email : "[email protected]",
     firstName : "John",
     lastName : "Doo"
}

Since the document doesn't exist, it will be created.

Then, we decided to change the lastName of the document because we spot a typo on it. For this, we use PATCH and the following JSON in the request.

{
     email : "[email protected]",
     lastName : "Doe"
}

After the request the document on the Data Entity will look like the following.

{
     email : "[email protected]",
     firstName : "John",
     lastName : "Doe"
}

Finally, the document became obsolete and we need to overwrite it using PUT and the following JSON.

{
     email : "[email protected]",
     firstName : "Jane"
}

The existing document with that email address will be deleted and a new one will be created, as follow.

{
     email : "[email protected]",
     firstName : "Jane",
     lastName : ""
}

As you saw, lastName is empty.

Install Composer on OS X and use it globally

Magento 2 is here and we know it needs Composer, so let install it on OS X in a way it can be used globally.

Considering that installing Composer will create a composer.phar file, and our intention is to use it everywhere, anytime, besides the project we're working on, I decided to install it (means let the composer.phar be created) in /Applications/Composer (it makes sense to me, but you can choose the destination you want).

Installing Composer

First thing first, let's go to /Applications, create the Composer folder, and get in.

cd /Applications/  
mkdir Composer  
cd Composer  

Now we can install Composer for real using Curl.

curl -sS https://getcomposer.org/installer | php  

If everything goes as expected we'll get a nice message saying something like Composer successfully installed to... and we can proceed to the next step.

Making it globally available

Now that we have the composer.phar file we need (actually, we want) to have it globally available by typing composer in the command line. To achieve this let's start by moving our composer.phar file to /usr/bin/.

sudo mv composer.phar /usr/bin/  

And finally let's create an alias on our bash profile.

alias composer="php /usr/bin/composer.phar"  

Reload the profile, and that's all it takes to get Composer on OS X. You can now go to any folder you want and type composer about. If something nice appears it means everything is okay.

I might be skipping the part where I'm supposed to explain how to edit and reload the bash profile. It was on purpose because it always different depending on the terminal you use (for example mines it's located in ~/.zshrc because I use Oh My ZSH!).

You can try the following in order to access and edit the bash profile.

vim ~/.bash_profile  

What's VTEX Master Data?

The VTEX Master Data is what the platform offers to look under the hood. There we can find all the clients' data, orders' information, addresses and other kind of data divided into different Data Entities.

The clients's information like first name, email address, identification, phone numbers, if the client is willing to receive newsletter or not, and other information be default or custom is not invisible or totally private for the store owner. On the contrary, it's there to be read and accessed.

Is a database-ish, that works as a CRM data repository too, the deepest you'll be able to sink considering it's a SaaS platform.

Data Entity

The data is separated into different Data Entities based on the nature of the data itself.

We have Data Entities for Clients (CL), Addresses (AD), Orders (OD), Stores (SO) and a lot more but the most important one, yet, it's the one for the Clients.

Data Entities from VTEX Master Data

Each Data Entity has specific attributes. For example, inside CL you can find, among others, the attribute document which type is Varchar 50, attribute firstName type Varchar 50, attribute isNewsletterOptIn type Boolean, and more.

Another interesting attribute type is Relationship. For example, on the Data Entity OD (for the Orders) there's an attribute known as addressId to relate a document from OD, an order, with an address from the Data Entity AD (for the Addresses).

A little lost? Think it's a MySQL database where each Data Entity is a table and the attributes are the fields. What's left it's the manipulating of the data (add new documents, delete existing documents or edit existing ones).

Data Entity's attributes from VTEX Master Data

The attributes have properties too. An attribute can be configured to establish permissions to read it, to edit it or to use it as filter, among other minor configurations.

The power of the VTEX Master Data comes to light when we perform request to it in order to play with the data. In order to do that we generally aim our request to a specific Data Entity with a filter to retrieve the documents that matches that filter (here we use an attribute with permission to be used as filter) and from the results we ask for specific attributes to read the values (we can do that only on attributes that have permission to be read it). If the idea is to edit the documents consider that we can only alter attributes that, as you probably already guest, have permissions to be edit it.

Remember, a VTEX store by default comes with default Data Entities and default attributes. That's not the limit. Do you need an attribute to save the favorite ice cream flavour of the customer?, you can create it.

Locating the data and other information

The best part of the VTEX Master Data is how we can manipulate it (either just to read it or to edit it) using HTTP request to GET, POST, PUT, PATCH and DELETE documents from the Data Entities. But this is not part of this post scope, feel free to check other publications tagged with VTEX Master Data to go deeper on this topic.

The data is also available via web on {store}.vtexcrm.com.br where you can play with the documents, and on {store}.ds.vtexcrm.com.br where you can configure the Data Entities and attributes.

Everything I just described about the VTEX Master Data is just a glimpse of all it can offer. Check the last two URLs in the paragraph above to discover more features.