Skip to main content
knowledgecenter.avangate.com

Key generators

Overview

For digital products like software and phone cards, product activation means sending the client an email message containing either a binary key (especially for software products) or an activation alphanumeric code. The key or the code is used for unlocking the subscriptions or getting access to website content.

This feature is implemented by storing the keys or codes using two different types of lists:

  • Static lists
  • Dynamic lists

Static lists

To create static lists:

  1. Navigate to Fulfillment under Setup.
  2. Enter a name and select the list type: Static.
  3. Choose the type of codes your customers receive:
    • A single activation code shared by all shoppers. 
    • Multiple codes that you can enter manually, one per line. You can also tweak the way that Avangate handles:
      • Duplicates. Your list may or may not contain duplicate codes. 
      • Codes in the context of number of units purchased. Send  multiple codes according to product quantity or a single code regardless of the number of units purchased. 
      • Notifications when a list is running low on available codes. 
  4. Assign the products for which Avangate delivers codes from the list. 

Dynamic lists

Use this option if you host the key generator on your servers.

Workflow

  1. Avangate queries your code generator.
  2. Your code generator creates the codes and provides them to Avangate.
  3. Avangate delivers the codes to your shoppers in fulfillment emails, on the Thank You page and in their myAccount. 

Code generator output

For each approved order, Avangate queries your server with certain parameters (see parameters table) that you can use to generate codes / binary keys. Depending on the code or key format (alphanumeric or binary) the Avangate system expects either a:

  • XML file (alphanumeric code or key)
  • Or a binary file response (binary code or key)

add a Dynamic list 

To create dynamic lists:

  1. Navigate to Fulfillment under Setup.
  2. Enter a name and select the list type: Dynamic.
  3. Enter the URL corresponding to the location on your server where you host your key generator.
  4. Assign the products for which Avangate delivers codes from the list. 

Method and parameters

Avangate sends the following parameters to your server using HTTP/HTTPS POST using the URL you supplied when configuring the dynamic list.

Field name

Description

PID

Unique, system generated Avangate product ID .

PCODE

The product code you control. 

INFO

Additional information sent with the order.

REFNO

Order reference from Avangate system. (maximum 9 characters)

REFNOEXT

Order reference from your own system, sent when the order has been generated

PSKU Product SKU

TESTORDER

Specifies if the order is for testing purpose. The only possible values are YES or NO.

QUANTITY

Ordered products quantity. (number of subscriptions for example)

FIRSTNAME

Shopper first name. (maximum 40 characters allowed)

LASTNAME

Shopper last name. (maximum 40 characters allowed)

COMPANY

Shopper company name 

FAX Fax number.

EMAIL

Shopper email address. (maximum 40 characters allowed)

PHONE Shopper phone number.

LANG

ISO 639-1 two-letter code. Language used for transaction. ("en" or "ro" for example)

COUNTRY

Shopper country. (maximum 50 characters allowed)

COUNTRY_CODE ISO code for the shopper country.

CITY

Shopper city. (maximum 30 characters allowed)

ZIPCODE

Shopper zip code. (max 20 characters allowed)

LICENSE_TYPE

Sent for subscriptions. Possible values:

  • REGULAR: for new subscriptions/licenses 
  • TRIAL: for paid trial subscriptions/licenses
  • RENEWAL: for renewed subscriptions/licenses
  • UPGRADE: for upgraded subscriptions/licenses

LICENSE_REF

Unique, system-generated subscription reference number in the Avangate system. Maximum 10 characters.

LICENSE_EXP

Subscription expiration date in the format: 2003-12-25 02:30:45

When the product has been purchased with the "one time fee" (lifetime) options enabled, the value is: 9999-12-31 23:59:59

The LICENSE_EXP datetime stamp takes into account the custom time zone you selected via system settings under Account settings, or Avangate's default GMT+02:00

LICENSE_LIFETIME

This parameter indicates whether Avangate generated a lifetime subscription for the product.

  • 1 = lifetime subscription is on for the product 
  • 0 = lifetime subscription is off for the product
PARTNER_CODE

Possible values:

  • Empty = ecommerce order
  • Partner code

If sent, the partner code is also included when the HMAC is calculated.

TIMEZONE The time zone you selected or the default GMT+02:00 time zone of the Avangate system.

 

If you configure additional order fields and Avangate collects this type of info during the checkout process, it will be included in the POST request:

