9 支付系统¶
Pico支付,是基于Pico账户体系进行的游戏币支付系统,结算方式以现行的Pico公司下的游戏货币单位为准(P币)。
9.1 准备工作¶
9.1.1 支付需要如下的应用参数¶
- APP ID : 开发者平台为应用生成的唯一标识
- APP KEY : 开发者平台为应用生成的Key
- APP Secret : 开发者平台为应用生成的用于支付的Key
- Developer ID : 开发者的ID,也叫商户ID
9.1.2 申请成为Pico开发者¶
Pico开发者平台网址 :https://developer.pico-interactive.com/
开发者在接入支付SDK时,需要在开发者平台创建应用并获取 应用参数 ,对应流程如下:
- 在 Pico 开发者平台页 点击「成为开发者」按钮。
- 在「账户」弹窗中选择您账户所在区域:中国大陆/其他地区 后,点击「注册」按钮。

- 验证您的账户
若您选择了「中国大陆」,您需要填写您的手机号并设置您的密码,通过短信验证您的手机号;
若您选择了「其他地区」,您需要填写您的邮箱并设置您的密码,并选择您账号所在国家/地区,通过验证邮件验证您的邮箱账号;
![]()
- 恭喜!您已经正式成为 Pico 开发者啦。
注意: Pico 开发者账户注册完全免费
9.1.3 获取应用参数¶
- 获取 Publisher ID
申请成为开发者后,进入开发者管理平台,点击「我的应用」,进入「应用」后点击「API」菜单,即可查看到开发者 ID,该 ID 将作为支付系统中商户的唯一标志。
- 创建应用,选择支付类型
开发者可以从管理中心进入到创建应用阶段,点击创建应用,然后进入相应平台完善应用的相关信息:

图9.1 完善应用的相关信息
注意: 应用类型和支付相关,审核通过上架后,无法更改。一个应用只能选择一种。
- 选择应用 。支付时使用 P 币数量直接支付。
- 选择游戏 。支付是商品码支付。商品码为游戏中付费道具在开发者后台的唯一标识。
游戏类应用如果存在道具内付的情况,我们要求开发者必须采用开发者后台增加商品码的方式进行统一管理。
商品码支付配置界面如下:
再选择“游戏内支付配置”,配置游戏的内购信息:

图9.2 游戏内购配置
注意商品码的定义方式:
- 定义规则。首位为字母,仅允许输入字母及数字,不超过20个字符
- 不可重复。不同道具间的商品码不能重复
- 类型支持。目前支持可消耗道具 和 不可消耗道具 。可消耗道具为可重复购买的商品,如金币、血瓶等;不可消耗道具为一次性购买产品,如武器、解锁关卡。
- 获取 APP ID,APP KEY,APP Secret 参数
成功创建应用后,开发者平台会对其分配字符串,包括 APP ID、APP KEY、APP Secret :

图9.3 APP ID、APP KEY、APP Secret
至此,Developer ID ,APP ID、APP KEY、APP Secret 都已获取到了。
9.1.4 OnlinePico Settings中配置应用参数¶
进入Edit->Project Settings…,展开Plugins子项下的OnlinePico Settings,勾选“Enable Payment Module”,然后根据实际情况选择Region:如果需要国内上线则选择China,海外上线选择NonChina,国内海外同时上线则选择Both。

图9.4 选择上线地域
最后将获取的商户ID(Developer ID)、APPID、APP KEY、APP secret填入以下位置:

图9.5 填入字符串
9.2 使用支付系统¶
图9.6 支付系统使用流程
- 需要确保首先调用登录接口(Pico Login SDK或Pico Payment Login)返回成功后,再调用支付(Pico Payment Pay with Coin、Pico Payment Pay with Pay Coin)、查询订单(Pico Payment Query)、获取用户信息(Pico Payment Get User Info、Pico SDKGet User Info)等接口。
- 登陆成功后,如需登出操作,则调用登出接口(Pico Logout SDK、Pico Payment Logout)。
- 以上接口在使用过程中,均需要绑定回调事件获取调用结果,具体各个接口的详细用法见9.3 接口功能说明。
9.3 接口功能说明¶
9.3.1 登录¶
Pico为开发者提供基于Oauth2.0模式的认证授权,故用户支付前需要先进行登录操作。注:登陆部分可以只登陆一次,之后直接使用支付即可,登陆过期时间约为两周,过期后支付接口回有返回码(登陆过期码),用户只需再次登陆即可。
这里使用我们提供的PicoPaymentLogin节点或者PicoLoginSDK节点:
Pico Login SDK¶
通过该蓝图接口可以进行登录,并且通过绑定回调事件获取登录结果。
蓝图

