marcopolo Online payment
Sign up

If you choose not to use our SDKs for sending requests to our endpoints, you need to implement the authentication mechanism on your own.

The ANZ Worldline Payment Solutions API uses HMAC-SHA256 request signing, a keyed hash using SHA-256: You compute a cryptographic signature and include it in the request. This signature is based on

  • Your credentials (API Key/Secret).
  • Key request information in your request.

Upon receiving your request, our platform recalculates this very signature. If both signature match, we authenticate the request.

Implementation

To implement the authentication mechanism, you need to

  • Create a API Key/Secret as described in the "API key/secret configuration" chapter in our Authentication guide.
  • Prepare the key request information: HTTP method, request path, and required request headers.
  • Hash the key request information with the API Secret to compute the signature.
  • Send the request, including the key request information, the signature and API Key.

Follow these step-to-step instructions to implement the authentication mechanism.

  1. Collect key request information
  2. Prepare date headers
  3. Prepare canonical headers
  4. Build signature subject (create string-to-hash)
  5. Compute signature (calculate hash)
  6. Build authorisation header
  7. Send request

Collect key request information

Collect the key request information for your request to the ANZ Worldline Payment Solutions API:

Property Description
HTTP method Depending on the endpoint you want to send the request to, i.e. POST, GET etc.
Content-type header

Fixed value  "application/json" for request with a body (i.e. POST).

Empty string for requests without a body (i.e. GET).

Date header UTC timestamp in RFC 1123 format. Must be identical for both request header and the string-to-hash to compute the signature.
x-gcs-* headers Any x-gcs-* canonical custom headers you would like to send, sorted alphabetically in the string-to-hash to compute the signature.
Request path The relative path of the ANZ Worldline Payment Solutions API you send the request to, including the API version and your merchantId i.e. "/v2/merchantId/hostedcheckouts"


Prepare date headers

For the date header, create a UTC timestamp in RFC 1123 format. Use the same value for both the header and the signature subject (the string-to-hash).
Our platform will reject requests with a timestamp older than 5 minutes.


Prepare canonical headers

If you include any ANZ Worldline Payment Solutions-specifc x-gcs-* headers in your request, you need to normalise them according to these conventions:

  • Lowercase the header key.
  • Trim leading/trailing whitespace from the header value.
  • Collapse any internal whitespace runs in the header value to a single space.
  • Format each header key-value pair as {lowercased-key}:{normalized-value}.
  • Sort the canonical key-value pairs alphabetically by key.

If you include any x-gcs-* header in the signature subject, make sure to add it in the actual request.

Convention Raw value Normalised key/value
Lowercase header key X-GCS-Idempotency-Key x-gcs-idempotency-key
Trim leading/trailing whitespace header value ··header value·· header value
Collapse whitespace in header value header··value header value
Key-value pair formatting {raw-key}={raw-value} {lowercased-key}:{normalized-value}
Alphabetical order of canonical key-value pairs x-gcs-serverdatetime:2026-05-18T10:00:00Z
x-gcs-idempotency-key:abc 123
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT
x-gcs-idempotency-key:abc 123
x-gcs-serverdatetime:2026-05-18T10:00:00Z

Our code samples below use only the x-gcs-date / x-gcs-idempotency-key  headers to show how to handle ANZ Worldline Payment Solutions-specific x-gcs-* headers.
If you use our Server SDKs to call the ANZ Worldline Payment Solutions API, they automatically add and handle additional x-gcs-* headers.
These extra headers are not relevant for manual authentication, so we do not cover them here.


Build signature subject (create string-to-hash)

Concatenate the following elements (request method, headers, canonical headers, request path) in this order, each terminated by a newline character \n:

{HTTP_METHOD}\n
{Content-Type}\n
{Date}\n
{canonical-x-gcs-header-1}\n
{canonical-x-gcs-header-N}\n
{request-path}\n

  • If you include canonical x-gcs-* headers, make sure to order them by key as described above.
  • Content-Type is always an empty string for requests without a body. 

Typical examples for requests with/without a body look like this:

Request with a body (e.g. POST, PUT, PATCH)