Additional field names for orders

Field Name

Description

CUSTOM_FIELD_TEXT[]

Array with all the custom fields texts set per order.

CUSTOM_FIELD_VALUE[]

Array with all clients input corresponding for the text.

 

Additional field names for products

Field Name

Description

CUSTOM_FIELD_714585_TEXT[]

Array with all the custom fields texts set per product.

CUSTOM_FIELD_714585_VALUE[]

Array with all clients input corresponding for the text.

 

If you configure per-product price options Avangate also includes them in the POST request. Example: Avangate product ID (PID):714585

Field Name

Description

PRODUCT_OPTIONS_714585_TEXT[]

Array with all the price options texts set per product.

PRODUCT_OPTIONS_714585_VALUE[]

Array with all client input corresponding for the text.

PRODUCT_OPTIONS_714585_PRICE[]

Array with all the prices corresponding for each option (prices are in the order currency)

PRODUCT_OPTIONS_714585_OPERATOR[]

Array with all the prices operators (ADD, SUBSTRACT) corresponding for each price option

Generating the output

You can package your response as:

  • XML for alphanumerical codes (Basic XML or Advanced XML)
  • Binary attachment for binary keys
  • Error code sent in the HTTP headers

XML files have certain reserved characters that must be escaped in a certain way when used. The reserved characters and their corresponding escape sequences are: 

  • & (ampersand) converted to & 
  • < (less than) converted to &lt; 
  • > (greater than) converted to &gt; 
  • " (double quote) converted to " 
  • ' (single quote) converted to ' 

When XML files are created, please replace these characters in the attributes and inside the nodes with the correct escape sequence to have a syntactically correct XML document. These characters are documented in the XML specification: http://www.w3.org/TR/REC-xml/

HTTP Headers

HTTP servers use a large number of response and status codes, usually transparent for the navigator. These codes tell the browser if there was an error or not. The codes are embedded in the HTTP header of the client / server communication. The success status code is 200 while any other code is an error. For example, 400 means "Bad Request" and 403 means "Forbidden". HTTP response header examples:

  • HTTP/1.0 400 Bad Request
  • HTTP/1.0 200 OK

Content-type

Set the value of the Content-Type field to text/xml" in the header response when you package your response as XML.

Content-Type: text/xml

For all other valuess, Avangate s the content is a binary key.

Basic XML Answer format sample

Use the following template for your XML file :

<?xml version="1.0" encoding="UTF-8"?> 
<Data>
<code>SIMPLE_CODE 1</code>
<code>SIMPLE_CODE 2</code>
...
<code>SIMPLE_CODE N</code>
</Data>
XML tag Description
<code> The code you deliver for the product. Include each standalone code between <code></code> tags.
<description> Optional. A global description per delivery/fulfillment.

Binary Attachment format

If you deliver a binary file, instead of XML, set the value of Content-Disposition in the header as in the example below. Include the attached file containing the key in the body of the message. Set the filename using the 'Content-Disposition' field.

Content-Disposition: attachment; filename=key.bin

Advanced XML Answer format

If you need to attach additional installation instructions or details to the delivered codes, you can use the XML structure detailed below:

<?xml version="1.0" encoding = "UTF-8" ?>
<data>
<description>General description (for all codes below)</description>
<code>
<description>Description for CODE 1 and/or the binary.key</description>
<key>CODE1</key>
<file name="binary.key" content_type = "text/plain">
UklGRhTsCABBVkkgTElTVCQBAABoZHJsYXZpaDgAAABqBAEAgPQDAAAAAAAQAAEALQAAAAAA
AAACAAAAgD4AAEABAADwAAAAAAAAAAAAAAAAAAAAAAAAAExJU1R0AAAAc3RybHN0cmD4MQAA
</file>
</code>
<code>
<description>Description for CODE 2</description>
<key>CODE 2</key>
</code>
<code>
<description>Description for the the binary_2.key</description>
<file name = "binary_2.key" >
UklGRhTsCABBVkkgTElTVCQBAABoZHJsYXZpaDgAAABqBAEAgPQDAAAAAAAQAAEALQAAAAAA
AAACAAAAgD4AAEABAADwAAAAAAAAAAAAAAAAAAAAAAAAAExJU1R0AAAAc3RybHN0cmD4MQAA
</file>
</code>
</data>
XML tag Description
<key> The keyyou deliver for the product. Include each standalone code between <key></key> tags.
<description>

