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

10 Likes

When valid purchase key in input why this error:

The purchase code was invalidFailed to validate code due to an error: HTTP 404
Notice: Undefined property: stdClass::$item in D:\xampp\htdocs\php\key.php on line 49

Notice: Trying to get property ‘id’ of non-object in D:\xampp\htdocs\php\key.php on line 49
The purchase code provided is for a different item

1 Like

Try this:

  1. Go to https://build.envato.com/
  2. Click “Sign in” at the top
  3. Click the “API” page at the top
  4. In the sidebar, click “Private User Details” > “Look up sale by code”
  5. Enter the code into the textbox and click “Try it out!”

Does this work or does it say “no content”? If it says “no content”, the purchase code isn’t real.

Keep in mind the purchase code must belong to one of your buyers. It can’t be your own or someone else’s.

5 Likes
$code = "86781236-23d0-4b3c-7dfa-c1c147e0dece";

// If you took $code from user input it's a good idea to trim it:

$code = trim($code);
$url = 'https://api.envato.com/v3/market/author/sale?code='.urlencode(trim($code)).'';

    $response = wp_remote_get($url ,
    array(
        'headers' => array(
            'Authorization' => "Bearer " . $this->api_key,
            'User-Agent' => "Enter a description of your app here for the API team"
        )
    )
);

$body = json_decode($response['body']);

if ($body->item->id !== 17022701) 
throw new Exception("The purchase code provided is for a different item");

This code work for me , one great news is i am building a opensource WordPress plugin for the community , hope soon i am able to release.

2 Likes

Below code worked for me :

<?php
   	$code= trim($_GET['apikey']); // have we got a valid purchase code?
	$url = "https://api.envato.com/v3/market/author/sale?code=".$code;
	$curl = curl_init($url);
	$personal_token = "personal token from envato app";
	$header = array();
	$header[] = 'Authorization: Bearer '.$personal_token;
	$header[] = 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:41.0) Gecko/20100101 Firefox/41.0';
	$header[] = 'timeout: 20';
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curl, CURLOPT_HTTPHEADER,$header);
	$envatoRes = curl_exec($curl);
	curl_close($curl);
	$envatoRes = json_decode($envatoRes);
	//print_r($envatoRes);
	$date = new DateTime($envatoRes->supported_until);
	$boughtdate = new DateTime($envatoRes->sold_at);
	$bresult = $boughtdate->format('Y-m-d H:i:s');
	$sresult = $date->format('Y-m-d H:i:s');
		if (isset($envatoRes->item->name)) {   
				$data = " - Verification Success:  ({$envatoRes->item->name})  -  (Bought Date: {$bresult} )  - (Support Till: {$sresult})";
		} else {  
				$data= " - FAILED: Invalid Purchase Code";
		} 
	echo $data;
?>
3 Likes

Hi!
I got it working using the buyer option but not with the author/sale.
https://build.envato.com/api/#!/market_0/Author_Sale

Can it be, that it only works with keys of my items? I just have keys from purchases I made for testing. With this key, I always get “No content 404” exception. Also the API page results in “No content”.

I know that the code is working, because it works with this url

https://api.envato.com/v3/market/buyer/purchase

Is there any way to test it? Or do I need to purchase my own item to make a final test?

Thanks a lot!

Unfortunately there’s no way to test it without having a purchase code from one of your customers. Purchase codes for other authors won’t work.

:pencil2:  Edit: I’ve created a sandbox that you can use to test your code! Check the testing your code section at the top of this thread.

Envato Market Purchase Code Validator is a plugin by which Envato Market authors can easily validate their customers purchases while giving them support.

Thanks mate, your code worked for me. thanks for sharing.

Hi!
I have tested with my own keys for the buyer purchase but I get invalid purchase code. How did you manage to test? Did you change any part of the code?

Here is my code:
<?php
$code= trim($_GET['apikey']); // have we got a valid purchase code?
$url = "https://api.envato.com/v3/market/buyer/purchase?code=".$code;
$curl = curl_init($url);
$personal_token = "7VtLufxHZv3................";
$header = array();
$header[] = 'Authorization: Bearer '.$personal_token;
$header[] = 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:41.0) Gecko/20100101 Firefox/41.0';
$header[] = 'timeout: 20';
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPHEADER,$header);
$envatoRes = curl_exec($curl);
curl_close($curl);
$envatoRes = json_decode($envatoRes);
//print_r($envatoRes);
$date = new DateTime($envatoRes->supported_until);
$boughtdate = new DateTime($envatoRes->sold_at);
$bresult = $boughtdate->format('Y-m-d H:i:s');
$sresult = $date->format('Y-m-d H:i:s');
if (isset($envatoRes->item->name)) {
$data = " - Verification Success: ({$envatoRes->item->name}) - (Bought Date: {$bresult} ) - (Support Till: {$sresult})";
} else {
$data= " - FAILED: Invalid Purchase Code";
}
echo $data;
?>

I don’t advise you use a browser-like user agent for automated requests. In some cases, we drop traffic that pretends to be a browser like user agent as a part of our security measures. You should instead use your own identifier.

What is the API response you receive here? Is it a 200? Or 4xx?

1 Like

from the output I get no response code only "FAILED: Invalid Purchase Code"; Is there a way I can test to get it to output the response code as 200, or 4xx?

There will be a HTTP response code, you might just not be outputting it. You probably want to var_dump($envatoRes); and inspect it for the status.

For a CURL request you can get the response code using curl_getinfo. You must call it before closing the handle with curl_close.

$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
var_dump($code); // int(200)
curl_close($curl);

Also, I don’t believe you will be able to test with purchase codes from your own purchases - it has to be one of your buyers.

I presume that buyers would need to create and provide a personal token for that code to work, Would they?

Is there a way we can test our code without purchasing the product ourselves? Having the buyer purchase code is not enough because we need also a personal token.

I’m asking all these because there’s no way to test any of this. Buyers are reporting issues we cannot test nor develop.

If I have to purchase my own plugin to be able to test and develop it’s a shame and we as developers need a better way to ensure our code works. Even buying our own plugin is not a solution because we need Envato to approve our plugin to then buy it to then check the validation, while other users can also buy the plugin without validation.

I don’t see how we can create a good flow for buyers from the beginning.

Thank you.

No, only the author must register a personal token, so long as you use the proper endpoint which I documented in my post above.

Having the buyer’s purchase code is enough. For proof, enter the purchase code here on the sandbox: https://build.envato.com/api#market_0_Author_Sale

The sample PHP code I provided in my post above is fully tested, works, and will be enough to verify your buyer’s purchase code with your own personal token.

:pencil2:  Edit: I’ve created a sandbox that you can use to test your code! Check the testing your code section at the top of this thread.

1 Like

How would I test this without any sales or buyers (pre-release, pre-dev stage)?

to verify you must need purchase code. so, without sale mean purchase code you will not be able to check/test.

There’s no official sandbox for testing, unfortunately. Personally, I like to set up a few pages on my own server that return example responses from the API, and test using those.

See my post way way way at the top of this thread for example success and error responses.

:pencil2:  Edit: I’ve created a sandbox that you can use to test your code! Check the testing your code section at the top of this thread.