callback
After the current commissioned energy is completed, it will try to send a message to notify the initiator. There are currently two places where you can set the callback address.
- In My settings, set the default callback address
- Specify the callback_url parameter to overwrite the default callback address when placing an order.
data = {
'energy_amount': '32000',
'period': '1H',
'receive_address': 'TExWKszFWYTKZH8LYiovAPKzS3L9MLZ4kw',
'callback_url': 'http://{mydomain}/callback',
'out_trade_no': '123456',
}
Respond & Retry
The response body is as follows
{
'active_hash': '', // If activated, activated txid
'bandwidth_hash': '5e342a821de72542d7b341039c34af631d0551cfcd4b67c272', // If there is bandwidth, the txid of the bandwidth
'energy_amount': 32000, // Order energy
'out_trade_no': '123456', // External order number
'pay_amount': 32170.005048646104, // Actual commission energy
'serial': '886294f5204ac2fc1430f5a7d9215a80', // Internal order number
'txid': '2610c200efc8a90601758715405fa6be4597469e854591975d113b720a762ec2', // txid of energy
'status': 40, // Status 40 is successful, 41 is failed
type': 'energy', // type
'receive_address': 'Txxxxxx', // receive address
'source': 'manual/api/auto_delegate/count_delegate', // order source
}
If you receive the callback information, you only need to return HTTP 200
, which means that the message has been processed and this callback will no longer be sent. Otherwise, it will try to send repeatedly until the expected response is obtained. The current retry frequency is 15s, 15s, 30s, 180s, 600s, 1200s, 1800s
. If there is no response, it will not be sent again.
Verification
When the server sends a callback message, it will use the order API to sign. The receiver needs to verify the signature and then perform logical processing, otherwise unnecessary losses may be caused. The server's signature process and order initiation [signature](/general/ sign.md), the receiver needs to use the timestamp in the callback request for encryption, and verify whether the SIGNATURE
of the callback is consistent.
Code Example
import hmac
import hashlib
import time
import json
def callback(self, request):
def _verify_signature(signature, timestamp):
api_secret = '0285A2741D0E76E2E187260EB23E51851D48403A756333E7D0CF845406ABF3E8'
json_data = json.dumps(request.data, sort_keys=True)
message = f"{timestamp}&{json_data}"
expected_signature = hmac.new(api_secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected_signature)
signature = request.META.get('HTTP_SIGNATURE')
timestamp = request.META.get('HTTP_TIMESTAMP')
if not all([signature, timestamp]):
return None
if time.time() - float(timestamp) > 100:
# 视情况,是否要继续处理
print('timestamp expired')
if _verify_signature(signature, timestamp):
print(request.data)
else:
print('verify signature failed')
return Response({})
<?php
$api_secret = "0285A2741D0E76E2E187260EB23E51851D48403A756333E7D0CF845406ABF3E8";
$headers = []; // your headers here
$body = []; // your body data here
$timestamp = $headers['TIMESTAMP'];
$signature = $headers['SIGNATURE'];
ksort($body);
$json_data = json_encode($body, JSON_UNESCAPED_SLASHES);
$message = $timestamp . '&' . $json_data;
$expected_signature = hash_hmac('sha256', $message, $api_secret);
if (hash_equals($signature, $expected_signature)) {
echo "Signature is valid.\n";
print_r($body);
} else {
echo "Signature is invalid.\n";
}
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.*;
public class Main {
public static void main(String[] args) {
String apiSecret = "0285A2741D0E76E2E187260EB23E51851D48403A756333E7D0CF845406ABF3E8";
Map<String, String> headers = new HashMap<>(); // your headers here
Map<String, Object> body = new HashMap<>(); // your body data here
String timestamp = headers.get("TIMESTAMP");
String signature = headers.get("SIGNATURE");
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
TreeMap<String, Object> sortedBody = new TreeMap<>(body);
String jsonData = gson.toJson(sortedBody);
String message = timestamp + "&" + jsonData;
String expectedSignature = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, apiSecret).hmacHex(message);
if (StringUtils.equals(signature, expectedSignature)) {
System.out.println("Signature is valid.");
System.out.println(body);
} else {
System.out.println("Signature is invalid.");
}
}
}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
)
type Body map[string]interface{}
func (b Body) MarshalJSON() ([]byte, error) {
keys := make([]string, 0, len(b))
for k := range b {
keys = append(keys, k)
}
sort.Strings(keys)
result := "{"
for _, k := range keys {
value, _ := json.Marshal(b[k])
result += fmt.Sprintf(`"%s":%s,`, k, string(value))
}
result = result[:len(result)-1] + "}"
return []byte(result), nil
}
func main() {
apiSecret := "0285A2741D0E76E2E187260EB23E51851D48403A756333E7D0CF845406ABF3E8"
headers := map[string]string{} // your headers here
body := Body{} // your body data here
timestamp := headers["TIMESTAMP"]
signature := headers["SIGNATURE"]
jsonData, _ := json.Marshal(body)
message := timestamp + "&" + string(jsonData)
mac := hmac.New(sha256.New, []byte(apiSecret))
mac.Write([]byte(message))
expectedSignature := hex.EncodeToString(mac.Sum(nil))
if signature == expectedSignature {
fmt.Println("Signature is valid.")
fmt.Println(body)
} else {
fmt.Println("Signature is invalid.")
}
}
api_secret="0285A2741D0E76E2E187260EB23E51851D48403A756333E7D0CF845406ABF3E8"
timestamp=$HTTP_TIMESTAMP
signature=$HTTP_SIGNATURE
body=$(echo $HTTP_BODY | jq -S .) # Requires jq to sort keys in json.
message="${timestamp}&${body}"
expected_signature=$(echo -n "${message}" | openssl dgst -sha256 -hmac "${api_secret}")
if [ "${signature}" = "${expected_signature}" ]; then
echo "Signature is valid."
echo "${body}"
else
echo "Signature is invalid."
fi