支付

业务流程

以下是支付时序图, 相关接口调用都必须在合作方服务器端完成。image

支付流程说明1.如时序1预支付前需要先判断用户是否具有相关的权益。参见【判断用户是否有相关权益】2.如时序2会返回当前用户相关权益信息。3.若用户已具有相关权益,直接执行行时序10使用用户自身权益(需带上合作方订单号)。参见【使用用户自身权益】4.若用户没有相关权益,执行时序3预支付下单接口(需带上合作方订单号),开发平台会返回相关信息及合作方订单号。参见【预下单接口】5.时序5将 billno带给WPS 客户端,WPS客户端会完成后续的支付流程。6.时序8 WPS客户端会将支付成功信息同步通知给合作方客户端。7.时序9 开发平台会异步将支付成功信息通知给合作方服务端(是否支付成功以此为准)。8.开发平台会记录用户的购买记录、使用记录,并和合作方订单号关联,后续以此对账。

相关接口说明

1, 判断用户是否有相关权益2, 预下单接口3, 使用用户自身权益4, 零售下单5, 支付完成后的回调6, 支付完成后的回调实例

1, 判断用户是否有相关权益

判断用户是否有相关权益

接口说明

判断用户是否有相关权益

请求说明

[POST] https://openapi.wps.cn/oauthapi/v2/vas/service/usable

参数说明

参数参数类型是否必须说明
access_tokenstringrequiredaccess_token
appidstringrequired应用唯一标识
openidstringrequired用户标识openid
service_idstringrequired服务id,开发者对接后可用的服务
total_numint64required查询数量,仅消耗类型的服务需要传对应的数量,其他的传0

Header说明

Header名称是否必须说明
Content-typerequired值为:application/json

返回

