Two Tap API

Two Tap allows programmers to place orders on ecommerce sites using code. There are two ways of using it: via the HTML5 interface, or via the API.


HTML5 Interface

The HTML5 interface is a wrapper around our API. It's the easiest way to integrate Two Tap in your applications.

To use it just point a web browser, a UIWebView (iOS), or a WebView (Android) to https://checkout.twotap.com with a list of products, your public api token, a unique token for the transaction, and a confirm url.

At the end of the checkout flow the interface will call the confirm_url with the unique_token and a purchase_id to ask for the final order confirmation. More information about this below.

The HTML5 interface is stateless, which means you can't add products to an existing cart. For multi-products / multi-retailers we recommend you store the list of product urls locally (localstorage, NSUserDefaults), and open the interface once the users presses your checkout button. You don't have to do any extra UI work, since you would have a 'checkout' button (that contains a count of products in the cart) anyways. When the shoppers presses that button pass us that array of product urls.

The interface acccepts both GET requests with query arguments and POST requests with body arguments. We recommend you use GET requests for quick debugging, and POST requests in production. POST requests ensure you won't hit the maximum number of characters allowed in an URL in browsers.

Parameters via GET


#  https://checkout.twotap.com?
#  public_token=your_public_token&
#  unique_token=an_unique_token&
#  confirm_url=your_sms_confirm_url&
#  products=product_1_url,product_2_url&
#  affiliate_links=product_1_affiliate_url,product_2_affiliate_url
            

We recommend you encode each individual url to avoid any issues (js: encodeURIComponent(), ruby: CGI.escape(), python: urllib.parse.quote()).
public_token The public token from your Two Tap publisher interface.
unique_token An unique token generated by you that is sent back with the confirm url call.
confirm_url An URL on your end where the interface will ask you to confirm the purchase. Only ports 80 (http) and 443 (https) are allowed for outbound connections. Using GET the interface will default to the SMS flow.
products A list of products urls separated by commas. We encourage you to encode each product url beforehand.
affiliate_links (Optional) A list of affiliate links for the above product urls.
test_mode (Optional) dummy_data or fake_confirm. A way to test the interface without making actual purchases.
custom_css_url (Optional) An url to your custom css file. Has to be served over https.
show_close_button (Optional) true or false. If true a close button is displayed in the top right corner of the interface. On tap it triggers a postMessage event called 'close_pressed'.
standard_top_banner_url
standard_top_banner_message
success_top_banner_url
success_top_banner_message

(Optional) Add these params if you want a banner at the top of the interface. This is useful in case you want a custom back button or need to display a message.

At the end of the checkout process the interface will set the banner with the values of success_*.

Parameters via POST

public_token The public token from your Two Tap publisher interface.
unique_token An unique token generated by you that is sent back with the confirm url call.
confirm

A hash that tells the checkout interface which API flow to use. Only ports 80 (http) and 443 (https) are allowed for outbound connections. We recommend the SMS flow for ease of use.

{ method: 'sms', sms_confirm_url: 'http(s)://your_endpoint', sms_finished_url: 'http(s)://your_endpoint (optional)' }.

{ method: 'http', http_confirm_url: 'http(s)://your_endpoint', http_finished_url: 'http(s)://your_endpoint' }.

For more information see the SMS flow or API flow.

Transform this object into JSON before sending it.

products

An array of hashes like:

[ { 'url': 'the product url', 'affiliate_link': '(Optional) an affiliate link', 'input_fields': '(Optional) a hash of product values, ex: { 'quantity': 2 }.' } ].

Transform this object into JSON before sending it. Max 15 products in one session.

input_fields

(Optional) If you want to prefill information about the user/purchase you can send the values. Send a hash like:

{ 'shipping_state': 'California', 'coupons': { site_id: 'coupon_value' } }.

Transform this object into JSON before sending it.


See a list of possible keys and values.

test_mode (Optional) dummy_data or fake_confirm. A way to test the interface without making actual purchases.
custom_css_url (Optional) An url to your custom css file. Has to be served over https.
close_button

(Optional) Add hash like below if you want a close button on the top right side of the interface. You don't need to specify an action. By default it will send a postMessage that you can catch and handle on both desktop and iOS.

{ show: 'true', action: (optional)'redirect', url: (optional)'http://where-to-redirect_to' }

retry_url (Optional) In case of failures (like payment issues) shoppers receive an SMS with a link to retry the purchase. By default this restarts the checkout in Safari, however you can tweak the retry_url to send them back to your app. See the 'Restarting purchases' section below.
top_banner

(Optional) Add hash like below if you want a banner at the top of the interface:

{ 'standard_message': 'message', 'standard_url': 'url', 'success_message': 'message', 'success_url': 'url' }.

This is useful in case you want a custom back button or need to display a message.

At the end of the checkout process the interface will set the banner with the values of success_*.

hide_intro (Optional) true or false. By default an intro screen is displayed once to all new users. You can disable it.
intro_messages

(Optional) The intro screen displays a title, three benefits, and a dismiss button. Add hash like below if you want to tweak the messages:

{ 'title': 'title', 'first': 'first', 'second': 'second', 'third': 'third', 'button': 'button' }.

Testing the HTML5 interface

There are two test modes in the interface: dummy_data and fake_confirm.

dummy_data

In this test mode the HTML5 interface will go through a pre-created purchase without making any API calls to the underlying stores.

fake_confirm

In the fake_confirm test mode the HTML5 interface will go through the purchase as usual, except at the final 'Purchase Confirm' step where the system wil return a fake OK and not place the order.

It's important to note that stores validate payment information when the order is actually placed, consequently store data errors (like card not valid) can appear in the last step which are going to be ignored.

Web Integration Example

We provide a very simple skeleton example for a desktop website integrating the HTML5 interface in this github repository.

Helpful notes

Two Tap fires certain postMessage events during the purchase flow. The object is a hash that looks like: { action: Action, various_args }.

Action Returns Description
cart_contents_changed cart_contents This event fires anytime a cart is changed or when a purchase has been completed successfully.
cart_contents looks like { siteId: { productMD5: { title, image, price, url, original_url } } }
cart_finalized This event fires anytime a purchase has completed successfully.
close_pressed This event fires if the user presses the close button (see the show_close_button parameter).

iOS Integration Example

We provide a very simple skeleton example for a iOS app integrating the HTML5 interface in this github repository.

Helpful notes

We provide a couple of helper functions that you can use in your iOS workflow.

You can use the stringByEvaluatingJavaScriptFromString: function to run them.


NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:@"currentCartJSON()"];
            
Function Description
currentCartJSON() Returns the current updated cart in JSON form. On a finalized purchase the result will be empty.
The hash format is { siteId: { productMD5: { title, image, price } } }
postMessagesJSON() The checkout interface triggers postMessages alerts. Since it's impossible to push these updates on mobile devices postMessagesJSON() can be polled periodically to retrieve what's being sent.


API Interface

You can use the API to create your own personalized checkout experience.

The interaction is divided into three separate tasks which have to be executed in order: '/cart', '/purchase', '/purchase/confirm'.

As we are growing to support hundreds of stores, even though we check the integrations constantly, edge cases and failures are bound to happen.

If you want to integrate the Two Tap API directly you should consider using the HTTP integration flow or SMS integration flow. Implementing these flows allows us to intervene in case of issues in the background and resolve them quickly without the shopper noticing anything.


Product Availability

To ask for product information use the /cart endpoint.


SMS Checkout flow

The SMS flow is an easy way to integrate Two Tap. The API will handle the shopper communication via SMS, and all you have to do is confirm the purchase.

The diagram below describes how you should interact with the API to create an SMS flow.

SMS confirm flow

To start the HTTP SMS flow send a confirm option like below with the /purchase request.

