# Demo

# Go


package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	AccessKey string = "RfV6BKQb"
	SecretKey string = "b326231bf57bdcc4060d9bf05634b59535ad5d85"
	BaseUrl   string = "http://192.168.1.75:18713"
	ApiPath   string = "/api/v3/wallet/pay"
)

type CryptoPayment struct {
	ExternalOrderId       string `json:"externalOrderId,omitempty" binding:"required,max=64"`
	CashierChainType      string `json:"cashierChainType,omitempty" binding:"omitempty,oneof=ETH TRON BSC"`
	CashierTokenType      string `json:"cashierTokenType,omitempty" binding:"omitempty,oneof=USDT USDC TUSD BUSD"`
	CashierCryptoAmount   string `json:"cashierCryptoAmount,omitempty" binding:"omitempty,amountCrypto" err:"cant be more than 6 decimal"`
	CashierCurrencyAmount string `json:"cashierCurrencyAmount,omitempty" binding:"omitempty,amountCurrency" err:"cant be more than 2 decimal"`
	CashierCurrencyType   string `json:"cashierCurrencyType,omitempty" binding:"omitempty,oneof=USD BRL"`
	HiddenMerchantLogo    *int64 `json:"hiddenMerchantLogo" binding:"omitempty,oneof=0 1"`
	HiddenMerchantName    *int64 `json:"hiddenMerchantName" binding:"omitempty,oneof=0 1"`
	NotifyUrl             string `json:"notifyUrl,omitempty" binding:"omitempty,url"`
	Remark                string `json:"remark,omitempty" binding:"omitempty,max=1024"`
}

func main() {
	params := make(map[string]string)
	// header params
	params["access_key"] = AccessKey
	params["timestamp"] = strconv.FormatInt(time.Now().UnixMilli(), 10)
	params["nonce"] = randStr(36)

	// Params body struct
	var yes int64
	yes = 1
	payment := CryptoPayment{
		ExternalOrderId:     randStr(32),
		CashierChainType:    "TRON",
		CashierTokenType:    "USDT",
		CashierCryptoAmount: "19.06",
		HiddenMerchantLogo:  &yes,
		HiddenMerchantName:  &yes,
		NotifyUrl:           "http://127.0.0.1/callback.go",
		Remark:              "测试生成",
	}

	// Params body json string
	jsonStr, _ := json.Marshal(payment)
	// trans json string to map
	jsonMap := reflectJson2Map(string(jsonStr))

	for key, value := range jsonMap {
		params[key] = value

	}

	// sort params
	var sortKeys []string
	for k := range params {
		if params[k] != "" {
			sortKeys = append(sortKeys, k)
		}
	}
	sort.Strings(sortKeys)

	// combine params
	var finalStrForSign string
	for _, k := range sortKeys {
		finalStrForSign = finalStrForSign + k + "=" + params[k] + "&"
	}

	// trim last &
	finalStrForSign = strings.TrimRight(finalStrForSign, "&")
	fmt.Printf("combine params: %s\n", finalStrForSign)

	// Sign
	key := []byte(SecretKey)
	mac := hmac.New(sha1.New, key)
	mac.Write([]byte(finalStrForSign))

	// Base64
	sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
	fmt.Printf("sign result: %s\n", sign)

	// create request use post method
	request, _ := http.NewRequest("POST", BaseUrl+ApiPath, bytes.NewBuffer([]byte(jsonStr)))
	request.Header.Set("Content-type", "application/json;charset=utf-8")
	request.Header.Set("sign", sign)
	request.Header.Set("access_key", params["access_key"])
	request.Header.Set("timestamp", params["timestamp"])
	request.Header.Set("nonce", params["nonce"])

	// request
	resp, _ := http.DefaultClient.Do(request)
	defer resp.Body.Close()

	bodyData, _ := ioutil.ReadAll(resp.Body)
	fmt.Printf("response body: %s\n", string(bodyData))

}