输入:
delegate类型:
Log in Delegate:绑定Login回调事件,如下图所示:
- 回调事件参数:
- bool类型:
- IsSucceed:
- true:成功
- false:失败
- IsSucceed:
- FString类型:
- Reason: 通过字符串输出登录失败原因,见表 9-1 Reason Code对照表
- bool类型:
- 回调事件参数:
输出: 无
返回值: 无
Pico Payment Login¶
通过该蓝图接口可以进行登录,并且通过绑定回调事件获取登录结果。
蓝图

输入:
delegate类型:
Log in Delegate:绑定Login回调事件,如下图所示:
- 回调事件参数:
- bool类型:
- IsSucceed:
- true:成功
- false:失败
- IsSucceed:
- FString类型:
- Reason: 通过字符串输出登录失败原因,见表 9-1 Reason Code对照表
- bool类型:
- 回调事件参数:
输出: 无
返回值: 无
9.3.2 登出¶
登出操作可以使用PicoLogoutSDK或者PicoPaymentLogout节点:
Pico Logout SDK¶
通过该蓝图接口可以进行登出,并且通过绑定回调事件获取登出结果。
蓝图

输入:
delegate类型:
Log in Delegate:绑定Logout回调事件,如下图所示:
- 回调事件参数:
- bool类型:
- IsSucceed:
- true:成功
- false:失败
- IsSucceed:
- FString类型:
- Reason: 通过字符串输出登录失败原因,见表 9-1 Reason Code对照表
- bool类型:
- 回调事件参数:
输出: 无
返回值: 无
Pico Payment Logout¶
通过该蓝图接口可以进行登出,并且通过绑定回调事件获取登出结果。
蓝图

输入:
delegate类型:
Log in Delegate:绑定Logout回调事件,如下图所示:
回调事件参数:
bool类型:
- IsSucceed:
- true:成功
- false:失败
- IsSucceed:
FString类型:
- Reason:通过字符串输出登录失败原因,见表 9-1 Reason Code对照表
输出: 无
返回值: 无
表 9-1 Reason Code对照表
错误信息 | 说明 | 原因 |
---|---|---|
TIME_ERROR | 时间戳错误 | 本地时间和服务器时间相差超过8分钟 |
SYSTEM_USER_NOT_FIND_ERROR | 用户未找到 | 用户中心token对应的用户未找到 |
SYSTEM_USER_TOEKN_NOT_FIND_ERROE | 用户token寻找失败 | 用户中心token未找到 |
SYSTEM_USER_TOKEN_CHECK_FAILURE_ERROR | 用户token验证失败 | 用户中心token失效 |
SYSTEM_USER_TOKEN_UNKNOWN_ERROR | 用户token验证失败 | 用户中心token检查失败 |
APP_CHECK_ERROR | 应用验证失败 | APP ID等参数填写错误 |
9.3.3 支付¶
Pico Payment Pay with Coin¶
通过该蓝图接口可以使用P币进行支付。
蓝图

输入:

FString类型:
- OrderNumber:商户自己定义的订单号,32个字符内、可包含字母和数字
- OrderTitle:订单标题
- ProductDetail:商品描述
- Notify Url:欲通知的URL(非必填),必须为直接可访问的url,不能携带参数
int类型:
- PicoCoinCount:花费P币数额
delegate类型:
Pay Order with Coin Delegate:绑定支付回调事件,如下图所示:
- 回调事件参数:
- FString类型:
- Code:见表9-2 支付Code与Message对应表
- Message:见表9-2 支付Code与Message对应表
- FString类型:
输出: 无
返回值: 无
Pico Payment Pay with Code¶
通过该蓝图接口可以使用支付码支付。
蓝图

输入:

FString类型:
- OrderNumber:商户自己生成的订单号,32个字符内、可包含字母和数字
- OrderTitile:订单标题
- Product Detail:商品描述
- Notify Url:欲通知的URL(非必填),必须为直接可访问的url,不能携带参数
- PicoPayCode:即商品代码,用户通过9.1.1游戏内支付配置获取
delegate类型:
Pay Order with Coin Delegate:绑定支付回调事件,如下图所示:
- 回调事件参数:
- FString类型:
- Code:见表 9-2 支付Code与Message对应表
- Message:见表 9-2 支付Code与Message对应表
- FString类型:
- 回调事件参数:
输出: 无
返回值: 无
表 9-2 Code与Message对应表
Code | Message 信息 |
---|---|
00000 | 网络异常 |
10000 | 登录成功 |
10001 | 用户未登陆 |
10002 | 请输入正确金额 |
10003 | 登陆过期,请重新登陆 |
11000 | 商户验证成功 |
11001 | 商户验证失败 |
11002 | 用户验证参数错误或请求过期 |
11003 | 商户未验证 |
12000 | 支付成功 |
12001 | 支付失败 |
12003 | P币不足 |
12004 | 余额可用 |
13000 | 生成订单 |
13001 | 获取数据失败 |
13002 | 生成订单失败 |
14000 | 查询订单成功 |
14001 | 订单不存在/有误 |
14002 | 用户取消支付操作 |
15000 | 未输入商品信息 |
15001 | 未输入预付ID |
15002 | 请输入Pico支付订单号或商户订单号 |
NOAUTH | 商户无此接口权限 |
SYSTEMERROR | 系统错误 |
APP_ID_NOT_EXIST | APP_ID不存在 |
MCHID_NOT_EXIST | MCHID 不存在 |
APP_ID_MCHID_NOT_MATCH | app_id和mch_id不匹配 |
LACK_PARAMS | 缺少参数 |
SIGNERROR | 签名错误 |
NO_DATA | 没有查询到数据/用户未充值 |
ORDER_EXIST | 订单已存在 |
PAY_CODE_NOT_EXIST | 消费代码不存在 |
PAY_CODE_EXIST | 用户已对商品代码消费 |
9.3.4 查询订单¶
Pico Payment Query Order¶
通过该蓝图接口可以查询订单。
蓝图

输入:
FString类型:
- Order Number:订单号(String)
delegate类型:
Query Order Delegate:绑定订单查询回调事件,如下图所示:
回调事件参数: - FString类型:
- Code:见表 9-2 支付Code与Message对应表
- Message:见表 9-2 支付Code与Message对应表
输出: 无
返回值: 无
9.3.5 获取用户信息¶
Pico Payment Get User Info¶
蓝图

输入:
delegate类型:
User Info Delegate:绑定获取用户信息回调事件,如下图所示:
- FString类型:
- Info:一个未经处理的Json串(string),见查询结果范例与表 9-3 OnPicoGetUserInfoCallback输出参数ret_code码及ret_msg一览
输出: 无
返回值: 无
Pico SDKGet User Info¶
蓝图