This step is not necessary if you are using the HTML5 interface.


#!/usr/bin/env ruby

fields_input = __INPUT__
products = __PRODUCTS__

confirm = {}
confirm['method'] = 'sms'
confirm['phone'] = 'phone number'
confirm['sms_confirm_url'] = 'An endpoint on your end where you will handle confirmations.'
confirm['sms_finished_url'] = 'An endpoint on your end where we will send the result of the purchase.'
confirm['retry_url'] = 'your_url_type://purchase/%%PURCHASE_ID%%/retry'

response = RestClient.post "https://api.twotap.com/v1.0/purchase?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input, 
  products: products,
  confirm: confirm 
}

SMS confirm URL

Right at the end the HTML5 interface and API will ask you to perform the last '/purchase/confirm' API call.

This is done for security reasons and also as a way for you to retrieve/store purchase information.

At this step you should check that the unique_token and list of products match the ones from your initial call.

You have to return the JSON response of the '/purchase/confirm' call.


// example POST API callback:
// POST http://an_url/callback
//
// encoded JSON body arguments:
// purchase_id
// unique_token - a unique token if sent by the HTML5 interface
// sites { 
//   [site_id] {
//     products: { productMD5: { title, price, image, url } }
//     prices: { sales_tax, shipping_price, coupon_value, gift_card_value, final_price }
//     status: 'done or failed'
//     status_messages: [ 'An array of messages in case of failures' ]
//   }
// }

// example nodejs implementation:
exports.confirmURLCallback = function(req, res) {
  var privateToken = 'YOUR_PRIVATE_TWOTAP_TOKEN';
  var testMode = req.body.test_mode;

  var callPath = '/v1.0/purchase/confirm?private_token=' + privateToken; 

  // Pass whatever testMode is being used to the API call.
  if (testMode && testMode.length > 0) {
    callPath += "&test_mode=" + testMode;
  }

  var purchaseId  = req.body.purchase_id;
  var uniqueToken = req.body.unique_token;
  var sites       = req.body.sites; 

  // ...Check validity of request...
  // Check that you have created the unique token.
  // And that the product urls were part of the 
  // original request.

  // Call the Two Tap api to confirm (note https).
  rest.post('https://api.twotap.com' + callPath, {
    data: { purchase_id: purchaseId },

  }).on('complete', function(data, response) {
    // This is important! 
    // This is where you are feeding back the confirmation to the API.
    res.json(data);
    return;
  });  
};

SMS finished URL

This endpoint is called after we've finished the purchase process.

Alternatively you can hit our /purchase/status endpoint to fetch information like order_ids, and status of all stores.


// example POST API callback:
// POST http://an_url/callback
//
// encoded JSON body arguments:
// purchase_id
// sites { 
//   [site_id] {
//     order_id: 'order id'
//     status: 'done or failed'
//     status_messages: [ 'An array of messages in case of failures' ]
//   }
// }

// Example nodejs implementation:
exports.confirmURLCallback = function(req, res) {
  var purchaseId = req.body.purchase_id;
  // ...
  res.json({});
}

Retrying SMS purchases

Using the SMS flow if a purchase doesn't go through, which in most cases means payment failure, shoppers receive an SMS with a link to restart the order.

By default the message looks like this: "You can restart your purchase anytime: http://ttap.co/XXXXXX". This opens in Safari, keeping all your branding and options, allowing the shopper to re-enter the information.

Often times on iOS you don't want to open the links in Safari, and you want to redirect to your app. Tweaking the 'retry_url' allows you to do this.


retry_url: 'your_url_type://purchase/%%PURCHASE_ID%%/retry'
            

When they get back to your app in the 'retry' flow you are receiving a purchase_id. All you have to do is to open an UIWebView pointing it to https://checkout.twotap.com/retry?purchase_id=%%PURCHASE_ID%%.


HTTP Checkout flow

The HTTP flow allows you more flexibility and control with shopper communication. You can use push notifications or other ways of prompting her to confirm purchases.

The diagram below describes how you should interact with the API to create an HTTP flow.

HTTP confirm flow

To start the HTTP API flow send a confirm option like below with the /purchase request.

This step is not necessary if you are using the HTML5 interface.


#!/usr/bin/env ruby

fields_input = __INPUT__
products = __PRODUCTS__

confirm = {}
confirm['method'] = 'http'
confirm['http_confirm_url']  = 'An endpoint on your end where you will handle confirmations.'
confirm['http_finished_url'] = 'An endpoint on your end where we will send the result of the purchase.'

response = RestClient.post "https://api.twotap.com/v1.0/purchase?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input, 
  products: products, 
  confirm: confirm 
}

HTTP confirm url

The HTTP confirm url is called with the product information, purchase prices, but most importantly two variables 'confirm_with_user (true or false)', and 'confirm_message'.

If our estimates were lower than the final purchase prices, confirm_with_user will be true, and we'll ask you to confirm the new values with the shopper. The confirmation message is in the confirm_message variable.

When you are ready hit our /purchase/confirm endpoint which will finalize the proccess.

Please note that 2 minutes after this call the session will expire.


// example POST API callback:
// POST http://an_url/callback
//
// encoded JSON body arguments:
// purchase_id
// unique_token - If using HTML5 interface
// sites { 
//   [site_id] {
//     products: { productMD5: { title, price, image, url } }
//     prices: { sales_tax, shipping_price, coupon_value, gift_card_value, final_price }
//     status: 'done or failed'
//     status_messages: [ 'An array of messages in case of failures' ]
//   }
// }
// confirm_with_user - true or false
// confirm_message - A string representing a message to ask the user 
// test_mode - empty, dummy_fake, or fake_confirm

// Example nodejs implementation:
exports.confirmURLCallback = function(req, res) {
  var purchaseId      = req.body.purchase_id;
  var sites           = req.body.sites;
  var confirmWithUser = req.body.confirm_with_user;
  var confirmMessage  = req.body.confirm_message;

  if (confirmWithUser) {
    // Confirm with shopper, use confirmMessage.
  } else {
    confirm();
  }  

  res.json({});
}

// Call this when you are ready to confirm the purchase:
function confirm() {
  var privateToken = 'YOUR_PRIVATE_TWOTAP_TOKEN';
  var testMode = req.body.test_mode;

  var callPath = '/v1.0/purchase/confirm?private_token=' + privateToken; 
  // Pass whatever testMode is being used to the API call.
  if (testMode && testMode.length > 0) {
    callPath += "&test_mode=" + testMode;
  }

  // Call the Two Tap api to confirm (note https).
  rest.post('https://api.twotap.com' + callPath, {
    data: { purchase_id: purchaseId },
  }).on('complete', function(data, response) {
  });

  res.json({});
}

HTTP finished URL

This endpoint is called after we've finished the purchase process. We'll send you a final_message which contains a confirmation you could give to the user.

Alternatively you can hit our /purchase/status endpoint to fetch information like order_ids, and status of all stores.


// example POST API callback:
// POST http://an_url/callback
//
// encoded JSON body arguments:
// purchase_id
// sites { 
//   [site_id] {
//     order_id: 'order id'
//     status: 'done or failed'
//     status_messages: [ 'An array of messages in case of failures' ]
//   }
// }
// final_message - A string representing a confirmation message to the user

// Example nodejs implementation:
exports.confirmURLCallback = function(req, res) {
  var purchaseId = req.body.purchase_id;
  var finalMessage = req.body.final_message;
  
  // Send final_message to the user.

  res.json({});
}

API methods

Cart

The first request you have to make is to '/cart'. Send '/cart' a list of products and it will return product information (title, price, image) and the required fields that the user has to fill in to finalize the purchase.

The response will include the required fields for one or two possible checkout flows: authenticated (authCheckout, if the user has an account on that website), and as guest (noauthCheckout).