POST\n
application/json\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-idempotency-key:<unique-idempotency-key>\n
/v1/{merchantId}/payments\n

Request without a body (e.g. GET, DELETE)

GET\n
\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
/v1/{merchantId}/payments/{paymentId}\n


Compute signature (calculate hash)

Hash the signature subject (string-to-hash) using HMAC-SHA256 (UTF-8 encoding) with your API Secret. Then Base64-encode the resulting signature: 

Skip code example Copy
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

// The following code should be placed inside a method that declares: throws Exception
String apiSecret = "YourAPISecret";
String signatureSubject =
    "POST" + "\n" +
    "application/json" + "\n" +
    "Mon, 18 May 2026 10:00:00 GMT" + "\n" +
    "x-gcs-idempotency-key:abc-def-456" + "\n" +
    "/v1/{merchantId}/payments" + "\n";

// Convert apiSecret / signatureSubject into byte arrays
byte[] keyBytes = apiSecret.getBytes(StandardCharsets.UTF_8);
byte[] subjectBytes = signatureSubject.getBytes(StandardCharsets.UTF_8);

// Hash the signatureSubject
Mac hmac = Mac.getInstance("HmacSHA256");
hmac.init(new SecretKeySpec(keyBytes, "HmacSHA256"));
String signature = Base64.getEncoder().encodeToString(hmac.doFinal(subjectBytes));
using System;
using System.Security.Cryptography;
using System.Text;

string apiSecret = "YourAPISecret";
string signatureSubject =
    "POST" + "\n" +
    "application/json" + "\n" +
    "Mon, 18 May 2026 10:00:00 GMT" + "\n" +
    "x-gcs-idempotency-key:abc-def-456" + "\n" +
    "/v1/{merchantId}/payments" + "\n";

// Convert apiSecret / signatureSubject into byte arrays
byte[] keyBytes = Encoding.UTF8.GetBytes(apiSecret);
byte[] subjectBytes = Encoding.UTF8.GetBytes(signatureSubject);

// Hash the signatureSubject
using HMACSHA256 hmac = new HMACSHA256(keyBytes);
string signature = Convert.ToBase64String(hmac.ComputeHash(subjectBytes));
import crypto from 'crypto';

const apiSecret = 'YourAPISecret';
const signatureSubject =
    'POST' + '\n' +
    'application/json' + '\n' +
    'Mon, 18 May 2026 10:00:00 GMT' + '\n' +
    'x-gcs-idempotency-key:abc-def-456' + '\n' +
    '/v1/{merchantId}/payments' + '\n';

// Hash the signatureSubject
const signature = crypto
    .createHmac('SHA256', apiSecret)
    .update(signatureSubject)
    .digest('base64');
<?php

$apiSecret = 'YourAPISecret';
$signatureSubject =
    'POST' . "\n" .
    'application/json' . "\n" .
    'Mon, 18 May 2026 10:00:00 GMT' . "\n" .
    'x-gcs-idempotency-key:abc-def-456' . "\n" .
    '/v1/{merchantId}/payments' . "\n";

// Hash the signatureSubject
$signature = base64_encode(hash_hmac('sha256', $signatureSubject, $apiSecret, true));
import hmac
import hashlib
from base64 import b64encode

api_secret = 'YourAPISecret'
signature_subject = (
    'POST' + '\n' +
    'application/json' + '\n' +
    'Mon, 18 May 2026 10:00:00 GMT' + '\n' +
    'x-gcs-idempotency-key:abc-def-456' + '\n' +
    '/v1/{merchantId}/payments' + '\n'
)

# Hash the signature_subject
signature = b64encode(
    hmac.new(api_secret.encode('utf-8'), signature_subject.encode('utf-8'), hashlib.sha256).digest()
).decode('utf-8')
require 'openssl'
require 'base64'

api_secret = 'YourAPISecret'
signature_subject =
  'POST' + "\n" +
  'application/json' + "\n" +
  'Mon, 18 May 2026 10:00:00 GMT' + "\n" +
  'x-gcs-idempotency-key:abc-def-456' + "\n" +
  '/v1/{merchantId}/payments' + "\n"

# Hash the signature_subject
digest    = OpenSSL::Digest.new('SHA256')
signature = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, api_secret, signature_subject)).strip


