API Key – Shared Secret (X-Pay Token)

Navigate to...

Some Visa Developer APIs require an API Key-Shared Secret Authenticationwhich Visa refers to as X-Pay Token. To invoke an API using X-Pay Token, you will need an API Key and a Shared Secret, which is provided on the project details page.

Tutorial Video

Generating the X-Pay Token

To successfully invoke your Visa Developer APIs, which use X-Pay Token, your project must include the following:


Add the API Key as the query parameter.


Include the Accept and X-Pay Token in the request header as shown in the sample below.

Field Value
Accept application/json
X-PAY-TOKEN x-pay-token*

Note: Refer to the section below on how to generate the x-pay-token value with SHA256 HMAC.

    Sample Header

    GET /vdp/helloworld?apikey=KSKDFJOP934ALSFDJP34 HTTP/1.0
    Host: sandbox.api.visa.com
    Accept: application/json
    X-PAY-TOKEN: xv2:1455716783:f5d15ed23f825ac69cd42e6fa187a175ecf7e9566ce4f21e11bad49bed4cc363


Request payload. Include any resource-specific request parameters in the request payload before you make a request.


Test different scenarios using the test data provided on the Project dashboard.

Generating the X-Pay Token

To generate the X-Pay Token, follow these steps:


Generate a message string by concatenating the following parameters:
message = timestamp + resource_path + query_string + request_body

Note: For some products the context path is skipped while computing x-pay-token:




This is the current timestamp in UTC (in seconds).


This is the API endpoint you would like to invoke after the context path.

For majority of the products, the context path that is used to compute the x-pay-token is the resource path excluding the first path parameter (also referred to as the base path).

For the products in bold below, the entire resource path should be used to compute the x-pay-token.


Part of the URL to be skipped while computing x-pay token


Request URL

Path to be used in x-pay-token calculation

Visa Token Service










Visa Token Service Provisioning and Credential Management




VDP Hello World




All other Products





  • Cybersource




  • Visa Checkout





The apikey is a required query parameter. Query parameters should be in lexicographical order.


This is the API endpoint-specific request body string


Create the X-Pay Token, as shown below:

XPayToken = "xv2:" + timestamp + ":" + SHA256HMAC(shared_secret, message)

Note: The shared_secret is the shared secret from the Project Dashboard.

Constructing the HTTP Header

Depending on your system needs, Visa Developer APIs will allow you to setup one or more HTTP header variables as illustrated in the following table.

Variable Name Value


Specify request format

  • XML Use text/xml
  • JSON Use application/json

If not specified, expects JSON.



Specify request format

  • XML Use text/xml
  • JSON Use application/json

If not specified, defaults to request format.

Testing X-Pay-Token Connectivity Using SOAPUI

Testing X-Pay Token Connectivity Using SOAPUI

SOAPUI is a free and open source Web Service Functional Testing solution. With an easy-to-use graphical user interface, SOAP UI allows you to rapidly create and execute web service API functional tests. It is highly recommended that you use SOAP UI, or a similar connectivity tool, to establish your initial connection to the Visa Developer sandbox.

Note: Prior to working with SOAPUI, you must Register and create a project that uses x-pay-token as an authentication service. Make sure that you have a valid API key and shared secret (available on Project dashboard).



Download SoapUI 5.4 from https://www.soapui.org.


Once installed, open SOAPUI and select File > New REST Project.

Use the following URI: https://sandbox.api.visa.com/vdp/helloworld?apikey=<value of your API key> 
(replace the value of API key query string parameter with the actual API key that you received from the Visa Developer Platform).

Image of SoapUI's application. Modal window titled New REST Project with URI input field.


Once the project is created, in project navigator, right click the URI, and select Generate TestSuite. Specify name for your test suite and click OK.

Image of Soap UI's application. Menu with focus on Generate TestSuite option.


In the project navigator, fully expand the newly created test suite and locate the test steps.


Right click Test Steps, select Add Step, then select Groovy Script as shown below. Specify name for your script and click OK.

Image of Soap UI's application. Menu with focus on Generate TestSuite option.