// json trans to map
func reflectJson2Map(j string) map[string]string {
	params := make(map[string]string)
	var event map[string]interface{}
	decoder := json.NewDecoder(bytes.NewBufferString(j))
	decoder.UseNumber()
	err := decoder.Decode(&event)
	if err != nil {
		return nil
	}
	for k, v := range event {
		params[k] = fmt.Sprintf("%v", reflect.ValueOf(v))
	}
	return params
}

// struct trans to map
func reflectStruct2Map(s interface{}) map[string]string {
	marshal, err := json.Marshal(s)
	if err != nil {
		return nil
	}
	params := reflectJson2Map(string(marshal))
	return params
}

// gen random string
func randStr(length int) string {
	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, length)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

# Java


package io.hambit;

import com.alibaba.fastjson.JSON;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class JavaHambitDemo {
    final static String ACCESS_KEY = "RfV6BKQb";
    final static String SECRET_KEY = "b326231bf57bdcc4060d9bf05634b59535ad5d85";
    final static String BASE_URL = "http://192.168.1.75:18713";
    final static String API_PATH = "/api/v3/wallet/pay";

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
        Map map = ParamsSortMap.getSortMap();

        // Params header
        map.put("access_key", ACCESS_KEY);
        map.put("timestamp",String.valueOf(System.currentTimeMillis()));
        map.put("nonce", UUID.randomUUID().toString().trim());

        CryptoPayment cryptoPayment = new CryptoPayment();
        cryptoPayment.setCashierChainType("ETH");
        cryptoPayment.setCashierTokenType("USDT");
        cryptoPayment.setCashierCryptoAmount("100.008");
        cryptoPayment.setExternalOrderId(UUID.randomUUID().toString().trim().replace("-",""));
        cryptoPayment.setNotifyUrl("http://192.168.1.75:18713/api/v3/wallet/pay/notify");
        cryptoPayment.setRemark("remark");
        cryptoPayment.setHiddenMerchantLogo(1);
        cryptoPayment.setHiddenMerchantName(1);

        // trans to json string use alibaba fastjson lib
        String jsonString = JSON.toJSONString(cryptoPayment);
        // trans to json map
        Map _map = (Map) JSON.parse(jsonString);
        for (Object key : _map.keySet()) {
            map.put(key, _map.get(key));
        }

        // Combine
        Set keySet = map.keySet();
        String finalStrForSign = "";
        for (Object next : keySet) {
            finalStrForSign = finalStrForSign + next + "=" + map.get(next) + "&";
        }
        finalStrForSign = finalStrForSign.substring(0, finalStrForSign.length() - 1);
        System.out.println("combine params: " + finalStrForSign);

        // Sign
        SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
        Mac hmac = Mac.getInstance("HmacSHA1");
        hmac.init(secretKeySpec);
        byte[] hash = hmac.doFinal(finalStrForSign.getBytes(StandardCharsets.UTF_8));

        // Base64
        String sign = new BASE64Encoder().encodeBuffer(hash);
        System.out.println("sign result: " + sign);

        // Http if you use other http client library, may be had some diff
        String accessUrl = BASE_URL + API_PATH;
        URL url = new URL(accessUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        connection.setUseCaches(false);
        connection.setConnectTimeout(1000 * 5);
        connection.setReadTimeout(1000 * 60);
        connection.setInstanceFollowRedirects(true);
        // Set header
        connection.setRequestProperty("Content-type", "application/json;charset=utf-8");
        connection.setRequestProperty("sign", sign.replaceAll("\n",""));
        connection.setRequestProperty("access_key", ACCESS_KEY);
        connection.setRequestProperty("timestamp", map.get("timestamp").toString());
        connection.setRequestProperty("nonce", map.get("nonce").toString());

        // connect
        connection.connect();
        // write json string
        OutputStream out =connection.getOutputStream();
        out.write(jsonString.getBytes());
        out.flush();
        InputStream in = connection.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
        StringBuilder response = new StringBuilder();
        String tmp;
        while ((tmp = bufferedReader.readLine()) != null) {
            response.append(tmp);
        }
        in.close();
        out.close();
        bufferedReader.close();
        connection.disconnect();
        System.out.println("response body: " + response);

    }
}

