Skip to main content

概览

ECardPay API基于 HTTPS 协议,接入方应按照说明进行POST请求,请参见 API列表.

所有API的请求体和响应体均采用 JSON 格式 并采用 UTF-8 编码。接入方应在请求头中包含 Content-Type: application/json.

所有API的请求和响应都包含一些 公共参数 , 分别位于请求头和响应头,详见 公共参数.

所有API的请求和响应都进行了加密签名, 详见加密机制签名机制.

公共参数

请求头

名称必填描述备注示例值
apiKeyYApi 调用 Key预先配置.
serviceYAPI名称see API列表.createCard
versionYAPI版本当前版本 2.0.2.0
requestIdY请求流水号每个请求唯一,建议使用UUID.64c9183e-1f89-4c32-b2d2-ba79201e4ed4
timestampY请求时间UNIX时间戳(毫秒).1577851018000
signY请求数据签名详见 签名机制.7b73bb09b4aab6a7c80 (部分数据)

响应头

名称描述备注示例值
serviceAPI名称请求头原样返回.createCard
versionAPI版本请求头原样返回.2.0
requestId请求流水号请求头原样返回.64c9183e-1f89-4c32-b2d2-ba79201e4ed4
timestamp响应时间UNIX时间戳(毫秒).1277851018000
code响应码枚举值,3位数字,详见响应码下拉框.200 (非200表示API调用失败)
sign响应数据签名详见签名机制.7b73bb09b4aab6a7c80 (部分数据)
响应码
CodeMessage备注
200succeed成功.
400payload decrypt failed无法解密数据.
405unsupported method请求方法不支持。(仅支持POST
406too many requests请求过于频繁.
407verify sign failed无法验证签名.
408app api key not findApi Key不存在.
409request id is null or duplicate请求流水号重复.
417the parameter is null or invalid请求头必填参数为空.
500system error(not fixed)系统错误.

API列表

名称方法请求头service参数
创建卡POSTcreateCard
注销卡POSTcancelCard
充值卡POSTrechargeCard
查询卡POSTqueryCardDetail
更新卡POSTchangeCard
可用卡段POSTcardBins
卡交易POSTcardTransactions
账户余额查询POSTqueryAccountBalance

加密机制

为保证数据的安全性,ECardPay会对每个请求和响应进行加密。

接入方与ECardPay需使用对称加密算法AES/ECB/PKCS5Padding,对请求体响应体进行加密与解密,结果采用Base64 编码。

请求体

原始请求体各字段含义详见下方各API说明。

接入方: 对原始请求体先使用AES密钥加密,再进行Base64编码。结果作为实际请求体payload字段的值。发送实际请求体

ECardPay: 对收到的实际请求体payload字段的值先进行Base64解码,再使用AES密钥解密。结果即为原始请求体

原始请求体
{
"key_1": "value_1",
"key_2": "value_2",
"key_3": "value_3"
}
实际请求体
{
"payload": "ewogICJrZXlfMSI6ICJ2YWx1ZV8xIiwKICAia2V5XzIiOiAidmFsdWVfMiIsCiAgImtleV8zIjogInZhbHVlXzMiCn0="
}

响应体

原始响应体各字段含义详见下方各API说明。

ECardPay: 对原始响应体先使用AES密钥加密,再进行Base64编码。结果作为实际响应体payload字段的值。发送实际响应体

接入方:对收到的实际响应体payload字段的值先进行Base64解码,再使用AES密钥解密。结果即为原始响应体

原始响应体
{
"key_1": "value_1",
"key_2": "value_2",
"key_3": "value_3"
}
实际响应体
{
"payload": "5L2XuwCQ3tnkL2Xk0J6i7MJtd6gkladDmN0WR8vzoi1ViSZ69/nSr8I9lykShKbTy5JnWD0O/Ly8mqn7GJNxvd1WX5CV2gU+q5Lmg1KJrgg="
}
Java工具类示例
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

import static java.nio.charset.StandardCharsets.UTF_8;

public class AesUtils {

private static final Base64.Encoder ENCODER = Base64.getEncoder();
private static final Base64.Decoder DECODER = Base64.getDecoder();

public static String aesEncrypt(SecretKey key, String plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherBytes = cipher.doFinal(plaintext.getBytes(UTF_8));
return new String(ENCODER.encode(cipherBytes), UTF_8);
}

public static String aesDecrypt(SecretKey key, String ciphertext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] cipherBytes = DECODER.decode(ciphertext.getBytes(UTF_8));
return new String(cipher.doFinal(cipherBytes), UTF_8);
}

public static SecretKey newAesKey(String keyStr) {
return new SecretKeySpec(DECODER.decode(keyStr.getBytes(UTF_8)), "AES");
}

public static String toAesKeyStr(SecretKey key) {
return new String(ENCODER.encode(key.getEncoded()), UTF_8);
}
}

签名机制

为保证API的安全调用,ECardPay会对每个请求和响应通过签名进行身份验证。

接入方与ECardPay需使用签名算法HMAC SHA256,对请求待签字符串和响应待签字符串进行签名,签名结果采用Hex编码,对应请求头和响应头的sign字段。

请求待签字符串

所有字段按如下顺序依次使用|符号连接。

apiKey|service|version|requestId|timestamp|payload

apiKey,service,version,requestId and timestamp 来自请求头中对应字段。 payload 来自实际请求体中payload字段。

接入方:对请求待签字符串使用HMAC-SHA256生成消息摘要作为签名,再进行hex编码。结果作为请求头中sign字段的值。

响应待签字符串

所有字段按如下顺序依次使用|符号连接。

service|version|requestId|timestamp|code|payload

service,version,requestId,timestamp,code 来自响应头中对应字段。 payload 来自实际响应体中payload字段。

ECardPay:对响应待签字符串使用HMAC-SHA256生成消息摘要作为签名,再进行hex编码。结果作为请求头中sign字段的值。

Java工具类示例
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class HmacTest {

public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String hmacSHA256Algorithm = "HmacSHA256";
String data = "e4664784e85e82799696acbf70580bbdcf0bfbf4|createCard|2.0|a5ebc0ba-b7ec-11ed-afa1-0242ac120002|1277851018000|ewogICJrZXlfMSI6ICJ2YWx1ZV8xIiwKICAia2V5XzIiOiAidmFsdWVfMiIsCiAgImtleV8zIjogInZhbHVlXzMiCn0=";
String key = "886f04ad550d95459ec1d3af1747a844ed32951852e491b3cddea61aca5b2630";

SecretKeySpec secretKeySpec = new SecretKeySpec(hex2Bytes(key), hmacSHA256Algorithm);
Mac mac = Mac.getInstance(hmacSHA256Algorithm);
mac.init(secretKeySpec);
String sigStr = bytesToHex(mac.doFinal(data.getBytes()));
System.out.println(sigStr);
}

private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}

public static byte[] hex2Bytes(String str)
{
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < bytes.length; i++)
{
bytes[i] = (byte) Integer
.parseInt(str.substring(2 * i, 2 * i + 2), 16);
}
return bytes;
}
}