Trustoo
  1. Reviews Data API
Trustoo
  • Reviews Widget Integration
  • Reviews Data API
    • API Quick Start
    • Response Code
    • Webhook Description
    • Create shop webhook
      POST
    • Update shop webhook
      POST
    • Delete shop webhook
      POST
    • Get shop webhooks
      GET
    • Get review detail
      GET
    • Get review list
      GET
    • Get order list
      GET
    • Get customer event list
      GET
    • Unsubscribe
      POST
    • Get the video upload url
      GET
    • Create review
      POST
    • Delete review
      POST
    • Create customer exclusion
      POST
    • Delete customer exclusion
      POST
    • Get customer exclusion list
      GET
    • Get rating
      GET
  1. Reviews Data API

API Quick Start

Third-party applications can obtain a store's review data from Trustoo after being authorized by the store. By subscribing to webhooks, you can receive updates on review data.

INFO

⚠️ Important Update (November 2025)

We have upgraded our signature algorithm from SHA-256 with salt to HMAC-SHA256 for enhanced security and better cross-platform compatibility.

Key Changes:

  • ✅ More secure authentication using industry-standard HMAC
  • ✅ JSON key order no longer affects signature
  • ✅ Compatible with major platforms like Shopify and Stripe
  • ⚠️ Breaking Change: Old signatures are no longer valid

Migration Required: All existing integrations must update their signature generation code. See section 2.2 for updated implementation examples in all languages.

1.User Authorization Information

After the merchant is required to provide Trustoo's public token and private token, it can obtain authorization from the merchant's Trustoo system. In the following text, Trustoo's public token and private token are all referred to as public token and private token.

image.png

1.1 public token Merchant's Public Token

The public token is the unique identifier of the store in the third-party application.

1.2 private token Merchant's Private Token

The private token of the user is a crucial key used to authorize third-party applications to access review information. Please keep it confidential and only provide it to trusted developers. In case of any leakage, please contact us promptly for a reset.

CAUTION

The original private token becomes immediately invalid after a reset. Please contact all authorized third-party applications to update the private token.

TIP

Merchants can view the public token and private token in the Trustoo backend of their store.

2.Trustoo Data Interaction Guide

The API address for our production environment is https://rapi.trustoo.io/

2.1 Information for Required Headers

HEADER NAMEDESCRIPTIONEXAMPLE
public-tokenIt is the unique identifier of the store in the third-party application.GfKu22TCaj4gULtrA3/OgA==
timestampThis is a Unix timestamp.1700465771
signIt is the unique identifier of the store in the third-party application.c3f6910a4dc12969c819f47621e5adf608e2a33f4db0575988bd4fc19173a582

Post Reviews API Doc
This is the detailed content

# Note: Generate a fresh timestamp and signature for each request
# The signature shown below is just an example - you must generate your own

curl --location --request POST 'https://rapi.trustoo.io/api/v1/openapi/create_shop_webhook' \
--header 'Public-token: GfKu22TCaj4gULtrA3/OgA==' \
--header 'sign: [your_generated_signature_here]' \
--header 'timestamp: [current_unix_timestamp]' \
--header 'Content-Type: application/json' \
--data-raw '{
    "topic":"review/created",
    "url":"https://your-webhook-url.com"
}'

# To generate the signature:
# 1. Build string: timestamp=[timestamp]|{"topic":"review/created","url":"https://your-webhook-url.com"}
# 2. Calculate: HMAC-SHA256(string, private_token)
# 3. Convert to lowercase hex format

Example with actual values:

# Given:
# - timestamp: 1732180800
# - private_token: your_private_token
# - body: {"topic":"review/created","url":"https://your-webhook-url.com"}

# Signature string: timestamp=1732180800|{"topic":"review/created","url":"https://your-webhook-url.com"}
# Generated signature: ee5d60dd3fbc3c70e49405564f4830466a11382f088eb127c5c5237c5c9b3e91

curl --location --request POST 'https://rapi.trustoo.io/api/v1/openapi/create_shop_webhook' \
--header 'Public-token: GfKu22TCaj4gULtrA3/OgA==' \
--header 'sign: ee5d60dd3fbc3c70e49405564f4830466a11382f088eb127c5c5237c5c9b3e91' \
--header 'timestamp: 1732180800' \
--header 'Content-Type: application/json' \
--data-raw '{
    "topic":"review/created",
    "url":"https://your-webhook-url.com"
}'

2.2 Algorithm for Calculating 'sign'

We use HMAC-SHA256 (Hash-based Message Authentication Code) for signature generation, which is the same secure approach used by major platforms like Shopify and Stripe.

