How to verify a purchase code using the Envato API

This is a community guide for authors who want to implement purchase code verification on their items, websites, and applications. If you have any questions or feedback then please feel free to respond directly to this topic.

:pencil2:  This topic is a wiki. All active members of the forums can edit it. Don’t hesitate to make corrections or add in any new information. All contributions are welcome! :smiley:

:rocket: Just want some working code to get started quickly?

  • There are some functional code examples listed at the bottom of the guide.
  • There are also several unofficial libraries that you can quickly install into your project.

:bulb:  Be sure to check out the official documentation for the API as well!

:books: Table of contents


:gear: Authentication

Create a personal token

You need a “personal token” before you can validate purchase codes for your items. This is similar to a password that grants limited access to your account, but it’s exclusively for the API.

For purchase code verification you must select the following permissions (see this screenshot):

  • View and search Envato sites (selected by default)
  • View the user’s items’ sales history

After creating the token, copy and save it somewhere. Envato won’t show the token to you again.

Secure your personal token

Your personal token is like a password that grants limited access to your account. You need to keep it private and secure.

  • Don’t hardcode the personal token into your items.
  • Don’t share the token with anyone outside of your team.

If you ever believe a personal token has been compromised or accidentally made public, delete it at build.envato.com and create another.


:hammer_and_wrench: Sending requests

There are some gotcha’s with the Envato API, so please read this section carefully – especially the “caution!” admonitions.

Validate user input

When accepting purchase codes from user input, you should trim (remove surrounding spaces) and validate (with a regular expression) the codes to ensure they are in the correct format. Envato generates purchase codes with UUID v4. You can validate with the following expression.

(If you’re not familiar with regular expressions and the below looks illegible to you, check out the PHP code examples further down to see how you might use it in practice.)

/^([a-f0-9]{8})-(([a-f0-9]{4})-){3}([a-f0-9]{12})$/i

:rotating_light:  Caution! If you attempt to look up a purchase code that is formatted incorrectly, the request will be blocked with a 403 response. The response body may also not contain valid JSON, which could break your code. Check the format with the above expression to avoid this situation altogether.

Send the purchase code

The request to look up a purchase code is as follows:

GET https://api.envato.com/v3/market/author/sale?code=PURCHASE_CODE_HERE
Authorization: Bearer YOUR_PERSONAL_TOKEN_HERE
User-Agent: Purchase code verification

Add your personal token that you generated at build.envato.com into the Authorization header. The final result will look like this:

Authorization: Bearer s6IscWrJL3NsBnqvUy6Q3XiImvcZlwkn

Your user agent should briefly describe your application. This will be visible to the Envato Team and can help them manage your rate limit. Some examples:

User-Agent: Purchase code verification
User-Agent: themefusion.com support forums

:rotating_light:  Caution! Do not use a browser user agent for these requests. Those experienced with web scraping may feel inclined to use a browser agent out of habit. However, doing so in this case may render your requests blocked, as these API requests should never come from browsers.

Click to see an example of a browser agent
// Don't use this or you could get blocked!
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36

:page_facing_up: Handling responses

Valid purchase codes

For purchase codes that are valid and belong to one of your buyers, the response will look like this:

{
  "amount": "19.84",
  "sold_at": "2016-09-07T10:54:28+10:00",
  "license": "Regular License",
  "support_amount": "0.00",
  "supported_until": "2017-03-09T01:54:28+11:00",
  "item": {
    "id": 17022701,
    "name": "SEO Studio - Professional Tools for SEO",
    "description": "<strong>Full</strong> HTML description here...",
    "author_username": "baileyherbert",
    "updated_at": "2017-11-02T15:57:41+11:00",
    "site": "codecanyon.net",
    "price_cents": 2000,
    "published_at": "2016-07-13T19:07:03+10:00"
  },
  "buyer": "buyer_username_or_null",
  "purchase_count": 1
}

:rotating_light:  Caution! For purchases made before 2018, it was possible for buyers to check out as a guest, in which case the buyer field in the above response would be null. Guest checkout is no longer an option so this does not apply to newer items.

Invalid purchase codes

If the purchase code is formatted correctly but does not exist (or does not belong to one of your buyers), the response will look like this:

{
    "error": 404,
    "description": "No sale belonging to the current user found with that code"
}

If the purchase code was formatted incorrectly, you will get a 403 response from Cloudflare with the following plaintext content. You should validate the format of the purchase code to avoid this.

error code: 1020

Banned accounts

It’s worth mentioning that if a buyer’s account is suspended or banned by Envato then their purchase codes will return the above 404 error and can no longer be validated. In these cases, it’s no longer possible for the buyer to validate their purchase.