You can not add products to an existing cart. If you'd like to add more products feel free to start a new '/cart' call.

This request is processed in the background. You either have to call '/cart/status' which will retrieve the relevant information once the job is finished, or set 'finished_url' which will trigger a callback to your servers.

Initial request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

products = [ 'http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/' ]

response = RestClient.post "https://api.twotap.com/v1.0/cart?public_token=PUBLIC_TOKEN", { products: products }

puts response.body
                  

#!/bin/bash

curl https://api.twotap.com/v1.0/cart?public_token=PUBLIC_TOKEN --data "products:http://fab.com/sale/4850/product/11263/,http://fab.com/sale/4800/product/43565/"
                  

#!/usr/bin/python

import urllib
import urllib2

public_token = 'PUBLIC_TOKEN'
url = 'https://api.twotap.com/v1.0/cart?public_token=' + public_token
values = {'products': [ 'http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/' ]}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
print response.read()
                  

var xhr = new XMLHttpRequest();
var public_token = "PUBLIC_TOKEN";
xhr.open("POST", "https://api.twotap.com/v1.0/cart?public_token=" + public_token);
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send("products:http://fab.com/sale/4850/product/11263/,http://fab.com/sale/4800/product/43565/");
                  

var request = require("request");
var public_token = 'PUBLIC_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/cart?public_token=' + public_token,
  json: { "products": [ "http://fab.com/sale/4850/product/11263/", "http://fab.com/sale/4800/product/43565/" ]},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$public_token = 'PUBLIC_TOKEN';