Signature Generation Steps:

  1. Collect Parameters: Gather all data transmitted in the request body and query parameters, including the timestamp
  2. Sort Alphabetically: Sort all parameters by key name in ascending order (a-z)
  3. Build String: Concatenate parameters in the format key1=value1&key2=value2
  4. Append Body (for POST requests): If there is a request body, append it after a | separator
  5. Generate HMAC-SHA256: Use the private token as the secret key to compute HMAC-SHA256
  6. Convert to Hex: Convert the result to lowercase hexadecimal format

Key Advantages:

  • ✅ More secure than simple hashing
  • ✅ Industry standard authentication method
  • ✅ JSON key order doesn't affect signature
  • ✅ Compatible with all major programming languages

CAUTION

The timestamp is only valid within 15 minutes. It needs to be regenerated if it exceeds this timeframe.

Example of Generating 'sign' in GO
import (
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

func TestSignData(t *testing.T) {
	data := map[string]string{
		"banana":    "yellow",
		"apple":     "red",
		"grape":     "purple",
		"orange":    "orange",
		"timestamp": strconv.Itoa(int(time.Now().UTC().Unix())),
	}
	sign := SignData(data, "123")
	fmt.Println("=========sign=======:", sign)
}

// SignData signs the data using HMAC-SHA256
func SignData(data map[string]string, privateToken string) string {
	// 1. Retrieve and sort the keys
	var keys []string
	for key := range data {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	
	// 2. Traverse the sorted keys and concatenate them into a string
	var resultStr string
	for _, key := range keys {
		value := data[key]
		resultStr += fmt.Sprintf("%s=%s&", key, value)
	}

	// Remove the trailing "&"
	resultStr = strings.TrimSuffix(resultStr, "&")
	
	// 3. Generate HMAC-SHA256 signature
	h := hmac.New(sha256.New, []byte(privateToken))
	h.Write([]byte(resultStr))
	return fmt.Sprintf("%x", h.Sum(nil))
}

// For POST requests with JSON body, append body after "|" separator
func SignDataWithBody(data map[string]string, body string, privateToken string) string {
	// 1. Retrieve and sort the keys
	var keys []string
	for key := range data {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	
	// 2. Build signature string
	var resultStr string
	for _, key := range keys {
		value := data[key]
		resultStr += fmt.Sprintf("%s=%s&", key, value)
	}
	resultStr = strings.TrimSuffix(resultStr, "&")
	
	// 3. Append body if present
	if body != "" {
		resultStr = resultStr + "|" + body
	}
	
	// 4. Generate HMAC-SHA256 signature
	h := hmac.New(sha256.New, []byte(privateToken))
	h.Write([]byte(resultStr))
	return fmt.Sprintf("%x", h.Sum(nil))
}
Example of Generating 'sign' in JAVA
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;

public class Signer {
    /**
     * Sign data using HMAC-SHA256
     * @param data Parameters to sign
     * @param privateToken Private token (secret key)
     * @return Signature in hexadecimal format
     */
    public static String signData(Map<String, String> data, String privateToken) {
        try {
            // 1. Retrieve and sort the keys
            TreeMap<String, String> sortedData = new TreeMap<>(data);
            
            // 2. Traverse the sorted keys and concatenate them into a string
            StringBuilder resultStr = new StringBuilder();
            for (Map.Entry<String, String> entry : sortedData.entrySet()) {
                resultStr.append(String.format("%s=%s&", entry.getKey(), entry.getValue()));
            }

            // Remove the trailing "&"
            resultStr.deleteCharAt(resultStr.length() - 1);

            // 3. Generate HMAC-SHA256 signature
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(
                privateToken.getBytes(StandardCharsets.UTF_8),
                "HmacSHA256"
            );
            mac.init(secretKey);
            byte[] hashed = mac.doFinal(resultStr.toString().getBytes(StandardCharsets.UTF_8));

            // Convert to hexadecimal string
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashed) {
                hexString.append(String.format("%02x", b));
            }

            return hexString.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to generate signature", e);
        }
    }

    /**
     * Sign data with request body (for POST requests)
     * @param data Parameters to sign
     * @param body Request body JSON string
     * @param privateToken Private token (secret key)
     * @return Signature in hexadecimal format
     */
    public static String signDataWithBody(Map<String, String> data, String body, String privateToken) {
        try {
            // 1. Retrieve and sort the keys
            TreeMap<String, String> sortedData = new TreeMap<>(data);
            
            // 2. Build signature string
            StringBuilder resultStr = new StringBuilder();
            for (Map.Entry<String, String> entry : sortedData.entrySet()) {
                resultStr.append(String.format("%s=%s&", entry.getKey(), entry.getValue()));
            }
            resultStr.deleteCharAt(resultStr.length() - 1);

            // 3. Append body if present
            if (body != null && !body.isEmpty()) {
                resultStr.append("|").append(body);
            }

            // 4. Generate HMAC-SHA256 signature
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(
                privateToken.getBytes(StandardCharsets.UTF_8),
                "HmacSHA256"
            );
            mac.init(secretKey);
            byte[] hashed = mac.doFinal(resultStr.toString().getBytes(StandardCharsets.UTF_8));

            // Convert to hexadecimal string
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashed) {
                hexString.append(String.format("%02x", b));
            }

            return hexString.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to generate signature", e);
        }
    }

    public static void main(String[] args) {
        // Example usage
        Map<String, String> data = new TreeMap<>();
        data.put("key1", "value1");
        data.put("key2", "value2");
        data.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));

        String privateToken = "your_private_token";

        String signature = signData(data, privateToken);
        System.out.println("Signature: " + signature);
    }
}

