通用接口说明
通用请求是商户侧调用平台各业务接口都需要的参数,当前版本采用HTTP请求头部信息的方式。
回调IP地址
- 54.169.54.48
- 3.0.143.201
- 52.76.5.66
金额单位
Rupiah(元)
请求测试域名
| 参数 | 是否必须 | 内容 |
|---|---|---|
| API endpoint | 是 | https://test-merchant.hzpay.net/prex/ |
请求正式域名
| 参数 | 是否必须 | 内容 |
|---|---|---|
| API endpoint | 是 | https://merchant.hzpay.net/prex/ |
请求头部信息
WARNING
接口请求和回调中均包含请求头数据
| 参数 | 是否必须 | 内容 |
|---|---|---|
| Content-Type | 否 | application/json |
| Api-Key | 是 | 由平台分配的商户唯一标识 |
| Request-Id | 是 | 商户请求ID,由商户侧保证唯一,建议采用UUID、NanoID 或者长度在8位以上32位以下的无特殊符号字符串 |
| Timestamp | 是 | Long类型,当前时间与世界统一时间UTC(1970年1月1日)的差值,以毫秒为单位 |
| Sign | 是 | 用于核实身份的签名信息(签名计算方法见下面的签名说明) |
签名计算方法
代收签名计算 ✅
签名采用对【请求体】采用 SHA-256 加密算法进行加密计算,
通过分隔符”&”将 【商户唯一标识】、【请求体】、【商户请求 ID】、【时间戳】排列为一个组件,
通过分配给商户的加密密钥从上述组件中计算 HMAC-SHA256,并将加密结果转换为 BASE64 字符串的方式来生成。
假设参数如下:
- 商户唯一标识(api-key) : 934ns90d
- 商户加密密钥(api-secret): 90oa4dowox00o3cd
- 商户请求(requestId): d123455678892238729
- 请求时间戳(timestamp): 1687227487329
- 请求体(body): 如下所示
DANGER
注意:请求数据传过来的部分均需要加密,下述加密数据中的 body 仅为示例
签名计算示例:
java
public static String sha256(String body) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(body.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(hash);
}
public static String hmacSha256(String component, String secret) throws Exception {
byte[] decodedKey = secret.getBytes(StandardCharsets.UTF_8);
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
hmacSha256.init(originalKey);
hmacSha256.update(component.getBytes(StandardCharsets.UTF_8));
byte[] HmacSha256DigestBytes = hmacSha256.doFinal();
String signature = Base64.getEncoder().encodeToString(HmacSha256DigestBytes);
return signature;
}
加密数据:
String body = {"order":{"amount":10000,"callback":"http://yourdomain.com/callback _success","id":"9bddcb4114b24aceb49daa506b7b406d"}}
String component = Api-Key={yourApiKey}&Body-Hash={sha256(body)}&Request- Id={requestId}&Timestamp={timestamp};
String api-secret = 90oa4dowox00o3cd;
String sign = hmacSha256(component, api-secret);
// component: Api-Key=934ns90d&Body-Hash=VodvE2oJFTVS9AE6vRD+hFA8agUgEvkGxsY+QQys4uc=&Request-Id=123455678892238729&Timestamp=1687227487329
// sign: Oa6V892jbd3BovnCCug7UJ+RUcz1HvjK1WfhwVLOztI=php
<?php
function sha256($str) {
try {
$hash = hash('sha256', $str, true);
$base64Encoded = base64_encode($hash);
return $base64Encoded;
} catch (Exception $e) {
throw new Exception("SHA-256 加密失败", 0, $e);
}
}
function hmacSha256($component, $secret) {
try {
$decodedKey = utf8_decode($secret);
$originalKey = hash_hmac('sha256', $component, $decodedKey, true);
$signature = base64_encode($originalKey);
return $signature;
} catch (Exception $e) {
throw new Exception("HMAC SHA-256 计算失败", 0, $e);
}
}
try {
$body = '{"order":{"amount":10000,"callback":"http://yourdomain.com/callback_success", "id":"9bddcb4114b24aceb49daa506b7b406d"}}';
$bodyHash = sha256($body);
$list = [
"Api-Key=934ns90d",
"Body-Hash=" . $bodyHash,
"Request-Id=123455678892238729",
"Timestamp=1687227487329"
];
// 使用 implode 连接列表中的字符串
$before = implode("&", $list);
$sign = hmacSha256($before, "90oa4dowox00o3cd");
echo "Before: " . $before . "\n";
echo "Signature: " . $sign . "\n";
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
?>c#
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main()
{
string apiKey = "your-api-key";
string body = "your-request-body";
string requestId = "your-request-id";
string timestamp = "your-timestamp";
string secret = "your-secret";
try
{
string body = "{\n" +
" \"order\":{\n" +
" \"amount\":10000,\n" +
" \"id\":\"12345678932\",\n" +
" }\n" +
"}";
string sha256Hash = Sha256(body);
Console.WriteLine("Original String: " + body);
Console.WriteLine("SHA-256 Hash: " + sha256Hash);
string signature = HmacSha256(component, secret);
Console.WriteLine("Signature: " + signature);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
List<string> list = new List<string>
{
"Api-Key=" + apiKey,
"Body-Hash=" + sha256Hash,
"Request-Id=" + requestId,
"Timestamp=" + timestamp
};
// 使用 string.Join 连接列表中的字符串
string before = string.Join("&", list);
// 输出结果
Console.WriteLine("Concatenated String: " + before);
}
// 生成 body-hash
static string Sha256(string str)
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(str));
return Convert.ToBase64String(hashBytes);
}
}
// 生成签名
static string HmacSha256(string component, string secret)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(secret);
using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
{
byte[] componentBytes = Encoding.UTF8.GetBytes(component);
byte[] hashBytes = hmac.ComputeHash(componentBytes);
// 将字节数组转换为 Base64 编码的字符串
string signature = Convert.ToBase64String(hashBytes);
return signature;
}
}
}python
import hashlib
import hmac
import base64
def sha256(data):
"""Compute SHA-256 hash of the given data and return it as a Base64 encoded string."""
# Create a new sha256 hash object
message_digest = hashlib.sha256()
# Update the hash object with the bytes of the data
message_digest.update(data.encode('utf-8'))
# Get the hash bytes
hash_bytes = message_digest.digest()
# Encode the hash bytes to a Base64 encoded string
hash_base64 = base64.b64encode(hash_bytes).decode('utf-8')
return hash_base64
def hmac_sha256(data, key):
return base64.b64encode(hmac.new(key.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).digest()).decode('utf-8')
def merchant_sign(api_key, request_id, timestamp, body, api_secret):
try:
list_to_join = [
f"Api-Key={api_key}",
f"Body-Hash={sha256(body)}",
f"Request-Id={request_id}",
f"Timestamp={timestamp}"
]
before = "&".join(list_to_join)
print("before:", before)
return hmac_sha256(before, api_secret)
except Exception as e:
print(f"Error: {str(e)}")
return None
示例:
api_key = "ABCDWER12"
request_id = "123455678892238729"
timestamp = 1687227487329
body = """{"order":{"amount":10000,"id":"2024070410329301","ewallet_phone":"111111111"},"payment":{"payment_method_types":["DANA"]}}"""
api_secret = "AEKRIU1254838DJK"
signature = merchant_sign(api_key, request_id, timestamp, body, api_secret)
print("Signature:", signature)
# before: Api-Key=ABCDWER12&Body-Hash=gEomqJpTFfGEEQgJu+MaB+NIYfOMmSCyR8tH2qOIJAI=&Request-Id=123455678892238729&Timestamp=1687227487329
# Sign: 8U0AtOVcgRMWEGiu3hCDCuhKMUaqLh9TFg0urRTvujw=代付签名计算 ✅
签名采用对【APIKey】+【请求唯一标识】+【时间戳】+【APISecret】(平台分配给商户的加密密钥) 采用AES加密算法进行加密计算, 并将加密结果转换为BASE64字符串的方式来生成。
假设参数如下:
- APIKey:M12345
- APISecret:abcdef1234567890
- 请求唯一标识(Request-Id):11223344-5566-7788-9900-abcdabcdabcd
- 时间戳(Timestamp):1687227487329
签名计算示例:
java
import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
private static final String ALGORITHM = "AES";
public static String encrypt(String valueToEnc, String encKey) throws Exception {
Key key = new SecretKeySpec(encKey.getBytes(), ALGORITHM); // 使用 APISecret 生成加密密钥
Cipher c = Cipher.getInstance(ALGORITHM); // 获取 Cipher 实例
c.init(Cipher.ENCRYPT_MODE, key); // 初始化 Cipher 为加密模式
byte[] encValue = c.doFinal(valueToEnc.getBytes()); // 对 valueToEnc 字符串加密
return new String(Base64.encodeBase64(encValue)); // 返回加密结果的 Base64 编码
}
public static void main(String[] args) throws Exception {
String valueToEnc = "M12345" + "11223344-5566-7788-9900-abcdabcdabcd" + "1687227487329"; // APIKey + Request-Id + Timestamp
String encKey= "abcdef1234567890"; // APISecret
String sign = encrypt(valueToEnc, encKey);
// sign: XWtW50jBF1a3t8UiYoMO5JUZz5PO4mGdLXBybLgSi5FLx+rga286c0Y5Dr9lgSz3HGZauWLiIb7Vzv0JB z8+5Q==
}php
<?php
$apiKey = "M12345";
$requestId = "11223344-5566-7788-9900-abcdabcdabcd";
$timestamp = "1687227487329";
$valueToEnc = $apiKey . $requestId . $timestamp;
$encKey = "abcdef1234567890";
$algorithm = "aes-128-ecb";
try {
// OpenSSL 加密函数
$encValue = openssl_encrypt($valueToEnc, $algorithm, $encKey, OPENSSL_RAW_DATA);
// Base64 编码
$encValueBase64 = base64_encode($encValue);
echo $encValueBase64;
} catch (Exception $e) {
error_log($e->getMessage());
}
?>c#
using System;
using System.Security.Cryptography;
using System.Text;
class Program {
private const string ALGORITHM = "AES";
public static void Main(string[] args) {
string valueToEnc = "M1234511223344-5566-7788-9900-abcdabcdabcd1687227487329"; // APIKey + Request-Id + Timestamp
string encKey = "abcdef1234567890"; // APISecret
try {
// Create a new AES instance
using (Aes aes = Aes.Create()) {
aes.Key = Encoding.UTF8.GetBytes(encKey);
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
// Create an encryptor
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// Encrypt the data
byte[] encValue = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(valueToEnc), 0, valueToEnc.Length);
// Convert to Base64
string encValueBase64 = Convert.ToBase64String(encValue);
Console.WriteLine(encValueBase64);
}
} catch (Exception e) {
Console.Error.WriteLine(e.ToString());
}
}
}python
安装库实现AES加密 pip install pycryptodome
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad import base64
def main():
value_to_enc = " M1234511223344-5566-7788-9900-abcdabcdabcd1687227487329" # APIKey + Request-Id + Timestamp
enc_key = "abcdef1234567890" # APISecret
try:
# Ensure the key is 16 bytes long
key = enc_key.encode('utf-8')
# Initialize cipher with AES/ECB/PKCS5Padding
cipher = AES.new(key, AES.MODE_ECB)
# Pad the value to be encrypted
padded_value_to_enc = pad(value_to_enc.encode('utf-8'), AES.block_size)
# Perform encryption
enc_value = cipher.encrypt(padded_value_to_enc)
# Encode the encrypted value with Base64
encrypted_value_base64 = base64.b64encode(enc_value).decode('utf-8')
print(encrypted_value_base64)
except Exception as e:
print(f"Error: {str(e)}")
if __name__ == "__main__":
main()通用返回
当接口请求处理被支付平台接收后,无论是处理成功还是失败,接口均会返回包含如下内容的通用状态信息。
代收通用状态信息
json
{
"status": 1,
"data": {}
}代收状态代码说明
| 代码 | 说明 | 可能的原因 |
|---|---|---|
| 1 | 成功 | 成功 |
| 其他 | 失败 | 失败 |
代付通用状态信息
json
{
"status": 1,
"message": "PROCESSING SUCCESS",
"data": {}
}代付状态代码说明
| 代码 | 说明 | 可能的原因 |
|---|---|---|
| 0 | PROCESSING FAILED | 不能确定的外部原因造成的失败 |
| 1 | PROCESSING SUCCESS | 处理成功 |
| 10 | IP ILLEGAL | IP地址不合法,发起请求的服务器地址与备案的不符合 |
| 11 | IDENTITY ERROR | 身份验证不通过,用于核实身份的信息不正确 |
| 12 | PARAMETER FORMAT ERROR | 参数格式错误,JSON数据不正确 |
| 13 | NECESSARY PARAMETER MISS | 缺少必要的参数 |
| 14 | ORDER_ID REPEAT | 交易流水号重复 |
| 15 | BANK_ID ERROR | 代付银行编码错误 |
| 20 | REQUEST REJECTED | 请求被拒绝,请求过于频繁或者被限制 |
| 21 | PROCESSING TIMEOUT | 处理超时 |
| 23 | LOW_BALANCE | 余额不足 |
| 24 | ORDER_NOT_EXIST | 订单不存在 |
| 99 | UNKNOWN ERROR | 平台未知异常 |