class CryptoPayment {
    private String externalOrderId;
    private String cashierChainType;
    private String cashierTokenType;
    private String cashierCryptoAmount;
    private String cashierCurrencyAmount;
    private String cashierCurrencyType;
    private int hiddenMerchantLogo;
    private int hiddenMerchantName;
    private String notifyUrl;
    private String remark;

    public String getExternalOrderId() {
        return externalOrderId;
    }

    public void setExternalOrderId(String externalOrderId) {
        this.externalOrderId = externalOrderId;
    }

    public String getCashierChainType() {
        return cashierChainType;
    }

    public void setCashierChainType(String cashierChainType) {
        this.cashierChainType = cashierChainType;
    }

    public String getCashierTokenType() {
        return cashierTokenType;
    }

    public void setCashierTokenType(String cashierTokenType) {
        this.cashierTokenType = cashierTokenType;
    }

    public String getCashierCryptoAmount() {
        return cashierCryptoAmount;
    }

    public void setCashierCryptoAmount(String cashierCryptoAmount) {
        this.cashierCryptoAmount = cashierCryptoAmount;
    }

    public String getCashierCurrencyAmount() {
        return cashierCurrencyAmount;
    }

    public void setCashierCurrencyAmount(String cashierCurrencyAmount) {
        this.cashierCurrencyAmount = cashierCurrencyAmount;
    }

    public String getCashierCurrencyType() {
        return cashierCurrencyType;
    }

    public void setCashierCurrencyType(String cashierCurrencyType) {
        this.cashierCurrencyType = cashierCurrencyType;
    }

    public int getHiddenMerchantLogo() {
        return hiddenMerchantLogo;
    }

    public void setHiddenMerchantLogo(int hiddenMerchantLogo) {
        this.hiddenMerchantLogo = hiddenMerchantLogo;
    }

    public int getHiddenMerchantName() {
        return hiddenMerchantName;
    }

    public void setHiddenMerchantName(int hiddenMerchantName) {
        this.hiddenMerchantName = hiddenMerchantName;
    }

    public String getNotifyUrl() {
        return notifyUrl;
    }

    public void setNotifyUrl(String notifyUrl) {
        this.notifyUrl = notifyUrl;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }
}

class ParamsSortMap {
    public static Map getSortMap() {
        return new TreeMap<>(new AccsiiAscComparator());
    }
}

class AccsiiAscComparator implements Comparator {
    @Override
    public int compare(Object str1, Object str2) {
        return str1.toString().compareTo(str2.toString());
    }
}

# Python


import base64
import hmac
import json
import random
import time
from hashlib import sha1
from urllib import parse
import urllib.request

ACCESS_KEY = "RfV6BKQb"
SECRET_KEY = "b326231bf57bdcc4060d9bf05634b59535ad5d85"
BASE_URL = "http://192.168.1.75:18713"
API_PATH = "/api/v3/wallet/pay"


def hash_hmac(key, str, algorithm):
    hmac_code = hmac.new(key.encode(), str.encode(), algorithm).digest()
    return base64.b64encode(hmac_code).decode()


def generate_random_str(random_length=16):
    random_str = ''
    base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789'
    length = len(base_str) - 1
    for i in range(random_length):
        random_str += base_str[random.randint(0, length)]
    return random_str


