Postbacks to Merchants
After a lender reaches a decision on a financing request, WeGetFinancing sends an HTTP POST to the merchant’s configured postback URL. This is the primary mechanism for order fulfilment.

Create the order only after receiving an approved postback.
Postback URL Configuration
Section titled “Postback URL Configuration”Configure the postback URL in one of two ways:
- Per-merchant default — set in the partner portal under Integration → API Integration.
- Per-request override — pass
postback_urlin the Create Request body.
If neither is set, the postback is silently discarded (deleted from the queue without sending).
Payload Format
Section titled “Payload Format”The format depends on the version field sent in the original Create Request.
v1.9 — JSON (recommended)
Section titled “v1.9 — JSON (recommended)”Content-Type: application/json
Approval:
{ "version": "1.9", "request_token": "df0c3186b69be8aad35ff837a841d347", "merchant_transaction_id": "ORDER-123", "updates": { "status": "approved" }}Pre-approval:
{ "version": "1.9", "request_token": "df0c3186b69be8aad35ff837a841d347", "merchant_transaction_id": "ORDER-123", "updates": { "status": "preapproved" }}Rejection:
{ "version": "1.9", "request_token": "df0c3186b69be8aad35ff837a841d347", "updates": { "status": "rejected" }}Refund:
{ "version": "1.9", "request_token": "df0c3186b69be8aad35ff837a841d347", "updates": { "status": "refund", "amount": "1200.00" }}| Field | Type | Description |
|---|---|---|
version | string | Postback dialect ("1.9") |
request_token | string | The inv_id returned by the original create-request (the unique ID of the merchant request) |
merchant_transaction_id | string | Your original merchant_transaction_id, if provided (stored as cust_id_ext internally) |
updates.status | string | Decision status — see Status Values |
updates.amount | string | Refund amount — only present when status is "refund" |
The v1.9 body is constructed by PostBack.to_body() which maps the internal fields:
function: "transact"+method: "purchase"+inv_status: "Auth"→"approved"function: "transact"+method: "purchase"+inv_status: "AuthOnly"→"preapproved"function: "transact"+method: "void"→"rejected"function: "refund"→"refund"withamount
v1.0–v1.8 — Legacy JSON
Section titled “v1.0–v1.8 — Legacy JSON”Sent for merchants with version: "1.0" through "1.8".
Content-Type: application/json
The body contains the raw params dict with function, method, crl_id, and version fields added:
{ "version": "1.0", "inv_id": "df0c3186b69be8aad35ff837a841d347", "inv_status": "Auth", "function": "transact", "method": "purchase", "crl_id": "some-uuid"}| Field | Type | Description |
|---|---|---|
inv_id | string | Unique request identifier (same as request_token in v1.9) |
inv_status | string | "Auth" (approved) or "AuthOnly" (pre-approved) |
function | string | "transact" for loan decisions |
method | string | "purchase" (approval) or "void" (rejection) |
crl_id | string | UUID correlation ID (unique per postback attempt) |
v0.2 / v0.3 — URL-encoded (legacy)
Section titled “v0.2 / v0.3 — URL-encoded (legacy)”Content-Type: application/x-www-form-urlencoded
- v0.2 — uses
methodfor the function name andtypefor purchase/void. - v0.3+ — uses
functionandmethodas field names (same as v1.0 JSON but URL-encoded).
These formats are deprecated. Migrate to version: "1.9".
Status Values
Section titled “Status Values”v1.9 status | v1.0 inv_status | Internal method | Meaning |
|---|---|---|---|
approved | Auth | purchase | Loan fully approved — safe to create the order |
preapproved | AuthOnly | purchase | Pre-approval only; customer must still complete the process |
rejected | — | void | Loan rejected or voided |
refund | — | — | Loan refunded; updates.amount contains the refund amount |
Cryptographic Signature
Section titled “Cryptographic Signature”Every outbound postback includes two integrity headers:
| Header | Description |
|---|---|
x-timestamp | Unix timestamp (seconds) at time of sending |
x-signature | SHA-256 hex digest — see construction below |
Signature Construction
Section titled “Signature Construction”x-signature = SHA256( x-timestamp + key_id + raw_body + key_secret )Where:
x-timestamp— the value of thex-timestampheader (string, not integer)key_id— merchant’sdhd_username(the API username from account settings)raw_body— the exact request body bytes as sent — do not parse/re-serializekey_secret— merchant’sdhd_password(the API password from account settings)
Signature Verification
Section titled “Signature Verification”import hashlibimport hmac
def verify_postback(headers: dict, raw_body: str, key_id: str, key_secret: str) -> bool: timestamp = headers.get("x-timestamp", "") received_sig = headers.get("x-signature", "") expected_sig = hashlib.sha256( (timestamp + key_id + raw_body + key_secret).encode() ).hexdigest() # constant-time comparison return hmac.compare_digest(expected_sig, received_sig)// crypto is built in to Node 18+, Bun, and Deno.const crypto = require('crypto');
function verifyPostback(headers, rawBody, keyId, keySecret) { const timestamp = headers['x-timestamp'] ?? ''; const received = headers['x-signature'] ?? ''; const expected = crypto .createHash('sha256') .update(timestamp + keyId + rawBody + keySecret) .digest('hex'); // constant-time comparison; lengths must match first return ( expected.length === received.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received)) );}import * as crypto from 'crypto';
function verifyPostback( headers: Record<string, string>, rawBody: string, keyId: string, keySecret: string,): boolean { const timestamp = headers['x-timestamp'] ?? ''; const received = headers['x-signature'] ?? ''; const expected = crypto .createHash('sha256') .update(timestamp + keyId + rawBody + keySecret) .digest('hex'); // constant-time comparison; lengths must match first return ( expected.length === received.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received)) );}function verifyPostback(array $headers, string $rawBody, string $keyId, string $keySecret): bool { $timestamp = $headers['x-timestamp'] ?? ''; $received = $headers['x-signature'] ?? ''; $expected = hash('sha256', $timestamp . $keyId . $rawBody . $keySecret); // constant-time comparison return hash_equals($expected, $received);}require "digest"require "openssl"
def verify_postback(headers, raw_body, key_id, key_secret) timestamp = headers["x-timestamp"] || "" received = headers["x-signature"] || "" expected = Digest::SHA256.hexdigest(timestamp + key_id + raw_body + key_secret) # constant-time comparison OpenSSL.secure_compare(expected, received)endimport ( "crypto/sha256" "crypto/subtle" "encoding/hex")
func verifyPostback(headers map[string]string, rawBody, keyID, keySecret string) bool { timestamp := headers["x-timestamp"] received := headers["x-signature"] sum := sha256.Sum256([]byte(timestamp + keyID + rawBody + keySecret)) expected := hex.EncodeToString(sum[:]) // constant-time comparison return subtle.ConstantTimeCompare([]byte(expected), []byte(received)) == 1}use sha2::{Digest, Sha256};
fn verify_postback( timestamp: &str, received_sig: &str, raw_body: &str, key_id: &str, key_secret: &str,) -> bool { let mut hasher = Sha256::new(); hasher.update(timestamp.as_bytes()); hasher.update(key_id.as_bytes()); hasher.update(raw_body.as_bytes()); hasher.update(key_secret.as_bytes()); let expected = hex::encode(hasher.finalize()); // constant-time comparison expected.len() == received_sig.len() && expected .bytes() .zip(received_sig.bytes()) .fold(0u8, |acc, (a, b)| acc | (a ^ b)) == 0}import java.nio.charset.StandardCharsets;import java.security.MessageDigest;import java.util.HexFormat;
static boolean verifyPostback( String timestamp, String receivedSig, String rawBody, String keyId, String keySecret) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest( (timestamp + keyId + rawBody + keySecret).getBytes(StandardCharsets.UTF_8)); String expected = HexFormat.of().formatHex(digest); // constant-time comparison return MessageDigest.isEqual( expected.getBytes(StandardCharsets.UTF_8), receivedSig.getBytes(StandardCharsets.UTF_8));}import java.nio.charset.StandardCharsetsimport java.security.MessageDigestimport java.util.HexFormat
fun verifyPostback( timestamp: String, receivedSig: String, rawBody: String, keyId: String, keySecret: String,): Boolean { val digest = MessageDigest.getInstance("SHA-256") .digest((timestamp + keyId + rawBody + keySecret).toByteArray(StandardCharsets.UTF_8)) val expected = HexFormat.of().formatHex(digest) // constant-time comparison return MessageDigest.isEqual( expected.toByteArray(StandardCharsets.UTF_8), receivedSig.toByteArray(StandardCharsets.UTF_8), )}using System.Security.Cryptography;using System.Text;
static bool VerifyPostback( string timestamp, string receivedSig, string rawBody, string keyId, string keySecret){ byte[] hash = SHA256.HashData( Encoding.UTF8.GetBytes(timestamp + keyId + rawBody + keySecret)); string expected = Convert.ToHexString(hash).ToLowerInvariant(); // constant-time comparison return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(receivedSig));}Retry Behaviour
Section titled “Retry Behaviour”The postback system maintains a state machine for each merchant account and processes postbacks one at a time.
Per-Postback Backoff
Section titled “Per-Postback Backoff”Each postback carries its own backoff schedule:
| Parameter | Value |
|---|---|
| Initial retry delay | 60 seconds |
| Max delay between retries | 72 hours (259,200 seconds) |
| Backoff strategy | Exponential doubling (last_delay * 2, capped at max) |
| Maximum postback age | 7 days (604,800 seconds) |
After 7 days without a successful delivery, the postback is permanently abandoned.
Per-Account Backoff
Section titled “Per-Account Backoff”An account-level backoff is also applied when a server error occurs:
| Parameter | Value | Source |
|---|---|---|
| Initial account delay | 113 seconds | DEFAULT_MERCHANT_RETRY |
| Max account delay | ~3.7 hours (13,331 seconds) | DEFAULT_MAX_RETRY_DELAY |
Account-level backoff pauses all postbacks for that merchant, not just the failed one. It resets when a postback succeeds.
Non-Retryable Postbacks
Section titled “Non-Retryable Postbacks”Certain postback types are never retried after the first delivery failure:
| Condition | Reason |
|---|---|
inv_status: "AuthOnly" (pre-approved) | Prevents race conditions if the loan gets purchased while retrying |
method: "void" (rejected) | Rejection notification is informational; retrying is unnecessary |
Only approved postbacks (method: "purchase", inv_status: "Auth") receive the full exponential retry treatment.
Cluster Behaviour
Section titled “Cluster Behaviour”The postback agent adds a 300-second penalty to postbacks created on a different database node, promoting locality — the same node that created the postback handles its delivery.
Response Required from Your Server
Section titled “Response Required from Your Server”| Version | Required response |
|---|---|
| v1.9 | Any HTTP status < 300 |
| v < 1.9 | HTTP 200 with body OK (exact string) |
A non-conforming response triggers a retry (subject to the rules above).
Lenders Without Postback Support
Section titled “Lenders Without Postback Support”All lenders support postbacks except Payd and Leap Theory.
Querying Postback History
Section titled “Querying Postback History”WeGetFinancing exposes a read API on the merchant resource for querying pending postback documents.
GET /merchant/{merchant_id}/postbacksGET /merchant/{merchant_id}/postbacks/{crl_id}POST /merchant/{merchant_id}/postbacksList Postbacks
Section titled “List Postbacks”GET /merchant/{merchant_id}/postbacksReturns a list of PostBack documents linked to the merchant account. Each item is identified by its crl_id (correlation UUID).
Response: Array of postback objects. Each item:
| Field | Type | Description |
|---|---|---|
body | string | Serialised postback payload that was (or will be) sent to the merchant |
Items are keyed by crl_id in the collection; navigate to the individual endpoint to fetch a specific one.
curl -u "$USERNAME:$PASSWORD" \ -H "Accept: application/json" \ "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks"import httpx # or: import requests as httpx
result = httpx.get( "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks", auth=(username, password),).json()http -a "$USERNAME:$PASSWORD" GET \ https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks// fetch is built in to Node 18+, Bun, Deno, and browsers.// In the browser, swap Buffer.from(...).toString('base64') for btoa(`${username}:${password}`).const res = await fetch('https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks', { headers: { Accept: 'application/json', Authorization: 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), },});const result = await res.json();interface PostbackItem { body: string; // serialised postback payload that was (or will be) sent}
const res = await fetch('https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks', { headers: { Accept: 'application/json', Authorization: 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), },});const result = (await res.json()) as PostbackItem[];require 'vendor/autoload.php';use GuzzleHttp\Client;
$client = new Client;$res = $client->get('https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks', [ 'auth' => [$username, $password],]);$result = json_decode($res->getBody(), true);require "net/http"require "json"require "uri"
uri = URI("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks")
req = Net::HTTP::Get.new(uri, "Accept" => "application/json")req.basic_auth(username, password)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }result = JSON.parse(res.body)import ( "encoding/json" "net/http")
req, _ := http.NewRequest("GET", "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks", nil)req.Header.Set("Accept", "application/json")req.SetBasicAuth(username, password)
resp, _ := http.DefaultClient.Do(req)defer resp.Body.Close()
var result []map[string]anyjson.NewDecoder(resp.Body).Decode(&result)use serde_json::Value;
let result: Value = reqwest::Client::new() .get("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks") .basic_auth(username, Some(password)) .send() .await? .json() .await?;import java.net.URI;import java.net.http.*;import java.util.Base64;
String auth = Base64.getEncoder().encodeToString((username + ":" + password).getBytes());HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks")) .header("Accept", "application/json") .header("Authorization", "Basic " + auth) .GET() .build();
HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString());// parse response.body() as a JSON array of postback objectsimport java.net.URIimport java.net.http.HttpClientimport java.net.http.HttpRequestimport java.net.http.HttpResponseimport java.util.Base64
val auth = Base64.getEncoder().encodeToString("$username:$password".toByteArray())val request = HttpRequest.newBuilder() .uri(URI.create("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks")) .header("Accept", "application/json") .header("Authorization", "Basic $auth") .GET() .build()
val response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString())// parse response.body() as a JSON array of postback objectsusing System.Net.Http.Headers;using System.Net.Http.Json;using System.Text;
using var client = new HttpClient();client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")));
var result = await client.GetFromJsonAsync<List<Dictionary<string, object>>>( "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks");Get Individual Postback
Section titled “Get Individual Postback”GET /merchant/{merchant_id}/postbacks/{crl_id}Returns a single postback record by its correlation UUID.
Response:
{ "body": "{\"version\":\"1.9\",\"request_token\":\"df0c...\",\"updates\":{\"status\":\"approved\"}}"}Create Test Postback (Sandbox Only)
Section titled “Create Test Postback (Sandbox Only)”POST /merchant/{merchant_id}/postbacksManually triggers a postback for an existing loan. Only enabled in the staging/sandbox dataspace (action.enabled(call.model_call('is_staging'))). Attempting this on production will return an error.
Request
Section titled “Request”| Field | Type | Required | Description |
|---|---|---|---|
type | enum | Yes | purchase (success) or void (rejection/end) |
inv_status | enum | Yes | AuthOnly (pre-approved) or Auth (funded) |
inv_id | string | Yes | The loan inv_id to postback. Options are dynamically populated from existing merchant loans. |
Response: HTTP 201 Created on success.
curl -u "$USERNAME:$PASSWORD" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -X POST \ "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks" \ -d '{"type":"purchase","inv_status":"Auth","inv_id":"df0c3186b69be8aad35ff837a841d347"}'import httpx # or: import requests as httpx
response = httpx.post( "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks", json={"type": "purchase", "inv_status": "Auth", "inv_id": "df0c3186b69be8aad35ff837a841d347"}, auth=(username, password),)response.raise_for_status()http -a "$USERNAME:$PASSWORD" POST \ https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks \ type=purchase \ inv_status=Auth \ inv_id=df0c3186b69be8aad35ff837a841d347// fetch is built in to Node 18+, Bun, Deno, and browsers.// In the browser, swap Buffer.from(...).toString('base64') for btoa(`${username}:${password}`).const res = await fetch('https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), }, body: JSON.stringify({ type: 'purchase', inv_status: 'Auth', inv_id: 'df0c3186b69be8aad35ff837a841d347', }),});// res.status === 201 on successconst res = await fetch('https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), }, body: JSON.stringify({ type: 'purchase', inv_status: 'Auth', inv_id: 'df0c3186b69be8aad35ff837a841d347', }),});// res.status === 201 on successrequire 'vendor/autoload.php';use GuzzleHttp\Client;
$client = new Client;$client->post('https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks', [ 'auth' => [$username, $password], 'json' => ['type' => 'purchase', 'inv_status' => 'Auth', 'inv_id' => 'df0c3186b69be8aad35ff837a841d347'],]);require "net/http"require "json"require "uri"
uri = URI("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks")payload = { type: "purchase", inv_status: "Auth", inv_id: "df0c3186b69be8aad35ff837a841d347" }
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")req.basic_auth(username, password)req.body = payload.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }# res is Net::HTTPCreated (201) on successimport ( "bytes" "encoding/json" "net/http")
payload := map[string]any{ "type": "purchase", "inv_status": "Auth", "inv_id": "df0c3186b69be8aad35ff837a841d347",}body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks", bytes.NewReader(body))req.Header.Set("Content-Type", "application/json")req.SetBasicAuth(username, password)
resp, _ := http.DefaultClient.Do(req)defer resp.Body.Close()// resp.StatusCode == 201 on successuse serde_json::json;
let payload = json!({ "type": "purchase", "inv_status": "Auth", "inv_id": "df0c3186b69be8aad35ff837a841d347"});
let resp = reqwest::Client::new() .post("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks") .basic_auth(username, Some(password)) .json(&payload) .send() .await?;// resp.status() == 201 on successimport java.net.URI;import java.net.http.*;import java.util.Base64;
String body = """ {"type": "purchase", "inv_status": "Auth", "inv_id": "df0c3186b69be8aad35ff837a841d347"}""";
String auth = Base64.getEncoder().encodeToString((username + ":" + password).getBytes());HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks")) .header("Content-Type", "application/json") .header("Authorization", "Basic " + auth) .POST(HttpRequest.BodyPublishers.ofString(body)) .build();
HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString());// response.statusCode() == 201 on successimport java.net.URIimport java.net.http.HttpClientimport java.net.http.HttpRequestimport java.net.http.HttpResponseimport java.util.Base64
val body = """ {"type": "purchase", "inv_status": "Auth", "inv_id": "df0c3186b69be8aad35ff837a841d347"}""".trimIndent()
val auth = Base64.getEncoder().encodeToString("$username:$password".toByteArray())val request = HttpRequest.newBuilder() .uri(URI.create("https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks")) .header("Content-Type", "application/json") .header("Authorization", "Basic $auth") .POST(HttpRequest.BodyPublishers.ofString(body)) .build()
val response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString())// response.statusCode() == 201 on successusing System.Net.Http.Headers;using System.Net.Http.Json;using System.Text;
using var client = new HttpClient();client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")));
var payload = new{ type = "purchase", inv_status = "Auth", inv_id = "df0c3186b69be8aad35ff837a841d347",};
var response = await client.PostAsJsonAsync( "https://api.sandbox.wegetfinancing.com/merchant/1234/postbacks", payload);// response.StatusCode == HttpStatusCode.Created (201) on successPostback Verification (Test curl)
Section titled “Postback Verification (Test curl)”To simulate a v1.9 postback from your local environment for integration testing:
TIMESTAMP=$(date +%s)KEY_ID="your_dhd_username"KEY_SECRET="your_dhd_password"BODY='{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}'
SIGNATURE=$(printf "%s%s%s%s" "$TIMESTAMP" "$KEY_ID" "$BODY" "$KEY_SECRET" | sha256sum | awk '{print $1}')
curl -X POST https://your-shop.com/api/postback \ -H "Content-Type: application/json" \ -H "x-timestamp: $TIMESTAMP" \ -H "x-signature: $SIGNATURE" \ -d "$BODY"import hashlib, time, requests
url = "https://your-shop.com/api/postback"key_id, key_secret = "your_dhd_username", "your_dhd_password"timestamp = str(int(time.time()))body = '{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}'
signature = hashlib.sha256(f"{timestamp}{key_id}{body}{key_secret}".encode()).hexdigest()
requests.post(url, data=body, headers={ "Content-Type": "application/json", "x-timestamp": timestamp, "x-signature": signature,})TIMESTAMP=$(date +%s)KEY_ID="your_dhd_username"KEY_SECRET="your_dhd_password"BODY='{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}'
SIGNATURE=$(printf "%s%s%s%s" "$TIMESTAMP" "$KEY_ID" "$BODY" "$KEY_SECRET" | sha256sum | awk '{print $1}')
echo "$BODY" | http POST https://your-shop.com/api/postback \ Content-Type:application/json \ "x-timestamp:$TIMESTAMP" \ "x-signature:$SIGNATURE"import crypto from 'node:crypto';
const url = 'https://your-shop.com/api/postback';const keyId = 'your_dhd_username', keySecret = 'your_dhd_password';const timestamp = String(Math.floor(Date.now() / 1000));const body = '{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}';
const signature = crypto.createHash('sha256') .update(timestamp + keyId + body + keySecret) .digest('hex');
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-timestamp': timestamp, 'x-signature': signature }, body,});import crypto from 'node:crypto';
const url = 'https://your-shop.com/api/postback';const keyId = 'your_dhd_username', keySecret = 'your_dhd_password';const timestamp: string = String(Math.floor(Date.now() / 1000));const body = '{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}';
const signature = crypto.createHash('sha256') .update(timestamp + keyId + body + keySecret) .digest('hex');
const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-timestamp': timestamp, 'x-signature': signature }, body,});// your endpoint should respond 2xxconsole.log(res.status);use GuzzleHttp\Client;
$keyId = "your_dhd_username";$keySecret = "your_dhd_password";$timestamp = (string) time();$body = '{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}';
$signature = hash('sha256', $timestamp . $keyId . $body . $keySecret);
(new Client)->post('https://your-shop.com/api/postback', [ 'body' => $body, 'headers' => [ 'Content-Type' => 'application/json', 'x-timestamp' => $timestamp, 'x-signature' => $signature, ],]);require "net/http"require "digest"require "uri"
uri = URI("https://your-shop.com/api/postback")key_id, key_secret = "your_dhd_username", "your_dhd_password"timestamp = Time.now.to_i.to_sbody = '{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}'
signature = Digest::SHA256.hexdigest("#{timestamp}#{key_id}#{body}#{key_secret}")
req = Net::HTTP::Post.new(uri, { "Content-Type" => "application/json", "x-timestamp" => timestamp, "x-signature" => signature,})req.body = bodyNet::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }import ( "crypto/sha256" "encoding/hex" "net/http" "strconv" "strings" "time")
url := "https://your-shop.com/api/postback"keyID, keySecret := "your_dhd_username", "your_dhd_password"timestamp := strconv.FormatInt(time.Now().Unix(), 10)body := `{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}`
sum := sha256.Sum256([]byte(timestamp + keyID + body + keySecret))signature := hex.EncodeToString(sum[:])
req, _ := http.NewRequest("POST", url, strings.NewReader(body))req.Header.Set("Content-Type", "application/json")req.Header.Set("x-timestamp", timestamp)req.Header.Set("x-signature", signature)http.DefaultClient.Do(req)use sha2::{Digest, Sha256};
let url = "https://your-shop.com/api/postback";let (key_id, key_secret) = ("your_dhd_username", "your_dhd_password");let timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH).unwrap().as_secs().to_string();let body = r#"{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}"#;
let signature = hex::encode(Sha256::digest(format!("{timestamp}{key_id}{body}{key_secret}")));
reqwest::Client::new() .post(url) .header("Content-Type", "application/json") .header("x-timestamp", ×tamp) .header("x-signature", signature) .body(body) .send() .await?;import java.net.URI;import java.net.http.*;import java.security.MessageDigest;import java.util.HexFormat;
String keyId = "your_dhd_username", keySecret = "your_dhd_password";String timestamp = String.valueOf(System.currentTimeMillis() / 1000);String body = "{\"version\":\"1.9\",\"request_token\":\"df0c3186b69be8aad35ff837a841d347\",\"updates\":{\"status\":\"approved\"}}";
byte[] digest = MessageDigest.getInstance("SHA-256") .digest((timestamp + keyId + body + keySecret).getBytes());String signature = HexFormat.of().formatHex(digest);
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://your-shop.com/api/postback")) .header("Content-Type", "application/json") .header("x-timestamp", timestamp) .header("x-signature", signature) .POST(HttpRequest.BodyPublishers.ofString(body)) .build();HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());import java.net.URIimport java.net.http.HttpClientimport java.net.http.HttpRequestimport java.net.http.HttpResponseimport java.security.MessageDigest
val keyId = "your_dhd_username"; val keySecret = "your_dhd_password"val timestamp = (System.currentTimeMillis() / 1000).toString()val body = """{"version":"1.9","request_token":"df0c3186b69be8aad35ff837a841d347","updates":{"status":"approved"}}"""
val digest = MessageDigest.getInstance("SHA-256") .digest((timestamp + keyId + body + keySecret).toByteArray())val signature = digest.joinToString("") { "%02x".format(it) }
val request = HttpRequest.newBuilder() .uri(URI.create("https://your-shop.com/api/postback")) .header("Content-Type", "application/json") .header("x-timestamp", timestamp) .header("x-signature", signature) .POST(HttpRequest.BodyPublishers.ofString(body)) .build()HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())using System.Security.Cryptography;using System.Text;
var keyId = "your_dhd_username";var keySecret = "your_dhd_password";var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();var body = "{\"version\":\"1.9\",\"request_token\":\"df0c3186b69be8aad35ff837a841d347\",\"updates\":{\"status\":\"approved\"}}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(timestamp + keyId + body + keySecret));var signature = Convert.ToHexString(hash).ToLowerInvariant();
using var client = new HttpClient();var request = new HttpRequestMessage(HttpMethod.Post, "https://your-shop.com/api/postback"){ Content = new StringContent(body, Encoding.UTF8, "application/json"),};request.Headers.Add("x-timestamp", timestamp);request.Headers.Add("x-signature", signature);await client.SendAsync(request);