Refunded purchases

If you ever refund a purchase, then similar to banned accounts, the corresponding purchase code will return the above 404 error and can no longer be validated. The same is automatically true for sale reversals and refunds manually issued by Envato Support.

List of status codes

  • 200 – Purchase code is valid.
  • 404 – Purchase code was not found (or) did not belong to one of your items.
  • 403 – Your personal token is incorrect or is missing the required permission.
    • It could also mean the purchase code is formatted incorrectly or has surrounding whitespace.
  • 401 – The authorization header is missing or not in the correct format.

:test_tube: Testing your code

There is no official sandbox for testing your script at this time. However, I’ve created my own personal sandbox and you’re more than welcome to use it. You can find the source code here.

Change the URL in your script to the following:

https://sandbox.bailey.sh/v3/market/author/sale?code=

You must also use the following personal token for this sandbox via the Authorization header:

cFAKETOKENabcREPLACEMExyzcdefghj

This fake token is now required for the sandbox to work, and is used to help you ensure you’re setting the authorization header correctly. If you send this token in the wrong format, you’ll receive a very helpful error with guidance on what you did wrong.

:warning:  WARNING! Please make sure you don’t accidentally send your real personal token to this sandbox. If you do send a realistic-looking token, the sandbox will return a 403 error and the body will alert you about the mistake, in this case please delete the token at build.envato.com and generate another one to be safe.

Now test your script with the following valid purchase code:

  • Valid purchase code86781236-23d0-4b3c-7dfa-c1c147e0dece

For a positive user experience, please test your script with these purchase codes as well. These will cause different kinds of errors, so make sure your script handles them properly.

  • Invalid purchase code94252c00-df24-4cf5-99dd-49fc17e23043
  • Improper code02b6429f-9274-9a70-03ce49bc5a48
  • Valid code with whitespace – Try using the valid purchase code above, but add some spaces before and/or after it to make sure that you’re trimming it correctly.

After you’ve confirmed that your code is working, revert your script to the official URL and your real personal token. You can test that everything is working by sending the following request to the real API – you will receive a 404 error if everything is correct.

https://api.envato.com/v3/market/author/sale?code=86781236-23d0-4b3c-7dfa-c1c147e0dece

:zap: Code examples

PHP (Function)

This ready-to-go function accepts the purchase code as its sole argument and returns a parsed object. It will throw an exception if the code is incorrect, or if there was an unexpected issue.

Replace the YOUR_PERSONAL_TOKEN_HERE on line 2 with your personal token.

:warning: Note: Don’t put this code inside your item. Instead, place it on your own server, and have your item communicate with your server instead. This is required to keep your personal token secure – it’s like a password for your account.

function getPurchaseCode($code) {
	$personalToken = "YOUR_PERSONAL_TOKEN_HERE";

	// Surrounding whitespace can cause a 404 error, so trim it first
	$code = trim($code);

	// Make sure the code looks valid before sending it to Envato
	// This step is important - requests with incorrect formats can be blocked!
	if (!preg_match("/^([a-f0-9]{8})-(([a-f0-9]{4})-){3}([a-f0-9]{12})$/i", $code)) {
		throw new Exception("Invalid purchase code");
	}

	$ch = curl_init();
	curl_setopt_array($ch, array(
		CURLOPT_URL => "https://api.envato.com/v3/market/author/sale?code={$code}",
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_TIMEOUT => 20,
		CURLOPT_HTTPHEADER => array(
			"Authorization: Bearer {$personalToken}",
			"User-Agent: Purchase code verification script"
		)
	));

	$response = @curl_exec($ch);
	$responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

	if (curl_errno($ch) > 0) {
		throw new Exception("Failed to connect: " . curl_error($ch));
	}

	switch ($responseCode) {
		case 404: throw new Exception("Invalid purchase code");
		case 403: throw new Exception("The personal token is missing the required permission for this script");
		case 401: throw new Exception("The personal token is invalid or has been deleted");
	}

	if ($responseCode !== 200) {
		throw new Exception("Got status {$responseCode}, try again shortly");
	}

	$body = @json_decode($response);

	if ($body === false && json_last_error() !== JSON_ERROR_NONE) {
		throw new Exception("Error parsing response, try again");
	}

	return $body;
}

Example usage:

try {
	// Pass in the purchase code from the user
	$sale = getPurchaseCode("PURCHASE_CODE_HERE");

	// Example: Check if the purchase is still supported
	$supportDate = strtotime($sale->supported_until);
	$supported = $supportDate > time() ? "Yes" : "No";

	echo "Item: {$sale->item->name} <br>";
	echo "Buyer: {$sale->buyer} <br>";
	echo "License: {$sale->license} <br>";
	echo "Supported until: {$sale->supported_until} <br>";
	echo "Currently supported?: {$supported} <br>";
}
catch (Exception $ex) {
	// Print the error so the user knows what's wrong
	echo $ex->getMessage();
}

