Bonjour, je rencontre de nouveau un souci avec une API
le fournisseur me donne une Clé et secret ID
je calcule un HASH ensuite que je mets en base 64
je crée ensuite un autorizationHeader

authorizationHeader := 'GCS v1HMAC:' + apiKey + ':' + hmacSignature;

que je mets ainsi dans ma requête
Headers[0] := TNameValuePair.Create('Authorization', authorizationHeader);
Headers[1] := TNameValuePair.Create('Date', dateRFC1123);
Headers[2] := TNameValuePair.Create('Content-Type', contentType);
Headers[3] := TNameValuePair.Create('Accept', 'application/json');

en faisant ça j'ai systématiquement une erreur 403

[18:08:12] Response Content:
[18:08:12] {
"errorId": "cd56e7ef-9d2a-4dbf-901b-f12868521003",
"errors": [
{
"code": "9007",
"id": "ACCESS_TO_MERCHANT_NOT_ALLOWED",
"category": "DIRECT_PLATFORM_ERROR",
"message": "ACCESS_TO_MERCHANT_NOT_ALLOWED",
"httpStatusCode": 403
}
],
"status": 403
}
[18:08:12]
[18:08:12] ERROR 403: ACCESS_TO_MERCHANT_NOT_ALLOWED.
[18:08:12] Check merchant access, API key permissions, and IP whitelist.
[18:08:12]
[18:08:12] === Payment Links API Test Completed ===


Je me demande s'il ne faut pas utiliser
TOAuth1Authenticator mais je ne sais pas comment le paramétrer ici, si quelqu'un a une idée, je mets le code complet de la procédure ci dessous.


Merci d'avance pout toute aide.




procedure TPaymentLinksTestForm.CreatePaymentLink;
var
HTTPClient: THTTPClient;
RequestBodyStream: TStringStream;
Response: IHTTPResponse;
Headers: TNetHeaders;

pspid: string;
apiKey: string;
secretKey: string;

requestMethod: string;
contentType: string;
baseURL: string;
resourcePath: string;
fullURL: string;

dateRFC1123: string;
stringToHash: string;
hmacSignature: string;
authorizationHeader: string;

jsonBody: string;
responseContent: string;
begin
try
LogMessage('=== Payment Links API Test Started ===');

// =========================================================
// CONFIGURATION
// =========================================================
pspid := 'PRINTEMPS4';

// Replace these values with the real API key and secret.
// Important: if the previous secret was shared externally, rotate it.
apiKey := 'YOUR_API_KEY_HERE';
secretKey := 'YOUR_SECRET_KEY_HERE';

requestMethod := 'POST';
contentType := 'application/json';

baseURL := 'https://payment.preprod.direct.worldline-solutions.com';

// This path is used BOTH for the URL and for the HMAC string.
// For the HMAC, it must be the path only, not the full URL.
resourcePath := '/v2/' + pspid + '/paymentlinks';

fullURL := baseURL + resourcePath;

LogMessage('PSPID: ' + pspid);
LogMessage('Request Method: ' + requestMethod);
LogMessage('Content-Type: ' + contentType);
LogMessage('Base URL: ' + baseURL);
LogMessage('Resource Path: ' + resourcePath);
LogMessage('Full URL: ' + fullURL);
LogMessage('');

// =========================================================
// DATE HEADER
// =========================================================
dateRFC1123 := GetRFC1123DateTime;

LogMessage('Date header: ' + dateRFC1123);
LogMessage('');

// =========================================================
// HMAC SIGNATURE
// =========================================================
// Worldline HMAC string format:
//
// POST
// application/json
// Tue, 10 Jun 2026 12:34:56 GMT
// /v2/PRINTEMPS4/paymentlinks
//
// Use LF only: #10
// Do not use CRLF: #13#10
// Do not add the full host.
// Do not add an extra blank line unless the API documentation requires
// signed x-gcs headers.
stringToHash :=
requestMethod + #10 +
contentType + #10 +
dateRFC1123 + #10 +
resourcePath;

