Quickstart

Get your API credentials, authenticate, and send your first request.

Step 1: Get your API credentials

In order to use the PXP API, you'll need to get your client ID, create a token, and get the token's ID and value.

To get your credentials:

  1. In the Unity Portal, go to Merchant setup > Merchant groups.
  2. Select a merchant group.
  3. Click the Inbound calls tab.
  4. Copy the Client ID in the top-right corner.
  5. Click New token.
  6. Choose a number of days before token expiry. For example, 30.
  7. Click Save to confirm. Your token is now created.
  8. Copy the token ID and token value. Make sure to keep these confidential to protect the integrity of your authentication process.

📘

As best practice, we recommend regularly generating and implementing new tokens.

Step 2: Set up webhooks

The PXP platform can automatically inform your application about events in real time using webhook notifications. This is the easiest way to keep track of transaction events, such as authorisations, captures, and cancellations.

To set up webhooks for your merchant group:

  1. In the Unity Portal, go to Merchant setup > Merchant groups.
  2. Select a merchant group.
  3. Click the Webhooks tab.
  4. Enter the URL you want PXP to send notifications to and generate a hash key.
  5. Click Save to confirm. Your webhooks are now set up.

Step 3: Prepare your API request

Now that you have your credentials and have set up your webhooks, you'll need to prepare your API request. In this example, we'll be authorising a card for an e-commerce transaction.

To do this, you'll need to supply:

  • Your merchant and site identifier. These are automatically generated by PXP and supplied during onboarding.
  • A merchantTransactionId of your choice that represents this specific transaction. For example, ECOM_TEST001.
  • A merchantTransactionDate that corresponds to the date and time when the transaction is being initiated, in ISO 8601 format. For example, 2025-03-18 08:51:02.826445+00:00.

You can replace the placeholders in the following sample:

{
  "merchant": "{merchantValue}",
  "site": "{siteValue}",
  "merchantTransactionId": "{merchantTransactionId}",
  "merchantTransactionDate": "{merchantTransactionDate}",
  "transactionMethod": {
    "intent": "Authorisation",
    "entryType": "Ecom",
    "fundingType": "Card"
  },
  "fundingData": {
    "card": {
      "primaryAccountNumber": "4111111111111111",
      "expiryMonth": "11",
      "expiryYear": "2028"
    }
  },
  "amounts": {
    "transaction": 20,
    "currencyCode": "GBP"
  }
}

Step 4: Generate a unique request ID and HMAC signature

Our platform uses HMAC (Hash-based Message Authentication Code) with SHA256 for authentication to ensure secure communication and data integrity. This method involves creating a signature by hashing your request data with a secret key, which must then be included in the HTTP headers of your API request.

To authenticate, you'll need to:

  • Create a unique request ID (UUID or GUID) for each API request.
  • Use your token value and request details to create an HMAC signature with cryptographic functions.

Step 5: Add HTTP headers to your request

Once you've created the HMAC signature, you'll need to include it in the HTTP headers of your API request alongside other identifying information.

The following table describes how to format these headers:

Header nameDescriptionFormat
Authorization HeaderThe HMAC signature. This is made up of the authentication scheme, (e.g., PXP-UST1), your tokenId, the timestamp, and the HMAC value."Authorization: PXP-UST1 {tokenId}:{timestamp}:{hmac}"
X-Request-Id HeaderThe unique request ID that you generated."X-Request-Id: {requestId}"
X-Client-Id HeaderYour clientId , which identifies your specific client application."X-Client-Id: {clientId}"

Examples

using System;
using System.Security.Cryptography;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;

public class ApiAuthentication
{
    private static readonly HttpClient client = new HttpClient();

    public static async void SendAuthenticatedRequest(string requestUri, string requestBody, string tokenValue, string tokenId, string clientId)
    {
        // Generating unique request ID and timestamp
        var requestId = Guid.NewGuid().ToString();
        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

        // Preparing the data for HMAC signature
        // Concatenating timestamp, requestId, requestPath, and requestBody for HMAC
        var requestPath = new Uri(requestUri).AbsolutePath.Trim('/');
        var hmacRequestData = $"{timestamp}{requestId}{requestPath}{requestBody}";

        // Creating HMAC signature using the concatenated data and tokenValue
        var hmac = CreateHmacSignature(hmacRequestData, tokenValue).ToUpper();

        // Constructing the authentication header with HMAC signature
        var authHeader = $"PXP-UST1 {tokenId}:{timestamp}:{hmac}";

        // Setting up the request with necessary headers
        // Authorization header includes the HMAC signature
        // X-Request-Id and X-Client-Id headers include requestId and clientId respectively
        var request = new HttpRequestMessage(HttpMethod.Post, requestUri)
        {
            Content = new StringContent(requestBody, Encoding.UTF8, "application/json")
        };

        request.Headers.Authorization = new AuthenticationHeaderValue("Authorization", authHeader);
        request.Headers.Add("X-Request-Id", requestId);
        request.Headers.Add("X-Client-Id", clientId);

        // Sending the request and receiving the response
        var response = await client.SendAsync(request);
        var responseContent = await response.Content.ReadAsStringAsync();

        // Handle response as needed (not part of authentication steps)
    }