Build authorisation header

Add the Authorisation GCS v1HMAC header to your request, consisting of the computed signature and your API Key:
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>


Send request

When sending your request to the ANZ Worldline Payment Solutions API, include all headers from the signature subject (create string-to-hash) and the Authorisation header.

Raw HTTP request with a body

POST /v1/{merchantId}/payments HTTP/1.1
Host: api.example.com
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
Content-Type: application/json
x-gcs-date: Mon, 18 May 2026 10:00:00 GMT
x-gcs-idempotency-key: <unique-idempotency-key>

{ ... request body ... }

Raw HTTP request without a body

GET /v1/{merchantId}/payments/{paymentId} HTTP/1.1
Host: api.example.com
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
x-gcs-date: Mon, 18 May 2026 10:00:00 GMT


Troubleshooting mismatches

Upon receiving your request, our platform tries to match the signature and the API key from the Authorisation header. If this matching fails, our platform will reject your request, throwing an error message like this:

{
  "errorId": "error-id",
  "errors": [
    {
      "code": "9007",
      "id": "ACCESS_TO_MERCHANT_NOT_ALLOWED",
      "category": "DIRECT_PLATFORM_ERROR",
      "message": "ACCESS_TO_MERCHANT_NOT_ALLOWED",
      "httpStatusCode": 403
    }
  ],
  "status": 403
}

To troubleshoot such errors, make sure to follow these guidelines:

  • Use the correct environment: Do not mix up API Keys/Secrets/accounts from the test/prod environment.
  • Avoid expired API Keys/Secrets.
  • Keep the Date header within 5 minutes and use the same date in the header and the signature.
  • For requests without a body, set Content-Type to an empty string. Do not omit or set to null.
  • Apply the formatting/normalisation rules for x-gcs-* headers.
  • Send the same headers in the request that you have used for concatenating the signature subject (string-to-hash).

Refer to our dedicated API Troubleshooting guide for analysing and solving other platform errors.

Full code example

Use this full code example that combines all previous steps into a single working implementation:

Helper class

Skip code example Copy
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;

public class HmacAuthHelper {

    private static final List METHODS_WITHOUT_BODY = List.of("GET", "DELETE");

    /**
     * Builds the GCS v1HMAC Authorization header value.
     *
     * @param method         HTTP method (e.g. "POST")
     * @param path           Request path (e.g. "/v1/{entityId}/payments")
     * @param contentType    Content-Type header value; ignored for body-less methods
     * @param date           RFC 1123 UTC date string — must match the Date header sent with the request
     * @param apiKeyId       Your API Key ID
     * @param apiSecret      Your API Secret
     * @param idempotencyKey Optional value of the platform-specific X-GCS-Idempotency-Key header (null if not sent)
     */
    public static String buildAuthorizationHeader(
            String method, String path, String contentType, String date,
            String apiKeyId, String apiSecret, String idempotencyKey) throws Exception {

        // Normalize Content-Type — body-less methods always use empty string
        if (METHODS_WITHOUT_BODY.contains(method.toUpperCase())) {
            contentType = "";
        }

        // Normalize the platform-specific x-gcs-idempotency-key header (lowercase key, trimmed/collapsed value)
        String platformHeader = null;
        if (idempotencyKey != null && !idempotencyKey.isBlank()) {
            platformHeader = "x-gcs-idempotency-key:" + idempotencyKey.trim().replaceAll("\\s+", " ");
        }

        // Build the signature subject
        StringBuilder subject = new StringBuilder();
        subject.append(method.toUpperCase()).append("\n"); // method\n
        subject.append(contentType).append("\n");          // content-type\n
        subject.append(date).append("\n");                 // date\n
        if (platformHeader != null) {
            subject.append(platformHeader).append("\n");   // x-gcs-idempotency-key\n
        }
        subject.append(path).append("\n");                 // path\n

        // Sign with HMAC-SHA256 and Base64-encode
        byte[] keyBytes = apiSecret.trim().getBytes(StandardCharsets.UTF_8);
        byte[] subjectBytes = subject.toString().getBytes(StandardCharsets.UTF_8);
        Mac hmac = Mac.getInstance("HmacSHA256");
        hmac.init(new SecretKeySpec(keyBytes, "HmacSHA256"));
        String signature = Base64.getEncoder().encodeToString(hmac.doFinal(subjectBytes));

        return "GCS v1HMAC:" + apiKeyId.trim() + ":" + signature;
    }
}
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