if __name__ == '__main__':
    body = dict()
    params = dict()

    # header
    params["access_key"] = ACCESS_KEY
    params["timestamp"] = str(round(time.time() * 1000))
    params["nonce"] = generate_random_str(36)

    # body
    body["externalOrderId"] = generate_random_str(32)
    body["cashierChainType"] = "BSC"
    body["cashierTokenType"] = "USDC"
    body["cashierCryptoAmount"] = "19.0087"
    body["hiddenMerchantLogo"] = 1
    body["notifyUrl"] = "https://payment.hambit.io/callback/do"
    body["hiddenMerchantName"] = 1
    body["remark"] = "测试代码"

    # foreach key in body
    for key in body:
        params[key] = body[key]

    # Sort and Combine
    keys = sorted(params.keys())
    qs0 = '&'.join(['%s=%s' % (key, params[key]) for key in keys])
    # need replace Boolean because python boolean output True False
    qs0 = qs0.replace("True", "true").replace("False", "false")
    print("sign string: " + qs0)

    # Sign
    sign = hash_hmac(SECRET_KEY, qs0, sha1)
    print("sign result: " + sign)

    # request
    header = {
        'access_key': ACCESS_KEY,
        'timestamp': params["timestamp"],
        'nonce': params["nonce"],
        'sign': sign,
        'Content-Type': 'application/json;charset=utf-8',
    }
    URL = BASE_URL + API_PATH
    request = urllib.request.Request(url=URL, headers=header)
    file = urllib.request.urlopen(request, json.dumps(body).encode("utf-8", 'ignore'))
    print("response body: " + file.read().decode("utf-8", 'ignore'))

# PHP


<?php
function get_rand_str($length): string
{
    $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghilkmnopqrstuvwxyz1234567890';
    $randStr = str_shuffle($str);
    $rand= substr($randStr,0,$length);
    return $rand;
}

$AccessKey = "RfV6BKQb";
$SecretKey  = "b326231bf57bdcc4060d9bf05634b59535ad5d85";
$BaseUrl   = "http://192.168.1.75:18713";
$ApiPath   = "/api/v3/wallet/pay";

$params = array(
    'access_key' => $AccessKey,
    'timestamp' => (int)(microtime(true)*1000),
    'nonce' => get_rand_str(36),
    'externalOrderId' => get_rand_str(32),
    'cashierChainType' => 'ETH',
    'cashierTokenType' => 'USDC',
    'cashierCryptoAmount' => '19.987',
    'hiddenMerchantLogo' => 1,
    'hiddenMerchantName' => 1,
    'notifyUrl' => 'https://www.example.com/notify',
    'remark' => 'USER',
);

ksort($params);

// Encode and Combine
global $paramString;
foreach ($params as $key => $value) {
    $paramString .= $key.'='.  $value   .'&';
}
$paramString = substr($paramString,0,-1);
$sign = base64_encode(hash_hmac("sha1", $paramString, $SecretKey, $raw_output=TRUE));
print "sign string: " .$paramString . "\n";
print "sign result: " .$sign. "\n";

// Http
$method ="POST";
$curl = curl_init($BaseUrl.$ApiPath);

// Header
$headers = [
    'sign: '. $sign,
    'access_key: ' .$AccessKey,
    'timestamp: ' .$params['timestamp'],
    'nonce: ' .$params['nonce'],
    'Content-Type: application/json;charset=utf-8',
];
// remove header params
unset($params['access_key']);
unset($params['timestamp']);
unset($params['nonce']);

// post request
$payload = json_encode($params);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$server_output = curl_exec ($curl);
curl_close ($curl);

# C#


using System;
using System.Runtime.InteropServices;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
using Newtonsoft.Json;

String ACCESS_KEY = "RfV6BKQb";
String SECRET_KEY = "b326231bf57bdcc4060d9bf05634b59535ad5d85";
String BASE_URL = "http://192.168.1.75:18713";
String API_PATH = "/api/v3/wallet/pay";

// Params header
Dictionary<string, object> dic = new Dictionary<string, object>();
string chars = "ABCDEFGHIJKLMNOPQRSTUWVXYZ0123456789abcdefghijklmnopqrstuvwxyz";
Random random = new Random();
dic.Add("access_key", ACCESS_KEY);
dic.Add("timestamp", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString());
dic.Add("nonce", new string(Enumerable.Repeat(chars, 36).Select(s => s[random.Next(chars.Length)]).ToArray()));

// Body
dic.Add("externalOrderId", new string(Enumerable.Repeat(chars, 32).Select(s => s[random.Next(chars.Length)]).ToArray()));
dic.Add("cashierChainType", "BSC");
dic.Add("cashierTokenType", "BUSD");
dic.Add("cashierCryptoAmount", "99.998");
dic.Add("hiddenMerchantLogo", 1);
dic.Add("notifyUrl", "https://payment.hambit.io/callback/do");
dic.Add("hiddenMerchantName", 1);
dic.Add("remark", "测试代码");