$url = 'https://api.twotap.com/v1.0/cart?public_token='.$public_token;
$products = array('products' => 'http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/');
$response = Requests::post($url, array(), $products);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    url := "https://api.twotap.com/v1.0/cart?public_token=" + public_token
    var jsonStr = []byte(`{"products":"http://fab.com/sale/4850/product/11263/,http://fab.com/sale/4800/product/43565/"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/cart?public_token=" + public_token);
      dynamic payload = new ExpandoObject ();
      payload.products = new String[] { "http://fab.com/sale/4850/product/11263/", "http://fab.com/sale/4800/product/43565/" };
      string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json)).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Request

POST https://api.twotap.com/v1.0/cart?public_token=PUBLIC_TOKEN

Parameters

products A list of product URLs. Max 15 in one request, the API will silently drop anything above that.
finished_url (Optional) An endpoint where Two Tap can POST the product information once the information is retrieved.
test_mode (Optional) dummy_data or fake_confirm. A way to test the API the interface without making actual purchases.

Response


{
  "cart_id": "50f414b9e6a4869bf6000009",
  "message": "still_processing",
  "description": "Still processing."
}                    
            

Response description

cart_id An ID to use when checking the cart status.
message The status, which will be 'still_processing'.
description A more human friendly description of the status.

Cart status

Call '/cart/status' to see the status of an '/cart' request. The response is different if response['message'] is 'still_processing', 'failed', or 'done'.

Status request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/cart/status?public_token=PUBLIC_TOKEN&cart_id=#{cart_id}"

puts response.body
                  

#!/bin/bash

PUBLIC_TOKEN=PUBLIC_TOKEN
CART_ID=CART_ID

curl https://api.twotap.com/v1.0/cart/status?public_token=$PUBLIC_TOKEN\&cart_id=$CART_ID
                  

#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
cart_id = 'CART_ID'
r = requests.get('https://api.twotap.com/v1.0/cart/status?public_token=' + public_token + '&cart_id=' + cart_id)
print(r.text)
                  

var xhr = new XMLHttpRequest();
var public_token = "PUBLIC_TOKEN";
var cart_id = "CART_ID";
xhr.open("GET", "https://api.twotap.com/v1.0/cart/status?public_token=" + public_token + "&cart_id=" + cart_id);
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(null);
                  

var request = require("request");
var public_token = 'PUBLIC_TOKEN';
var cart_id = 'CART_ID';
request({
  url: 'https://api.twotap.com/v1.0/cart/status?public_token=' + public_token + '&cart_id=' + cart_id,
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$public_token = 'PUBLIC_TOKEN';
$cart_id = 'CART_ID';
$url = 'https://api.twotap.com/v1.0/cart/status?public_token='.$public_token.'&cart_id='.$cart_id;
$response = Requests::get($url);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    cart_id := "CART_ID"
    url := "https://api.twotap.com/v1.0/cart/status?public_token=" + public_token + "&cart_id=" + cart_id
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var cart_id = "CART_ID";
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/cart/status?public_token=" + public_token + "&cart_id=" + cart_id);
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Request

GET https://api.twotap.com/v1.0/cart/status?public_token=PUBLIC_TOKEN

Parameters

cart_id The cart id that is sent by '/cart'.
test_mode (Optional) dummy_data or fake_confirm. A way to test the API the interface without making actual purchases.

Still processing response


{
  "sites": {
    "50f414b9e6a4869bf6000002": {
      "cart": {
        "8aa20fab1787c6f9eef77a0bd8a70ae3": {
          "last_message": "Fetching title.",
          "url": "http://fab.com/sale/4850/product/11263/",
          "status": "done"
        },
        "aa5cb27ddd44c3cb639d11419d9574db": {
          "last_message": "Fetching required fields.",
          "url": "http://fab.com/sale/4800/product/43565/",
          "status": "still_processing"
        }
      },
      "info": {
        "name": "Fab",
        "url": "fab.com"
      }
    }
  },
  "cart_id": "50f414b9e6a4869bf6000009",
  "message": "still_processing",
  "description": "Still processing."
} 
          

Completed response


{
  "sites": {
    "50f414b9e6a4869bf6000002": {
      "info": {
        "name": "Fab",
        "url": "fab.com"
      },
      "coupon_support": true,
      "gift_card_support": true,
      "shipping_options": {
        "cheapest": "Usually 7-10 days",
        "fastest": "Usually 1-3 days",
      },
      "required_fields": {
        "authCheckout": {
          "country": {
            "input_options": [],
            "input_type": "text",
            "input_name": "INPUT"
          }
        },
        "noauthCheckout": {
          "address": {
            "field_type": "shipping",
            "input_values": [],
            "input_type": "text",
            "input_name": "INPUT"
          },
          "last_name": {
            "field_type": "shipping",
            "input_values": [],
            "input_type": "text",
            "input_name": "INPUT"
          },
          "first_name": {
            "field_type": "shipping",
            "input_values": [],
            "input_type": "text",
            "input_name": "INPUT"
          },
           "shipping_country": {
            "field_type": "shipping",
            "input_values": [
              {
                "value": "",
                "text": "Please Select"
              },
              {
                "value": "AG",
                "text": "Antigua And Barbuda"
              },
              {
                "value": "AR",
                "text": "Argentina"
              },
            ],
            "input_type": "select-one",
            "input_name": "SELECT"
          }
        },
        "login": {
          "password": {
            "input_options": [],
            "input_type": "password",
            "input_name": "INPUT"
          },
          "email": {
            "input_options": [],
            "input_type": "text",
            "input_name": "INPUT"
          }
        }
      },
      "add_to_cart": {
        "367bcd6f21fc7568b135c387d3fa5d57": {
          "required_fields": {
            "quantity": {
              "input_type": "text",
              "input_name": "INPUT"
            },
            "Size": {
              "input_type": "select-one",
              "input_name": "SELECT"
            },
            "Color": {
              "input_type": "select-one",
              "input_name": "SELECT"
            }
          },
          "required_field_values": {
            "Color": [
              {
                "value": "",
                "text": "First, choose color",
                "dep": {
                  "Size": [
                    {
                      "value": "",
                      "text": "Then, choose size",
                      "dep": {}
                    }
                  ]
                }
              },
              {
                "value": "13233550|1055745",
                "text": "Red",
                "dep": {
                  "Size": [
                    {
                      "value": "",
                      "text": "Then, choose size",
                      "dep": {}
                    },
                    {
                      "value": "13233550|13442751",
                      "text": "28",
                      "dep": {}
                    },
                    {
                      "value": "13233550|13442753",
                      "text": "30",
                      "dep": {}
                    },
                  ]
                }
              },
              {
                "value": "13233550|1055747",
                "text": "Black",
                "dep": {
                  "Size": [
                    {
                      "value": "",
                      "text": "Then, choose size",
                      "dep": {}
                    },
                    {
                      "value": "13233550|13442771",
                      "text": "28",
                      "dep": {}
                    },
                  ]
                }
              },
              {
                "value": "13233550|1068432",
                "text": "New Blue",
                "dep": {
                  "Size": [
                    {
                      "value": "",
                      "text": "Then, choose size",
                      "dep": {}
                    },
                    {
                      "value": "13233550|13442762",
                      "text": "29",
                      "dep": {}
                    },
                    {
                      "value": "13233550|13442763",
                      "text": "30",
                      "dep": {}
                    }
                  ]
                }
              }
            ]
          },          
          "url": "http://fab.com/sale/4850/product/11263",
          "image": "//d15bx2axpp9a1x.cloudfront.net/product/92931-360x360-1355670380-primary.png",
          "alt_images": [ 
            "//d15bx2axpp9a1x.cloudfront.net/product/92931-360x360-1355670381-primary.png",
            "//d15bx2axpp9a1x.cloudfront.net/product/92931-360x360-1355670382-primary.png"
          ],
          "price": "41€",
          "title": "Heart Pendant Red Small",
          "status": "done"
        }
      },
      "failed_to_cart": {},
        "8aa20fab1787c6f9eef77a0bd8a70ae3": {
          "field_values": {},
          "url": "http://fab.com/sale/4800/product/43565/"
        },
      }
    }
  },
  "unknown_urls": [
    "http://www.google.com"
  ],
  "cart_id": "511fa680692d5d9e06000001",
  "message": "has_failures",
  "description": "Encountered some errors while parsing the urls."
}                        
            

Response description

message The status, which can be 'still_processing', 'has_failures', or 'done'.
description A more human friendly description of the status.
unknown_urls An array with URLs that Two Tap does not support yet.
sites A hash that contains information grouped by a siteID.
sites[id][info] A hash that contains the 'name' and 'url' of the site.
sites[id][coupon_support] true or false. If we have coupon support for a certain site.
sites[id][gift_card_support] true or false. If we have gift card support for a certain site.
sites[id][shipping_options] Can be empty, or a hash that contains available shipping options. { "cheapest": "description", "fastest": "description" }.
sites[id][required_fields]

A hash that contains the required fields grouped by three possible flows: 'login', 'authCheckout', and 'noauthCheckout'.

For non-authenticated checkouts send the 'noauthCheckout' fields. For authenticated checkouts send the 'authCheckout' and 'login' fields.

sites[id][required_fields][flow] A hash that contains the required fields grouped by the field name.
sites[id][required_fields][flow][field] A hash that contains input_name, input_type, and input_options (in case of SELECT).
sites[id][add_to_cart] A hash that contains product information grouped by it's URL MD5.
sites[id][add_to_cart][productMD5][last_message] The latest status message sent by the request processor. This is used during the 'still_processing' phase.
sites[id][add_to_cart][productMD5][required_fields] A hash that contains the product required fields grouped by their field name.
sites[id][add_to_cart][productMD5][required_field_values] A hash that contains the possible product required field values as a dependency tree.
sites[id][add_to_cart][productMD5][url] URL of the product.
sites[id][add_to_cart][productMD5][image] The product's image URL.
sites[id][add_to_cart][productMD5][alt_images] The product's secondary images as an array.
sites[id][add_to_cart][productMD5][price] The product's price.
sites[id][add_to_cart][productMD5][title] The product's title.
sites[id][add_to_cart][productMD5][status] The processing status for this product. Can be 'done' or 'still_processing'.
sites[id][failed_to_cart] Similar to sites[id][add_to_cart] however it lists which products have failed processing for some reason.

Cart estimates

This endpoint will return an estimated sales tax, shipping price, and final price for a certain cart.

The more information you can give it the more accurate it becomes.

Usually you should send the shopper selected product options. Some product attribute combinations have different prices so in order to have a valid estimates the attributes are recommended.

You can also send the shoppers shipping city, state, and zip code for more accurate sales tax calculations.

Request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]
fields_input = { 
  [site_id]: { 
    addToCart: { 
      [product_md5]: { 
        size: 'M'
      }
    },
    noauthCheckout: {
      shipping_zip: '94303',
      shipping_city: 'City',
      shipping_state: 'State'
    },
    shipping_option: 'cheapest'
  } 
}

products = [ 'http://fab.com/sale/4850/product/11263/', 'http://fab.com/sale/4800/product/43565/' ]

response = RestClient.post "https://api.twotap.com/v1.0/cart/estimates?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input, 
  products: products 
}

puts response.body
            

#!/bin/sh

PUBLIC_TOKEN=PUBLIC_TOKEN

curl "https://api.twotap.com/v1.0/cart/estimates?public_token=$PUBLIC_TOKEN" --header "Content-Type: application/json" --request POST --include --data-binary '{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID: {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        }
      },
      "noauthCheckout": {
        "shipping_zip": "94303",
        "shipping_city": "City",
        "shipping_state": "State"
      },
      "shipping_option": "cheapest"
    }
  }
}'
                  

#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
payload = {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        }
      },
      "noauthCheckout": {
        "shipping_zip": "94303",
        "shipping_city": "City",
        "shipping_state": "State"
      },
      "shipping_option": "cheapest"
    }
  }
};
r = requests.post('https://api.twotap.com/v1.0/cart/estimates?public_token=' + public_token, data=payload)
print(r.text)
                  

var xhr = new XMLHttpRequest();
var public_token = "PUBLIC_TOKEN";
xhr.open("POST", "https://api.twotap.com/v1.0/cart/estimates?public_token=" + public_token);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(JSON.stringify(
{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        },
        "noauthCheckout": {
          "shipping_zip": "94303",
          "shipping_city": "City",
          "shipping_state": "State"
        },
        "shipping_option": "cheapest"
      }
    }
  }
}));
                  

var request = require("request");
var public_token = 'PUBLIC_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/cart/estimates?public_token=' + public_token,
  json: {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "addToCart": {
        "PRODUCT_MD5": {
          "size": "M" 
        }
      },
      "noauthCheckout": {
        "shipping_zip": "94303",
        "shipping_city": "City",
        "shipping_state": "State"
      },
      "shipping_option": "cheapest"
    }
  }
},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$public_token = 'PUBLIC_TOKEN';
$url = 'https://api.twotap.com/v1.0/cart/estimates?public_token='.$public_token;
$payload = array(
  "cart_id" => "CART_ID",
  "fields_input" => array(
    "SITE_ID" => array(
      "addToCart" => array(
        "PRODUCT_MD5" => array(
          "size" => "M"
        )
      ),
      "noauthCheckout" => array(
        "shipping_zip" => "94303",
        "shipping_city" => "City",
        "shipping_state" => "State"
      ),
      "shipping_option" => "cheapest"
    )
  )
);
$response = Requests::post($url, array(), $payload);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    url := "https://api.twotap.com/v1.0/cart/estimates?public_token=" + public_token
    var jsonStr = []byte(`{
      "cart_id": "CART_ID",
      "fields_input": {
        "SITE_ID": {
          "addToCart": {
            "PRODUCT_MD5": {
              "size": "M" 
            }
          },
          "noauthCheckout": {
            "shipping_zip": "94303",
            "shipping_city": "City",
            "shipping_state": "State"
          },
          "shipping_option": "cheapest"
        }
      }
    }`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Add("Content-Type", "application/json")
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}s
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var cart_id = "CART_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/cart/estimates?public_token=" + public_token);
      dynamic payload = new ExpandoObject ();
      payload.cart_id = cart_id;
      payload.fields_input = new ExpandoObject ();
      payload.fields_input.SITE_ID = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5 = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5.size = "M";
      payload.fields_input.SITE_ID.noauthCheckout = new ExpandoObject ();
      payload.fields_input.SITE_ID.noauthCheckout.shipping_zip = "94303";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_city = "City";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_state = "State";
      payload.fields_input.shipping_option = "cheapest";
      string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json)).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Request

POST https://api.twotap.com/v1.0/cart/estimates?public_token=PUBLIC_TOKEN

Parameters

cart_id A cart id that has finished running.
fields A hash that looks like:
{
  "[site_id]":
    {
      "noauthCheckout": { required checkout fields },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      "shipping_option": { "cheapest or fastest" } (optional),
    }
}
products (Optional) An array of product URLs that have accepted by the user. This is useful in case a user removes some items between /cart and /purchase.

If specified fields_input should contain only the required data for those products.

Response


{
  "message":"done",
  "estimates":{
    "51bf1d1055a0f9e0d0000003":{
      "prices":{
        "shipping_price":"$5.00",
        "sales_tax":"$2.54",
        "final_price":"$31.54"
      }
    }
  }
}
            

Response description

message The status, which will be 'done'.
estimates A hash that looks like { [site_id]: { prices: { shipping_price, sales_tax, final_price } } }

Purchase

Purchase will retrieve the shipping price, sales tax, and final price of the order. It will start the checkout process but not finish it to ensure the data sent back is valid.

If in the '/cart' request the required_fields entry contains both 'authCheckout' and 'noauthCheckout' it means that two checkout flows are supported: logged in and not logged in. You have to pick one of the two flows. If you send both the API will proceed with the not logged in checkout flow.

This request needs to be POSTed with the field information that was required in '/cart', and uses only the valid products from that call.

To keep things simple if you're building your own native checkout always ask for the following fields, and send them all with each site_id. This way you don't need to look into each site's required fields.


  [ 
    'email',
    'shipping_title',
    'shipping_first_name',
    'shipping_last_name',
    'shipping_address',
    'shipping_city',
    'shipping_state',
    'shipping_country',
    'shipping_zip',
    'shipping_telephone',
    'billing_title',
    'billing_first_name',
    'billing_last_name',
    'billing_address',
    'billing_city',
    'billing_state',
    'billing_country',
    'billing_zip',
    'billing_telephone',
    'card_type',
    'card_number',
    'card_name',
    'expiry_date_year',
    'expiry_date_month',
    'cvv'
  ]

This request is processed in the background which means you either have to call '/purchase/status' or use the SMS API flow or HTTP API flow to receive callbacks.

Once this request finishes you will have 2 minutes available to call '/purchase/confirm' to confirm it.

On authenticated checkout flows (authCheckout) Two Tap currently supports only using the shipping and payment information the user has saved on that particular site.

If you are using the API directly we highly recommend you use an address validator like lob.com or easypost.com in your app/service. It's free.

Initial request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

cart_id = ARGV[0]

fields_input = { 
  [site_id]: {
    "noauthCheckout": { "email" => "shopper@gmail.com", "shipping_telephone" => "6503941234", "shipping_zip" => "94303", "shipping_state" => "California", "shipping_city" => "Palo Alto", "shipping_address" => "555 Palo Alto Avenue", ".." => ".." },
    "addToCart": {
      [product_md5]: { "quantity": 1 }
    }
  }
}

affiliate_links = {
  [site_id]: "http://affiliate_link"  
}

products = [ 'http://fab.com/sale/4850/product/11263' ]

response = RestClient.post "https://api.twotap.com/v1.0/purchase?public_token=PUBLIC_TOKEN", { 
  cart_id: cart_id, 
  fields_input: fields_input, 
  affiliate_links: affiliate_links, 
  products: products 
}

puts response.body
            

#!/bin/sh

PUBLIC_TOKEN=PUBLIC_TOKEN

curl "https://api.twotap.com/v1.0/purchase?public_token=$PUBLIC_TOKEN" --header "Content-Type: application/json" --request POST --include --data-binary '
{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
   "SITE_ID": "http://affiliate_link"  
  },
  "products": "http://fab.com/sale/4850/product/11263"
}'
                  

#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
payload = {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
   "SITE_ID": "http://affiliate_link"  
  },
  "products": "http://fab.com/sale/4850/product/11263"
};
r = requests.post('https://api.twotap.com/v1.0/purchase?public_token=' + public_token, data=payload)
print(r.text)
                  

var xhr = new XMLHttpRequest();
var public_token = "PUBLIC_TOKEN";
xhr.open("POST", "https://api.twotap.com/v1.0/purchase?public_token=" + public_token);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(JSON.stringify({
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
   "SITE_ID": "http://affiliate_link"  
  },
  "products": "http://fab.com/sale/4850/product/11263"
}));
                  

var request = require("request");
var public_token = 'PUBLIC_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/purchase?public_token=' + public_token,
  json: {
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
  "SITE_ID": "http://affiliate_link"  
  },
  "products": "http://fab.com/sale/4850/product/11263"
},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$public_token = 'PUBLIC_TOKEN';
$url = 'https://api.twotap.com/v1.0/purchase?public_token='.$public_token;
$payload = array(
  "cart_id" => "CART_ID",
  "fields_input" => array(
    "SITE_ID" => array(
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart" => array(
        "[product_md5]" => array( "quantity" => 1 )
      )
    )
  ),
  "affiliate_links" => array(
   "SITE_ID" => "http://affiliate_link"  
  ),
  "products" => "http://fab.com/sale/4850/product/11263"
);
$response = Requests::post($url, array(), $payload);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    url := "https://api.twotap.com/v1.0/purchase?public_token=" + public_token
    var jsonStr = []byte(`{
  "cart_id": "CART_ID",
  "fields_input": {
    "SITE_ID": {
      "noauthCheckout": { "email": "shopper@gmail.com", "shipping_telephone": "6503941234", "shipping_zip": "94303", "shipping_state": "California", "shipping_city": "Palo Alto", "shipping_address": "555 Palo Alto Avenue", "..": ".." },
      "addToCart": {
        "[product_md5]": { "quantity": 1 }
      }
    }
  },
  "affiliate_links": {
  "SITE_ID": "http://affiliate_link"  
  },
  "products": "http://fab.com/sale/4850/product/11263"
}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Add("Content-Type", "application/json")
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var cart_id = "CART_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase?public_token=" + public_token);
      dynamic payload = new ExpandoObject ();
      payload.cart_id = cart_id;
      payload.fields_input = new ExpandoObject ();
      payload.fields_input.SITE_ID = new ExpandoObject ();
      payload.fields_input.SITE_ID.noauthCheckout = new ExpandoObject ();
      payload.fields_input.SITE_ID.noauthCheckout.email = "shopper@gmail.com";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_telephone = "6503941234";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_zip = "94303";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_city = "Palo Alto";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_state = "California";
      payload.fields_input.SITE_ID.noauthCheckout.shipping_address = "555 Palo Alto Avenue";
      payload.fields_input.SITE_ID.addToCart = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5 = new ExpandoObject ();
      payload.fields_input.SITE_ID.addToCart.PRODUCT_MD5.quantity = 1;
      payload.affiliate_links = new ExpandoObject ();
      payload.affiliate_links.SITE_ID = "http://affiliate_link";
      payload.products = new String[] { "http://fab.com/sale/4850/product/11263" };
      string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json)).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Request

