简介
WingSteamExeSDK主要是为满足PC端Steam平台Windows的游戏需要,为此类游戏提供基于Steam平台的登录、支付、数据收集等通用功能。以下文档中会把WingSteamExeSDK简称为SDK。
1.cp接入流程
相关步骤指引:
1.第一步,安装和接受邀请,参考《第5.1章 成为steam应用测试者》
2.第二步,集成和接入,参考《第2章 集成》以及之后的章节进行接入
3.第三步,本地调试,参考《第5.2章 本地开发测试》和《第5.3章 日志调试与沙盒支付》
2.集成
2.1.SDK库选择
SDK提供了多个版本的库文件,可以根据项目需求使用。
序号 | 平台 | 是否MT编译 | lib库文件 | SDK下载 |
---|---|---|---|---|
1 | x86 | 否 | wingsdk.lib | 下载 |
2 | x64 | 否 | wingsdk64.lib | 下载 |
3 | x86 | 是 | wingsdk_mt.lib | 下载(推荐) |
4 | x64 | 是 | wingsdk64_mt.lib | 下载 |
MT编译版本:已经内置VC运行库内容,不依赖于外部VC运行库。
普通(MD)编译版本:该版本会使用附带的vcruntime140.dll,msvcp140.dll,vcruntime140_1.dll这几个文件。
对应的dll版本:
版本 | 包含文件 | 备注 |
---|---|---|
x86 | libcrypto-3-wing.dll libssl-3-wing.dll sdkencryptedappticket.dll steam_api.dll wingsdk.dll msvcp140.dll vcruntime140.dll |
|
x64 | ibcrypto-3-x64-wing.dll libssl-3-x64-wing.dll sdkencryptedappticket64.dll steam_api64.dll wingsdk64.dll msvcp140.dll vcruntime140.dll vcruntime140_1.dll |
|
x86_mt | libcrypto-3-wing-mt.dll libssl-3-wing-mt.dll sdkencryptedappticket.dll steam_api.dll wingsdk_mt.dll |
|
x64_mt | libcrypto-3-x64-wing-mt.dll libssl-3-x64-wing-mt.dll sdkencryptedappticket64.dll steam_api64.dll wingsdk64_mt.dll |
2.2.项目集成
以VisualStudio项目工程为例
1.下载最新版本SDK压缩包,并解压
2.SDK头文件引入
1)复制include文件夹及其内容到项目中,主要包含wing目录下的wing_api.h头文件
2)右键项目->属性->C/C++->常规->附加包含目录,填入include文件夹路径,例如include文件夹是复制到项目根目录下,则路径可以填写:$(ProjectDir)include
3.SDK库文件引入
1)复制lib文件夹及其内容到项目中,并在项目属性页->链接器->常规->附加库目录,中填入lib文件夹路径,例如lib文件夹是复制到项目根目录下,则路径可以填写:$(ProjectDir)lib
2)在项目属性页->链接器->输入->附加依赖项中填入sdk的lib文件,如果是x86编译,则填入wingsdk.lib,如果是x64编译,则填入wingsdk64.lib,如果使用mt版本,则选择对应带mt后缀lib文件。
4.SDK的Api使用
1)在项目中引入。以前面步骤的配置路径作为示例,那么引入的路径为 #include “wing/wing_api.h”
2)在项目中使用。wing_api.h的接口为命名空间+方法的使用方式,例如:sdk的初始化方法使用,WingProxy::Init(“appId”,“appKey”,[](int code,string msg,string data){});
5.项目编译后,需要把sdk用到的dll文件复制到编译后的exe程序目录中,否则项目无法正常运行。具体版本对应的dll文件,参考前面dll版本文件对应表。例如x86的dll内容如下图:
6.运行程序
2.3.三方库版本
三方库 | 版本 | 包含dll(只列举x86版本) | 功能 |
---|---|---|---|
OpenSSL | 3.1.3 | libcrypto-3-wing.dll libssl-3-wing.dll |
网络请求 |
SteamSDK | 1.5.8 | steam_api.dll sdkencryptedappticket.dll |
Steam平台SDK |
3.接口说明
3.1.使用方式
sdk的api采用命名空间+方法的形式定义的,WingProxy是所有sdk接口的命名空间,以支付是否可用方法为例,使用方式:
1 2 3 |
bool isPayAvailable = WingProxy::IsPayAvailable(); |
3.2.回调函数
sdk接口的参数中,除了一些常见类型参数,在wing_api.h中还声明了WingCallback回调函数,参数说明如下:
参数 | 类型 | 说明 | |
---|---|---|---|
code | int | 状态码,具体取值参考《状态码说明》章节 | |
msg | std::string | 状态码说明 | |
data | std::string | Json字符串,里面包含该接口会返回的字段,例如登录接口登录成功后返回的data内容:{“msg”:”token refreshed”,”code”:200,”isFirstLogin”:0,”userId”:7822920,”token”:”30_o1hejvBB22LKMu”} |
4.SDK接口
4.1.初始化
调用下面接口对SDK进行初始化,建议在程序开始时首先调用,确保后续其他sdk接口可以正常使用
1 2 3 |
WingProxy::Init(string appId, string appKey, WingCallback callback); |
参数说明:
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
appId | std::string | Y | appId,具体值从运营处获取 |
appKey | std::string | Y | appKey,具体值从运营处获取 |
WingCallback | WingCallback | Y | 回调函数 |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
std::string appId = "9910e80...80429f5"; // 从运营处获取 std::string appKey = "oHg7c7pW....N8YTWJN"; // 从运营处获取 WingProxy::Init(appId, appKey, [](int code, std::string msg, std::string data) { if (code == 200) { MessageBoxA(NULL, "初始化成功.", "提示", MB_OK | MB_ICONINFORMATION); } else { MessageBoxA(NULL, ("初始化失败.错误" + msg).c_str(), "提示", MB_OK | MB_ICONINFORMATION); } }); |
4.2.退出程序
调用下面接口对SDK内资源进行释放,必须在程序退出前调用。
1 2 3 |
WingProxy::Exit(); |
4.3.登录
使用下面接口进行登录
1 2 3 |
WingProxy::Login(WingCallback callback); |
参数说明:
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
WingCallback | Y | 回调函数 |
WingCallback的data内容字段说明:
字段名 | 类型 | 必填 | 说明 |
---|---|---|---|
userId | long | Y | 用户Id |
token | string | Y | 在线token |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
WingProxy::Login([](int code, std::string msg, std::string data) { if (code == 200) { json jsonData = json::parse(data); long userId = jsonData["userId"]; std::string token = jsonData["token"]; std::string tips = "登录成功."; tips.append("\nUserId:").append(std::to_string(userId)) .append("\nToken:").append(token); MessageBoxA(NULL, tips.c_str(), "提示", MB_OK); } else { MessageBoxA(NULL, ("登录失败.错误" + msg).c_str(), "提示", MB_OK); } }); |
4.4.退出登录
调用下面接口退出账号
1 2 |
WingProxy::Logout(); |
4.5.检查支付是否可用
进行支付之前,需要先判断支付是否可用
1 2 |
bool isPayAvailable = WingProxy::IsPayAvailable(); |
返回值为bool类型,true 表示支付可用,false表示支付不可用。
4.6.购买商品
进行支付之前,需要先判断支付是否可用
1 2 |
WingProxy::CallPay(std::string productId, std::string extInfo, WingCallback callback); |
参数说明:
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
productId | std::string | Y | WINGSDK平台的商品ID |
extInfo | std::string | Y | CP 扩展信息字段,限长512(JSON格式),WING服务器到CP服务器发货通知时原样返回给CP。如果CP的通知发货地址是动态变化的(比如每个服务区的地址都不一致),可以通过此字段设置:参数格式为标准JSON,参数名为 deliverUrl,参考格式{ “deliverUrl”:” http://game.com/deliver.do”, “otherInfo”:”otherInfo”,“merId”:””}merId字段(选填),收款商户ID,使用场景:同一个支付渠道下有多个不同的收款验证信息(或收款帐号)。无内容建议传入空字符串“”。 |
WingCallback | Y | 回调函数 |
WingCallback的data内容字段说明:
字段名 | 类型 | 必填 | 说明 |
---|---|---|---|
orderId | std::string | Y | 订单ID |
productId | std::string | Y | WINGSDK平台的商品ID |
currency | std::string | Y | 基准货币 |
amount | long long | Y | 基准货币价格 |
quantity | int | Y | 购买数量,目前一般都是1 |
extInfo | std::string | N | 扩展信息字段,内容与该方法传入的extInfo相同 |
status | int | Y | 1表示支付成功,其他表示支付失败 |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
bool isPayAvailable = WingProxy::IsPayAvailable(); if (!isPayAvailable) { // 支付前需要判断支付是否可用 MessageBoxA(NULL, "支付不可用,请稍后重试", "提示", MB_OK); return; } std::string strProductId = "game001"; std::string strExtInfo = "MyInfo|10086"; WingProxy::CallPay(strProductId, strExtInfo, [](int code, std::string msg, std::string data) { if (code != 200) { MessageBoxA(NULL, ("支付失败. 错误:" + msg).c_str(), "提示", MB_OK); } else { json jsonData = json::parse(data); std::string orderId = jsonData["orderId"]; std::string productId = jsonData["productId"]; std::string currency = jsonData["currency"]; long long amount = jsonData["amount"]; int quantity = jsonData["quantity"]; std::string extInfo = jsonData["extInfo"]; int status = jsonData["status"]; std::string tips = ""; tips.append("\n订单ID:").append(orderId) .append("\n商品ID:").append(productId) .append("\n基准币种:").append(currency) .append("\n基准货币价格:").append(std::to_string(amount)) .append("\n购买数量:").append(std::to_string(quantity)) .append("\n透传信息:").append(extInfo) .append("\n订单状态:").append(std::to_string(status)) .append("\n\n") .append(status == 1 ? "支付成功" : "支付失败"); MessageBoxA(NULL, tips.c_str(), "提示", MB_OK); } }); |
4.7.游戏必要参数设置
WINGSDK包括serverId等游戏参数,这些参数主要用于数据跟踪和统计。
该部分参数必须严格按照文档进行设置,在后续的接口中会使用到这些参数,没有按照要求配置会导致部分接口调用失败。
参数设置时机除了按照说明设置,另外需要特别注意在游戏启动时的设置时机,具体参考 流程要求
4.7.1 设置服务器ID
当用户的服务器ID发生改变时,需要调用设置服务器ID接口设置新的服务器ID,例如每次进入服务
1 2 |
WingProxy::SetServerId(string serverId); |
注意:设置服务器id的操作在每次选服后都需要进行,必须在调用其他事件前设置
4.7.2.设置GameUserId
当游戏角色ID发生改变时,需要调用设置接口设置新的gameUserId,例如成功登录账号后、切换账号成功后
1 2 |
WingProxy::SetGameUserId(string gameUserId); |
注意:
1.必须在调用其他事件前设置。
2.游戏角色ID是游戏角色在游戏中的ID,不是WINGSDK自身的UserId。
3.若未创角前拿不到游戏角色ID,可以先设置-1,创角后再设置一次正确的角色ID
4.7.3.设置用户等级Level
当用户角色等级发生改变时,需要调用设置等级接口设置新的等级,例如开始进入游戏、等级提升等。
1 2 |
WingProxy::SetLevel(int level); |
注意:第一次进服获取玩家等级或玩家等级变更后,需要及时调用这个接口设置玩家等级,必须在调用其他事件前设置。
4.7.4.设置玩家游戏昵称
设置游戏玩家的昵称,调用接口:
1 2 |
WingProxy::SetNickName(string nickName); |
注意:
1.当玩家登录、登出游戏,或修改昵称时,需要及时调用这个接口设置玩家昵称。
2.调用该接口设置昵称后,玩家进行购买时会自动记录昵称到订单信息中。
4.8.数据收集
4.8.1.流程要求
WINGSDK数据收集使用在游戏的过程中打点的方式,如图所示:
注意:
1.以上流程图中涉及到的几个接口是有时序要求的,请参考流程图中的逻辑步骤进行设置:WingProxy::SetServerId()、WingProxy::SetGameUserId()、WingProxy::SetLevel()、WingProxy::SetNickName()、ghw_user_import事件、ghw_user_create事件。
2.其它的事件如ghw_level_achieved、ghw_self_tutorial_completed等请根据对应业务逻辑,在对应业务发生时调用接口发送。
序号 | 事件(接口)名称 | 事件描述 | 事件作用 | 建议触发点 | 备注 |
---|---|---|---|---|---|
1 | SetServerId | 设置服务器ID | 标记玩家当前所在的服务器,后台根据该字段统计每个服务器的数据 | 登录游戏服成功后 | |
2 | SetGameUserId | 设置玩家角色ID | 标记玩家当前的游戏角色ID,后台根据该字段统计玩家的数据 | 登录游戏服成功后 | |
3 | SetLevel | 设置玩家当前等级 | 标记玩家当前的游戏角色等级 | 玩家等级发生变更后,如登录游戏服成功后、玩家完成升级后 | |
4 | SetNickName | 设置玩家昵称 | 标记玩家当前的游戏昵称 | 玩家设置昵称后 | |
5 | ghw_user_import | 玩家登录游戏服 | 记录玩家登录游戏服的动作,后台根据该事件统计导入数、登录数、导入留存等数据 | 玩家登录游戏服成功后 | 需要先调用SetServerId、SetGameUserId、SetLevel接口 |
6 | ghw_user_create | 玩家创建角色 | 记录玩家创建角色的动作,后台根据该事件统计创角数 | 玩家创建角色成功后 | 需要先调用SetServerId、SetGameUserId、SetLevel接口 |
7 | ghw_user_info_update | 更新用户信息 | 更新用户信 | 玩家信息更新时 | 需要先调用SetServerId、SetGameUserId、SetNickname接口 |
8 | ghw_level_achieved | 更新玩家等级 | 更新玩家等级,后台根据此字段更新玩家等级 | 玩家达到新的等级时 | 需要先调用setLevel接口更新玩家等级 |
9 | ghw_self_lv_x | 更新关键等级 | 更新关键等级 | 关键等级到达时 | 属于自定义事件 |
10 | ghw_self_tutorial_completed | 完成新手任务 | 完成新手任务 | 完成新手任务时 | 属于自定义事件 |
4.8.2.发送事件
使用下面接口发送事件
1 2 |
WingProxy::PostEvent(const string& eventName, const string& params = "", double eventValue = numeric_limits<double>::quiet_NaN()) |
参数说明:
参数 | 类型 | 必填 | 说明 |
---|---|---|---|
eventName | const std::string& | Y | 事件名称 |
params | const std::string& | N | 事件参数,可以不传,默认空。以json字符串的形式传递。例如需要设置isFirstEnter为1,则应该传递内容为:{“isFirstEnter”:1}。 |
eventValue | double | N | 事件价值,可以不传,默认无。 |
代码示例:
1 2 3 4 5 |
std::string strEventName = "ghw_user_import"; std::string strEventParams = "{\"isFirstEnter\":1}"; double value = 9.9; WingProxy::PostEvent(strEventName, strEventParams,value); |
4.8.3.预定义事件
4.8.3.1.ghw_user_import导入用户事件(进服)
说明:导入用户事件,玩家第一次进某个服时调用
参数名 | 类型 | 说明 | 必填 | 备注 |
---|---|---|---|---|
isFirstEnter | int | 是否第一次进服 | Y | 0->否;1->是;默认为0 |
注意:发送ghw_user_import事件前需调用设置服务器ID接口更新服务器id、设置gameUserId接口更新游戏用户id
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* ghw_user_import 导入用户事件 事件描述 : 玩家登录游戏服 事件作用 : 记录玩家登录游戏服的动作,后台根据该事件统计导入数、登录数、导入留存等数据 建议触发点 : 玩家登录游戏服成功后 调用前提 : 需要先调用SetServerId、SetGameUserId接口、SetLevel接口 必填字段 : isFirstEnter 类型int 是否第一次进服 0→否 1→是; 默认为0 */ WingProxy::SetServerId(服务器ID); WingProxy::SetGameUserId(游戏用户ID); WingProxy::SetLevel(用户等级); //未创角时可以传0 std::string strEventName = "ghw_user_import"; std::string strEventParams = "{\"isFirstEnter\":1}"; WingProxy::PostEvent(strEventName, strEventParams); |
4.8.3.2.ghw_user_create 创建角色
说明:创建游戏角色,游戏角色创建时调用
参数名 | 类型 | 说明 | 必填 | 备注 |
---|---|---|---|---|
nickname | string | 角色名(昵称) | Y | |
registerTime | long long | 创建时间 | Y | 注册时间戳,13位数,单位为毫秒(1970以后) |
gender | number | 角色性别 | N | 0 女,1 男,2 未知 |
roleType | string | 角色类型 | N | |
vip | number | 等级 | N | |
bindGameGold | number | 绑定钻石 | N | |
gameGold | number | 用户钻石数 | N | |
fighting | number | 战斗力 | N | |
status | number | 状态 | N | 状态标识,-1: 锁定,1:未锁定 |
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* ghw_user_create 创角事件 事件描述 : 玩家创建角色 事件作用 : 记录玩家创建角色的动作,后台根据该事件统计创角数 建议触发点 : 玩家创建角色成功后 调用前提 : 需要先调用SetServerId、SetGameUserId、SetLevel接口 必填字段 : nickname 昵称 registerTime 注册时间戳 单位为毫秒(1970以后) 可选字段 : roleType、gender、vip、bindGameGold、gameGold、fighting、status 具体参考博客 */ WingProxy::SetServerId(服务器ID); WingProxy::SetGameUserId(游戏用户ID); WingProxy::SetLevel(等级); std::string strEventName = "ghw_user_create"; //只有必传参数 std::string strEventParams = "{\"nickname\":\"Gooood\",\"registerTime\":1234567890123}"; //包含非必传参数 //std::string strEventParams = "{\"nickname\":\"Gooood\",\"registerTime\":1234567890123,\"gender\":1,\"roleType\":\"战士\",\"vip\":11,\"bindGameGold\":25467,\"gameGold\":98797878,\"fighting\":515747966,\"status\":1}"; WingProxy::PostEvent(strEventName, strEventParams); |
4.8.3.3.ghw_user_info_update 更新用户信息
参数名 | 类型 | 说明 | 必填 | 备注 |
---|---|---|---|---|
roleType | String | 角色类型 | N | |
nickname | String | 角色名称 | Y | 无昵称时,可填写空字符串 |
vip | int | 等级 | N | |
status | int | 状态 | N | 状态标识,-1:锁定,1:未锁定 |
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* ghw_user_info_update 更新用户信息 事件描述 : 更新用户信息 事件作用 : 更新用户信 建议触发点 : 玩家信息更新时 调用前提 : 需要先调用SetServerId、SetGameUserId、SetNickname接口 必填字段 : nickname std::string 昵称 可选字段 : roleType std::string 角色类型 vip int 等级 status int 状态 状态标识,-1:锁定,1:未锁定 */ WingProxy::SetServerId(服务器ID); WingProxy::SetGameUserId(游戏用户ID); WingProxy::SetNickname(用户昵称); std::string strEventName = "ghw_user_info_update"; //只有必传参数 std::string strEventParams = "{\"nickname\":\"Gooood\"}"; //包含非必传参数 //std::string strEventParams = "{\"nickname\":\"Gooood\",\"roleType\":\"战士\",\"vip\":11,\"status\":1}"; WingProxy::PostEvent(strEventName, strEventParams); |
4.8.3.4.ghw_level_achieved 等级增长事件
说明:统计玩家等级增长事件,达到等级时调用。
参数名 | 类型 | 说明 | 必填 | 备注 |
---|---|---|---|---|
score | number | 账户分数 | N | |
fighting | number | 战斗力 | N |
注意:发送ghw_level_achieved事件前需调用设置用户等级level接口更新用户等级信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* ghw_level_achieved 更新玩家等级 事件描述 : 更新玩家等级 事件作用 : 更新玩家等级,后台根据此字段更新玩家等级 建议触发点 : 玩家达到新的等级时 调用前提 : 需要先调用SetLevel接口更新玩家等级 必填字段 : 可选字段 : score long long 账户分数 fighting long long 战斗力 */ WingProxy::SetLevel(等级); std::string strEventName = "ghw_level_achieved"; // 可选参数 // std::string strEventParams = "{\"score\":112344656,\"fighting\":12345656}"; WingProxy::PostEvent(strEventName); |
4.8.4.自定义事件
如果发送的事件不属于预定义事件范围,事件发送时会自动补上ghw_self_前缀,例如关键等级5的事件名称传入,lv_5,最终发送的事件名称为ghw_self_lv_5。
4.8.4.1.ghw_self_lv_x关键等级事件
代码示例:
1 2 3 |
// 关键等级事件,ghw_self_lv_x,关键等级为5时发送lv_5即可 WingProxy::PostEvent("lv_5"); |
4.6.4.2.ghw_self_tutorial_completed完成新手任务
1 2 3 |
// 完成新手任务事件,ghw_self_tutorial_completed,发送tutorial_completed即可 WingProxy::PostEvent("tutorial_completed"); |
5.本地开发调试
5.1.成为Steam应用测试者
在应用上线前,成为Steam应用测试者,可以以测试者的身份在steam客户端中拥有该游戏,并进行测试。
步骤:
1.研发提供steam账号邮箱给运营
2.运营在steam后台邀请该账号成为测试者
3.steam账号邮箱会接收到邀请邮件,需要点击接受邀请
4.运营在steam后台确认后,把该steam账号添加到对应游戏组,即可拥有该游戏权限
5.研发重新登录steam账号后查看是否已拥有目标游戏
5.2 本地开发测试
5.2.1 本地测试方式一
在应用未上线时,如果需要在本地进行开发测试,可以在本地exe的同级目录,增加一个steam_appid.txt,内容为steam应用的apppid(appid向运营获取),如steam的appid为3141480,则内容如下图:
完成steam_appid.txt添加后,运行steam客户端,此时就可以直接运行本地exe进行SDK相关api的接入测试了。
注意:开发完成后打包时,需要移除steam_appid.txt
5.2.2 本地测试方式二
cp打个exe包(此时可以不接sdk),然后让运营上传到steam后台,cp从steam上下载此游戏,然后通过游戏设置-管理-浏览本地文件,找到游戏下载路径。cp可以使用本地开发调试包进行替换,然后从steam上启动此游戏进行测试。
5.3 日志调试与沙盒支付
SDK的日志默认是关闭状态的,开启日志后会在程序目录下生成winglog.log日志文件,可以查看SDK的日志信息。
开启SDK调试日志:
1.SDK日志的开启需要提供设备的ClientId,在用户的隐藏目录AppData下找到SharedConf配置文件,以文本的形式查看文件可以得到ClientId值。
具体路径:C:\Users\用户名\AppData\Local\WingSteamExeSdk\SharedConf
文本内容参考:{“ClientId”:”5c56b3400000000000000e0a76c72″}
2.把ClientId的值提供给运营,运营在后台操作加入测试设备
3.重启已接入SDK的程序,进行SDK相关操作,即可在winlog.log查看SDK输出的相关日志信息。
沙盒支付:
同上面一样,加入测试设备后,重启已接入SDK的程序,即可进行沙盒支付进行支付相关测试
6.状态码说明
状态码 | 说明 |
---|---|
200 | 操作成功 |
-400 | 一般错误 |
-401 | SDK未初始化 |
-402 | Steam初始化失败 |
-403 | Steam登录授权超时 |
-404 | Steam登录授权失败 |
-405 | 未登录 |
-500 | 服务器异常 |
-501 | 服务器异常:json解析异常 |
400 | 失败 |
500 | 服务器内部故障 |
501 | 所请求接口或页面未实现 |
4010 | 无效appId: appId不存在或未开启 |
4011 | 无效osign:osign校验失败 |
4012 | 请求已过期:ots校验失败 |
4013 | 第三方平台验证失败 |
4017 | 用户不存在(没有找到) |
4019 | 无效orderId |
4020 | 订单验证失败 |
4023 | 未找到渠道信息 |
4025 | 汇率转换失败 |
4026 | 支付渠道已关闭 |
4029 | 登录渠道已关闭 |
5005 | 该设备不能支付或者不允许支付 |
5006 | 支付过程中出错 |