PHP This is the detailed content
<?php

/**
 * Sign data using HMAC-SHA256
 * @param array $data Parameters to sign
 * @param string $privateToken Private token (secret key)
 * @return string Signature in hexadecimal format
 */
function signData($data, $privateToken) {
    // Retrieve and sort the keys
    $keys = array_keys($data);
    sort($keys);

    // Traverse the sorted keys and concatenate them into a string
    $resultStr = '';
    foreach ($keys as $key) {
        $value = $data[$key];
        $resultStr .= "$key=$value&";
    }

    // Remove the trailing "&"
    $resultStr = rtrim($resultStr, '&');

    // Generate HMAC-SHA256 signature
    $signature = hash_hmac('sha256', $resultStr, $privateToken);

    return $signature;
}

/**
 * Sign data with request body (for POST requests)
 * @param array $data Parameters to sign
 * @param string $body Request body JSON string
 * @param string $privateToken Private token (secret key)
 * @return string Signature in hexadecimal format
 */
function signDataWithBody($data, $body, $privateToken) {
    // Retrieve and sort the keys
    $keys = array_keys($data);
    sort($keys);

    // Build signature string
    $resultStr = '';
    foreach ($keys as $key) {
        $value = $data[$key];
        $resultStr .= "$key=$value&";
    }
    $resultStr = rtrim($resultStr, '&');

    // Append body if present
    if ($body !== '' && $body !== null) {
        $resultStr .= '|' . $body;
    }

    // Generate HMAC-SHA256 signature
    $signature = hash_hmac('sha256', $resultStr, $privateToken);

    return $signature;
}

// Example usage for GET request
$data = array(
    'param1' => 'value1',
    'param2' => 'value2',
    'timestamp' => time(),
    // Add other parameters...
);

$privateToken = 'your_private_token';
$signature = signData($data, $privateToken);

echo "Signature: " . $signature . "\n";

// Example usage for POST request with body
$postData = array(
    'timestamp' => time()
);
$bodyJson = json_encode(array(
    'topic' => 'review/created',
    'url' => 'https://your-webhook-url.com'
));

$postSignature = signDataWithBody($postData, $bodyJson, $privateToken);
echo "POST Signature: " . $postSignature . "\n";
?>

node.js This is the detailed content
const crypto = require('crypto');

/**
 * Sign data using HMAC-SHA256
 * @param {Object} data - Parameters to sign
 * @param {string} privateToken - Private token (secret key)
 * @returns {string} Signature in hexadecimal format
 */
function signData(data, privateToken) {
    // Retrieve and sort the keys
    const keys = Object.keys(data).sort();

    // Traverse the sorted keys and concatenate them into a string
    let resultStr = '';
    for (const key of keys) {
        const value = data[key];
        resultStr += `${key}=${value}&`;
    }

    // Remove the trailing "&"
    resultStr = resultStr.slice(0, -1);

    // Generate HMAC-SHA256 signature
    const signature = crypto
        .createHmac('sha256', privateToken)
        .update(resultStr, 'utf8')
        .digest('hex');

    return signature;
}

/**
 * Sign data with request body (for POST requests)
 * @param {Object} data - Parameters to sign
 * @param {string} body - Request body JSON string
 * @param {string} privateToken - Private token (secret key)
 * @returns {string} Signature in hexadecimal format
 */
function signDataWithBody(data, body, privateToken) {
    // Retrieve and sort the keys
    const keys = Object.keys(data).sort();

    // Build signature string
    let resultStr = '';
    for (const key of keys) {
        const value = data[key];
        resultStr += `${key}=${value}&`;
    }
    resultStr = resultStr.slice(0, -1);

    // Append body if present
    if (body) {
        resultStr += '|' + body;
    }

    // Generate HMAC-SHA256 signature
    const signature = crypto
        .createHmac('sha256', privateToken)
        .update(resultStr, 'utf8')
        .digest('hex');

    return signature;
}

// Example usage for GET request
const data = {
    "param1": 'value1',
    "param2": 'value2',
    "timestamp": Math.floor(Date.now() / 1000).toString(),
    // Add other parameters...
};

const privateToken = 'your_private_token';
const signature = signData(data, privateToken);