{ "result":0 "msg":"success", "data":{ "docervip":946656000, #说明:稻壳会员过期时间 "supervip":946656000, #说明:超级会员过期时间 "wpsvip":946656000, #说明:WPS会员过期时间 "expire_time":946656000, #说明:特权过期时间 "total":TOTAL #说明:特权剩余总次数 }}

2, 预下单接口

预下单接口

接口说明

预下单接口

请求说明

[POST] https://openapi.wps.cn/oauthapi/v2/vas/pay/preorder

参数说明

参数参数类型是否必须说明
access_tokenstringrequiredaccess_token
appidstringrequired应用唯一标识
openidstringrequired用户标识openid
service_idstringrequired服务id,开发者对接后可用的服务
total_numint64required查询数量,仅消耗类型的服务需要传对应的数量,其他的传0
billnostringrequired合作方自己的订单号,需要合作方每次下单时保证唯一未使用的订单号,长度不超过32位字符
subjectstringoptional购买内容,当服务类型为第三方自己的服务时传对应的服务id
csourcestringrequired购买来源
client_ipstringrequired客户端IP,由接入方获取客户端ip后传过来

Header说明

Header名称是否必须说明
Content-typerequired值为:application/json

返回

{ "result":0 "msg":"success"}

3, 使用用户自身权益

使用用户自身权益

接口说明

使用用户自身权益

请求说明

[POST] https://openapi.wps.cn/oauthapi/v2/vas/service/use

参数说明

参数参数类型是否必须说明
access_tokenstringrequiredaccess_token
appidstringrequired应用唯一标识
openidstringrequired用户标识openid
service_idstringrequired服务id,开发者对接后可用的服务
total_numint64required查询数量,仅消耗类型的服务需要传对应的数量,其他的传0
billnostringrequired合作方自己的订单号,需要合作方每次下单时保证唯一未使用的订单号,长度不超过32位字符

Header说明

Header名称是否必须说明
Content-typerequired值为:application/json

返回

{ "result":0 "msg":"success"}

4, 零售下单

零售下单

接口说明

零售下单

请求说明

[POST] https://openapi.wps.cn/oauthapi/v2/vas/pay/customorder

参数说明

参数参数类型是否必须说明
access_tokenstringrequiredaccess_token
appidstringrequired应用唯一标识
billnostringrequired合作方自己的订单号,需要合作方每次下单时保证唯一未使用的订单号,长度不超过32位字符
openidstringrequired用户标识openid
paymentstringrequired支付方式,目前只支持 qrcode: 二维码支付 ios: ios支付 (预下单) android_wechat: 安卓微信支付 (预下单) android_alipay: 安卓支付宝支付 (预下单)
service_idstringrequired服务id,开发者对接后可用的服务
subjectstringrequired购买内容,当服务类型为第三方自己的服务时传对应的服务id
csourcestringrequired购买来源
total_feeint64required订单金额(单位: 分)
countint64required购买数量

Header说明

Header名称是否必须说明
Content-typerequired值为:application/json

返回

{ "result":0 "msg":"success", "data":{ "url":"URL", "billno":"BILLNO" }}

5, 支付完成后的回调

支付完成后的回调

接口说明

支付完成后的回调

请求说明

[POST] redirect_uri?billno=BILLNO&app_id=APPID&service_id=SERVICEID&sig=SIG

参数说明

参数参数类型是否必须说明
billnostringrequired合作方自己的订单号,需要合作方每次下单时保证唯一未使用的订单号,长度不超过32位字符
app_idstringrequired应用唯一标识
service_idstringrequired用户实际购买的服务id
sigstringrequired签名值

返回

  1. 接口需要直接输出”ok”字符串,代表通知处理业务成功,其他输出都会视为回调失败,失败后前三次间隔1s左右,失败三次后间隔半小时重试一次

6, 支付完成后的回调实例

支付完成后的回调实例下载Demo

  1. 将请求的参数和对应的值以keyvalue形式放在数组中,对key按自然序排列后。
  2. 对数组循环以key=value形式拼接成字符串str,将screatkey拼接在str后面,再md5加密成32位小写字符串。

java 代码实现:

  1. import java.util.TreeMap;
  2. import java.util.Map.Entry;
  3. public class Sig{
  4. public static void main(String []args){
  5. TreeMap<String, String> params = new TreeMap<String, String>();
  6. params.put("billno", "");
  7. params.put("app_id", "");
  8. params.put("service_id", "");
  9. params.put("sig", "");
  10. System.out.println(validSig(params));
  11. }
  12. private static String getKeySortparamters(TreeMap<String, String> map) {
  13. StringBuffer sb = new StringBuffer();
  14. if (map == null) {
  15. return "";
  16. }
  17. for (Entry<String, String> entry : map.entrySet()) {
  18. sb.append(entry.getKey()).append("=").append(entry.getValue());
  19. }
  20. return sb.toString();
  21. }
  22. public static Boolean validSig(TreeMap<String, String> map){
  23. String RequestSig = map.get("sig");
  24. map.remove("sig");
  25. MD5Util md5 = new MD5Util();
  26. String sort = getKeySortparamters(map);
  27. String sig = md5.getMD5(getKeySortparamters(map) + "YOUR SECRET KEY");
  28. if(sig.equals(RequestSig)){
  29. return true;
  30. }
  31. return false;
  32. }
  33. }

php 代码实现:

  1. $params = array(
  2. 'billno' => "",
  3. 'app_id' => '',
  4. 'sig' => '',
  5. 'service_id' => '',
  6. );
  7. $sig_helper = new Sig();
  8. $key = 'xxxxx'; // 支付回调密钥
  9. list($allow_access, $msg) = $sig_helper->wps_valid_invoke($params, $key);
  10. <?php
  11. class Sig
  12. {
  13. public function wps_generate_sig($param_array, $key)
  14. {
  15. $str = '';
  16. //对param_array中的参数名称进行升序排序
  17. if (is_array($param_array)) {
  18. $this->wps_mksort($param_array);
  19. //按照如下格式转换数组为string格式
  20. $str = $this->wps_key_value($param_array);
  21. } else {
  22. $str = $param_array;
  23. }
  24. //string末端补充api_secret密钥
  25. $str .= $key;
  26. //生成32位小写MD5为最终的数据签名
  27. return array(TRUE, md5($str));
  28. }
  29. /**
  30. * 多维数组的 ksort 排序
  31. * @param array $a
  32. */
  33. private function wps_mksort(& $a)
  34. {
  35. ksort($a);
  36. foreach ($a as &$value) {
  37. if (is_array($value)) {
  38. $this->wps_mksort($value);
  39. }
  40. }
  41. }
  42. private function wps_key_value($data)
  43. {
  44. $str = "";
  45. if (is_array($data)) {
  46. foreach ($data as $k => $v) {
  47. if ($v !== NULL) {
  48. $str .= $k . "=" . $this->wps_key_value($v);
  49. }
  50. }
  51. } else {
  52. $str = $data;
  53. }
  54. return $str;
  55. }
  56. //参数校验
  57. public function wps_valid_invoke($param, $signKey)
  58. {
  59. //加密键值
  60. $sig_keys = array('sig', 'pass');
  61. foreach ($sig_keys as $key) {
  62. if (isset($param[$key])) {
  63. $sig_request = $param[$key];
  64. unset($param[$key]);
  65. }
  66. }
  67. if (!isset($sig_request)) {
  68. return array(FALSE, 'needSigParam');
  69. }
  70. list($success, $sig) = $this->wps_generate_sig($param, $signKey);
  71. if (!$success) {
  72. return array($success, $sig);
  73. }
  74. if ($sig != $sig_request) {
  75. return array(FALSE, 'wrongSigParam');
  76. }
  77. return array(TRUE, 'passed');
  78. }
  79. }