    // Helper method to create HMAC signature
    private static string CreateHmacSignature(string data, string key)
    {
        var encoding = new UTF8Encoding();
        byte[] keyBytes = encoding.GetBytes(key);
        byte[] messageBytes = encoding.GetBytes(data);
        using (var hmacsha256 = new HMACSHA256(keyBytes))
        {
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            return BitConverter.ToString(hashmessage).Replace("-", "");
        }
    }
}
import uuid
import time
import hmac
import hashlib
import requests
from urllib.parse import urlparse

class ApiAuthentication:
    @staticmethod
    def send_authenticated_request(request_uri, request_body, token_value, token_id, client_id):
        # Generating unique request ID and timestamp
        request_id = str(uuid.uuid4())
        timestamp = str(int(time.time()))

        # Preparing the data for HMAC signature
        # Concatenating timestamp, requestId, requestPath, and requestBody for HMAC
        request_path = urlparse(request_uri).path.strip('/')
        hmac_request_data = f"{timestamp}{request_id}{request_path}{request_body}"

        # Creating HMAC signature using the concatenated data and token_value
        hmac_signature = ApiAuthentication.create_hmac_signature(
            hmac_request_data, 
            token_value
        ).upper()

        # Constructing the authentication header with HMAC signature
        auth_header = f"PXP-UST1 {token_id}:{timestamp}:{hmac_signature}"

        # Setting up the request headers
        headers = {
            'Authorization': auth_header,
            'X-Request-Id': request_id,
            'X-Client-Id': client_id,
            'Content-Type': 'application/json'
        }

        # Sending the request and receiving the response
        response = requests.post(
            request_uri,
            headers=headers,
            data=request_body
        )

        return response.text

    @staticmethod
    def create_hmac_signature(data, key):
        # Create HMAC SHA256 signature
        key_bytes = key.encode('utf-8')
        message_bytes = data.encode('utf-8')
        
        hmac_obj = hmac.new(
            key_bytes,
            message_bytes,
            hashlib.sha256
        )
        
        # Convert to hexadecimal
        return hmac_obj.hexdigest()
<?php