public static class HmacAuthHelper
{
    private static readonly string[] MethodsWithoutBody = ["GET", "DELETE"];

    public static string BuildAuthorizationHeader(
        string method,
        string path,
        string contentType,
        string date,
        string apiKeyId,
        string apiSecret,
        string? idempotencyKey = null)
    {
        // Content-Type is empty for methods without a request body
        if (MethodsWithoutBody.Contains(method.ToUpperInvariant()))
            contentType = string.Empty;

        // Normalize the platform-specific x-gcs-idempotency-key header: lowercase key, collapse whitespace in value
        string? platformHeader = string.IsNullOrWhiteSpace(idempotencyKey)
            ? null
            : "x-gcs-idempotency-key:" + Regex.Replace(idempotencyKey.Trim(), @"\s+", " ");

        // Build the signature subject string (each element followed by \n)
        StringBuilder subject = new StringBuilder();
        subject.Append(method.ToUpperInvariant() + "\n"); // method\n
        subject.Append(contentType + "\n");               // content-type\n
        subject.Append(date + "\n");                      // date\n
        if (platformHeader != null)
            subject.Append(platformHeader + "\n");        // x-gcs-idempotency-key\n
        subject.Append(path + "\n");                      // path\n

        // Convert apiSecret / signatureSubject into byte arrays
        byte[] keyBytes     = Encoding.UTF8.GetBytes(apiSecret.Trim());
        byte[] subjectBytes = Encoding.UTF8.GetBytes(subject.ToString());

        // Hash the signatureSubject and Base64-encode the result
        using HMACSHA256 hmac = new HMACSHA256(keyBytes);
        string signature = Convert.ToBase64String(hmac.ComputeHash(subjectBytes));

        return $"GCS v1HMAC:{apiKeyId.Trim()}:{signature}";
    }
}
$content.node
<?php

class HmacAuthHelper
{
    private static array $methodsWithoutBody = ['GET', 'DELETE'];

    public static function buildAuthorizationHeader(
        string $method,
        string $path,
        string $contentType,
        string Nov 11, 2026, 2:13:21 PM,
        string $apiKeyId,
        string $apiSecret,
        ?string $idempotencyKey = null
    ): string {
        // Normalize Content-Type — body-less methods always use empty string
        if (in_array(strtoupper($method), self::$methodsWithoutBody)) {
            $contentType = '';
        }

        // Normalize the platform-specific x-gcs-idempotency-key header (lowercase key, trimmed/collapsed value)
        $platformHeader = null;
        if ($idempotencyKey !== null && trim($idempotencyKey) !== '') {
            $platformHeader = 'x-gcs-idempotency-key:' . preg_replace('/\s+/', ' ', trim($idempotencyKey));
        }

        // Build the signature subject
        $subject  = strtoupper($method) . "\n"; // method\n
        $subject .= $contentType . "\n";        // content-type\n
        $subject .= Nov 11, 2026, 2:13:21 PM . "\n";               // date\n
        if ($platformHeader !== null) {
            $subject .= $platformHeader . "\n"; // x-gcs-idempotency-key\n
        }
        $subject .= $path . "\n";               // path\n

        // Sign with HMAC-SHA256 and Base64-encode
        $signature = base64_encode(hash_hmac('sha256', $subject, trim($apiSecret), true));

        return 'GCS v1HMAC:' . trim($apiKeyId) . ':' . $signature;
    }
}
import hmac
import hashlib
import re
from base64 import b64encode

