Authentification manuelle (sans SDK)
Si vous choisissez de ne pas utiliser nos SDK pour envoyer des requêtes à nos endpoints, vous devez implémenter vous même le mécanisme d'authentification.
L'API CAWL ecommerce utilise une signature de requête HMAC SHA256, un type d'algorithme de hachage à clé construit à partir de la fonction de hachage SHA-256 : vous calculez une signature cryptographique et vous l'incluez dans la requête. Cette signature est basée sur
- Vos identifiants (API Key/Secret).
- Les informations clés de la requête que vous envoyez.
À la réception de votre requête, notre plateforme recalcule cette même signature. ISi les deux signature correspondent, nous authentifions la requête.
Implementation
Pour implémenter le mécanisme d'authentification, vous devez
- Créer une
API Key/Secretcomme décrit dans le chapitre "Configuration de l'API Key/Secret" de notre guide Authentification.. - Préparer les informations de la requête : méthode HTTP, chemin de la requête et en-têtes de requête requis.
- Hacher les informations de la requête avec le
API Secretafin de calculer lasignature. - Envoyer la requête en incluant les informations clés de la requête, la
signatureet laAPI Key.
Suivez ces instructions pas à pas pour implémenter le mécanisme d'authentification.
- Collecter les informations clés de la requête
- Préparer les en-têtes de date
- Préparer les en-têtes canoniques
- Construire le sujet de la signature (créer la chaîne à hacher)
- Calculer la signature (calculer le hash)
- Construire l'en‑tête d’autorisation
- Envoyer la requête
Collecter les informations clés de la requête
Collectez les informations clés de la requête que vous envoyez à l'API CAWL ecommerce:
| Propriété | Description |
|---|---|
| Méthode HTTP | En fonction de l'endpoint auquel vous souhaitez envoyer la requête, c'est à dire POST, GET etc. |
| En-tête Content-type |
Valeur fixe « application/json » pour les requêtes avec un corps (par exemple POST). Chaîne vide pour les requêtes sans corps (par exemple GET). |
| En-tête Date | Horodatage UTC au format RFC 1123. Il doit être identique à la fois dans l'en‑tête de la requête et dans la chaîne à hacher (string‑to‑hash) pour calculer la signature. |
| En-têtes x-gcs-* | Tous les en têtes personnalisés canoniques x-gcs-* que vous souhaitez envoyer, triés par ordre alphabétique dans la chaîne à hacher pour calculer la signature. |
| Chemin de la requête | Le chemin relatif de l'API CAWL ecommerce à laquelle vous envoyez la requête, incluant la version de l'API et votre merchantId par exemple "/v2/merchantId/hostedcheckouts" |
Préparer les en-têtes de date
Pour l'en-tête de date, créez un horodatage UTC au format RFC 1123. Utilisez la même valeur à la fois pour l'en-tête et pour le sujet de la signature (la chaîne à hacher).
Notre plateforme rejettera les requêtes dont l'horodatage est antérieur de plus de 5 minutes.
Préparer les en-têtes canoniques
Si vous incluez des en-têtes x-gcs-* spécifiques à CAWL dans votre requête, vous devez les normaliser selon les conventions suivantes :
- Mettre la clé de l'en-tête en minuscules.
- Supprimer les espaces en début et fin de valeur de l'en‑tête.
- Réduire toute séquence d'espaces internes dans la valeur de l'en‑tête à un seul espace.
- Formater chaque paire clé‑valeur d'en‑tête comme {lowercased-key}:{normalized-value}.
- Trier les paires clé‑valeur canoniques par ordre alphabétique de la clé.
Si vous incluez un en‑tête x-gcs-* dans le sujet de la signature, veillez à aussi l'ajouter dans la requête envoyée.
| Convention | Valeur brute | Clé/valeur normalisée |
|---|---|---|
| Clé d'en-tête en minuscules | X-GCS-Idempotency-Key | x-gcs-idempotency-key |
| Suppression des espaces en début et fin de valeur de l'en-tête | ··header value·· | header value |
| Réduction des espaces multiples dans la valeur de l'en-tête | header··value | header value |
| Formatage des paires clé-valeur | {raw-key}={raw-value} | {lowercased-key}:{normalized-value} |
| Ordre alphabétique des paires clé-valeur canoniques | 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 |
Nos exemples utilisent uniquement l'en-tête x-gcs-idempotency-key pour illustrer la manière de gérer les en-têtes x-gcs-* spécifiques à CAWL.
Si vous utilisez nos SDK Serveur pour appeler l'API CAWL ecommerce, ils ajoutent et traitent automatiquement d'autres en-têtes x-gcs-* supplémentaires.
Ces en-têtes supplémentaires ne sont pas pertinents pour le processus d'authentification manuelle, c'est pourquoi nous ne les détaillons pas ici.
Construire le sujet de la signature (créer la chaîne à hacher)
Concaténez les éléments suivants (méthode de la requête, en‑têtes, en‑têtes canoniques, chemin de la requête) dans cet ordre, chacun terminé par un caractère de nouvelle ligne \n :
{HTTP_METHOD}\n
{Content-Type}\n
{Date}\n
{canonical-x-gcs-header-1}\n
{canonical-x-gcs-header-N}\n
{request-path}\n
- Si vous incluez des en-têtes canoniques x-gcs-* veillez à les ordonner par clé comme décrit ci‑dessus.
- Content-Type est toujours une chaîne vide pour les requêtes sans corps.
Des exemples typiques de requêtes avec ou sans corps ressemblent à ceci :
Requête avec un corps (par exemple 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
Requête sans corps (par exemple 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
Calculer la signature (calculer le hash)
Hachez le sujet de la signature (string-to-hash) en utilisant HMAC-SHA256 (encodage UTF-8) avec votre API Secret. Puis encodez en Base64-encode la signature obtenue:
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-date:Mon, 18 May 2026 10:00:00 GMT" + "\n" +
"x-gcs-idempotency-key:" + "\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-date:Mon, 18 May 2026 10:00:00 GMT" + "\n" +
"x-gcs-idempotency-key:" + "\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-date:Mon, 18 May 2026 10:00:00 GMT' + '\n' +
'x-gcs-idempotency-key:' + '\n' +
'/v1/{merchantId}/payments' + '\n';
// Hash the signatureSubject
const signature = crypto
.createHmac('SHA256', apiSecret)
.update(signatureSubject)
.digest('base64');
' . "\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-date:Mon, 18 May 2026 10:00:00 GMT' + '\n' +
'x-gcs-idempotency-key:' + '\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-date:Mon, 18 May 2026 10:00:00 GMT' + "\n" +
'x-gcs-idempotency-key:' + "\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
Construire l'en‑tête d’autorisation
Ajoutez l'en-tête Authorisation GCS v1HMAC à votre requête, composé de la signature calculée et de votre API Key:
Authorization: GCS v1HMAC:<your-api-key>:<base64-encoded-signature>
Envoyer la requête
Lorsque vous envoyez votre requête à l'API CAWL ecommerce, incluez tous les en têtes utilisés dans le sujet de la signature (chaîne à hacher) ainsi que l'en tête Authorisation.
Requête HTTP brute avec corps
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 ... }
Requête HTTP brute sans corps
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
Dépannage des écarts de signature
À la réception de votre requête, notre plateforme tente de faire correspondre la signature et la API Key figurant dans l'en tête Authorisation header. Si cette correspondance échoue, notre plateforme rejettera votre requête et renverra un message d’erreur de ce type :
{
"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
}
Pour résoudre ce type d'erreurs, veillez à respecter les consignes suivantes :
- Utilisez le bon environnement : ne mélangez pas les API Keys/Secrets/comptes des environnements de test et de production.
- Évitez d’utiliser des API Keys/Secrets.
- Conservez un en-tête Date avec un décalage maximal de 5 minutes et utilisez la même date dans l’en-tête et dans la signature.
- Pour les requêtes sans corps, définissez Content-Type sur une string vide. Ne l'omettez pas et ne le définissez pas à null.
- Appliquez les règles de formatage/normalisation pour les en-têtes x-gcs-*.
- Envoyez dans la requête exactement les mêmes en-têtes que ceux utilisés pour concaténer le sujet de la signature (string-to-hash).
Consultez notre guide dédié Dépannage de l'API guide pour analyser et résoudre d'autres erreurs de la plateforme.
Exemple de code complet
Reportez vous à notre guide de dépannage dédié pour analyser et résoudre les autres erreurs de plateforme :
Classe utilitaire
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
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 xGcsHeaders Optional x-gcs-* headers as raw "Key: Value" strings
*/
public static String buildAuthorizationHeader(
String method, String path, String contentType, String date,
String apiKeyId, String apiSecret, List xGcsHeaders) throws Exception {
// Normalize Content-Type — body-less methods always use empty string
if (METHODS_WITHOUT_BODY.contains(method.toUpperCase())) {
contentType = "";
}
// Canonicalize x-gcs-* headers
List canonicalHeaders = xGcsHeaders.stream()
.map(h -> {
int idx = h.indexOf(":");
String key = h.substring(0, idx).trim().toLowerCase();
String value = h.substring(idx + 1).trim().replaceAll("\\s+", " ");
return key + ":" + value;
})
.sorted()
.collect(Collectors.toList());
// 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
for (String header : canonicalHeaders) {
subject.append(header).append("\n"); // x-gcs-header\n (one per line)
}
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.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,
params string[] xGcsHeaders)
{
// Content-Type is empty for methods without a request body
if (MethodsWithoutBody.Contains(method.ToUpperInvariant()))
contentType = string.Empty;
// Normalize and sort x-gcs-* headers: lowercase key, collapse whitespace in value
string[] canonicalHeaders = xGcsHeaders
.Select(h => {
int separatorIndex = h.IndexOf(":");
string key = h[..separatorIndex].Trim().ToLowerInvariant();
string value = Regex.Replace(h[(separatorIndex + 1)..].Trim(), @"\s+", " ");
return $"{key}:{value}";
})
.OrderBy(h => h)
.ToArray();
// 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
foreach (string header in canonicalHeaders)
subject.Append(header + "\n"); // x-gcs-header\n (one per line)
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}";
}
}
import crypto from 'crypto';
const METHODS_WITHOUT_BODY = ['GET', 'DELETE'];
function buildAuthorizationHeader(method, path, contentType, date, apiKeyId, apiSecret, xGcsHeaders = []) {
// Normalize Content-Type — body-less methods always use empty string
if (METHODS_WITHOUT_BODY.includes(method.toUpperCase())) {
contentType = '';
}
// Canonicalize x-gcs-* headers
const canonicalHeaders = xGcsHeaders
.map(h => {
const idx = h.indexOf(':');
const key = h.substring(0, idx).trim().toLowerCase();
const value = h.substring(idx + 1).trim().replace(/\s+/g, ' ');
return `${key}:${value}`;
})
.sort();
// Build the signature subject
let subject = method.toUpperCase() + '\n'; // method\n
subject += contentType + '\n'; // content-type\n
subject += date + '\n'; // date\n
for (const header of canonicalHeaders) {
subject += header + '\n'; // x-gcs-header\n (one per line)
}
subject += path + '\n'; // path\n
// Sign with HMAC-SHA256 and Base64-encode
const signature = crypto
.createHmac('SHA256', apiSecret.trim())
.update(subject)
.digest('base64');
return `GCS v1HMAC:${apiKeyId.trim()}:${signature}`;
}
<?php
class HmacAuthHelper
{
private static array $methodsWithoutBody = ['GET', 'DELETE'];
public static function buildAuthorizationHeader(
string $method,
string $path,
string $contentType,
string $date,
string $apiKeyId,
string $apiSecret,
array $xGcsHeaders = []
): string {
// Normalize Content-Type — body-less methods always use empty string
if (in_array(strtoupper($method), self::$methodsWithoutBody)) {
$contentType = '';
}
// Canonicalize x-gcs-* headers
$canonicalHeaders = [];
foreach ($xGcsHeaders as $h) {
$idx = strpos($h, ':');
$key = strtolower(trim(substr($h, 0, $idx)));
$value = preg_replace('/\s+/', ' ', trim(substr($h, $idx + 1)));
$canonicalHeaders[] = $key . ':' . $value;
}
sort($canonicalHeaders);
// Build the signature subject
$subject = strtoupper($method) . "\n"; // method\n
$subject .= $contentType . "\n"; // content-type\n
$subject .= $date . "\n"; // date\n
foreach ($canonicalHeaders as $header) {
$subject .= $header . "\n"; // x-gcs-header\n (one per line)
}
$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,
x_gcs_headers: list[str] = None) -> str:
if x_gcs_headers is None:
x_gcs_headers = []
# Normalize Content-Type — body-less methods always use empty string
if method.upper() in HmacAuthHelper.METHODS_WITHOUT_BODY:
content_type = ''
# Canonicalize x-gcs-* headers
canonical_headers = []
for h in x_gcs_headers:
idx = h.index(':')
key = h[:idx].strip().lower()
value = re.sub(r'\s+', ' ', h[idx + 1:].strip())
canonical_headers.append(f'{key}:{value}')
canonical_headers.sort()
# Build the signature subject
subject = method.upper() + '\n' # method\n
subject += content_type + '\n' # content-type\n
subject += date + '\n' # date\n
for header in canonical_headers:
subject += header + '\n' # x-gcs-header\n (one per line)
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 x_gcs_headers [Array] Optional x-gcs-* headers as raw 'Key: Value' strings
def self.build_authorization_header(method, path, content_type, date, api_key_id, api_secret, x_gcs_headers = [])
# Normalize Content-Type — body-less methods always use empty string
content_type = '' if METHODS_WITHOUT_BODY.include?(method.upcase)
# Canonicalize x-gcs-* headers
canonical_headers = x_gcs_headers.map do |h|
idx = h.index(':')
key = h[0, idx].strip.downcase
value = h[(idx + 1)..].strip.gsub(/\s+/, ' ')
"#{key}:#{value}"
end.sort
# Build the signature subject
subject = "#{method.upcase}\n" # method\n
subject += "#{content_type}\n" # content-type\n
subject += "#{date}\n" # date\n
canonical_headers.each { |header| subject += "#{header}\n" } # x-gcs-header\n (one per line)
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
Utilisation — requête avec un corps (POST)
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;
import java.util.List;
// 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 authorization = HmacAuthHelper.buildAuthorizationHeader(
"POST",
"/v1/{entityId}/payments",
"application/json",
date,
"",
"",
List.of(
"X-GCS-Date: " + date,
"X-GCS-Idempotency-Key: abc-def-456"
)
);
// Send the request — Date header 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-Date", date)
.header("X-GCS-Idempotency-Key", "abc-def-456")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{ ... }"))
.build();
HttpResponse 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 authorization = HmacAuthHelper.BuildAuthorizationHeader(
method: "POST",
path: "/v1/{entityId}/payments",
contentType: "application/json",
date: date,
apiKeyId: "",
apiSecret: "",
xGcsHeaders:
[
$"X-GCS-Date: {date}",
"X-GCS-Idempotency-Key: abc-def-456"
]
);
// Send the request — Date header 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-Date", date);
request.Headers.TryAddWithoutValidation("X-GCS-Idempotency-Key", "abc-def-456");
request.Content = new StringContent("{ ... }", Encoding.UTF8, "application/json");
const date = new Date().toUTCString();
// e.g. "Mon, 18 May 2026 10:00:00 GMT"
const authorization = buildAuthorizationHeader(
'POST',
'/v1/{entityId}/payments',
'application/json',
date,
'',
'',
[
`X-GCS-Date: Jul 9, 2026, 8:18:16 PM`,
'X-GCS-Idempotency-Key: abc-def-456',
]
);
// Send the request — Date header must match exactly what was passed above
const response = await fetch('/v1/{entityId}/payments', {
method: 'POST',
headers: {
'Authorization': authorization,
'Date': date,
'X-GCS-Date': date,
'X-GCS-Idempotency-Key': 'abc-def-456',
'Content-Type': 'application/json',
},
body: '{ ... }',
});
',
'',
[
"X-GCS-Date: Jul 9, 2026, 8:18:16 PM",
'X-GCS-Idempotency-Key: abc-def-456',
]
);
// Send the request — Date header 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: Jul 9, 2026, 8:18:16 PM",
"X-GCS-Date: Jul 9, 2026, 8:18:16 PM",
'X-GCS-Idempotency-Key: abc-def-456',
'Content-Type: application/json',
]);
org.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse@367faffe = 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"
authorization = HmacAuthHelper.build_authorization_header(
method='POST',
path='/v1/{entityId}/payments',
content_type='application/json',
date=date,
api_key_id='',
api_secret='',
x_gcs_headers=[
f'X-GCS-Date: {date}',
'X-GCS-Idempotency-Key: abc-def-456',
]
)
# Send the request — Date header 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-Date': date,
'X-GCS-Idempotency-Key': 'abc-def-456',
'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"
authorization = HmacAuthHelper.build_authorization_header(
'POST',
'/v1/{entityId}/payments',
'application/json',
date,
'',
'',
[
"X-GCS-Date: #{date}",
'X-GCS-Idempotency-Key: abc-def-456'
]
)
# Send the request — Date header 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-Date'] = date
request['X-GCS-Idempotency-Key'] = 'abc-def-456'
request['Content-Type'] = 'application/json'
request.body = '{ ... }'
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
Sujet de la signature résultante (avant la signature) :
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
Requête HTTP brute résultante :
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 ... }
Utilisation — requête sans corps (GET)
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;
import java.util.List;
// 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,
"",
"",
List.of("X-GCS-Date: " + date)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("/v1/{entityId}/payments/{paymentId}"))
.header("Authorization", authorization)
.header("Date", date)
.header("X-GCS-Date", date)
.GET()
.build();
HttpResponse 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: "",
apiSecret: "",
xGcsHeaders: [$"X-GCS-Date: {date}"]
);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/v1/{entityId}/payments/{paymentId}");
request.Headers.TryAddWithoutValidation("Authorization", authorization);
request.Headers.TryAddWithoutValidation("Date", date);
request.Headers.TryAddWithoutValidation("X-GCS-Date", date);
const date = new Date().toUTCString();
const authorization = buildAuthorizationHeader(
'GET',
'/v1/{entityId}/payments/{paymentId}',
'application/json', // ignored — overridden to '' internally for GET
date,
'',
'',
[`X-GCS-Date: Jul 9, 2026, 8:18:16 PM`]
);
const response = await fetch('/v1/{entityId}/payments/{paymentId}', {
method: 'GET',
headers: {
'Authorization': authorization,
'Date': date,
'X-GCS-Date': date,
},
});
',
'',
["X-GCS-Date: Jul 9, 2026, 8:18:16 PM"]
);
$ch = curl_init('/v1/{entityId}/payments/{paymentId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: $authorization",
"Date: Jul 9, 2026, 8:18:16 PM",
"X-GCS-Date: Jul 9, 2026, 8:18:16 PM",
]);
org.tuckey.web.filters.urlrewrite.UrlRewriteWrappedResponse@367faffe = 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='',
api_secret='',
x_gcs_headers=[f'X-GCS-Date: {date}']
)
request = urllib.request.Request(
'/v1/{entityId}/payments/{paymentId}',
method='GET',
headers={
'Authorization': authorization,
'Date': date,
'X-GCS-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,
'',
'',
["X-GCS-Date: #{date}"]
)
uri = URI('/v1/{entityId}/payments/{paymentId}')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = authorization
request['Date'] = date
request['X-GCS-Date'] = date
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |http| http.request(request) }
Sujet de la signature résultante (avant la signature) :
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
Requête HTTP brute résultante :
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