// Sort and Combine
String finalStrForSign = "";
var sortDic = dic.OrderBy(x => x.Key, new OrdinalComparer()).ToDictionary(x => x.Key, y => y.Value);
foreach (KeyValuePair<string, Object> kvp in sortDic)
{
    finalStrForSign += kvp.Key + "=" +kvp.Value + "&";
}
finalStrForSign = finalStrForSign.Substring(0, finalStrForSign.Length - 1);
Console.WriteLine("sign string: " + finalStrForSign);

// Sign
HMACSHA1 _hMacSha1 = new HMACSHA1(System.Text.Encoding.UTF8.GetBytes(SECRET_KEY));
byte[] inputBuffer = System.Text.Encoding.UTF8.GetBytes(finalStrForSign);
byte[] hashedBuffer = _hMacSha1.ComputeHash(inputBuffer);
String sign = Convert.ToBase64String(hashedBuffer);
Console.WriteLine("sign result: " + sign);

// Http if you use other http client library, may be had diff
HttpClient _httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.DefaultRequestHeaders.Add("sign", sign);
_httpClient.DefaultRequestHeaders.Add("access_key", ACCESS_KEY);
_httpClient.DefaultRequestHeaders.Add("timestamp", dic["timestamp"].ToString());
_httpClient.DefaultRequestHeaders.Add("nonce", dic["nonce"].ToString());

// remove header params
dic.Remove("access_key");
dic.Remove("timestamp");
dic.Remove("nonce");
var content = new StringContent(JsonConvert.SerializeObject(dic), Encoding.UTF8, "application/json");

// Http Request
var response = await _httpClient.PostAsync(BASE_URL + API_PATH, content);
Console.WriteLine(response.EnsureSuccessStatusCode());
Console.WriteLine(response.Content.ReadAsStringAsync().Result);



public class OrdinalComparer : System.Collections.Generic.IComparer<String>
{
    public int Compare(String x, String y)
    {
        return string.CompareOrdinal(x, y);
    }
}

# Node.js

import crypto from 'crypto';
import fetch from 'node-fetch';

function getRandStr(length) {
    const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghilkmnopqrstuvwxyz1234567890';
    const randStr = str.split('').sort(() => 0.5 - Math.random()).join('');
    return randStr.substr(0, length);
}

const AccessKey = "RfV6BKQb";
const SecretKey = "b326231bf57bdcc4060d9bf05634b59535ad5d85";
const BaseUrl = "http://192.168.1.75:18713";
const ApiPath = "/api/v3/wallet/pay";
const params = {
    access_key: AccessKey,
    timestamp: Math.floor(new Date().getTime()),
    nonce: getRandStr(36),
    externalOrderId: getRandStr(32),
    cashierChainType: 'ETH',
    cashierTokenType: 'USDC',
    cashierCryptoAmount: '19.987',
    hiddenMerchantLogo: 1,
    hiddenMerchantName: 1,
    notifyUrl: 'https://www.example.com/notify',
    remark: 'USER'
};

const paramString = Object.keys(params)
    .sort()
    .map(key => `${key}=${params[key]}`)
    .join('&');

const sign = crypto.createHmac('sha1', SecretKey)
    .update(paramString)
    .digest('base64');

console.log("sign string: " + paramString);
console.log("sign result: " + sign);

const method = "POST";
const headers = {
    'sign': sign,
    'access_key': AccessKey,
    'timestamp': params.timestamp.toString(),
    'nonce': params.nonce,
    'Content-Type': 'application/json;charset=utf-8'
};

delete params.access_key;
delete params.timestamp;
delete params.nonce;

const payload = JSON.stringify(params);

fetch(BaseUrl + ApiPath, {
    method: method,
    headers: headers,
    body: payload
})
    .then(response => response.text())
    .then(data => {
        console.log(data);
    })
    .catch(error => {
        console.error(error);
    });