class HmacAuthHelper:

    METHODS_WITHOUT_BODY = {'GET', 'DELETE'}

    @staticmethod
    def build_authorization_header(
            method: str,
            path: str,
            content_type: str,
            date: str,
            api_key_id: str,
            api_secret: str,
            idempotency_key: str | None = None) -> str:

        # Normalize Content-Type — body-less methods always use empty string
        if method.upper() in HmacAuthHelper.METHODS_WITHOUT_BODY:
            content_type = ''

        # Normalize the platform-specific x-gcs-idempotency-key header (lowercase key, trimmed/collapsed value)
        platform_header = None
        if idempotency_key and idempotency_key.strip():
            platform_header = 'x-gcs-idempotency-key:' + re.sub(r'\s+', ' ', idempotency_key.strip())

        # Build the signature subject
        subject = method.upper() + '\n'      # method\n
        subject += content_type + '\n'       # content-type\n
        subject += date + '\n'               # date\n
        if platform_header:
            subject += platform_header + '\n'  # x-gcs-idempotency-key\n
        subject += path + '\n'               # path\n

        # Sign with HMAC-SHA256 and Base64-encode
        signature = b64encode(
            hmac.new(api_secret.strip().encode('utf-8'), subject.encode('utf-8'), hashlib.sha256).digest()
        ).decode('utf-8')

        return f'GCS v1HMAC:{api_key_id.strip()}:{signature}'
require 'openssl'
require 'base64'

module HmacAuthHelper
  METHODS_WITHOUT_BODY = %w[GET DELETE].freeze

  # Builds the GCS v1HMAC Authorization header value.
  #
  # @param method          [String] HTTP method (e.g. 'POST')
  # @param path            [String] Request path (e.g. '/v1/{entityId}/payments')
  # @param content_type    [String] Content-Type header value; ignored for body-less methods
  # @param date            [String] RFC 1123 UTC date string — must match the Date header sent with the request
  # @param api_key_id      [String] Your API Key ID
  # @param api_secret      [String] Your API Secret
  # @param idempotency_key [String, nil] Optional value of the platform-specific X-GCS-Idempotency-Key header
  def self.build_authorization_header(method, path, content_type, date, api_key_id, api_secret, idempotency_key = nil)
    # Normalize Content-Type — body-less methods always use empty string
    content_type = '' if METHODS_WITHOUT_BODY.include?(method.upcase)

    # Normalize the platform-specific x-gcs-idempotency-key header (lowercase key, trimmed/collapsed value)
    platform_header = nil
    if idempotency_key && !idempotency_key.strip.empty?
      platform_header = "x-gcs-idempotency-key:#{idempotency_key.strip.gsub(/\s+/, ' ')}"
    end

    # Build the signature subject
    subject  = "#{method.upcase}\n"  # method\n
    subject += "#{content_type}\n"   # content-type\n
    subject += "#{date}\n"           # date\n
    subject += "#{platform_header}\n" if platform_header # x-gcs-idempotency-key\n
    subject += "#{path}\n"           # path\n

    # Sign with HMAC-SHA256 and Base64-encode
    digest    = OpenSSL::Digest.new('SHA256')
    signature = Base64.strict_encode64(
      OpenSSL::HMAC.digest(digest, api_secret.strip, subject)
    ).strip

    "GCS v1HMAC:#{api_key_id.strip}:#{signature}"
  end
end

Usage — request with a body (POST)

Skip code example Copy
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

// The following code should be placed inside a method that declares: throws Exception
String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC));
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
String idempotencyKey = "abc-def-456";

String authorization = HmacAuthHelper.buildAuthorizationHeader(
    "POST",
    "/v1/{entityId}/payments",
    "application/json",
    date,
    "YourAPIKeyId",
    "YourAPISecret",
    idempotencyKey
);

// Send the request — Date and X-GCS-Idempotency-Key headers must match exactly what was passed above
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("/v1/{entityId}/payments"))
    .header("Authorization", authorization)
    .header("Date", date)
    .header("X-GCS-Idempotency-Key", idempotencyKey)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("{ ... }"))
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using System;
using System.Net.Http;
using System.Text;

string date = DateTime.UtcNow.ToString("R");
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
string idempotencyKey = "abc-def-456";

string authorization = HmacAuthHelper.BuildAuthorizationHeader(
    method: "POST",
    path: "/v1/{entityId}/payments",
    contentType: "application/json",
    date: date,
    apiKeyId: "YourAPIKeyId",
    apiSecret: "YourAPISecret",
    idempotencyKey: idempotencyKey
);