If it is contained in the <data></data>node, then it is treated as a global description per delivery/fulfillment.

If it is embedded in the <code></code> node, then it is a description for that code.

<file> Optional field. A base64-encoded file.
<code>

A <code> tag answer must contain at least one of the elements <key>or <file>.


 

Security 

For security reasons we strongly recommend that you limit access to the key generator URL used to generate access codes or keys and grant access only to the these Avangate server IP networks

HASH field

Use the value of the HASH field to check data integrity. 

Field Name

Description

HASH

HASH signature md5 HMAC created using all the fields sent through HTTP/HTTPS POST.

Calculate the HASH signature

Example using the following data:

Field name

Length

Field value

PID

6

189645

PCODE

3

123

REFNO

7

1250747

REFNOEXT

0

 

TESTORDER

3

YES

QUANTITY

1

1

FIRSTNAME

4

John

LASTNAME 3 Doe

COMPANY

0  
EMAIL 17 info@avangate.com
LANG 2 en
COUNTRY 11 Netherlands

COUNTRY_CODE

2 nl
CITY 10

Amstelveen

ZIPCODE

4

1181

 

Source string for HMAC_MD5 is calculated by pre-pending each field value with its own length. The source string for the data above is:
618964531237125074703YES114John3Doe017info@avangate.com2en11Netherlands2nl10Amstelveen41181
Secret key for this example SECRETKEY
HASH value 364b47f4a21def26ee7758f697ca4bd9

 

Don't forget to check if the received order reference matches a valid reference order from your system.

Test orders

In the case of test orders, Avangate populates the TESTORDER parameter with the value YES. It is highly recommended to reply with testing codes or keys whenever receiving the above mentioned parameter in the server request, and not codes/keys you would send to actual shoppers.

Code sample

<?php
//Electronic Delivery
//$pass     = 'REPLACE_WITH_YOUR_KEY';  // pass to compute HASH
                                        //You can find your secret key here: https://secure.avangate.com/cpanel/account_settings.php

function array_to_string($data){
               $return = '';
               
               if(!is_array($data)){
                              $return  .= strlen($data).$data;
               }
               else{
                              foreach($data as $val){
                                             $return .= array_to_string($val);
                              }
               }
               return $return;
}

function hmac($key, $data){
   $b = 64; // byte length for md5
   if (strlen($key) > $b) {
       $key = pack('H*',md5($key));
   }
   $key  = str_pad($key, $b, chr(0x00));
   $ipad = str_pad('', $b, chr(0x36));
   $opad = str_pad('', $b, chr(0x5c));
   $k_ipad = $key ^ $ipad ;
   $k_opad = $key ^ $opad;
   return md5($k_opad  . pack('H*',md5($k_ipad . $data)));
}

function stripslashes_recursive(&$value, $key){
    $value = stripslashes($value);
}

function strip_magic_quotes($data){
    
    if (ini_get('magic_quotes_gpc') ){
        if(is_array($data)){
            array_walk_recursive($data, 'stripslashes_recursive');
            reset($data);
        }
        else{
            $data = stripslashes($data);
        }
    }
    
    return $data;
}


//some servers still have magic_quotes_gpc on
//to validate the hash we need the information Avangate sent, unaltered by client server
$cleaned_post = strip_magic_quotes($_POST);


$avangate_signature = $cleaned_post['HASH']; // HASH received

unset($cleaned_post['HASH']);

$hash = hmac($pass,array_to_string($cleaned_post)); //calculate the hash for the fields received(excluding the hash) to make sure the post is from Avangate and wasn't altered on the way