POST https://api.twotap.com/v1.0/purchase?public_token=PUBLIC_TOKEN

Parameters

cart_id The cart id that is sent by '/cart'.
fields_input For non-authenticated checkout a hash that has the form:
{
  "[site_id]":
    {
      "noauthCheckout": { required checkout fields },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      },
      "coupon": "coupon value" } (optional),
      "gift_card": { "number": "number", "pin": "pin") } (optional),
      "shipping_option": { "cheapest or fastest" } (optional),
    }
}
For authenticated checkout a hash that has the form:
{
  "[site_id]":
    {
      "authCheckout": { required checkout fields },
      "login": { required login fields },
      "addToCart": {
        "[productMD5_1]": "required product fields",
        "[productMD5_2]": "required product fields",
      }
      "coupon": "coupon value" } (optional),
      "gift_card": { "number": "number", "pin": "pin") } (optional),
      "shipping_option": { "cheapest or fastest" } (optional),
    }
}
affiliate_links (Optional) A hash that has the form:
{ "[site_id]": "affiliate_link" }
or if you need per product affiliate links:
{ "[site_id]": { "[productMD5]": "affiliate_link" } }
Please note that per product affiliate links will increase the checkout time.
confirm (Optional but highly recommended) Instead of polling /purchase/status Two Tap can send callbacks as it's processing the purchase. See the SMS API flow or HTTP API flow.
products (Optional) An array of product URLs that have accepted by the user. This is useful in case a user removes some items between /cart and /purchase.