PHP (WordPress)

Here’s the same function as above, but rewritten using wp_remote_get instead of cURL. This function accepts the purchase code as its sole argument and returns a parsed object. It will throw an exception if the code is incorrect, or if there was an unexpected issue.

Replace the YOUR_PERSONAL_TOKEN_HERE on line 2 with your personal token.

:warning: Note: Don’t put this code inside your item. Instead, place it on your own server, and have your item communicate with your server instead. This is required to keep your personal token secure – it’s like a password for your account.

// TODO: Return WP_Error objects instead of throwing exceptions

function getPurchaseCode($code) {
	$personalToken = "YOUR_PERSONAL_TOKEN_HERE";

	// Surrounding whitespace can cause a 404 error, so trim it first
	$code = trim($code);

	// Make sure the code looks valid before sending it to Envato
	// This step is important - requests with incorrect formats can be blocked!
	if (!preg_match("/^([a-f0-9]{8})-(([a-f0-9]{4})-){3}([a-f0-9]{12})$/i", $code)) {
		throw new Exception("Invalid purchase code");
	}

	$url = "https://api.envato.com/v3/market/author/sale?code={$code}";
	$response = wp_remote_get($url, array(
		"headers" => array(
			"Authorization" => "Bearer {$personalToken}",
			"User-Agent" => "Purchase code verification script"
		)
	));

	if (is_wp_error($response)) {
		throw new Exception("Failed to look up purchase code");
	}

	$responseCode = wp_remote_retrieve_response_code($response);

	switch ($responseCode) {
		case 404: throw new Exception("Invalid purchase code");
		case 403: throw new Exception("The personal token is missing the required permission for this script");
		case 401: throw new Exception("The personal token is invalid or has been deleted");
	}

	if ($responseCode !== 200) {
		throw new Exception("Got status {$responseCode}, try again shortly");
	}

	$body = @json_decode(wp_remote_retrieve_body($response));

	if ($body === false && json_last_error() !== JSON_ERROR_NONE) {
		throw new Exception("Error parsing response, try again");
	}

	return $body;
}

Example usage:

try {
	// Pass in the purchase code from the user
	$sale = getPurchaseCode("PURCHASE_CODE_HERE");

	// Example: Check if the purchase is still supported
	$supportDate = strtotime($sale->supported_until);
	$supported = $supportDate > time() ? "Yes" : "No";

	echo "Item: {$sale->item->name} <br>";
	echo "Buyer: {$sale->buyer} <br>";
	echo "License: {$sale->license} <br>";
	echo "Supported until: {$sale->supported_until} <br>";
	echo "Currently supported?: {$supported} <br>";
}
catch (Exception $ex) {
	// Print the error so the user knows what's wrong
	echo $ex->getMessage();
}

Node.js (npm package)

Node.js users can install my unofficial npm package conveniently named envato. It’s fully maintained, up to date, and written in TypeScript. I use it myself for many projects.

Installation:

npm install envato

Example usage:

import { Client } from 'envato';

// Add your personal token
const client = new Client('YOUR_PERSONAL_TOKEN_HERE');

// Example usage
async function getPurchaseCode(code) {
	try {
		const sale = await client.private.getSale(code);

		if (sale !== undefined) {
			console.log({
				item: sale.item.name,
				buyer: sale.buyer,
				license: sale.license,
				supportedUntil: sale.supported_until,
				supportedNow: sale.supported_until > new Date() ? 'Yes' : 'No'
			});
		}
		else {
			console.error('Invalid purchase code!');
		}
	}
	catch (error) {
		// Your code should only reach this point due to a server error,
		// an outage, or an invalid token
		console.error('Caught an exception:', error);
	}
}

:rocket: Libraries

Here are some existing libraries and wrappers that you can quickly import into your project. If there’s something missing from this list, feel free to add it. (Please refrain from posting libraries that were last updated before 2017).

PHP (Packagist):

Node.js (npm)

  • envato (unofficial) – A library for the Envato API with fully typed responses
  • envato-api-request – A tiny request wrapper for the Envato API

CodeCanyon (Premium libraries)

  • Enver – .NET library that comes with sample C# and VB projects

:gem: Plugins & scripts

Here are some plugins, scripts, and apps that can take care of purchase code verification for you. If there’s something missing from this list, feel free to add it, but all items must be open-source and free unless approved for sale on Envato Market.

CodeCanyon

WordPress.org

Browser extensions

8 Likes