输入:
delegate类型:
User Info Delegate:绑定获取用户信息回调事件,如下图所示:
- FString类型:
- Info:一个未经处理的Json串(string),见查询结果范例与表 9-3 OnPicoGetUserInfoCallback输出参数ret_code码及ret_msg一览
输出: 无
返回值: 无
查询结果范例
Info:一个未经处理的Json串(string),查询成功范例如下:
{"ret_code":"0000",
"data" : {
"aboutme":"",
"birthday" : 1460476800000,
"phone" : "13100000000",
"username" : "Admin",
"email" : "",
"gender" : "male",
"lastname" : "",
"openid" : "4f3148bdc34d9bca104927729a173b64",
"firstname" : "",
"avatar" : "http://172.31.83.11/upload/6dd6ee103714e967846c3d38ae48d511",
"signature" : "14a25d7219d8dfc91e55f63286ae5c0a",
"country" : "China",
"city" : ""
},
"ret_msg":"调用成功"
}
查询失败范例如下:
{
"ret_code":"00003000",
"ret_msg" : "签名验证失败"
}
其他ret_code码及ret_msg一览:
表 9-3 OnPicoGetUserInfoCallback输出参数ret_code码及ret_msg一览
ret_code | ret_msg |
---|---|
0000 | 请求成功 |
00020000 | 数据库操作失败 |
9999 | 系统错误 |
00001000 | 参数错误 |
00002000 | 数据解析失败 |
00003000 | 签名验证失败 |
00003001 | 时间验证失败 |
00060000 | 用户未找到 |
00060001 | 用户密码错误 |
00060002 | 用户登录未知错误 |
00061000 | 用户token未找到失败 |
00061001 | 用户token验证失败 |
00061002 | 用户token未知错误 |
00070001 | 应用验证失败 |
00071001 | 应用密钥验证失败 |
00080001 | OAUTH_CODE验证失败 |
00090001 | REFRESH_TOKEN验证失败 |
00100001 | ACCESS_TOKEN验证失败 |
00110001 | SCOPE验证失败 |
9.4 开发者服务端交互¶
支付完成后,支付系统会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
对后台通知交互时,如果支付系统收到商户的应答不是成功或超时,则认为通知失败,支付系统会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但不保证通知最终能成功。
同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知。推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
商户服务端需要实现下面的接口,用于接收来自Pico服务器请求,获取Pico支付系统的支付结果和用户信息:
表9.4 商户服务器需要实现的接口
名称 | 支付结果回调接口 |
请求类型 | POST |
请求URL | 支付,PayOrder传入的参数notify_url |
请求格式 | JSON |
返回格式 | JSON |
是否需要登录 | 是 |
请求参数 | 详见下面“表9.3 支付结果通知中的通知参数” |
请求参数示例 | |
返回参数 | 参数名 类型及范围 说明 ret_code string 错误代码。 ret_msg string 错误信息字符串。 详见“表9.4 返回结果” |
返回参数实例 | { “ret_code”:”SUCCESS”, “ret_msg”:”OK” } |
更新说明 |
表9-5 支付结果通知中的通知参数
字段名 | 变量名 | 必填 | 类型 | 描述 |
---|---|---|---|---|
返回状态码 | ret_code | 是 | String | SUCCESS/FAIL此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 |
返回信息 | ret_msg | 否 | String | 返回信息,如非空,为错误原因:签名失败参数格式校验错误 |
错误代码 | sub_code | 否 | String | 错误码 |
错误代码描述 | sub_msg | 否 | String | 错误返回的信息描述 |
Pico支付订单号 | trade_no | 是 | String | Pico支付订单号 |
商户订单号 | out_trade_no | 是 | String | 商户系统内部的订单号 |
应用ID | app_id | 是 | String | 平台审核通过的应用APP_ID |
商户ID | mch_id | 是 | String | 支付分配的商户号 |
用户标识 | open_id | 是 | String | 用户在商户appid下的唯一标识 |
设备号 | device_id | 否 | String | 终端设备号 |
随机字符串 | nonce_str | 是 | String | 随机字符串,不长于32位。推荐随机数生成算法(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3) |
签名 | signature | 是 | String | 签名,详见签名生成算法(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3) |
业务结果 | result_code | 是 | String | SUCCESS/FAIL |
交易类型 | trade_type | 是 | String | 支付类型 |
货币类型 | fee_type | 是 | String | 货币类型 |
总金额 | total_fee | 是 | String | 订单总金额 |
实收金额 | receipt_fee | 是 | String | 实收金额 |
买家付款的金额 | buyer_pay_fee | 否 | String | 买家付款的金额 |
代金券或立减优惠金额 | coupon_fee | 否 | String | 代金券或立减优惠金额 |
商家数据包 | attach | 否 | String | 商家数据包,原样返回 |
支付完成时间 | pay_time | 是 | String | 支付完成时间,格式为yyyy-MM-dd HH:mm:ss |
表9.6 返回结果
字段名 | 变量名 | 必填 | 类型 | 描述 |
---|---|---|---|---|
返回状态码 | ret_code | 是 | String | SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功 |
返回信息 | ret_msg | 否 | String | 返回信息,如非空,为错误原因:函数名失败参数格式校验错误 |
特别提醒: 商户系统对于支付结果通知的内容一定要做签名验证,防止数据泄漏导致出现“假通知”,造成资金损失。
函数名校验规则是:
1.返回的参数列表,去掉signautre参数,同时添加key=“app_secret”,value=paykey,然后根据key值进行自然排序,多个参数之间用&隔开,最后进行MD5加密
2.用加密后的字符串和获取到的signature进行比较
函数名函数如下:
/**
* result :获取的数据的map集合
* paykey :就是开发者平台上的paykey
*/
public static String createSign(Map<String, Object> result, String paykey)
{
if (result == null || result.size() == 0)
return null;
result.put("app_secret", paykey); //1.添加key = “app_secret”,value=payke
String sign = result.get("signature");//2.保存signature的值,用于校验
result.remove("signature"); //3.移除signature参数
String[] tmp = new String[result.size()];
int i = 0;
for (String key : result.keySet())
{
tmp[i++] = key;
}
Arrays.sort(tmp); //4.自然排序
String signTemp = "";
for (String string : tmp)
{
if (result.get(string) == null)
continue;
signTemp += string + "=" + URLEncoder.encode(result.get(string).toString()
, "utf-8") + "&";
}
if (signTemp.endsWith("&"))
signTemp = signTemp.substring(0, signTemp.length() - 1);
Log.i(TAG, "createSign: " + signTemp);
String localSign = MD5.MD5(sign); //5.生成MD5加密后的字符串
return localSign.equal(sign);//6.和2中的sign进行校验
}