LogMessage('String To Hash:');
LogMessage(StringReplace(stringToHash, #10, '\n', [rfReplaceAll]));
LogMessage('');

hmacSignature := ComputeHMACSHA256(stringToHash, secretKey);

authorizationHeader := 'GCS v1HMAC:' + apiKey + ':' + hmacSignature;

LogMessage('HMAC Signature: ' + hmacSignature);
LogMessage('Authorization Header: GCS v1HMAC:' + apiKey + ':<signature hidden>');
LogMessage('');

// =========================================================
// JSON BODY
// =========================================================
jsonBody :=
'{' + #13#10 +
' "expirationDate": "2026-08-01T14:15:22Z",' + #13#10 +
' "description": "TEST Payment Link - ' + FormatDateTime('yyyymmdd hhnn', Now) + '",' + #13#10 +
' "paymentLinkOrder": {' + #13#10 +
' "merchantReference": "PRINTEMPS4-' + FormatDateTime('yyyymmddhhnnss', Now) + '",' + #13#10 +
' "amount": {' + #13#10 +
' "amount": 1000,' + #13#10 +
' "currencyCode": "EUR"' + #13#10 +
' }' + #13#10 +
' },' + #13#10 +
' "recipientName": "Jean Bon",' + #13#10 +
' "merchantReference": "your-order-' + FormatDateTime('yyyymmddhhnnss', Now) + '"' + #13#10 +
'}';

LogMessage('JSON Body:');
LogMessage(jsonBody);
LogMessage('');

// =========================================================
// HTTP HEADERS
// =========================================================
SetLength(Headers, 4);

Headers[0] := TNameValuePair.Create('Authorization', authorizationHeader);
Headers[1] := TNameValuePair.Create('Date', dateRFC1123);
Headers[2] := TNameValuePair.Create('Content-Type', contentType);
Headers[3] := TNameValuePair.Create('Accept', 'application/json');

LogMessage('Headers:');
LogMessage(' Authorization: GCS v1HMAC:' + apiKey + ':<signature hidden>');
LogMessage(' Date: ' + dateRFC1123);
LogMessage(' Content-Type: ' + contentType);
LogMessage(' Accept: application/json');
LogMessage('');

// =========================================================
// EXECUTE REQUEST
// =========================================================
HTTPClient := THTTPClient.Create;
try
HTTPClient.ConnectionTimeout := 30000;
HTTPClient.ResponseTimeout := 30000;
HTTPClient.UserAgent := 'Delphi Payment Links Test';

RequestBodyStream := TStringStream.Create(jsonBody, TEncoding.UTF8);
try
RequestBodyStream.Position := 0;

LogMessage('Executing POST request...');
LogMessage('');

Response := HTTPClient.Post(
fullURL,
RequestBodyStream,
nil,
Headers
);

LogMessage('=== RESPONSE ===');
LogMessage('HTTP Status Code: ' + IntToStr(Response.StatusCode));
LogMessage('Status Text: ' + Response.StatusText);
LogMessage('');

responseContent := Response.ContentAsString(TEncoding.UTF8);

LogMessage('Response Content:');
LogMessage(responseContent);
LogMessage('');

case Response.StatusCode of
200, 201:
begin
LogMessage('SUCCESS: Payment Link created.');
end;

400:
begin
LogMessage('ERROR 400: Bad Request.');
LogMessage('Check the JSON payload/schema.');
end;

401:
begin
LogMessage('ERROR 401: Unauthorized.');
LogMessage('Check API key, secret key, Date header, HMAC string, and Content-Type.');
end;

403:
begin
LogMessage('ERROR 403: ACCESS_TO_MERCHANT_NOT_ALLOWED.');
LogMessage('Check merchant access, API key permissions, and IP whitelist.');
end;

406:
begin
LogMessage('ERROR 406: Not Acceptable.');
LogMessage('Check the endpoint URL, Accept header, Content-Type header, and Payment Links API activation.');
end;

else
begin
LogMessage('ERROR: HTTP ' + IntToStr(Response.StatusCode));
end;
end;

finally
RequestBodyStream.Free;
end;

finally
HTTPClient.Free;
end;

LogMessage('');
LogMessage('=== Payment Links API Test Completed ===');

except
on E: Exception do
begin
LogMessage('');
LogMessage('EXCEPTION: ' + E.ClassName + ' - ' + E.Message);
end;
end;
end;