if($hash != $avangate_signature){

    //post signature didn't match the calculated hash, log the information received, and maybe do other business logic
    mail( 'your_address@example.com','Avangate electronic delivery error', print_r($_POST,true)."\n\nAvangate signature = $avangate_signature, hash = $hash");
    
    //return a http header different than 200 to inform Avangate the request was malformed - you could also return 403 Forbidden or 404 Not Found to discourage potential attackers
    header('HTTP/1.0 400 Bad Request', false, 400);
    echo 'Invalid signature.'; //Avangate logs your answer for debug purposes
    
}else{



    //rudimentary example of business logic
    //this example uses only Avangate ProductId to decide the output
    
    switch($cleaned_post['PID'])
    {
        
         case '123': //for this product your key is a binary file
         
            header('HTTP/1.0 200 OK',false, 200); //inform Avangate you received a valid post, you shouldn't have to set this header explicitly
            header('Content-Type: application/octet-stream'); //set the mime type
            header('Content-Disposition: filename=key_123.bin'); //set the file name
            readfile(dirname(__FILE__).'/key_123.zip'); //output the file
            
            //make sure your script doesn't output anything after this point and/or stop script execution
            exit;
        break;
        
        
        case '1234': //for this product your key is a text file
        
            header('HTTP/1.0 200 OK',false, 200); //inform Avangate you received a valid post, you shouldn't have to set this header explicitly
            header('Content-Type: text/plain'); //set the mime type
            header('Content-Disposition: filename=key_1234.txt'); //set the file name
            readfile(dirname(__FILE__).'/key_1234.log'); //output the file
            
            //make sure your script doesn't output anything after this point and/or stop script execution
            exit;
        break;
        
        
        case '12345': //for this product your key is a string
        
            //build a basic xml with the following structure:
            $basic_xml =  '<?xml version="1.0"?>
                            <data>
                                <code>12345 key</code> <!-- your string key here -->
                            </data>';
                            
            header('HTTP/1.0 200 OK',false, 200); //inform Avangate you received a valid post, you shouldn't have to set this header explicitly                 
            header('Content-Type: text/xml');  
            echo  $basic_xml; //make sure the script doesn't output anything after this point and/or stop script execution
            exit;
        break;
        

        case '123456': //this product is a bundle and contains several string keys
        
            //the xml gets a bit more complicated
            $advanced_xml ='<?xml version="1.0"?>
                            <data>
                              <description>Bundle 123456</description> <!--General description (for all codes below), it will be displayed/sent to enduser-->
                              <code>
                                  <description>key for bundle component 1</description> <!--description for CODE 1, it will be displayed/sent to enduser -->
                                  <key>bundle comp 1 </key> <!-- string key -->
                              </code>
                              <code>
                                  <description>key for bundle component 2</description> <!--description for CODE 2, it will be displayed/sent to enduser -->
                                  <key>bundle comp 2 </key> <!-- string key -->
                              </code>
                            </data>';
            
            header('HTTP/1.0 200 OK',false, 200); //inform Avangate you received a valid post, you shouldn't have to set this header explicitly
            header('Content-Type: text/xml');
            echo  $advanced_xml; //make sure the script doesn't output anything after this point and/or stop script execution            
            exit;
        break;
        
        
        case '1234567': //this product is a bundle and one of the products has a string file and a file attached
        
            //the xml contains the new tags: file and extra
            $advanced_xml ='<?xml version="1.0"?>
                            <data>
                              <description>Bundle 123456</description> <!--General description (for all codes below), it will be displayed/sent to enduser-->
                              <code>
                                  <description>key for bundle component 1</description><!-- description for CODE 1, it will be displayed/sent to enduser -->
                                  <key>bundle comp 1 </key><!-- string key -->
                                   <file name="binary.key" content_type = "text/plain"><!-- the file name and content type will be used when offering the file for download to user.
                                   <![CDATA['.base64_encode(file_get_contents(dirname(__FILE__).'/key_p1_1234567.log')).']]>
                                   </file>
                              </code>
                              <code>
                                  <description>key for bundle component 2</description> <!--description for CODE 2, it will be displayed/sent to enduser -->
                                  <key>bundle comp 2 </key> <!-- string key -->
                                  <extra type="INSTALL_HOTLINE" label="Install hotline number">0740216669</extra> <!--the content and label of the extra tags is displayed in cpanel and sent to end-user through the electronic delivery mail-->
                                  <extra type="OPERATING_SYSTEM" label="Works on the following operating systems">Windows XP, Windows 7, Windows 8</extra> <!--the content and label of the extra tags is displayed in cpanel and sent to end-user through the electronic delivery mail-->
                              </code>
                            </data>';
                            
            header('HTTP/1.0 200 OK',false, 200); //inform Avangate you received a valid post, you shouldn't have to set this header explicitly
            header('Content-Type: text/xml');
            echo  $advanced_xml; //make sure the script doesn't output anything after this point and/or stop script execution
            exit;
        break;

     
    }
    
    

}