If specified fields_input should contain only the required data for those products.
test_mode (Optional) dummy_data or fake_confirm. A way to test the API the interface without making actual purchases.

Response


{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "still_processing",
  "description": "Still processing."
}              
            

Purchase status

Call '/purchase/status' to see the status of a '/purchase' request. The response is different if response['message'] is 'still_processing', 'failed', or 'done'.

Status Request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/purchase/status?public_token=PUBLIC_TOKEN&purchase_id=#{purchase_id}"

puts response.body
                  

#!/bin/bash

PUBLIC_TOKEN=PUBLIC_TOKEN
PURCHASE_ID=PURCHASE_ID

curl https://api.twotap.com/v1.0/purchase/status?public_token=$PUBLIC_TOKEN\&purchase_id=$PURCHASE_ID
                  

#!/usr/bin/python

import requests

public_token = 'PUBLIC_TOKEN'
purchase_id = 'PURCHASE_ID'
r = requests.get('https://api.twotap.com/v1.0/purchase/status?public_token=' + public_token + '&purchase_id=' + purchase_id)
print(r.text)
                  

var xhr = new XMLHttpRequest();
var public_token = "PUBLIC_TOKEN";
var purchase_id = "PURCHASE_ID";
xhr.open("GET", "https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&purchase_id=" + purchase_id);
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(null);
                  

