通讯说明
所有接口均使用 HTTPS 通信,数据包格式为 json(HTTP 请求的 content-type 字段必须使用 application/json)。
请求必须传认证信息,需要传认证码和认证算法。
响应要验证认证码。
所有接口参数名使用的字母均为小写。
数据包格式说明
请求包格式
请求格式示例(以 query_order 接口为例):
{"authen_info": {"a": {"authen_type": 1,"authen_code": "69E65DA0A32F18D059DBE81CA4D9D702470184966E12A03715584249788BD8DD"}},"request_content": "{\\"pay_mch_key\\": {\\"pay_platform\\": 1, \\"sub_pay_platform\\": 100, \\"out_shop_id\\": \\"sz011biKxOguirmBqiFR\\", \\"out_sub_mch_id\\": \\"sz01KzuCUOmw8yjtPite\\", \\"out_mch_id\\": \\"sz01lXKA6DKGjNzr2l4B\\"}, \\"trade_type\\": 1, \\"out_trade_no\\": \\"sz010002cz11564386781\\", \\"nonce_str\\": \\"E94C00688C3F429CA2B0B396BF823548\\", \\"order_client\\": {\\"staff_id\\": \\"1192\\", \\"machine_no\\": \\"1111\\", \\"terminal_type\\": 1, \\"sdk_version\\": \\"1.0\\", \\"device_id\\": \\"12345\\", \\"spbill_create_ip\\": \\"90.0.00.0\\"}}"}
详解:
请求包含两个字段:authen_info 和 request_content。前者表示认证信息,为 json 结构;后者表示请求具体内容,为 json 形式字符串化。
request_content 为具体请求内容的 json 字符串化结构,见各具体接口。
authen_info 结构有嵌套属性 a,字段为认证码:
字段 | 类型 | 说明 |
authen_type | Number(32) | 认证算法类型。只支持填1,即为 HMAC-SHA256 |
authen_code | String(64) | 认证码 |
认证码生成算法:HMAC-SHA256 认证密钥为服务商在云支付录入商户时,在子商户页面上生成的认证密钥。
响应包格式
响应包与请求包类似,包含两个字段:authen_info 和 response_content。前者表示认证信息(响应包仅有认证码方式),为 json 结构;后者表示响应具体内容,为 json 形式字符串化。响应格式示例(以交接班接口为例):
{"authen_info": {"a":{"authen_type": 1,"authen_code" : "1CB622818DF1E2D91741A5FE792F1EFDD2557343FBD1275628E832A95CBB0FBC"}},"response_content": "{\\"status\\":0,\\"description\\":\\"\\\\u64CD\\\\u4F5C\\\\u6210\\\\u529F\\\\u3002\\",\\"log_id\\":1167366844,\\"internal_status\\":0}"}
其它说明
各种 update 接口中,如选填字段不填写,则该字段不需修改。清空此字段时,需上传此字段的内容为空。
接口调用说明
交易接口中的门店信息,必须和子商户在云支付手机端商户管理系统设置的一致。
订单和退款单号说明
为了保护不同商户的订单号不重复,云支付为每个服务商录入的子商户分配“云支付订单前缀”,在云支付后台的商户详情中可以看到,该商户的订单和退款单必须以云支付子商户号做前缀。
数据包构造示例
下面以刷卡支付为例来介绍整个认证过程:
客户端向服务器发送请求
1. 判定使用场景。
在这个示例中,我们要发出的是刷卡支付请求。因此,需要使用认证算法来计算认证码。
2. 根据接口的输入参数,构造 json 格式字符串。
3. 将 json 格式字符串转换为字符串,并使用认证算法,认证 key 来计算认证码。
4. 构造 authen_info 结构。
5. 构造 json 格式的请求数据包,需要包含 authen_info 和 request_content 字段。
6. 将 json 格式请求转换为字符串,发送给服务器。
客户端验证服务器应答消息
1. 将响应包从 string 转换为 json 格式消息。
2. 取出 json 格式消息中的 authen_info 和 response_content 字段信息。
3. 使用认证算法和认证 key 对 response_content 计算认证码。
4. 将计算得到的认证码和 authen_info 中的 authen_code 进行比较;正确情况下,两者应该一致。
代码示例(C++)
构造请求字符串(以刷卡支付为例):
std::string MicroPay_request_str(){/* 1.构造业务请求参数 */Json::Value pay_mch_key; // 构造 pay_mch_keypay_mch_key["pay_platform"] = 1;pay_mch_key["out_mch_id"] = "sz013NzuonO6CMJd0rCB";pay_mch_key["out_sub_mch_id"] = "sz01ELTR281OFpmdAp6J";pay_mch_key["out_shop_id"] = "sz01qyoPJmd3j1hWmul4";Json::Value pay_content; // 构造 pay_contentpay_content["out_trade_no"] = "sz0100lmnx20171228151031";pay_content["author_code"] = "134680423163089456";pay_content["total_fee"] = 1;pay_content["fee_type"] = "CNY";pay_content["attach"] = "attach";Json::Value order_client; // 构造 order_clientorder_client["machine_no"] = "32-62-A8-14-B3-C0";order_client["sdk_version"] = "1.0";order_client["device_id"] = 1;order_client["spbill_create_ip"] = "192.168.100.75";order_client["staff_id"] = "1003";order_client["terminal_type"] = 2;Json::Value request_content; // 构造 request_contentrequest_content["pay_mch_key"] = pay_mch_key;request_content["pay_content"] = pay_content;request_content["order_client"] = order_client;request_content["nonce_str"] = "416492026bc84091bcaf7e74ea90ceba";Json::FastWriter w;std::string request_content_str = w.write(request_content);/* 2. 根据上面的业务请求参数计算认证码 */Json::Value authen;std::string hmac;calc_HMAC_SHA256(authen_key, request_content_str, &hmac); //计算认证码authen["authen_code"] = hmac;authen["authen_type"] = 1; //hmac_sha256 为1Json::Value authen_info;authen_info["a"] = authen; //认证码/* 3. 拼装最终请求参数 */Json::Value request; //构造最终发给服务器的请求request["request_content"] = request_content_str;request["authen_info"] = authen_info;std::string request_str = w.write(request);return request_str;}
计算认证码:
/*返回是否成功,成功时认证码存放于 hmac 指向的 string*/bool calc_HMAC_SHA256(const std::string &key, const std::string &input, std::string *hmac){unsigned char md[SHA256_DIGEST_LENGTH] = {0};//32 byteschar format_md[65] = {0};unsigned int md_len = sizeof(md);HMAC_CTX ctx;HMAC_CTX_init(&ctx);if (!HMAC_Init_ex(&ctx, key.data(), (int)key.length(), EVP_sha256(), NULL) ||!HMAC_Update(&ctx, (const unsigned char *)input.data(), input.length()) ||!HMAC_Final(&ctx, md, &md_len)) {HMAC_CTX_cleanup(&ctx);return false;}HMAC_CTX_cleanup(&ctx);for (int i = 0; i < 32; i++) {snprintf(&format_md[i * 2], 3, "%02x", md[i]); //二进制转为十六进制大写}hmac->assign(format_md);// 转大写transform(hmac->begin(), hmac->end(), hmac->begin(), ::toupper);return true;}