// Send the request — Date and X-GCS-Idempotency-Key headers must match exactly what was passed above
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/v1/{entityId}/payments");
request.Headers.TryAddWithoutValidation("Authorization", authorization);
request.Headers.TryAddWithoutValidation("Date", date);
request.Headers.TryAddWithoutValidation("X-GCS-Idempotency-Key", idempotencyKey);
request.Content = new StringContent("{ ... }", Encoding.UTF8, "application/json");
const date = new Date().toUTCString();
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
const idempotencyKey = 'abc-def-456';

const authorization = buildAuthorizationHeader(
    'POST',
    '/v1/{entityId}/payments',
    'application/json',
    date,
    'YourAPIKeyId',
    'YourAPISecret',
    idempotencyKey
);

// Send the request — Date and X-GCS-Idempotency-Key headers must match exactly what was passed above
const response = await fetch('/v1/{entityId}/payments', {
    method: 'POST',
    headers: {
        'Authorization': authorization,
        'Date': date,
        'X-GCS-Idempotency-Key': idempotencyKey,
        'Content-Type': 'application/json',
    },
    body: '{ ... }',
});
<?php

Nov 11, 2026, 2:13:21 PM = gmdate('D, d M Y H:i:s') . ' GMT';
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
$idempotencyKey = 'abc-def-456';

$authorization = HmacAuthHelper::buildAuthorizationHeader(
    'POST',
    '/v1/{entityId}/payments',
    'application/json',
    Nov 11, 2026, 2:13:21 PM,
    'YourAPIKeyId',
    'YourAPISecret',
    $idempotencyKey
);

// Send the request — Date and X-GCS-Idempotency-Key headers must match exactly what was passed above
$ch = curl_init('/v1/{entityId}/payments');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{ ... }');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: $authorization",
    "Date: Nov 11, 2026, 2:13:21 PM",
    "X-GCS-Idempotency-Key: $idempotencyKey",
    'Content-Type: application/json',
]);
org.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse@4436b922 = curl_exec($ch);
curl_close($ch);
import urllib.request
from email.utils import formatdate

date = formatdate(usegmt=True)
# e.g. "Mon, 18 May 2026 10:00:00 GMT"
idempotency_key = 'abc-def-456'

authorization = HmacAuthHelper.build_authorization_header(
    method='POST',
    path='/v1/{entityId}/payments',
    content_type='application/json',
    date=date,
    api_key_id='YourAPIKeyId',
    api_secret='YourAPISecret',
    idempotency_key=idempotency_key
)

# Send the request — Date and X-GCS-Idempotency-Key headers must match exactly what was passed above
request = urllib.request.Request(
    '/v1/{entityId}/payments',
    method='POST',
    data=b'{ ... }',
    headers={
        'Authorization': authorization,
        'Date': date,
        'X-GCS-Idempotency-Key': idempotency_key,
        'Content-Type': 'application/json',
    }
)
response = urllib.request.urlopen(request)
require 'net/http'
require 'uri'

date = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
# e.g. "Mon, 18 May 2026 10:00:00 GMT"
idempotency_key = 'abc-def-456'

authorization = HmacAuthHelper.build_authorization_header(
  'POST',
  '/v1/{entityId}/payments',
  'application/json',
  date,
  'YourAPIKeyId',
  'YourAPISecret',
  idempotency_key
)

# Send the request — Date and X-GCS-Idempotency-Key headers must match exactly what was passed above
uri = URI('/v1/{entityId}/payments')
request = Net::HTTP::Post.new(uri)
request['Authorization']         = authorization
request['Date']                  = date
request['X-GCS-Idempotency-Key'] = idempotency_key
request['Content-Type']          = 'application/json'
request.body = '{ ... }'

response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }

Resulting signature subject (before signing):

POST\n
application/json\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-idempotency-key:abc-def-456\n
/v1/{entityId}/payments\n


Resulting raw HTTP request:

POST /v1/{entityId}/payments HTTP/1.1
Authorization: GCS v1HMAC:<your-api-key-id>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
Content-Type: application/json
X-GCS-Date: Mon, 18 May 2026 10:00:00 GMT
X-GCS-Idempotency-Key: abc-def-456