var request = require("request");
var public_token = 'PUBLIC_TOKEN';
var purchase_id = 'PURCHASE_ID';
request({
  url: 'https://api.twotap.com/v1.0/purchase/status?public_token=' + public_token + '&purchase_id=' + purchase_id,
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$public_token = 'PUBLIC_TOKEN';
$purchase_id = 'PURCHASE_ID';
$url = 'https://api.twotap.com/v1.0/purchase/status?public_token='.$public_token.'&purchase_id='.$purchase_id;
$response = Requests::get($url);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    public_token := "PUBLIC_TOKEN"
    purchase_id := "PURCHASE_ID"
    url := "https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&purchase_id=" + purchase_id
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var public_token = "PUBLIC_TOKEN";
      var purchase_id = "PURCHASE_ID";
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase/status?public_token=" + public_token + "&purchase_id=" + purchase_id);
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Request

GET https://api.twotap.com/v1.0/purchase/status?public_token=PUBLIC_TOKEN

Parameters

purchase_id The purchase id that is sent by '/purchase'.
test_mode (Optional) dummy_data or fake_confirm. A way to test the API the interface without making actual purchases.

Still processing response


{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "still_processing",
  "description": "Still processing.",
  "sites": {
    "50f414b9e6a4869bf6000002": {
      "info": {
        "url": "fab.com",
        "name": "Fab"
      },
      "last_message": "Logging in.",
      "status": "still_processing"
    }
  }
}
          

Completed response


{
  "sites": {
    "50f414b9e6a4869bf6000002": {
      "prices": {
        "sales_tax": "$4.11",   
        "coupon_value": "$4.00",
        "gift_card_value": "$5.00",
        "final_price": "$51.10",
        "shipping_price": "$7.00"
      },
      "details": {
        "active_payment_method": "The active payment method (only for authenticated checkouts).",
        "active_shipping_address": "The active shipping address (only for authenticated checkouts).",
        "shipping_estimate": "The estimated delivery date",
      },
      "info": {
        "url": "fab.com",
        "name": "Fab"
      },
      "status": "done",
      "status_messages": [
        "An array of errors.",
        "Present only if status is failed."
      ]
    }
  },
  "pending_confirm": true,
  "purchase_id": "511294b0a73e4a9059000001",
  "message": "done"
}                      
            

Response description

purchase_id The purchase's id.
message The status, which can be 'still_processing', 'has_failures', or 'done'.
description A more human friendly description of the status.
pending_confirm Boolean, true or false. Whether /purchase/confirm was called.
sites A hash that contains information grouped by a siteID.
sites[id][prices] A hash that contains always contains the final_price, and may also contain a shipping_price, the sales_tax, a gift_card_value (if sent), a coupon_value (if sent).
sites[id][details] Contains information about the checkout, like the shipping_estimate. In the case of authenticated checkouts it can also include the active_payment_method and the active_shipping_address.
sites[id][order_id] After 'confirm' finished this could contain an order id.
sites[id][info] A hash that contains the 'name' and 'url' of the site.
sites[id][status] The status, which can be 'still_processing', 'has_failures', or 'done'.
sites[id][status_messages] An array of errors that is present if status is 'failed'.
sites[id]last_message] The latest status message sent by the request processor. This is used during the 'still_processing' phase.

Purchase Confirm

Purchase confirm finalizes the order. Only perform this request server side as it uses your private token.

This request is available for 2 minutes after '/purchase' finishes.

Initial request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.post "https://api.twotap.com/v1.0/purchase/confirm?private_token=PRIVATE_TOKEN", { purchase_id: purchase_id }

puts response.body
            

#!/bin/bash

PRIVATE_TOKEN=PRIVATE_TOKEN
PURCHASE_ID=PURCHASE_ID

curl https://api.twotap.com/v1.0/purchase/confirm?private_token=$PRIVATE_TOKEN --data-binary '{"purchase_id" : "$PURCHASE_ID"}'
                  

#!/usr/bin/python

import requests

private_token = 'PRIVATE_TOKEN'
payload = {"purchase_id": "PURCHASE_ID"}
r = requests.post('https://api.twotap.com/v1.0/purchase/confirm?private_token=' + private_token, data=payload)
print(r.text)
                  

var xhr = new XMLHttpRequest();
var private_token = "PRIVATE_TOKEN";
xhr.open("POST", "https://api.twotap.com/v1.0/purchase/confirm?private_token=" + private_token);
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(JSON.stringify({"purchase_id": "PURCHASE_ID"}));
                  

var request = require("request");
var private_token = 'PRIVATE_TOKEN';
request({
  url: 'https://api.twotap.com/v1.0/purchase/confirm?private_token=' + private_token,
  json: {"purchase_id": "PURCHASE_ID"},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$private_token = 'PRIVATE_TOKEN';
$url = 'https://api.twotap.com/v1.0/purchase/confirm?private_token='.$private_token;
$payload = array("purchase_id" => "PURCHASE_ID");
$response = Requests::post($url, array(), $payload);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    private_token := "PRIVATE_TOKEN"
    url := "https://api.twotap.com/v1.0/purchase/confirm?private_token=" + private_token
    var jsonStr = []byte(`{"purchase_id": "PURCHASE_ID"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      var private_token = "PRIVATE_TOKEN";
      var purchase_id = "PURCHASE_ID";
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/purchase/confirm?private_token=" + private_token);
      dynamic payload = new ExpandoObject ();
      payload.purchase_id = purchase_id;
      string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
      var result = client.PostAsync ("", new StringContent(json)).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Response


{
  "purchase_id": "50f414b9e6a4869bf6000010",
  "message": "still_processing",
  "description": "Still processing.",
}
            

Request

POST https://api.twotap.com/v1.0/purchase/confirm?private_token=PRIVATE_TOKEN

Parameters

purchase_id The purchase id that is sent by '/purchase'.
test_mode (Optional) dummy_data or fake_confirm. A way to test the API the interface without making actual purchases.

Validating input fields

Let's say you are capturing data from the user. Two Tap requires it to be in a certain format or it will reject it. This API method allows you to validate it before sending it with the purchase.

Request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

# A hash like { field_key: field_value }
flat_fields_input = { shipping_first_name: 'name', shipping_address: 'address' }
response = RestClient.post "https://api.twotap.com/v1.0/fields_input_validate", { flat_fields_input: flat_fields_input }

puts response.body
            

#!/bin/bash

curl https://api.twotap.com/v1.0/fields_input_validate --data-binary '{"shipping_first_name": "name", "shipping_address": "address"}'
                  

#!/usr/bin/python

import requests

payload = {"shipping_first_name": "name", "shipping_address": "address"}
r = requests.post('https://api.twotap.com/v1.0/fields_input_validate', data=payload)
print(r.text)
                  

var xhr = new XMLHttpRequest();
xhr.open("POST", 'https://api.twotap.com/v1.0/fields_input_validate');
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(JSON.stringify({"shipping_first_name": "name", "shipping_address": "address"}));
                  

var request = require("request");
request({
  url: 'https://api.twotap.com/v1.0/fields_input_validate',
  json: {"shipping_first_name": "name", "shipping_address": "address"},
  method: "POST"
}, function (err, reponse, body) {
  console.log(body); 
});
                  

require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$url = 'https://api.twotap.com/v1.0/fields_input_validate';
$payload = array("shipping_first_name" => "name", "shipping_address" => "address");
$response = Requests::post($url, array(), $payload);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "bytes"
)

func main() {
    url := "https://api.twotap.com/v1.0/fields_input_validate"
    var jsonStr = []byte(`{"shipping_first_name": "name", "shipping_address": "address"}`)
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/fields_input_validate");
      dynamic flat_fields_input = new ExpandoObject ();
      flat_fields_input.shipping_first_name = "name";
      flat_fields_input.shipping_address = "address";
      string json = Newtonsoft.Json.JsonConvert.SerializeObject(flat_fields_input);
      var result = client.PostAsync ("", new StringContent(json)).Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Response


{
  "message": "done or bad_required_fields",
  "description": "A string that lists the issues."
}
            

Supported sites

Query this url to get a list of our currently supported sites.

Request


#!/usr/bin/env ruby

require 'rubygems'
require 'rest_client'

purchase_id = ARGV[0]

response = RestClient.get "https://api.twotap.com/v1.0/supported_sites"

puts response.body
            

#!/bin/bash

curl https://api.twotap.com/v1.0/supported_sites
                  

#!/usr/bin/python

import requests

r = requests.get('https://api.twotap.com/v1.0/supported_sites');
print(r.text)
                  

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.twotap.com/v1.0/supported_sites");
xhr.onreadystatechange = function() {
  if (this.readyState == 4) {
    console.log(this.responseText);
  }
};
xhr.send(null);
                  

var request = require("request");
request({
  url: 'https://api.twotap.com/v1.0/supported_sites',
  method: "GET"
}, function (err, reponse, body) {
  console.log(body); 
});
                  


require_once 'Requests/library/Requests.php';

Requests::register_autoloader();
$url = 'https://api.twotap.com/v1.0/supported_sites';
$response = Requests::get($url);
echo $response->body;
                  

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
    url := "https://api.twotap.com/v1.0/supported_sites"
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
                  

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Dynamic;

public class EmptyClass
{
  public static void Main ()
  {
    using (var client = new HttpClient ()) {
      client.BaseAddress = new Uri ("https://api.twotap.com/v1.0/supported_sites");
      var result = client.GetAsync ("").Result;
      String resultContent = result.Content.ReadAsStringAsync ().Result;
      Console.WriteLine (resultContent);
    }
  }
}
                  

Response


[
  {
    "id": "52d7a85ece04fabdcd000005",
    "name": "Shoebuy",
    "url": "shoebuy.com",
    "logo": "https://core.twotap.com/system/recipes/logos/5374/e544/ce04/faea/d600/000f/small/Shoebuy_2.png?1400808613861"
  }
  [..]
]
            

Testing

There are two test modes available in the API: dummy_data and fake_confirm. You can send a &test_mode=[dummy_data or fake_confirm] keypair with any API calls.

For more information see testing in the HTML5 interface.

Examples

See complete checkout flows in different programming languages in this github repository.




The Wallet

The wallet allows users and publishers to store data with Two Tap and use them for purchases. The retrieving and storing of data is integrated in the '/cart' and '/purchase' requests if 'user_token' is sent as a parameter.


Storing data (/purchase)

Storing of data is done automatically during '/purchase' if a 'user_token' is supplied.


Retrieving data (/cart)

When posting a request to '/cart' if a 'user_token' is present the API will respond with the stored entries for the required fields.

The stored fields are grouped in three possible categories: 'shipping', 'payment', and 'login'. The user can store multiple shipping addresses, payment methods, and login credentials which are grouped together.

The 'shipping' and 'payment' groups are identified by a random id. 'login' groups are identified by the site_id they belong to.

Wallet Retrieval



# Similar to a regular '/cart' request however an 'user_token' is specified.
response = RestClient.post "https://api.twotap.com/v1.0/cart?public_token=PUBLIC_TOKEN", { 
  products: products, 
  user_token: USER_TOKEN 
}

# Completed Status Response
{
  "sites": {
    # ...
  },
  "stored_field_values": {
    "shipping": {
      "o6nfusor": {
        "data": {
          "email": {
            "value": "anemail@gmail.com"
          },
          "shipping_last_name": {
            "value": "Doe"
          },
          "shipping_first_name": {
            "value": "Joe"
          }
        },
        "name": "John, Doe, anemail@gmail.com"
      }
    },
    "login": {
      "50f414b9e6a4869bf6000002": {
        "data": {
          "email": {
            "value": "anuser@gmail.com"
          }
        },
        "name": "anuser@gmail.com"
      }
    }
  },
  # ...
}
            

Using data (/purchase)

Each 'shipping', 'payment', and 'login' groups have an id associated with it. These fields are used during '/purchase'.

Instead of adding the key=value pairs to the required field values just use 'shipping_group_id', 'payment_group_id', or 'login_group_id' with the associated value.

Wallet Usage


# During a /purchase request.
required_fields = { 
  "516dece5e6a4862049000001": {
    "noauthCheckout": { "shipping_group_id": "xvv4pldi",  "payment_group_id": "frx2d9d", "an_extra_required_field": "has this value" }, 
    "addToCart": {
      "6e69ada944a4c35bb320615b827ad60a": { "quantity": 1, "Size": "13233550|13442751", "Color": "13233550|1055745" }
    }
  }
}
            

Updating data

To update an existing entry in the wallet you need to send at least a field_type and a list of fields.

If you'd like to update an existing group also send field_group_id. Otherwise this endpoint will create a new group with your information.


response = RestClient.post "https://api.twotap.com/v1.0/wallet/store?public_token=PUBLIC_TOKEN", { 
  field_type: "shipping",
  field_group_id: "xvv4pldi (optional)",
  fields: { 
    shipping_first_name: "My new first name",
    shipping_last_name: "My new last name"
  }
}

# Completed Status Response
{ 
  message: "done",
  stored_field_values:
    "xvv4pldi": {
      "data": {
        "email": {
          "value": "anemail@gmail.com"
        },
        "shipping_last_name": {
          "value": "My new last name"
        },
        "shipping_first_name": {
          "value": "My new first name"
        }
      },
      "name": "My new first name, My new last name, anemail@gmail.com"
    }
}
            

Deleting data

To delete an entry from the wallet all you have to do is send a field_type and a field_group_id.


response = RestClient.post "https://api.twotap.com/v1.0/wallet/delete?public_token=PUBLIC_TOKEN", { 
  field_type: "shipping",
  field_group_id: "xvv4pldi"
}

# Completed Status Response
{ message: "done", group_id: "xvv4pldi" }
            

Possible keys and values

Two Tap works with a lot of merchants and as such we require a lot of input data. We've standardized some of them to ensure that the wallet is consistent on all purchases.

Possible keys

shipping
shipping_titleshipping_first_nameshipping_last_nameshipping_nameshipping_address
shipping_cityshipping_stateshipping_countryshipping_zipshipping_telephone
emailemail_confirmationpasswordpassword_confirmation
login
emailusernamepassword
payment
card_typecard_numbercard_nameexpiry_date_yearexpiry_date_month
cvvbilling_titlebilling_first_namebilling_last_namebilling_name
billing_addressbilling_citybilling_statebilling_countrybilling_zip
billing_telephone

Possible values

The fields listed here require data in the format below. If you don't respect this you will break user's wallets.

card_type
VisaMastercardAmerican ExpressDiscover
month_of_birth
0102030405
0607080910
1112
expiry_date_month
0102030405
0607080910
1112
expiry_date_year
20132014201520162017
20182019202020212022
2023
year_of_birth
19301931193219331934
19351936193719381939
19401941194219431944
19451946194719481949
19501951195219531954
19551956195719581959
19601961196219631964
19651966196719681969
19701971197219731974
19751976197719781979
19801981198219831984
19851986198719881989
19901991199219931994
19951996199719981999
20002001200220032004
20052006200720082009
20102011201220132014
shipping_title, billing_title
Mr.Mrs.Ms.
shipping_state, billing_state
AlabamaAlaskaAlbertaAmerican SamoaArizona
ArkansasArmed Forces AmericasArmed Forces EuropeArmed Forces PacificBritish Columbia
CaliforniaColoradoConnecticutDelawareDistrict of Columbia
District of ColumbiaFederated States of MicronesiaFloridaGeorgiaGuam
HawaiiIdahoIllinoisIndianaIowa
KansasKentuckyLouisianaMaineManitoba
Marshall IslandsMarylandMassachusettsMichiganMinnesota
MississippiMissouriMontanaNebraskaNevada
New BrunswickNew HampshireNew JerseyNew MexicoNew York
Newfoundland and LabradorNorth CarolinaNorth DakotaNorthern Mariana IslandsNorthwest Territories
Nova ScotiaNunavutOhioOklahomaOntario
OregonPalauPennsylvaniaPrince Edward IslandPuerto Rico
QuebecRhode IslandSaskatchewanSouth CarolinaSouth Dakota
TennesseeTexasU.S. Virgin IslandsUtahVermont
VirginiaWashingtonWest VirginiaWisconsinWyoming
Yukon
shipping_country, billing_country
AfghanistanAland IslandsAlbaniaAlgeriaAmerican Samoa
AndorraAngolaAnguillaAntarcticaAntigua and Barbuda
ArgentinaArmeniaArubaAustraliaAustria
AzerbaijanBahamasBahrainBangladeshBarbados
BelarusBelgiumBelizeBeninBermuda
BhutanBoliviaBosnia and HerzegowinaBotswanaBouvet Island
BrazilBritish Indian Ocean TerritoryBrunei DarussalamBulgariaBurkina Faso
BurundiCambodiaCameroonCanadaCape Verde
Cayman IslandsCentral African RepublicChadChileChina
Christmas IslandCocos (Keeling) IslandsColombiaComorosCongo
Congo, The Democratic Republic Of TheCook IslandsCosta RicaCote D'IvoireCroatia
CubaCyprusCzech RepublicDenmarkDjibouti
DominicaDominican RepublicEast TimorEcuadorEgypt
El SalvadorEquatorial GuineaEritreaEstoniaEthiopia
Falkland Islands (Malvinas)Faroe IslandsFijiFinlandFrance
French GuianaFrench Southern TerritoriesGabonGambiaGeorgia
GermanyGhanaGibraltarGreeceGreenland
GrenadaGuadeloupeGuamGuatemalaGuinea
Guinea-BissauGuyanaHaitiHeard And Mc Donald IslandsHonduras
Hong KongHungaryIcelandIndiaIndonesia
IranIraqIrelandIsle of ManIsrael
ItalyJamaicaJapanJordanKazakhstan
KenyaKiribatiKuwaitKyrgyzstanLao People's Democratic Republic
LatviaLebanonLesothoLiberiaLibyan Arab Jamahiriya
LiechtensteinLithuaniaLuxembourgMacauMacedonia, The Former Yugoslav Republic
MadagascarMalawiMalaysiaMaldivesMali
MaltaMarshall IslandsMartiniqueMauritaniaMauritius
MayotteMexicoMicronesia (Federated States Of)MoldovaMonaco
MongoliaMontenegroMontserratMoroccoMozambique
MyanmarNamibiaNauruNepalNetherlands
Netherlands AntillesNew CaledoniaNew ZealandNicaraguaNiger
NigeriaNiueNorfolk IslandNorth KoreaNorthern Mariana Islands
NorwayOmanPakistanPalauPalestenian Territories
PanamaPapua New GuineaParaguayPeruPhilippines
PitcairnPolandPortugalPuerto RicoQatar
ReunionRomaniaRussian FederationRwandaSaint Kitts And Nevis
Saint LuciaSaint Vincent And The GrenadinesSamoaSan MarinoSao Tome And Principe
Saudi ArabiaScotlandSenegalSerbiaSeychelles
Sierra LeoneSingaporeSlovakiaSloveniaSolomon Islands
SomaliaSouth AfricaSouth Georgia And The South Sandwich IslSouth KoreaSpain
Spain - Canary IslandsSri LankaSt BarthelemySt. HelenaSt. Pierre And Miquelon
SudanSurinameSvalbard And Jan Mayen IslandsSwazilandSweden
SwitzerlandSyrian Arab RepublicTahitiTaiwan, Province of ChinaTajikistan
Tanzania, United Republic OfThailandTogoTokelauTonga
Trinidad And TobagoTunisiaTurkeyTurkmenistanTurks And Caicos Islands
TuvaluUgandaUkraineUnited Arab EmiratesUnited Kingdom
United Kingdom - GuernseyUnited Kingdom - JerseyUnited States Minor Outlying IslandsUnited States of AmericaUruguay
UzbekistanVanuatuVatican City State (Holy See)VenezuelaVietnam
Virgin Islands (British)Virgin Islands (U.S)Wallis And Futuna IslandsWestern SaharaYemen
ZambiaZimbabwe