增加发货平台 2
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
|
||||
概述
|
||||
面向未绑定电商店铺的商户:由您的业务系统调用 API 发单,平台扣减钱包积分后履约;终端玩家通过返回的 H5 链接填写游戏账号并完成后续步骤。
|
||||
|
||||
对接文档可直接阅读,无需验证。App Key / App Secret 请在 开放 API → API 密钥 或 调用调试 中查看(仅主账号):未开动态口令时输入登录密码,已开启时输入 Google Authenticator 6 位验证码。
|
||||
|
||||
#通用约定
|
||||
项目 说明
|
||||
基础路径 http://skin-exchange.yiquyou.icu
|
||||
请求方式 本文档接口均为 POST
|
||||
Content-Type application/json(请求体为 JSON,签名使用原始 body 字符串)
|
||||
鉴权 Header X-App-Key、X-Timestamp、X-Sign,见 鉴权与签名
|
||||
#接口一览
|
||||
接口 路径
|
||||
商品列表 POST /api/open/v1/products/index
|
||||
创建订单 POST /api/open/v1/orders/store
|
||||
查询订单 POST /api/open/v1/orders/show
|
||||
余额查询 POST /api/open/v1/wallet/balance
|
||||
#统一响应结构
|
||||
字段 类型 说明
|
||||
code number 0 表示成功,非 0 为业务失败
|
||||
message string 提示文案
|
||||
data object 业务数据,失败时可能为空对象
|
||||
成功示例:
|
||||
|
||||
json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {}
|
||||
}
|
||||
失败示例:
|
||||
|
||||
json
|
||||
{
|
||||
"code": 1,
|
||||
"message": "钱包积分不足",
|
||||
"data": {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
名词
|
||||
名称 说明
|
||||
platform_order_no 您的业务单号(幂等键,请勿重复用于不同订单)
|
||||
order_no 平台生成的内部单号,查询详情或排查时使用
|
||||
product_code 商品编码,来自「商品列表」接口,创建订单时必填
|
||||
platform 开放 API 订单固定为 api
|
||||
notify_url 下单时传入的异步通知地址,订单状态变更时 POST 推送
|
||||
balance_points 商户钱包积分余额
|
||||
#对接流程
|
||||
code
|
||||
1. 拉取商品列表 → 记录 product_code
|
||||
2. 创建订单(扣积分)→ 获得 h5.recharge_url
|
||||
3. 将 H5 链接发给买家填写 UID / 完成绑定
|
||||
4. 轮询「查询订单」获取状态与玩家信息
|
||||
下单前请保证钱包积分充足;余额不足时不会创建订单。
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
鉴权
|
||||
每个请求在 Header 携带:
|
||||
|
||||
Header 说明
|
||||
X-App-Key App Key
|
||||
X-Timestamp Unix 秒级时间戳(字符串),与服务器相差不超过 5 分钟
|
||||
X-Sign 签名(见下)
|
||||
#签名算法
|
||||
code
|
||||
待签名字符串 = app_key + timestamp + raw_json_body
|
||||
sign = HMAC_SHA256(待签名字符串, app_secret) // 小写十六进制
|
||||
要点 说明
|
||||
raw_json_body 与 HTTP Body 完全一致 的原始字符串;签名后再原样发送
|
||||
是否排序 不需要对 JSON 字段排序;勿解析后再 json_encode 一次
|
||||
Header app_key、timestamp 只放在 Header,不参与 Body JSON
|
||||
中文 PHP 使用 JSON_UNESCAPED_UNICODE;Python 使用 ensure_ascii=False
|
||||
商户后台 开放 API → 调用调试 需先通过密码或动态口令验证,方可加载密钥并查看待签名字符串、签名结果与最终发包参数。
|
||||
|
||||
#PHP
|
||||
php
|
||||
<?php
|
||||
|
||||
$appKey = 'your_app_key';
|
||||
$appSecret = 'your_app_secret';
|
||||
$apiBase = 'http://skin-exchange.yiquyou.icu';
|
||||
|
||||
$payload = [
|
||||
'platform_order_no' => 'YOUR-ORDER-001',
|
||||
'product_code' => '10000001',
|
||||
'platform_buy_num' => 1,
|
||||
];
|
||||
|
||||
// 生成 body 后不要再改,签名与 curl 必须用同一字符串
|
||||
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
$timestamp = (string) time();
|
||||
$preSign = $appKey . $timestamp . $body;
|
||||
$sign = hash_hmac('sha256', $preSign, $appSecret);
|
||||
|
||||
$ch = curl_init($apiBase . '/api/open/v1/orders/store');
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'X-App-Key: ' . $appKey,
|
||||
'X-Timestamp: ' . $timestamp,
|
||||
'X-Sign: ' . $sign,
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
#Java
|
||||
依赖 JDK 标准库(javax.crypto),无需第三方包。
|
||||
|
||||
java
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
|
||||
public class OpenApiSignExample {
|
||||
|
||||
private static String hmacSha256Hex(String secret, String message) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
||||
byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
|
||||
StringBuilder hex = new StringBuilder(hash.length * 2);
|
||||
for (byte b : hash) {
|
||||
hex.append(String.format("%02x", b));
|
||||
}
|
||||
return hex.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String appKey = "your_app_key";
|
||||
String appSecret = "your_app_secret";
|
||||
String apiBase = "http://skin-exchange.yiquyou.icu";
|
||||
|
||||
// 与最终请求体一致;可用 Gson/Jackson 生成,但不要签名后再格式化
|
||||
String body = "{\"platform_order_no\":\"YOUR-ORDER-001\",\"product_code\":\"10000001\",\"platform_buy_num\":1}";
|
||||
String timestamp = String.valueOf(Instant.now().getEpochSecond());
|
||||
String preSign = appKey + timestamp + body;
|
||||
String sign = hmacSha256Hex(appSecret, preSign);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiBase + "/api/open/v1/orders/store"))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("X-App-Key", appKey)
|
||||
.header("X-Timestamp", timestamp)
|
||||
.header("X-Sign", sign)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = HttpClient.newHttpClient()
|
||||
.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.statusCode());
|
||||
System.out.println(response.body());
|
||||
}
|
||||
}
|
||||
#Python
|
||||
Python 3,hmac + hashlib 标准库。
|
||||
|
||||
python
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
APP_KEY = "your_app_key"
|
||||
APP_SECRET = "your_app_secret"
|
||||
API_BASE = "http://skin-exchange.yiquyou.icu"
|
||||
|
||||
payload = {
|
||||
"platform_order_no": "YOUR-ORDER-001",
|
||||
"product_code": "10000001",
|
||||
"platform_buy_num": 1,
|
||||
}
|
||||
|
||||
# separators 生成紧凑 JSON,与常见 PHP json_encode 一致;签名后勿再 dumps
|
||||
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
|
||||
timestamp = str(int(time.time()))
|
||||
pre_sign = APP_KEY + timestamp + body
|
||||
sign = hmac.new(
|
||||
APP_SECRET.encode("utf-8"),
|
||||
pre_sign.encode("utf-8"),
|
||||
hashlib.sha256,
|
||||
).hexdigest()
|
||||
|
||||
req = urllib.request.Request(
|
||||
API_BASE + "/api/open/v1/orders/store",
|
||||
data=body.encode("utf-8"),
|
||||
method="POST",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-App-Key": APP_KEY,
|
||||
"X-Timestamp": timestamp,
|
||||
"X-Sign": sign,
|
||||
},
|
||||
)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
print(resp.status)
|
||||
print(resp.read().decode())
|
||||
使用 requests 时:
|
||||
|
||||
python
|
||||
import requests
|
||||
|
||||
resp = requests.post(
|
||||
API_BASE + "/api/open/v1/orders/store",
|
||||
data=body.encode("utf-8"), # 传 bytes,避免库再次序列化
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-App-Key": APP_KEY,
|
||||
"X-Timestamp": timestamp,
|
||||
"X-Sign": sign,
|
||||
},
|
||||
timeout=30,
|
||||
)
|
||||
print(resp.status_code, resp.text)
|
||||
#Go
|
||||
go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func sign(appKey, appSecret, body string) (timestamp, sign string) {
|
||||
timestamp = fmt.Sprintf("%d", time.Now().Unix())
|
||||
mac := hmac.New(sha256.New, []byte(appSecret))
|
||||
mac.Write([]byte(appKey + timestamp + body))
|
||||
sign = hex.EncodeToString(mac.Sum(nil))
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
appKey := "your_app_key"
|
||||
appSecret := "your_app_secret"
|
||||
apiBase := "http://skin-exchange.yiquyou.icu"
|
||||
|
||||
// 建议用 struct + json.Marshal;保证签名用的 body 与 Post 发送的字节一致
|
||||
type orderReq struct {
|
||||
PlatformOrderNo string `json:"platform_order_no"`
|
||||
ProductCode string `json:"product_code"`
|
||||
PlatformBuyNum int `json:"platform_buy_num"`
|
||||
}
|
||||
bodyBytes, _ := json.Marshal(orderReq{
|
||||
PlatformOrderNo: "YOUR-ORDER-001",
|
||||
ProductCode: "10000001",
|
||||
PlatformBuyNum: 1,
|
||||
})
|
||||
body := string(bodyBytes)
|
||||
|
||||
timestamp, sig := sign(appKey, appSecret, body)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPost, apiBase+"/api/open/v1/orders/store", bytes.NewReader(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-App-Key", appKey)
|
||||
req.Header.Set("X-Timestamp", timestamp)
|
||||
req.Header.Set("X-Sign", sig)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
out, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(resp.Status, string(out))
|
||||
}
|
||||
#常见错误
|
||||
现象 原因
|
||||
签名校验失败 Body 与签名时不一致(多空格、字段顺序、Unicode 转义不同)
|
||||
timestamp 无效或已过期 服务器时间差超过 5 分钟
|
||||
缺少 app_key、timestamp 或 sign Header 名称或大小写错误(应为 X-App-Key 等)
|
||||
排查:用商户后台 调用调试 对比「待签名字符串」与己方代码输出是否一致。
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
商品列表
|
||||
拉取当前商户已授权且可售商品,默认仅 on_sale。下单时使用 data.list[].product_code(供货商品编码)。
|
||||
|
||||
#接口地址
|
||||
code
|
||||
POST /api/open/v1/products/index
|
||||
#请求方式
|
||||
POST
|
||||
|
||||
#请求参数
|
||||
Body 为 JSON 对象:
|
||||
|
||||
参数名 类型 必填 说明
|
||||
page number 否 页码,默认 1
|
||||
per_page number 否 每页条数,默认 20,最大 100
|
||||
filters object 否 筛选条件
|
||||
filters.status string 否 on_sale(默认)/ off_sale / draft / all
|
||||
filters.supply_product_name string 否 供货商品名称,模糊搜索
|
||||
#响应参数
|
||||
data 字段说明:
|
||||
|
||||
参数名 类型 说明
|
||||
data.list array 商品列表
|
||||
data.list[].product_code string 商品编码(下单必填,见创建订单)
|
||||
data.list[].name string 商品名称
|
||||
data.list[].channel string 供货渠道
|
||||
data.list[].external_product_id string | null 外部商品 ID
|
||||
data.list[].image_url string 商品图 URL,无图时为空字符串
|
||||
data.list[].status string 商户商品在售状态:on_sale / off_sale / draft
|
||||
data.list[].supply_status string 平台供货商品状态:on_sale / off_sale / draft
|
||||
data.list[].supply_price_points number 供货价(积分)
|
||||
data.list[].fee_points number 手续费(积分)
|
||||
data.list[].unit_cost_points number 单笔扣费积分 = 供货价 + 手续费
|
||||
data.list[].max_order_quantity number 单笔最大购买数量(platform_buy_num 上限)
|
||||
data.list[].sale_price_points number | null 商户自定售价(积分),可空
|
||||
data.total number 总条数
|
||||
data.page number 当前页码
|
||||
data.per_page number 每页条数
|
||||
下单扣费:unit_cost_points × platform_buy_num(platform_buy_num 见创建订单接口,且不得超过 max_order_quantity)。
|
||||
|
||||
#响应示例
|
||||
json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"product_code": "10000300",
|
||||
"name": "和平精英点券",
|
||||
"channel": "lewan",
|
||||
"external_product_id": "EXT-001",
|
||||
"image_url": "https://example.com/p.png",
|
||||
"status": "on_sale",
|
||||
"supply_status": "on_sale",
|
||||
"supply_price_points": 3000,
|
||||
"fee_points": 800,
|
||||
"unit_cost_points": 3800,
|
||||
"max_order_quantity": 99,
|
||||
"sale_price_points": null
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"per_page": 20
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
创建订单
|
||||
按 product_code(商品编码)下单并扣减钱包积分。相同 platform_order_no 重复请求返回原订单(幂等)。
|
||||
|
||||
#接口地址
|
||||
code
|
||||
POST /api/open/v1/orders/store
|
||||
#请求方式
|
||||
POST
|
||||
|
||||
#请求参数
|
||||
Body 为 JSON 对象:
|
||||
|
||||
参数名 类型 必填 说明
|
||||
platform_order_no string 是 您的业务单号(幂等键,最长 64 字符)
|
||||
product_code string 是 商品列表返回的 product_code
|
||||
platform_buy_num number 否 购买数量,默认 1;不得超过商品列表返回的 max_order_quantity
|
||||
platform_amount number 否 平台侧记录用金额,默认 0
|
||||
player_account string 否 玩家 UID;不传则由买家在 H5 填写
|
||||
player_game_region string 否 区服-系统(如 安卓 / 苹果)
|
||||
player_game_srv string 否 区服-渠道(如 微信 / QQ)
|
||||
player_game_role string 否 角色名
|
||||
submit_player boolean 否 true 且已传 player_account 时,直接提交玩家信息(状态 17);否则待买家在 H5 操作(状态 15)
|
||||
notify_url string 否 订单状态变更时的异步通知地址(http/https,最长 500 字符),见 订单异步通知
|
||||
#响应参数
|
||||
data.order 字段说明:
|
||||
|
||||
参数名 类型 说明
|
||||
data.order.order_no string 平台内部单号
|
||||
data.order.platform_order_no string 您的业务单号
|
||||
data.order.platform string 固定为 api
|
||||
data.order.product_name string 商品名称
|
||||
data.order.recharge_status number 充值状态码(见下表)
|
||||
data.order.recharge_status_label string 充值状态中文
|
||||
data.order.status number 同 recharge_status
|
||||
data.order.status_label string 同 recharge_status_label
|
||||
data.order.points_charged number 本单扣除积分
|
||||
data.order.player_account string 玩家 UID
|
||||
data.order.created_at string 创建时间 YYYY-MM-DD HH:mm:ss
|
||||
data.order.platform_buy_num number 购买数量
|
||||
data.order.player_game_region string 区服-系统
|
||||
data.order.player_game_srv string 区服-渠道
|
||||
data.order.player_game_role string 角色名
|
||||
data.order.recharge_bind_required number 是否需绑定 0 / 1
|
||||
data.order.recharge_bind_url string | null 绑定链接(发货中且需绑定时可能有值)
|
||||
data.order.recharge_bind_at string | null 绑定链接生成时间
|
||||
data.order.recharge_submit_at string | null 提交充值时间
|
||||
data.order.recharge_finish_at string | null 完成时间
|
||||
data.order.recharge_result_message string | null 充值结果说明
|
||||
data.order.updated_at string 更新时间
|
||||
data.order.h5.entry_url string H5 兑换首页(买家手动输入单号,一般少用)
|
||||
data.order.h5.recharge_url string 推荐:发给买家的充值/绑定链接
|
||||
#状态码
|
||||
值 含义
|
||||
15 待买家在 H5 提交信息
|
||||
17 买家已提交,处理中
|
||||
20 发货中
|
||||
30 成功
|
||||
40 失败
|
||||
50 需人工处理
|
||||
60 已取消
|
||||
开放 API 订单不含店铺券码/核销字段(platform_coupon_*、is_verifi 等)。
|
||||
|
||||
#响应示例
|
||||
待买家在 H5 填写 UID(recharge_status = 15):
|
||||
|
||||
json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "下单成功",
|
||||
"data": {
|
||||
"order": {
|
||||
"order_no": "202601011200001234",
|
||||
"platform_order_no": "YOUR-ORDER-001",
|
||||
"platform": "api",
|
||||
"product_name": "和平精英点券",
|
||||
"recharge_status": 15,
|
||||
"recharge_status_label": "待绑定",
|
||||
"status": 15,
|
||||
"status_label": "待绑定",
|
||||
"points_charged": 3800,
|
||||
"player_account": "",
|
||||
"created_at": "2026-01-01 12:00:00",
|
||||
"product_code": "10000001",
|
||||
"platform_buy_num": 1,
|
||||
"player_game_region": "",
|
||||
"player_game_srv": "",
|
||||
"player_game_role": "",
|
||||
"recharge_bind_required": 0,
|
||||
"recharge_bind_url": null,
|
||||
"recharge_bind_at": null,
|
||||
"recharge_submit_at": null,
|
||||
"recharge_finish_at": null,
|
||||
"recharge_result_message": null,
|
||||
"updated_at": "2026-01-01 12:00:00",
|
||||
"h5": {
|
||||
"entry_url": "http://skin-exchange.yiquyou.icu/h5/exchange",
|
||||
"recharge_url": "http://skin-exchange.yiquyou.icu/h5/bind?code=..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
已传 UID 且 submit_player: true(recharge_status = 17)时,recharge_url 可能指向绑定页或处理页,字段结构相同。
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
查询订单
|
||||
按平台内部单号或您的业务单号查询订单详情,响应结构与「创建订单」的 data.order 一致。
|
||||
|
||||
#接口地址
|
||||
code
|
||||
POST /api/open/v1/orders/show
|
||||
#请求方式
|
||||
POST
|
||||
|
||||
#请求参数
|
||||
Body 为 JSON 对象,二选一(不可同时为空):
|
||||
|
||||
参数名 类型 必填 说明
|
||||
platform_order_no string 条件 您的业务单号(开放 API 订单)
|
||||
order_no string 条件 平台内部单号
|
||||
#响应参数
|
||||
与 创建订单 的 data.order 字段相同,主要包括:
|
||||
|
||||
参数名 类型 说明
|
||||
data.order.order_no string 平台内部单号
|
||||
data.order.platform_order_no string 您的业务单号
|
||||
data.order.platform string 固定为 api
|
||||
data.order.product_name string 商品名称
|
||||
data.order.recharge_status number 充值状态码
|
||||
data.order.recharge_status_label string 充值状态中文
|
||||
data.order.points_charged number 扣除积分
|
||||
data.order.player_account string 玩家 UID
|
||||
data.order.recharge_submit_at string | null 提交时间
|
||||
data.order.recharge_finish_at string | null 完成时间
|
||||
data.order.recharge_result_message string | null 结果说明
|
||||
data.order.h5.entry_url string H5 兑换首页
|
||||
data.order.h5.recharge_url string 充值/绑定 H5 链接
|
||||
#状态码
|
||||
值 含义
|
||||
15 待买家在 H5 提交信息
|
||||
17 买家已提交,处理中
|
||||
20 发货中
|
||||
30 成功
|
||||
40 失败
|
||||
50 需人工处理
|
||||
60 已取消
|
||||
#响应示例
|
||||
json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"order": {
|
||||
"order_no": "202601011200001234",
|
||||
"platform_order_no": "YOUR-ORDER-001",
|
||||
"platform": "api",
|
||||
"product_name": "和平精英点券",
|
||||
"recharge_status": 30,
|
||||
"recharge_status_label": "充值成功",
|
||||
"status": 30,
|
||||
"status_label": "充值成功",
|
||||
"points_charged": 3800,
|
||||
"player_account": "1234567890",
|
||||
"created_at": "2026-01-01 12:00:00",
|
||||
"product_code": "10000001",
|
||||
"platform_buy_num": 1,
|
||||
"player_game_region": "安卓",
|
||||
"player_game_srv": "微信",
|
||||
"player_game_role": "测试角色",
|
||||
"recharge_bind_at": null,
|
||||
"recharge_submit_at": "2026-01-01 12:01:00",
|
||||
"recharge_finish_at": "2026-01-01 12:05:00",
|
||||
"recharge_result_message": null,
|
||||
"updated_at": "2026-01-01 12:05:00",
|
||||
"h5": {
|
||||
"entry_url": "http://skin-exchange.yiquyou.icu/h5/exchange",
|
||||
"recharge_url": "http://skin-exchange.yiquyou.icu/h5/bind?code=..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
订单不存在时:
|
||||
|
||||
json
|
||||
{
|
||||
"code": 1,
|
||||
"message": "订单不存在",
|
||||
"data": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
余额查询
|
||||
查询当前商户钱包可用积分余额。
|
||||
|
||||
#接口地址
|
||||
code
|
||||
POST /api/open/v1/wallet/balance
|
||||
#请求方式
|
||||
POST
|
||||
|
||||
#请求参数
|
||||
Body 可为空对象 {},仍需按 鉴权与签名 规则携带 Header 并签名。
|
||||
|
||||
#响应参数
|
||||
参数名 类型 说明
|
||||
data.balance_points number 当前可用积分余额
|
||||
#响应示例
|
||||
json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"balance_points": 12345
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
订单异步通知
|
||||
创建订单时可传入 notify_url。当该订单充值状态发生变化时,平台将向该地址 POST JSON 通知(队列异步发送,失败自动重试)。
|
||||
|
||||
#触发时机
|
||||
下单成功创建订单后(首次状态,如 15 待绑定)
|
||||
之后每次 recharge_status 变更(如 17 已提交、30 成功、40 失败等)
|
||||
仅 开放 API 订单(platform=api)且下单时传了有效 notify_url 才会推送。
|
||||
|
||||
#商户接收要求
|
||||
项目 说明
|
||||
方法 POST
|
||||
Content-Type application/json
|
||||
成功响应 HTTP 2xx(建议返回 {"code":0} 或纯文本 OK)
|
||||
超时 平台默认 10 秒,超时或 5xx 将按队列重试(默认最多 5 次)
|
||||
#签名
|
||||
Header:
|
||||
|
||||
Header 说明
|
||||
X-App-Key 您的 App Key
|
||||
X-Timestamp Unix 秒级时间戳
|
||||
X-Sign HMAC_SHA256(app_key + timestamp + raw_json_body, app_secret) 小写 hex
|
||||
验签时使用与请求开放 API 完全相同的原始 Body 字符串,勿重新格式化 JSON。
|
||||
|
||||
#通知 Body 结构
|
||||
json
|
||||
{
|
||||
"event": "order.status_changed",
|
||||
"order": {
|
||||
"order_no": "202601011200001234",
|
||||
"platform_order_no": "YOUR-ORDER-001",
|
||||
"platform": "api",
|
||||
"product_code": "10000001",
|
||||
"product_name": "和平精英点券",
|
||||
"recharge_status": 30,
|
||||
"recharge_status_label": "充值成功",
|
||||
"status": 30,
|
||||
"status_label": "充值成功",
|
||||
"points_charged": 3800,
|
||||
"player_account": "123456789",
|
||||
"notify_url": "https://your-server.com/open-api/order-notify",
|
||||
"recharge_finish_at": "2026-01-01 12:30:00",
|
||||
"updated_at": "2026-01-01 12:30:00"
|
||||
}
|
||||
}
|
||||
order 字段与「查询订单」接口结构基本一致(通知中不含 h5 链接;含 product_code)。
|
||||
|
||||
#PHP 验签示例
|
||||
php
|
||||
$rawBody = file_get_contents('php://input');
|
||||
$appKey = $_SERVER['HTTP_X_APP_KEY'] ?? '';
|
||||
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
|
||||
$sign = $_SERVER['HTTP_X_SIGN'] ?? '';
|
||||
$appSecret = 'your_app_secret';
|
||||
|
||||
$expected = hash_hmac('sha256', $appKey . $timestamp . $rawBody, $appSecret);
|
||||
if (!hash_equals($expected, $sign)) {
|
||||
http_response_code(401);
|
||||
exit('invalid sign');
|
||||
}
|
||||
|
||||
$data = json_decode($rawBody, true);
|
||||
// 处理 $data['order'] ...
|
||||
|
||||
http_response_code(200);
|
||||
echo json_encode(['code' => 0]);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
常见错误
|
||||
情况 说明
|
||||
code 非 0 业务失败(签名错误、商品不可售、余额不足等)
|
||||
余额不足 不写入订单,请充值积分后使用新单号重试
|
||||
重复 platform_order_no 返回已存在订单及当前状态,不会重复扣款
|
||||
+1
-1
@@ -253,7 +253,7 @@ npm run test:industry:http -- --baseUrl=https://ks.khhao.com --load --endpoint=q
|
||||
|
||||
```bash
|
||||
# .env 生产配置
|
||||
KUASHOU_INDUSTRY_APP_KEY=ks660621772091030245
|
||||
KUASHOU_INDUSTRY_APP_KEY=ks66xxx
|
||||
KUASHOU_INDUSTRY_SIGN_SECRET=OZR3AUW2...
|
||||
KUASHOU_INDUSTRY_ACCESS_TOKEN=ChFvYXV0aC5hY2Nlc3NUb2tlbh... # OAuth 获取
|
||||
KUASHOU_INDUSTRY_SEND_CALLBACK_ENABLED=true
|
||||
|
||||
Reference in New Issue
Block a user