.. _9 支付系统: 9 支付系统 =============================== Pico支付,是基于Pico账户体系进行的游戏币支付系统,结算方式以现行的Pico公司下的游戏货币单位为准(P币)。因为支付关联登录状态,所以需要内购、登录获取用户信息的功能,需要查看本章节。 9.1 准备工作 ------------------- 9.1.1 必备参数 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - **APP ID**: 开发者平台为应用生成的唯一标识 - **APP KEY** : 开发者平台为应用生成的Key - **APP Secret** : 开发者平台为应用生成的用于支付的Key - **Publisher ID** : 开发者的ID,也叫商户 ID 9.1.2 申请成为Pico开发者 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 开发者在接入支付SDK时,需要在开发者平台创建应用并获取 **应用参数**,对应流程如下: 1.在 `Pico 开发者平台`_ 页点击「 `成为开发者`_ 」按钮。 .. _Pico 开发者平台: https://developer.pico-interactive.com/ .. _成为开发者: https://devcenter.pico-interactive.com/#/organization 2.在「账户」弹窗中选择您账户所在区域:中国大陆/其他地区 后,点击「注册」按钮。 .. image:: _static/9.1.2.png 3.验证您的账户 - 若您选择了「中国大陆」,您需要填写您的手机号并设置您的密码,通过短信验证您的手机号; - 若您选择了「其他地区」,您需要填写您的邮箱并设置您的密码,并选择您账号所在国家/地区,通过验证邮件验证您的邮箱账号; .. image:: _static/9.1.1.png 4.勾选接受 Pico 「 `用户许可协议`_ 」和「`隐私政策`_ 」,点击「注册」按钮。 .. _用户许可协议: https://www.pico-interactive.com/cn/terms/user_terms.html .. _隐私政策: https://www.pico-interactive.com/cn/terms/privacy.html 5.恭喜!您已经正式成为 Pico 开发者啦。 *Pico 开发者账户注册完全免费 9.1.3 获取应用参数 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **1. 获取** ``Publisher ID`` 申请成为开发者后,进入开发者管理平台,点击「我的应用」,进入「应用」后点击「API」菜单,即可查看到开发者 ID,该 ID 将作为支付系统中商户的唯一标志。 **2. 创建应用,选择支付类型** 开发者可以从管理中心进入到创建应用阶段,点击创建应用,然后进入相应平台完善应用的相关信息: .. image:: _static/9.3.1.png 图9.1 完善应用的相关信息 **注意:应用类型和支付相关,审核通过上架后,无法更改。一个应用只能选择一种。** **1. 选择应用**。支付时使用 P 币数量直接支付。 **2. 选择游戏**。支付是商品码支付。商品码为游戏中付费道具在开发者后台的唯一标识。 游戏类应用如果存在道具内付的情况,我们要求开发者必须采用开发者后台增加商品码的方式进行统一管理。 商品码支付配置界面如下: 再选择“游戏内支付配置”,配置游戏的内购信息: .. image:: _static/9.4.1.png 图9.2 游戏内购配置 **注意商品码的定义方式:** - 定义规则。首位为字母,仅允许输入字母及数字,不超过20个字符 - 不可重复。不同道具间的商品码不能重复 - 类型支持。目前支持 ``可消耗道具`` 和 ``不可消耗道具`` 。可消耗道具为可重复购买的商品,如金币、血瓶等;不可消耗道具为一次性购买产品,如武器、解锁关卡。 **3. 获取** ``APP ID,APP KEY,APP Secret`` **参数** 成功创建应用后,开发者平台会对其分配字符串,包括 **APP ID**、**APP KEY**、**APP Secret**: .. image:: _static/9.5.1.png 图9.3 APP ID、APP KEY、APP Secret **至此,Publisher ID ,APP ID、APP KEY、APP Secret 都已获取到了。** 9.1.4 AndroidManifest.xml中配置应用参数 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 导入SDK工程,选择 ``File`` -> ``Build Setting`` 进入如下界面,然后点击 ``Android`` 图标。 .. image:: _static/9.6.1.png - 然后在上述界面中点击 Playaer Settings... 按钮进入到如下配置界面,点击 ``Custom AndroidManifest.xml``,会在 ``Project`` 中 ``Assets`` 下生成 ``AndroidManifest.xml`` 文件。 .. image:: _static/9.7.1.png - 将应用参数配置到如下位置 .. image:: _static/9.8.1.png - 必要的权限: .. code-block:: XML 开发者信息(国内和国外) **4. 参数配置** 国内配置参数:``pico_app_id`` ,``pico_app_key`` ,``pico_pay_key`` , ``pico_scope`` , ``pico_merchant_id`` 国外配置参数:``pico_app_id_foreign``,``pico_app_key_foreign`` ,``pico_pay_key_foreign`` ``pico_scope_foreign``,``pico_merchant_id_foreign`` **5. 参数注意事项** 1. ``pico_scope`` 和 ``pico_scope_foreign`` 为固定值 ``get_user_info`` 2. ``pico_app_key`` / ``pico_app_key_foreign`` 对应开发者平台上 **AppSecret** ,参见9.1.3。 **6. 国内,海外开发者区别** 上述参数中的开发者信息(国内)、开发者信息(国外)根据实际工程需要只填入一组即可。 **如果需要同时在国内和国外使用,则上述两套需要同时配置** ,相应的appid等参数以实际为准。 **代码示例如下:** .. code-block:: XML - Android版本兼容问题 a. Android9及以上版本,网络请求必须使用https,而目前默认及initserver的配置都是http的,系统会拦截报错。 修复方法:在Android Manifest.xml中增加兼容http的配置,具体如下: 在 AndroidManifest.xml 的 application 标签中添加 .. code-block:: XML android:usesCleartextTraffic="true" 9.2 使用支付系统 -------------------------------------------------- 9.2.1 创建预制体和回调接口脚本 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - **创建预制体**,名称必须为 **PicoPayment** - 创建回调接口脚本,**实现回调接口,接口名称不可变更**,挂载到PicoPayment预制体上 .. code-block:: C# public class Callback : MonoBehaviour { // 登录回调接口 public void LoginCallback(string LoginInfo) { ... // 参见9.3.1 授权登录 } // 获取用户信息回调接口 public void UserInfoCallback(string userInfo) { ... // 参见9.3.2 获取用户授权信息 } // 查询订单回调接口 public void QueryOrPayCallback(string queryOrPayInfo) { ... // 参见 9.3.3 和 9.3.4 // 支付和查询订单接口共用 } // 固定写法,不变,不可删除 public void ActivityForResultCallback(string activity) { PicoPaymentSDK.jo.Call("authCallback", activity); } } 示例: .. image:: _static/9.9.1.png 9.2.2 在回调接口中处理返回参数 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **接口返回参数,均是Json格式,对应接口参见接口说明** 9.2.3 顺序调用登录,支付等接口 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 需要先调用 **Login()** ,接口返回成功后,再调用 **GetUserAPI** 和 **Pay** 接口 .. image:: _static/9.2.1.png - 代码示例: 登录支付方法调用: .. code-block:: C# public class PicoDemo { // 1. 发起登录,调用 PicoPaymentSDK.Login public static void Login() { PicoPaymentSDK.Login(); } // 获取用户信息,调用 PicoPaymentSDK.GetUserAPI() public static void GetUserAPI() { PicoPaymentSDK.GetUserAPI(); } // 4.支付 ,调用 PicoPaymentSDK.Pay(payorderJson) // payorderJson 中参数,请参见 9.3.3 支付 支付部分 // 示例: // P币支付:{'subject':'游戏',' body ':'购买完整游戏','order_id ':'10000','total ':'10','goods_tag ':'game' } // 商品码支付:{'subject':'游戏','body ':'购买完整游戏','order_id ':'10000',','goods_tag ':'game','pay_code':'123' } public static void Pay(string payorderJson) { PicoPaymentSDK.Pay(payorderJson); } // 6.查询支付订单 public static void queryOrder(string orderId) { PicoPaymentSDK.QueryOrder(orderId); } } 登录支付回调: .. code-block:: C# public class Callback : MonoBehaviour { private static string IS_SUCCESS = "isSuccess"; private static string MSG = "msg"; private static string CODE = "code"; // 登录成功标志位 private static string LOGIN_SUCCESS = "true"; // 登录失败标志位 private static string LOGIN_FAIL = "false"; // 获取用户信息接口返回状态标志位 private static string GET_USER_RET_CODE = "ret_code"; // 获取用户信息接口返回状态信息 private static string GET_USER_RET_MESSAGE = "ret_msg"; // 获取用户信息接口返回信息内容 private static string GET_USER_RET_DATA = "data"; // 获取用户信息接口返回状态成功 private static string GET_USER_SUCCESS = "0000"; // 支付成功状态值 private static string PAY_SUCCESS = "12000"; // 查询成功状态值 private static string QUERY_ORDER_SUCCESS = "13000"; // 2. 登录回调接口 public void LoginCallback(string LoginInfo) { JsonData jsrr = JsonMapper.ToObject(LoginInfo); JsonData loginStatus = jsrr[IS_SUCCESS]; if(loginStatus != null && LOGIN_SUCCESS.Equals(loginStatus.ToString())) { Debug.Log("Login Success." + LoginInfo); // 3. 成功后,可以调用可以调用 GetUserAPI,或者Pay接口, // 此处以 GetUserAPI 为例 , 回调再 下方UserInfoCallback中 PicoDemo. GetUserAPI(); return; } Debug.Log("Login fail." + LoginInfo); } // 4. 获取用户信息回调接口 public void UserInfoCallback(string userInfo) { if(userInfo != null) { JsonData jsrr = JsonMapper.ToObject(userInfo); JsonData retCode = jsrr[GET_USER_RET_CODE]; if (retCode != null && GET_USER_SUCCESS.Equals(retCode.ToString())) { // 从Json中的 data 字段获取用户信息,包含openid // 具体参数字段,请参见 9.3.2 获取用户信息 JsonData data = jsrr[GET_USER_RET_DATA]; // 例如,获取用户名称 string userName = data["username"].ToString(); return; } } Debug.Log("getUserInfo fail , " + userInfo); } // 5. 查询订单回调接口 public void QueryOrPayCallback(string queryOrPayInfo) { if (queryOrPayInfo != null) { JsonData jsrr = JsonMapper.ToObject(queryOrPayInfo); JsonData code = jsrr[CODE]; JsonData msg = jsrr[MSG]; // 支付成功 if (code != null && PAY_SUCCESS.Equals(code.ToString())) { // 支付成功,可以做一些通知操作 Debug.Log("Pay successs."); return; } // 订单查询成功 if (code != null && QUERY_ORDER_SUCCESS.Equals(code.ToString())) { // 订单查询成功,具体json参数参考 9.3.4 查询订单 Debug.Log("Query order successs."); // 例如,获取订单支付结果(成功还是失败) // 获取订单状态 JsonData tradeStatus = msg["trade_status"]; // 获取订单号(开发者订单号) JsonData outTradeNo = msg["out_trade_no"]; if (tradeStatus !=null && "SUCCESS".Equals(tradeStatus.ToString())) { Debug.Log("Order id" + outTradeNo.ToString() + " result is success "); } return; } } Debug.Log("QueryOrPayCallback fail , " + queryOrPayInfo); } // 固定写法,不变,不可删除 public void ActivityForResultCallback(string activity) { PicoPaymentSDK.jo.Call("authCallback", activity); } } - 实现方式可以参考示例Demo .. image:: _static/9.10.1.png 图9.4 支付Demo 9.3 功能接口说明 ------------------------------ 9.3.1 登录 ^^^^^^^^^^^^^^^^^^^^^ Login ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void Login() **功能**:登录 **参数**:无 **返回值**:数据以LoginCallback方式返回 **调用方式**:PicoPaymentSDK.Login(); LoginCallback ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void LoginCallback(string LoginOrUserInfo) **功能**:登录回调 **参数**: - string LoginOrUserInfo:后台返回的登录信息,Json格式的字符串,如下 .. code-block:: JSON // success 成功 LoginOrUserInfo = {"isSuccess":"true","msg":"message"} // failed 失败 LoginOrUserInfo = {"isSuccess":"false","msg":"message"} 登录失败时msg: +--------------------------------------+--------------------+----------------------------------------+ | 错误信息 | 说明 | 原因 | +======================================+====================+========================================+ | 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.2 获取用户信息 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetUserAPI ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void GetUserAPI() **功能**:获取用户信息 **参数**:无 **返回值**:无 **调用方式**:PicoPaymentSDK.GetUserAPI(); UserInfoCallback ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void UserInfoCallback(string userInfo) **功能**:获取用户信息的回调 **参数**: - string userInfo 后台返回的用户信息,Json格式的字符串 其中openid为用户在当前应用的唯一标识(必有,应用更换appid时,会改变此值,请务必注意)。 参数格式如下: 获取成功Success,返回Json格式信息 .. code-block:: JSON { "ret_code":"0000", "data":{ "aboutme":"", "birthday":1460476800000, //long类型值,出生日期,默认时间是某年某月某日的零点 "phone":"13100000000", //手机号,使用手机注册用户必有 "username":"Admin", //用户名,可以被用户修改 "email":"", //邮箱,使用邮箱注册用户必有 "gender":"male", "lastname":"", "openid":"4f3148bdc34d9bca104927729a173b64", //用户唯一标识,必有 "firstname":"", "avatar":"http://172.31.83.11/upload/123123123", "country":"China", //国家 可在官网进行设置 "city":"" //城市 可在官网进行设置 }, "ret_msg":"调用成功" } **返回值**:无 9.3.3 支付 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Pay ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**::public void Pay(string payOrderJson) **功能**:支付 **参数**: - string payOrderJson:包含订单信息的Json字符串,如下: +-----------------+-------------+------------------------+----------------------------------------+ | 参数名称 | 必须 | 依赖 | 说明 | +=================+=============+========================+========================================+ | subject | 是 | 无 |订单标题,商品概述 | +-----------------+-------------+------------------------+----------------------------------------+ | body | 是 | 无 |订单描述,商品的详细描述 | +-----------------+-------------+------------------------+----------------------------------------+ | order_id | 是 | 必须唯一 |订单编号,由开发者生成的编号,不超过64个| | | | |字符,请查看下方订单号生成规则建议 | +-----------------+-------------+------------------------+----------------------------------------+ | total | 是/否 | 应用类型 必填 |商品总价,为大于0的整数,直接支付时输 | | | | 游戏类型 不填 |入,商品码支付时不输入 | +-----------------+-------------+------------------------+----------------------------------------+ | goods_tag | 是 | 无 |商品标签 | +-----------------+-------------+------------------------+----------------------------------------+ | pay_code | 是/否 | 应用类型 必填 |商品码,必须和开发者平台配置的相一致, | | | | 游戏类型 不填 |直接支付时不输入,商品码支付时输入 | +-----------------+-------------+------------------------+----------------------------------------+ .. code-block:: none P币直接支付 PicoPaymentSDK.Pay("{'subject':'游戏',' body ':'购买完整游戏','order_id ':'10000','total ':'10','goods_tag ':'game' }"); 商品码: PicoPaymentSDK.Pay("{'subject':'游戏','body ':'购买完整游戏','order_id ':'10000',','goods_tag ':'game','pay_code':'123' }"); 注意订单号生成规则建议:order_id在支付系统中是唯一的,所以为保证单个用户在同一个app上订单号唯一,需要使用 “openID+自定义订单号“的形式进行命名。 **返回值**:无 **调用方式**:PicoPaymentSDK.Pay(string payOrderJson); QueryOrPayCallback ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void QueryOrPayCallback(string queryOrPayInfo) **功能**:支付回调 **参数**: - string queryOrPayInfo:后台返回支付信息,是Json格式的字符串 .. code-block:: none 支付成功,code值等于12000 {"code ":"12000","msg":"PAY_SUCCESS"} 支付失败 {"code ":"错误码","msg":"错误信息"} code及对应信息如下: ====================== ============================================== ============================================ Code Msg 说明 ====================== ============================================== ============================================ 11001 USER_NOT_LOGIN_OR_EXPIRED 用户登录失效 11004 MISSING_APP_PARAMETERS 没有配置APP ID等参数 12000 PAY_SUCCESS 支付成功 12002 ENTER_AMOUNT_ERROR 支付时输入错误值 12003 PCOIN_NOT_ENOUGH P币不足 12006 NOT_ENTER_ORDER_INFO 没有输入订单信息 12007 PAY_ORDER_EXIST 订单已存在 12008 PAY_CODE_NOT_EXIST 未找到可以使用的商品码 12009 PAY_CODE_ALERADY_CONSUMED 此商品码(一般是一次性的)以消费,不可再使用 14001 SDK_LOCAL_ERROR SDK内部错误 14004 NETWORK_ERROR 网络错误或者http配置错误 15001 SYSTEM_ERROR 支付服务端异常/时间异常 15003 SERVICE_APP_PARAMETER_NOT_MATCH APP ID等参数配置错误 ====================== ============================================== ============================================ **返回值**:无 9.3.4 查询订单 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ QueryOrder ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void QueryOrder(string orderId) **功能**:查询订单 **参数**: - string orderId:需要查询的订单编号,需要输入开发者支付时候的订单号 **返回值**:无 **调用方式**:PicoPaymentSDK.QueryOrder(string orderId); QueryOrPayCallback ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **函数名**:public void QueryOrPayCallback(string queryOrPayInfo) **功能**:查询订单的回调 **参数**:queryOrPayInfo:后台返回的订单信息 .. code-block:: none 查询成功,code值等于13000 {"code ":"13000","msg":"订单信息"} 查询失败 {"code ":"错误码","msg":"错误信息"} 成功时的订单信息如下: .. code-block:: none { "trade_no":"22016082314719505878171324", //Pico支付订单号 "open_id":"4f3148bdc34d9bca104927729a173b64", // 用户openid "ret_msg":"", "coupon_fee":0.00, "fee_type":"PIC", // 支付类型 "pay_time":1471950587000, //支付完成时间 "nonce_str":"yiUzuv4VQO1OXBAzVyZSRztOmRgIOioT", "out_trade_no":"12345678903", //商家订单号 "trade_status":"SUCCESS", //SUCCESS—支付成功 "trade_type":"EGG", "result_code":"SUCCESS", "mch_id":"company_id", "ret_code":"SUCCESS", "sub_msg":"OK", "total_fee":100.00, //订单总金额 "app_id":"bf18ac2de375095d63428134e44d1867", // 应用appid "sub_code":"SUCCESS", "receipt_fee":100.00, //实收金额 "buyer_pay_fee":100.00 //买家付款的金额 } code及对应信息如下: ====================== ============================================== ====================== Code Msg 说明 ====================== ============================================== ====================== 11001 USER_NOT_LOGIN_OR_EXPIRED 用户登录失效 13000 QUERY_ORDER_SUCCESS 查询订单成功 13003 NOT_ENTER_ORDER_ID 没有输入订单号 13006 QUERY_ORDER_NOT_EXIST 所查询的订单不存在 14001 SDK_LOCAL_ERROR SDK内部错误 14004 NETWORK_ERROR 网络错误或者http配置错误 15001 SYSTEM_ERROR 支付服务端异常/时间异常 15003 SERVICE_APP_PARAMETER_NOT_MATCH APP ID等参数配置错误 ====================== ============================================== ====================== 9.4 开发者服务端交互 --------------------------------------------- 支付完成后,支付系统会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。 对后台通知交互时,如果支付系统收到商户的应答不是成功或超时,则认为通知失败,支付系统会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但不保证通知最终能成功。 同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知。推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。 商户服务端需要实现下面的接口,用于接收来自Pico服务器请求,获取Pico支付系统的支付结果和用户信息: ================= ======================================================================== 名称 支付结果回调接口 请求类型 POST 请求URL 支付,PayOrder传入的参数notify_url 请求格式 JSON 返回格式 JSON 是否需要登录 是 请求参数 详见下面“表9.1 支付结果通知中的返回参数的信息” 请求参数示例 返回参数 详见下面“表9.2 返回的参数” 返回参数实例 { "ret_code":"SUCCESS", "ret_msg":"OK" } 更新说明 ================= ======================================================================== 表9.1 支付结果通知中的通知参数 ==================== =============== ====== ======== =============================================================================== 字段名 变量名 必填 类型 描述 ==================== =============== ====== ======== =============================================================================== 返回状态码 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位。推荐 `随机数生成算法`_ 函数名 signature 是 String 函数名,详见 `函数名生成算法`_ 业务结果 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 ==================== =============== ====== ======== =============================================================================== .. _随机数生成算法: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3 .. _函数名生成算法: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3 表9.2 返回结果 =================== =============== ====== ======== ================================================================================ 字段名 变量名 必填 类型 描述 =================== =============== ====== ======== ================================================================================ 返回状态码 ret_code 是 String SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功 返回信息 ret_msg 否 String 返回信息,如非空,为错误原因:函数名失败参数格式校验错误 =================== =============== ====== ======== ================================================================================ 特别提醒:商户系统对于支付结果通知的内容一定要做函数名验证,防止数据泄漏导致出现“假通知”,造成资金损失。 函数名校验规则是: 1. 返回的参数列表,去掉signautre参数,同时添加 key = “app_secret”,value=paykey,然后根据key值进行自然排序,多个参数之间用&隔开,最后进行MD5加密 2. 用加密后的字符串和获取到的signature进行比较 相关函数如下: .. code-block:: C# /** * result :获取的数据的map集合 * paykey :开发者平台上的paykey */ public static String createSign(Map 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 = sign.substring(0, signTemp .length() - 1); Log.i(TAG, "createSign: " + signTemp ); String localSign = MD5.MD5(signTemp ); //5.生成MD5加密后的字符串 return localSign.equal(sign);//6.和2中的sign进行校验 }