Paste the following contents into the script body. Make sure to replace the API key and the shared secret with your own values from the Visa Developer Center.
Run the script (by clicking the green chevron) and make sure that the value of x-pay-token is visible in the Log Output window.

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
def hmac(String secretKey, String data) {
Mac mac = Mac.getInstance("HmacSHA256")
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256")
byte[] digest = mac.doFinal(data.getBytes())
return digest
def sharedSecret = ‘VALUE_OF_YOUR_SHARED_SECRET’
def URI = "helloworld"
def QS = "apikey="+APIKey
def timeStampUTC = String.valueOf(System.currentTimeMillis().intdiv(1000L))
def payload = ""
def HMACDigest = hmac(sharedSecret, timeStampUTC + URI + QS + payload)
def encodedDigest = HMACDigest.encodeHex().toString()
def XPayToken = "xv2:"+ timeStampUTC + ":" + encodedDigest
testRunner.testCase.setPropertyValue("xpayToken", XPayToken)


Double click Request 1 (the test step just above your new, for example: "groovy" script). Select Headers tab and click the green plus sign to add a new header.


Add a custom header for the X-Pay Token. The value of this header is set dynamically, when you run the script.

Note: You will need to use the following as the header value: ${#TestCase#xpayToken}.
The header name will be static, and should be set to: x-pay-token. The resulting header setting will appear, as follows.


Run your test suite by first executing the script (by clicking the green chevron), and secondly by sending the test request (green chevron on the request page). If all is correct, you should see the current timestamp in the response.

Sample Code for API Key – Shared Secret (X-Pay Token)

import java.math.BigInteger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SignatureException;

public static String generateXpaytoken(String resourcePath, String queryString, String requestBody, String sharedSecret) throws SignatureException {  
    String timestamp = timeStamp();  
    String beforeHash = timestamp + resourcePath + queryString + requestBody;
    String hash = hmacSha256Digest(beforeHash, sharedSecret);  
    String token = "xv2:" + timestamp + ":" + hash;  
    return token;  

private static String timeStamp() {  
        return String.valueOf(System.currentTimeMillis()/ 1000L);  

private static String hmacSha256Digest(String data, String sharedSecret)  
        throws SignatureException {  
    return getDigest("HmacSHA256", sharedSecret, data, true);  

private static String getDigest(String algorithm, String sharedSecret, String data,  boolean toLower) throws SignatureException {  
    try {  
        Mac sha256HMAC = Mac.getInstance(algorithm);  
        SecretKeySpec secretKey = new SecretKeySpec(sharedSecret.getBytes(StandardCharsets.UTF_8), algorithm);  

        byte[] hashByte = sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));  
        String hashString = toHex(hashByte);  

        return toLower ? hashString.toLowerCase() : hashString;
    } catch (Exception e) {  
        throw new SignatureException(e);  

private static String toHex(byte[] bytes) {  
    BigInteger bi = new BigInteger(1, bytes);  
    return String.format("%0" + (bytes.length << 1) + "X", bi);  
from calendar import timegm  
from datetime import datetime  
from hashlib import sha256  
import hmac  

def _get_x_pay_token(shared_secret, resource_path, query_string, body):  
    timestamp = str(timegm(datetime.utcnow().timetuple()))  
    pre_hash_string = timestamp + resource_path + query_string + body  
    hash_string = hmac.new(shared_secret,  
    return 'xv2:' + timestamp + ':' + hash_string
var timestamp = Math.floor(Date.now() / 1000);  
var preHashString = timestamp + resourcePath + queryParams + postBody;  
var crypto = require('crypto');  
var hashString = crypto.createHmac('SHA256', sharedSecret).update(preHashString).digest('hex');  
var xPayToken = 'xv2:' + timestamp + ':' + hashString;
def get_xpay_token(shared_secret, resource_path, query_string, request_body) 
require 'digest' 
timestamp = Time.now.getutc.to_i.to_s 
hash_input = timestamp + resource_path + query_string + request_body 
hash_output = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), shared_secret, hash_input) 
return "xv2:" + timestamp + ":" + hash_output  
private static string getTimestamp() { 
    long timeStamp = ((long) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds) / 1000; 
    return timeStamp.ToString(); 

private static string getHash(string data) { 
    var hashString = new HMACSHA256(Encoding.ASCII.GetBytes(SHARED_SECRET)); 
    var hashbytes = hashString.ComputeHash(Encoding.ASCII.GetBytes(data)); 
    string digest = String.Empty; 

    foreach (byte b in hashbytes) { 
        digest += b.ToString("x2"); 

    return digest; 

private static string getXPayToken(string resourcePath, string queryString, string requestBody) { 
    string timestamp = getTimestamp(); 
    string sourceString = timestamp + resourcePath + queryString  + requestBody; 
    string hash = getHash(sourceString); 
    string token = "xv2:" + timestamp + ":" + hash; 
    return token; 
$time = time(); 
$ pre_hash_string = $time.$resource_path.$query_string.$body; 
$hashtoken = "xv2:".$time.":".hash_hmac('sha256', $ pre_hash_string, $secret);