class ApiAuthentication 
{
    public static function sendAuthenticatedRequest($requestUri, $requestBody, $tokenValue, $tokenId, $clientId) 
    {
        // Generating unique request ID and timestamp
        $requestId = self::generateUuid();
        $timestamp = time();

        // Preparing the data for HMAC signature
        // Concatenating timestamp, requestId, requestPath, and requestBody for HMAC
        $requestPath = trim(parse_url($requestUri, PHP_URL_PATH), '/');
        $hmacRequestData = $timestamp . $requestId . $requestPath . $requestBody;

        // Creating HMAC signature using the concatenated data and tokenValue
        $hmac = strtoupper(self::createHmacSignature($hmacRequestData, $tokenValue));

        // Constructing the authentication header with HMAC signature
        $authHeader = "PXP-UST1 {$tokenId}:{$timestamp}:{$hmac}";

        // Setting up the request headers
        $headers = [
            'Authorization: ' . $authHeader,
            'X-Request-Id: ' . $requestId,
            'X-Client-Id: ' . $clientId,
            'Content-Type: application/json'
        ];

        // Initialize cURL session
        $ch = curl_init($requestUri);
        
        // Set cURL options
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $requestBody,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true
        ]);

        // Send the request and get response
        $response = curl_exec($ch);
        
        // Check for errors
        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch));
        }
        
        // Close cURL session
        curl_close($ch);

        return $response;
    }

    private static function createHmacSignature($data, $key) 
    {
        return hash_hmac('sha256', $data, $key);
    }

    private static function generateUuid() 
    {
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000,
            mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpRequest.Builder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class ApiAuthentication {

    private static final HttpClient client = HttpClient.newHttpClient();

    public static void sendAuthenticatedRequest(String requestUri, String requestBody, String tokenValue, String tokenId, String clientId) throws Exception {
        // Generating unique request ID and timestamp
        String requestId = UUID.randomUUID().toString();
        String timestamp = String.valueOf(Instant.now().getEpochSecond());

        // Preparing the data for HMAC signature
        String requestPath = URI.create(requestUri).getPath().replaceAll("^/|/$", "");
        String hmacRequestData = timestamp + requestId + requestPath + requestBody;

        // Creating HMAC signature
        String hmac = createHmacSignature(hmacRequestData, tokenValue);

        // Constructing the authentication header
        String authHeader = "PXP-UST1 " + tokenId + ":" + timestamp + ":" + hmac;

        // Setting up the request with necessary headers
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(requestUri))
            .header("Authorization", authHeader)
            .header("X-Request-Id", requestId)
            .header("X-Client-Id", clientId)
            .POST(BodyPublishers.ofString(requestBody))
            .build();

        // Sending the request and receiving the response
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        // Handle response as needed
        System.out.println(response.body());
    }

    private static String createHmacSignature(String data, String key) throws NoSuchAlgorithmException {
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha256_HMAC.init(secretKey);
            byte[] hash = sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hash);
        } catch (Exception e) {
            throw new RuntimeException("Error while creating HMAC signature", e);
        }
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    public static void main(String[] args) {
        try {
            sendAuthenticatedRequest("Your API URL", "Your Request Body", "Your Token Value", "Your Token ID", "Your Client ID");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
require 'uri'
require 'net/http'
require 'json'
require 'securerandom'
require 'openssl'

class ApiAuthentication
  class << self
    def send_authenticated_request(request_uri, request_body, token_value, token_id, client_id)
      # Generating unique request ID and timestamp
      request_id = SecureRandom.uuid
      timestamp = Time.now.to_i.to_s

      # Preparing the data for HMAC signature
      # Concatenating timestamp, requestId, requestPath, and requestBody for HMAC
      request_path = URI(request_uri).path.sub(/^\/+/, '')
      hmac_request_data = "#{timestamp}#{request_id}#{request_path}#{request_body}"

      # Creating HMAC signature using the concatenated data and token_value
      hmac = create_hmac_signature(hmac_request_data, token_value).upcase

      # Constructing the authentication header with HMAC signature
      auth_header = "PXP-UST1 #{token_id}:#{timestamp}:#{hmac}"

      # Create URI object
      uri = URI(request_uri)

      # Create HTTP object
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = uri.scheme == 'https'

      # Create Request object
      request = Net::HTTP::Post.new(uri.path)

      # Add headers
      request['Authorization'] = auth_header
      request['X-Request-Id'] = request_id
      request['X-Client-Id'] = client_id
      request['Content-Type'] = 'application/json'
      request.body = request_body

      # Send request and get response
      response = http.request(request)
      
      # Return response body
      response.body
    rescue StandardError => e
      raise "Request failed: #{e.message}"
    end

    private

    def create_hmac_signature(data, key)
      # Create HMAC SHA256 signature
      OpenSSL::HMAC.hexdigest(
        OpenSSL::Digest.new('sha256'),
        key,
        data
      )
    end
  end
end
require 'uri'
require 'net/http'
require 'json'
require 'securerandom'
require 'openssl'

class ApiAuthentication
  class << self
    def send_authenticated_request(request_uri, request_body, token_value, token_id, client_id)
      # Generating unique request ID and timestamp
      request_id = SecureRandom.uuid
      timestamp = Time.now.to_i.to_s

      # Preparing the data for HMAC signature
      request_path = URI(request_uri).path.sub(/^\/+/, '')
      hmac_request_data = "#{timestamp}#{request_id}#{request_path}#{request_body}"

      # Creating HMAC signature using the concatenated data and token_value
      hmac = create_hmac_signature(hmac_request_data, token_value).upcase

      # Constructing the authentication header with HMAC signature
      auth_header = "PXP-UST1 #{token_id}:#{timestamp}:#{hmac}"

      # Create URI object
      uri = URI(request_uri)

      # Create HTTP object
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = uri.scheme == 'https'

      # Create Request object
      request = Net::HTTP::Post.new(uri.path)

      # Add headers
      request['Authorization'] = auth_header
      request['X-Request-Id'] = request_id
      request['X-Client-Id'] = client_id
      request['Content-Type'] = 'application/json'
      request.body = request_body

      # Send request and get response
      response = http.request(request)
      response.body
    rescue StandardError => e
      raise "Request failed: #{e.message}"
    end

    private

    def create_hmac_signature(data, key)
      OpenSSL::HMAC.hexdigest(
        OpenSSL::Digest.new('sha256'),
        key,
        data
      )
    end
  end
end
import Foundation
import CommonCrypto

class ApiAuthentication {
    static func sendAuthenticatedRequest(
        requestUri: String,
        requestBody: String,
        tokenValue: String,
        tokenId: String,
        clientId: String,
        completion: @escaping (Result<String, Error>) -> Void
    ) {
        // Generating unique request ID and timestamp
        let requestId = UUID().uuidString
        let timestamp = String(Int(Date().timeIntervalSince1970))
        
        // Preparing the data for HMAC signature
        guard let url = URL(string: requestUri),
              let requestPath = url.path.removingPrefix("/") else {
            completion(.failure(AuthError.invalidUrl))
            return
        }
        
        // Concatenating timestamp, requestId, requestPath, and requestBody for HMAC
        let hmacRequestData = "\(timestamp)\(requestId)\(requestPath)\(requestBody)"
        
        // Creating HMAC signature using the concatenated data and tokenValue
        let hmac = createHmacSignature(data: hmacRequestData, key: tokenValue).uppercased()
        
        // Constructing the authentication header with HMAC signature
        let authHeader = "PXP-UST1 \(tokenId):\(timestamp):\(hmac)"
        
        // Create URL request
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue(authHeader, forHTTPHeaderField: "Authorization")
        request.setValue(requestId, forHTTPHeaderField: "X-Request-Id")
        request.setValue(clientId, forHTTPHeaderField: "X-Client-Id")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = requestBody.data(using: .utf8)
        
        // Create URL session task
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let error = error {
                completion(.failure(error))
                return
            }
            
            guard let data = data,
                  let responseString = String(data: data, encoding: .utf8) else {
                completion(.failure(AuthError.invalidResponse))
                return
            }
            
            completion(.success(responseString))
        }
        
        // Start the task
        task.resume()
    }
    
    private static func createHmacSignature(data: String, key: String) -> String {
        guard let keyData = key.data(using: .utf8),
              let messageData = data.data(using: .utf8) else {
            return ""
        }
        
        let keyBytes = keyData.map { $0 }
        let messageBytes = messageData.map { $0 }
        
        var macData = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        
        messageBytes.withUnsafeBufferPointer { messageBytesPtr in
            keyBytes.withUnsafeBufferPointer { keyBytesPtr in
                CCHmac(
                    CCHmacAlgorithm(kCCHmacAlgSHA256),
                    keyBytesPtr.baseAddress,
                    keyBytes.count,
                    messageBytesPtr.baseAddress,
                    messageBytes.count,
                    &macData
                )
            }
        }
        
        return macData.map { String(format: "%02hhx", $0) }.joined()
    }
}

// Helper extension
extension String {
    func removingPrefix(_ prefix: String) -> String? {
        guard self.hasPrefix(prefix) else { return self }
        return String(self.dropFirst(prefix.count))
    }
}

// Error enum
enum AuthError: Error {
    case invalidUrl
    case invalidResponse
}

// Example usage of completion handler:
/*
ApiAuthentication.sendAuthenticatedRequest(
    requestUri: "https://api.example.com/endpoint",
    requestBody: "{}",
    tokenValue: "your_token",
    tokenId: "your_token_id",
    clientId: "your_client_id"
) { result in
    switch result {
    case .success(let response):
        print("Success: \(response)")
    case .failure(let error):
        print("Error: \(error)")
    }
}
*/

⚠️

Always ensure that the time on your client is synchronised with the server to avoid issues with timestamp validation.

Step 6: Send your first API request

You can now go ahead and send your API request.

In return, you should receive:

  • A200 response containing a systemTransactionId that identifies this specific transaction. You can use this to modify your transaction later.
  • A webhook that matches the transaction's state. For example, an Authorisation webhook.
{
  "state": "Authorised",
  "approvalCode": "123456",
  "merchantTransactionId": "{merchantTransactionId}",
  "systemTransactionId": "1ed768bb-e88a-4636-91ae-67927ccbb02b",
  "providerTransactionId": "1ed768bb-e88a-4636-91ae-67927ccbb02b",
  "fundingData": {
    "cardScheme": "Visa",
    "gatewayTokenId": "1ed768bb-e88a-4636-91ae-67927ccbb02b",
    "schemeTokenNumber": "4837261112345678",
    "avsResult": "noInformationAvailable",
    "cvcResult": "noInformationAvailable",
    "providerResponse": {
      "provider": "PXPFinancial",
      "code": "00",
      "message": "Approved",
      "merchantId": "77772182",
      "avsResult": "D",
      "cvcResult": "A",
      "schemeTransactionId": "TX1234567890123456",
      "paymentAccountReference": "PAR12345678901234567890",
      "electronicCommerceIndicatorAdjustment": "01",
      "settlementDate": "2025-03-25T00:00:00.000Z"
    }
  }
}