{ ... request body ... }

Usage — request without a body (GET)

Skip code example Copy
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

// The following code should be placed inside a method that declares: throws Exception
String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC));

String authorization = HmacAuthHelper.buildAuthorizationHeader(
    "GET",
    "/v1/{entityId}/payments/{paymentId}",
    "application/json", // ignored — overridden to "" internally for GET
    date,
    "YourAPIKeyId",
    "YourAPISecret",
    null                // no platform-specific header for GET
);

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("/v1/{entityId}/payments/{paymentId}"))
    .header("Authorization", authorization)
    .header("Date", date)
    .GET()
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using System;
using System.Net.Http;

string date = DateTime.UtcNow.ToString("R");

string authorization = HmacAuthHelper.BuildAuthorizationHeader(
    method: "GET",
    path: "/v1/{entityId}/payments/{paymentId}",
    contentType: "application/json", // ignored — overridden to "" internally for GET
    date: date,
    apiKeyId: "YourAPIKeyId",
    apiSecret: "YourAPISecret"
);

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/v1/{entityId}/payments/{paymentId}");
request.Headers.TryAddWithoutValidation("Authorization", authorization);
request.Headers.TryAddWithoutValidation("Date", date);
const date = new Date().toUTCString();

const authorization = buildAuthorizationHeader(
    'GET',
    '/v1/{entityId}/payments/{paymentId}',
    'application/json', // ignored — overridden to '' internally for GET
    date,
    'YourAPIKeyId',
    'YourAPISecret'
);

const response = await fetch('/v1/{entityId}/payments/{paymentId}', {
    method: 'GET',
    headers: {
        'Authorization': authorization,
        'Date': date,
    },
});
<?php

Nov 11, 2026, 2:13:21 PM = gmdate('D, d M Y H:i:s') . ' GMT';

$authorization = HmacAuthHelper::buildAuthorizationHeader(
    'GET',
    '/v1/{entityId}/payments/{paymentId}',
    'application/json', // ignored — overridden to '' internally for GET
    Nov 11, 2026, 2:13:21 PM,
    'YourAPIKeyId',
    'YourAPISecret'
);

$ch = curl_init('/v1/{entityId}/payments/{paymentId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: $authorization",
    "Date: Nov 11, 2026, 2:13:21 PM",
]);
org.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse@4436b922 = curl_exec($ch);
curl_close($ch);
import urllib.request
from email.utils import formatdate

date = formatdate(usegmt=True)

authorization = HmacAuthHelper.build_authorization_header(
    method='GET',
    path='/v1/{entityId}/payments/{paymentId}',
    content_type='application/json',  # ignored — overridden to '' internally for GET
    date=date,
    api_key_id='YourAPIKeyId',
    api_secret='YourAPISecret'
)

request = urllib.request.Request(
    '/v1/{entityId}/payments/{paymentId}',
    method='GET',
    headers={
        'Authorization': authorization,
        'Date': date,
    }
)
response = urllib.request.urlopen(request)
require 'net/http'
require 'uri'

date = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')

authorization = HmacAuthHelper.build_authorization_header(
  'GET',
  '/v1/{entityId}/payments/{paymentId}',
  'application/json', # ignored — overridden to '' internally for GET
  date,
  'YourAPIKeyId',
  'YourAPISecret'
)

uri = URI('/v1/{entityId}/payments/{paymentId}')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = authorization
request['Date']          = date

response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }

Resulting signature subject (before signing):

GET\n
\n
Mon, 18 May 2026 10:00:00 GMT\n
x-gcs-date:Mon, 18 May 2026 10:00:00 GMT\n
/v1/{entityId}/payments/{paymentId}\n

Resulting raw HTTP request:

GET /v1/{entityId}/payments/{paymentId} HTTP/1.1
Authorization: GCS v1HMAC:<your-api-key-id>:<base64-encoded-signature>
Date: Mon, 18 May 2026 10:00:00 GMT
X-GCS-Date: Mon, 18 May 2026 10:00:00 GMT

Was this page helpful?

Do you have any comments?

Thank you for your response.