iPeakoin API Notifications
This document list and describes the events and data models used in iPeakoin Notifications.
WebHook
iPeakoin uses Webhooks to notify a single callback URL provided by the client in a specified format. iPeakoin will send different payloads according to the scenarios described further below, and the trigger event can be understood in terms of the Template field in the payload body.
After receiving a notification, needs to return a reply packet within 5 seconds. Otherwise, iPeakoin considers the notification failed and sends the notification repeatedly.
The same notification may be sent multiple times, and duplicate notifications must be handled correctly. If it has been processed, return success directly to iPeakoin.
The client should return the specified return code. If no corresponding return code is received after sending the callback URL, the iPeakoin system will consider the push unsuccessful. The return fields are as follows:
Field | Type | Description |
---|---|---|
received | boolean | Receiving identifier |
Retry interval
Retry number | Interval | Retry number | Interval |
---|---|---|---|
1 | 10 seconds | 9 | 7 minutes |
2 | 30 seconds | 10 | 8 minutes |
3 | 1 minute | 11 | 9 minutes |
4 | 2 minutes | 12 | 10 minutes |
5 | 3 minutes | 13 | 20 minutes |
6 | 4 minutes | 14 | 30 minutes |
7 | 5 minutes | 15 | 1 hour |
8 | 6 minutes | 16 | 2 hours |
Common Considerations
All current and future implementations of notification messages have the following attributes:
Name | Type | Description | Sample |
---|---|---|---|
id | UUID | notification identifier | 32b0216b-66d9-498b-a4bc-17612d9cb6cd |
businessType | string | The businessType of notification | AssetsDeposit |
sign | string | signature | 055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd |
Account Notification
AccountRegistered
When you create a subaccount, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "AccountRegistered",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Account Object.
KYC
When you create a subaccount through KYC, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "KYC",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Account Object.
FaceAuthentication
When submitting face authentication, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "FaceAuthentication",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a FaceAuthentication Object.
Quantum Card Notification
CreateCard
Create a quantum card, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "CreateCard",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Card Object.
FrozenCard
Frozen quantum card, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "FrozenCard",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Card Object.
UnfrozenCard
Unfrozen quantum card, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "UnfrozenCard",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Card Object.
DeleteCard
Delete quantum card, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "DeleteCard",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Card Object.
CardTransaction
When the quantum card generates a transaction, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "CardTransaction",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a CardTransaction Object.
FrozenAmount
Frozen the quantum card amount, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "FrozenAmount",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a CardTransaction Object.
UnfrozenAmount
Unfrozen the quantum card amount, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "UnfrozenAmount",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a CardTransaction Object.
BudgetTransaction
When a budget generates a transaction, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "UnfrozenAmount",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a BudgetTransaction Object.
Global Account Notification
CreateGlobalAccount
Open global accounts in compliance, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "CreateGlobalAccount",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a BankAccount Object.
GlobalAccountTransaction
When a global account generates a transaction, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "CreateGlobalAccount",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a GlobalAccountTransaction Object.
Crypto Asset Notification
AssetsDeposit
Once you process a successful deposit, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "AssetsDeposit",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Deposit Object.
AssetsWithdrawal
Once you process a successful withdrawal, you should see a notification message on your local server shell that looks like the following.
{
"id": "6a94b9c7-40d6-4007-a5d0-a96d714a1108",
"businessType": "AssetsWithdrawal",
"data": { ... },
"sign": "055144f3c59f2412d5575daee1ecc473b028378d3c3e359970fc96b33314e5bd"
}
The data
payload will be a Withdrawal Object.
Signature
Each request initiated by iPeakoin contains a sign parameter that can be used to verify the authenticity of the request from iPeakoin. For each request, the data of the data parameter is fetched and processed through the HMAC-SHA256 hash function.
- Set of parameters to be signed
const params = {
"id": "ee74c872-8173-4b67-81b1-5746e7d5ab88",
"accountId": null,
"holderId": "d2bd6ab3-3c28-4ac7-a7c4-b7eed5eee367",
"currency": "USD",
"settlementCurrency": null,
"counterparty": "SAILINGWOOD;;US;1800948598;;091000019",
"transactionAmount": 11,
"fee": 0,
"businessType": "Inbound",
"status": "Closed",
"transactionTime": "2021-11-22T07:34:10.997Z",
"transactionId": "124d3804-defa-4033-9f30-1d8b0468e506",
"clientTransactionId": null,
"createTime": "2021-11-22T07:34:10.997Z",
"appendFee": 0,
};
- The parameter set key to be signed is sorted in ascending order according to "ASCII code of the first character of the string" (if the ASCII code value is the same in the sorting process, the next digit is incremented in sequence for comparison).
const keys = Object.keys(params);
keys.sort();
- Concatenates a string, and empty values are filled with empty strings
accountId=&appendFee=0&businessType=Inbound&clientTransactionId=&counterparty=SAILINGWOOD;;US;1800948598;;091000019&createTime=2021-11-22T07:34:10.997Z¤cy=USD&fee=0&holderId=d2bd6ab3-3c28-4ac7-a7c4-b7eed5eee367&id=ee74c872-8173-4b67-81b1-5746e7d5ab88&settlementCurrency=&status=Closed&transactionAmount=11&transactionId=124d3804-defa-4033-9f30-1d8b0468e506&transactionTime=2021-11-22T07:34:10.997Z
- CLIENT_SECRET is used to perform hmac-sha256 signature on the concatenated string, and the hexadecimal encoding is used to obtain signature.
const hmac = crypto.createHmac('sha256', '25d55ad283aa400af464c76d713c07ad');
const sign = hmac.update('Concatenates a string').digest('hex');
Example Code
const crypto = require('crypto');
function joinStr(params) {
const keys = Object.keys(params);
keys.sort();
const result = [];
for (const key of keys) {
let val = params[key];
if (val == null) {
val = '';
} else if (typeof val === 'object') {
if (!Array.isArray(val)) {
const fields = Object.keys(val);
fields.sort();
const res = {};
for (const field of fields) {
res[field] = val[field];
}
val = res;
}
val = JSON.stringify(val);
}
result.push(`${key}=${val}`);
}
return result.join('&');
}
const params = {
'createTime': '2023-05-31T07:29:46.784Z',
'budgetId': null,
'provider': 'PrepaidCard_493728',
'currency': 'USD',
'qbitCardNoLastFour': '1234',
'id': 'b9ce056b-c1f8-4f19-b014-d7be02a54598',
'status': 'Active',
'useType': '79f22263-a3fe-4347-8a40-2af6bf422839',
'label': 'ce08100b-fca8-4a13-bbfc-c381aeaec5d0',
'balanceId': 'ab43462f-93b3-4540-8601-11d759948ee7',
'cardAddress': {
'country': 'US',
'postalCode': '94402',
'addressLine2': '',
'addressLine1': '20 Barneson ave',
'state': 'California',
'city': 'San Mateo'
},
'accountId': '01eba490-5f9c-48a6-aa2d-7bcfdff0d720',
'token': '0ef85b24-866f-4c03-a7e8-459e3742642b',
'userName': 'test test'
};
const hmac = crypto.createHmac('sha256', '25d55ad283aa400af464c76d713c07ad');
const sign = hmac.update(joinStr(params)).digest('hex');
console.log(sign);
// => 178997e5960603afc573a28743d1680e3719a400e83936076f4dae4cb123a35a
package com.ipeakoin.demo;
import com.alibaba.fastjson.JSON;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class Signature {
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : bytes) {
formatter.format("%02x", b);
}
return sb.toString();
}
public static byte[] sign(String str, String secret) throws NoSuchAlgorithmException, InvalidKeyException {
Key sk = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance(sk.getAlgorithm());
mac.init(sk);
return mac.doFinal(str.getBytes());
}
public static String joinStr(Map<String, Object> data) {
String[] keys = data.keySet().toArray(new String[0]);
Arrays.sort(keys);
StringBuilder sb = new StringBuilder();
for (String key : keys) {
Object val = data.get(key);
if (val == null) {
val = "";
}
if (val instanceof Map<?, ?>) {
val = JSON.toJSONString(new TreeMap<>((Map<?, ?>) val));
}
sb.append(key).append("=").append(val).append("&");
}
String str = sb.toString();
return str.substring(0, str.length() - 1);
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
Map<String, String> address = new HashMap<>();
address.put("addressLine1", "20 Barneson ave");
address.put("addressLine2", "");
address.put("city", "San Mateo");
address.put("country", "US");
address.put("postalCode", "94402");
address.put("state", "California");
Map<String, Object> data = new HashMap<>();
data.put("id", "b9ce056b-c1f8-4f19-b014-d7be02a54598");
data.put("accountId", "01eba490-5f9c-48a6-aa2d-7bcfdff0d720");
data.put("token", "0ef85b24-866f-4c03-a7e8-459e3742642b");
data.put("status", "Active");
data.put("currency", "USD");
data.put("provider", "PrepaidCard_493728");
data.put("userName", "test test");
data.put("createTime", "2023-05-31T07:29:46.784Z");
data.put("qbitCardNoLastFour", "1234");
data.put("label", "ce08100b-fca8-4a13-bbfc-c381aeaec5d0");
data.put("useType", "79f22263-a3fe-4347-8a40-2af6bf422839");
data.put("balanceId", "ab43462f-93b3-4540-8601-11d759948ee7");
data.put("budgetId", null);
data.put("cardAddress", address);
String signStr = bytesToHex(sign(joinStr(data), "25d55ad283aa400af464c76d713c07ad"));
System.out.println(signStr);
// => 178997e5960603afc573a28743d1680e3719a400e83936076f4dae4cb123a35a
}
}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
)
func getKeys(m map[string]any) []string {
j := 0
keys := make([]string, len(m))
for k := range m {
keys[j] = k
j++
}
return keys
}
func joinStr(params map[string]any) string {
keys := getKeys(params)
sort.Strings(keys)
var result []string
for _, key := range keys {
val := params[key]
if val == nil {
val = ""
}
_, ok := val.(map[string]any)
if ok {
bytes, _ := json.Marshal(&val)
val = string(bytes)
fmt.Println(val)
}
result = append(result, fmt.Sprintf("%s=%s", key, fmt.Sprintf("%v", val)))
}
return strings.Join(result, "&")
}
func sign(message string, secret string) string {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(message))
return hex.EncodeToString(h.Sum(nil))
}
func main() {
params := map[string]any{
"createTime": "2023-05-31T07:29:46.784Z",
"budgetId": nil,
"provider": "PrepaidCard_493728",
"currency": "USD",
"qbitCardNoLastFour": "1234",
"id": "b9ce056b-c1f8-4f19-b014-d7be02a54598",
"status": "Active",
"useType": "79f22263-a3fe-4347-8a40-2af6bf422839",
"label": "ce08100b-fca8-4a13-bbfc-c381aeaec5d0",
"balanceId": "ab43462f-93b3-4540-8601-11d759948ee7",
"cardAddress": map[string]any{
"country": "US",
"postalCode": "94402",
"addressLine2": "",
"addressLine1": "20 Barneson ave",
"state": "California",
"city": "San Mateo",
},
"accountId": "01eba490-5f9c-48a6-aa2d-7bcfdff0d720",
"token": "0ef85b24-866f-4c03-a7e8-459e3742642b",
"userName": "test test",
}
fmt.Println(sign(joinStr(params), "25d55ad283aa400af464c76d713c07ad"))
// => 178997e5960603afc573a28743d1680e3719a400e83936076f4dae4cb123a35a
}
import json
import hashlib
import hmac
def sign(message, secret):
encryption = hmac.new(bytes(secret, encoding='UTF-8'), bytes(message, encoding='UTF-8'), hashlib.sha256)
return encryption.hexdigest()
def join_str(origin):
keys = list(origin.keys())
keys.sort()
content = []
for key in keys:
val = origin[key]
if val is None:
val = ''
if isinstance(val, dict):
val = json.dumps(val, sort_keys=True, ensure_ascii=False, separators=(',', ':'))
content.append(key + '=' + str(val))
return '&'.join(content)
if __name__ == '__main__':
data = {
'createTime': '2023-05-31T07:29:46.784Z',
'budgetId': None,
'provider': 'PrepaidCard_493728',
'currency': 'USD',
'qbitCardNoLastFour': '1234',
'id': 'b9ce056b-c1f8-4f19-b014-d7be02a54598',
'status': 'Active',
'useType': '79f22263-a3fe-4347-8a40-2af6bf422839',
'label': 'ce08100b-fca8-4a13-bbfc-c381aeaec5d0',
'balanceId': 'ab43462f-93b3-4540-8601-11d759948ee7',
'cardAddress': {
'country': 'US',
'postalCode': '94402',
'addressLine2': '',
'addressLine1': '20 Barneson ave',
'state': 'California',
'city': 'San Mateo'
},
'accountId': '01eba490-5f9c-48a6-aa2d-7bcfdff0d720',
'token': '0ef85b24-866f-4c03-a7e8-459e3742642b',
'userName': 'test test'
}
sign_str = sign(join_str(data), '25d55ad283aa400af464c76d713c07ad')
print(sign_str)
# => 178997e5960603afc573a28743d1680e3719a400e83936076f4dae4cb123a35a
Updated about 2 months ago