console.log('Signature:', signature);

// Example usage for POST request with body
const postData = {
    "timestamp": Math.floor(Date.now() / 1000).toString()
};

const bodyJson = JSON.stringify({
    "topic": 'review/created',
    "url": 'https://your-webhook-url.com'
});

const postSignature = signDataWithBody(postData, bodyJson, privateToken);
console.log('POST Signature:', postSignature);

C# This is the detailed content
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static void Main()
    {
        // Example usage for GET request
        Dictionary<string, string> data = new Dictionary<string, string>
        {
            { "param1", "value1" },
            { "param2", "value2" },
            { "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() },
            // Add other parameters...
        };

        string privateToken = "your_private_token";
        string signature = SignData(data, privateToken);

        Console.WriteLine("Signature: " + signature);

        // Example usage for POST request with body
        Dictionary<string, string> postData = new Dictionary<string, string>
        {
            { "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() }
        };

        string bodyJson = "{\"topic\":\"review/created\",\"url\":\"https://your-webhook-url.com\"}";
        string postSignature = SignDataWithBody(postData, bodyJson, privateToken);

        Console.WriteLine("POST Signature: " + postSignature);
    }

    /// <summary>
    /// Sign data using HMAC-SHA256
    /// </summary>
    /// <param name="data">Parameters to sign</param>
    /// <param name="privateToken">Private token (secret key)</param>
    /// <returns>Signature in hexadecimal format</returns>
    static string SignData(Dictionary<string, string> data, string privateToken)
    {
        // Retrieve and sort the keys
        var keys = data.Keys.ToList();
        keys.Sort();

        // Traverse the sorted keys and concatenate them into a string
        StringBuilder resultStr = new StringBuilder();
        foreach (var key in keys)
        {
            string value = data[key];
            resultStr.AppendFormat("{0}={1}&", key, value);
        }

        // Remove the trailing "&"
        resultStr.Length--;

        // Generate HMAC-SHA256 signature
        using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(privateToken)))
        {
            byte[] hashedBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(resultStr.ToString()));
            return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
        }
    }

    /// <summary>
    /// Sign data with request body (for POST requests)
    /// </summary>
    /// <param name="data">Parameters to sign</param>
    /// <param name="body">Request body JSON string</param>
    /// <param name="privateToken">Private token (secret key)</param>
    /// <returns>Signature in hexadecimal format</returns>
    static string SignDataWithBody(Dictionary<string, string> data, string body, string privateToken)
    {
        // Retrieve and sort the keys
        var keys = data.Keys.ToList();
        keys.Sort();

        // Build signature string
        StringBuilder resultStr = new StringBuilder();
        foreach (var key in keys)
        {
            string value = data[key];
            resultStr.AppendFormat("{0}={1}&", key, value);
        }
        resultStr.Length--;

        // Append body if present
        if (!string.IsNullOrEmpty(body))
        {
            resultStr.Append("|");
            resultStr.Append(body);
        }

        // Generate HMAC-SHA256 signature
        using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(privateToken)))
        {
            byte[] hashedBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(resultStr.ToString()));
            return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
        }
    }
}

2.2.1 Signature Examples

GET Request Signature Example

Endpoint: GET /api/v1/openapi/get_reviews

Query Parameters:

product_ids=9228589138176
page_size=20
page=1

Step 1: Add timestamp

product_ids=9228589138176
page_size=20
page=1
timestamp=1732180800

Step 2: Sort alphabetically

page=1
page_size=20
product_ids=9228589138176
timestamp=1732180800

Step 3: Build signature string

page=1&page_size=20&product_ids=9228589138176&timestamp=1732180800

Step 4: Generate HMAC-SHA256

HMAC-SHA256(signature_string, private_token)
Result: c3648110bc3f1f63e7ed54fc4390448bfd2a131b405aeed91cd26a42b3ee9a93
POST Request Signature Example

Endpoint: POST /api/v1/openapi/create_shop_webhook

Request Body:

{
    "topic": "review/created",
    "url": "https://your-webhook-url.com"
}

Step 1: Query parameters (only timestamp for POST)

timestamp=1732180800

Step 2: Build signature string (params + | + body)

timestamp=1732180800|{"topic":"review/created","url":"https://your-webhook-url.com"}

Step 3: Generate HMAC-SHA256

HMAC-SHA256(signature_string, private_token)
Result: ee5d60dd3fbc3c70e49405564f4830466a11382f088eb127c5c5237c5c9b3e91

Important Notes:

  • ✅ Use the exact JSON string you're sending (don't parse and re-serialize)
  • ✅ JSON key order doesn't matter because we use the raw string
  • ✅ The | separator is used between parameters and body

2.3、Interaction Flowchart

image.png

Modified at 2025-11-20 11:38:05
Previous
Reviews Widget Integration
Next
Response Code
Built with