Kuaishou.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. <?php
  2. namespace app\api\complex;
  3. use think\Exception;
  4. /**
  5. * 快手
  6. */
  7. class Kuaishou implements ComplexInterface
  8. {
  9. private $channel = 'jcyx';
  10. private $open_url = "https://open.kuaishou.com/";
  11. private $pay_url = "https://allin.kuaishoupay.com/";
  12. private $qmPrivateKey = ''; // 祈盟公钥
  13. private $ksPublicKey = ''; // 快手私钥
  14. // 渠道效验
  15. public function getSpecialParam($data,$polyChannelGame = []){
  16. if (!$paramArr = json_decode($polyChannelGame['param'], true)) {
  17. return false;
  18. }
  19. $param = [
  20. 'app_id' => $paramArr['app_id'],
  21. 'game_id' => $data['user_id'],
  22. 'game_token' => $data['channel_token'],
  23. ];
  24. $result = getHttpGet($this->open_url . 'game/user_info/v2', $param);
  25. $res = json_decode($result, true);
  26. if ($res['result'] == 1) {
  27. return $data['user_id'];
  28. } else {
  29. return false;
  30. }
  31. }
  32. // 登陆效验
  33. public function checkLogin($data, $polyChannelGame = [])
  34. {
  35. return ['user_id' => $data['user_id']];
  36. }
  37. // ## 下单
  38. public function getOrder($data)
  39. {
  40. if (!$channelConfig = json_decode($data['channel_info']['param'], true)) {
  41. return false;
  42. }
  43. if(empty($data['extparam']) || empty($data['extparam']['user_id']) || empty($data['extparam']['channel_id'])){
  44. return ['code' => -200, 'msg' => '登录失效,请重新登录!', 'param' => []];
  45. }
  46. $requiredKeys = ['roleid', 'rolename', 'rolelevel', 'serverid', 'server_name', 'product_id', 'amount', 'notify_url'];
  47. $missingKeys = checkDataHasKeys($data, $requiredKeys);
  48. if (!empty($missingKeys)) {
  49. return ['code' => -200, 'msg' => '缺失参数:' . implode(', ', $missingKeys), 'param' => []];
  50. }
  51. try {
  52. $param = [
  53. "app_id" => $channelConfig['app_id'],
  54. "channel_id" => $data['extparam']['channel_id'],
  55. "game_id" => $data['extparam']['user_id'],
  56. "role_id" => $data['roleid'],
  57. "role_name" => $data['rolename'],
  58. "role_level" => $data['rolelevel'],
  59. "server_id" => $data['serverid'],
  60. "server_name" => $data['server_name'],
  61. "product_id" => $data['product_id'],
  62. "product_name" => $data['product_name']??'',
  63. "product_desc" => $data['product_desc']??'',
  64. "money" => formatYuanToFen($data['amount']),
  65. "currency_type" => 'CNY',
  66. "notify_url" => $data['notify_url'],
  67. "extension" => json_encode(['order_id' => $data['pay_info']['orderid']]),
  68. "user_ip" => $data['pay_info']['ip'],
  69. "third_party_trade_no" => $data['pay_info']['orderid'],
  70. ];
  71. $param['sign'] = $this->handleSIgn($param, $this->getKeyData('pay'));
  72. }catch (Exception $e){
  73. return ['code' => -200, 'msg' => $e->getMessage()];
  74. }
  75. return ['code' => 200, 'msg' => 'success', 'param' => $param];
  76. }
  77. // 回调订单参数
  78. public function getData($data)
  79. {
  80. $orderData = json_decode($data['extension'], true);
  81. if(empty($orderData['order_id'])){
  82. return false;
  83. }
  84. return [
  85. 'orderid' => $orderData['order_id'],//我方聚合订单
  86. 'amount' => formatFenToYuan($data['money']), // 充值金额(单位:元)
  87. 'sub_orderid' => $data['allin_trade_no'],//渠道订单
  88. ];
  89. }
  90. // 回调效验
  91. public function paySign($data, $polyChannelGame = [])
  92. {
  93. // 回调范例:{"app_id":"ks684828619578671316","role_id":"202005201314","server_id":"1","product_id":"kdzz_goods_001","money":"1","extension":"{\"order_id\":\"CMWL17452303393425188518\"}","allin_trade_no":"AI677228795252780746","data":"{\"environment\":\"PRODUCTION\"}","sign":"k0SSvITO06zSsC74nwWnePYe0cFyoneAlYeMGDSDx9ZZr4Lqal\/LWFRRMQbL1hxhQ4VcUpOyqpY+B5h7U1KsgO0gh\/6VQDK0ZbMrxP8RnGnalo5ua1d+FkiIYMcSlx\/5848XAC4GbHRCLUClAE+IQ4B4s6djxv5Usujvma\/4gIwW53TWJrbU+F3Pzi0G9arMoRFkl9fqgO1nEtSDeX8oZH1+cOHFoONIkZXa4z+Y7sErC0FuJHOKQSznzSa5iAcgEXqG1cfCvLo0nVRDoyJ\/AIb36GEerHzK0RomXd+5LrHyf\/zyE4VJzpgh\/JaslcSC7vQUf7MrqmJqDnHSHSCE4w=="}
  94. try {
  95. $requiredKeys = ['app_id', 'role_id', 'server_id', 'product_id', 'money', 'extension', 'allin_trade_no', 'sign'];
  96. $missingKeys = checkDataHasKeys($data, $requiredKeys);
  97. if (!empty($missingKeys)) {
  98. return "ERROR: 回调参数不匹配!";
  99. }
  100. $param = [
  101. 'app_id' => $data['app_id'],
  102. 'role_id' => $data['role_id'],
  103. 'server_id' => $data['server_id'],
  104. 'product_id' => $data['product_id'],
  105. 'money' => $data['money'],
  106. 'extension' => $data['extension'],
  107. 'allin_trade_no' => $data['allin_trade_no'],
  108. 'data' => $data['data']??'',
  109. 'notify_detail' => $data['notify_detail']??'',
  110. 'sign' => $data['sign'],
  111. ];
  112. $result = $this->handleSignVerify($param, $this->getKeyData('notify'), $param['sign']);
  113. if ($result) {
  114. return true;
  115. }
  116. } catch (Exception $e){
  117. return "ERROR: ".$e->getMessage();
  118. }
  119. return false;
  120. }
  121. public function getFail($msg = '')
  122. {
  123. echo 'fail:'.$msg;
  124. exit();
  125. }
  126. public function getSuccess($msg = '')
  127. {
  128. echo 'success';
  129. exit();
  130. }
  131. /**
  132. * 设置需要存储的数据
  133. *
  134. * @param $input 请求参数
  135. * @return array
  136. */
  137. public function setParam($input){
  138. if($input == '' || is_null($input)) return "";
  139. return [
  140. "user_id" => $input['user_id'],
  141. "channel_id" => $input['extparam'],
  142. ];
  143. }
  144. /**
  145. * 获取签名秘钥
  146. *
  147. * @param $type 类型 pay|notify
  148. * @return string
  149. */
  150. public function getKeyData($type = 'pay')
  151. {
  152. $key_file = APP_PATH . 'common/config/complex/qm_private_key.pem';
  153. if ($type == 'notify') {
  154. $key_file = APP_PATH . 'common/config/complex/kuaishou_public.pem';
  155. }
  156. if (!file_exists($key_file)) {
  157. throw new Exception('签名秘钥文件不存在');
  158. }
  159. return file_get_contents($key_file);
  160. }
  161. /**
  162. * 处理签名数据
  163. *
  164. * @param $data
  165. *
  166. * @return string
  167. */
  168. public function handleSignData($data){
  169. ksort($data);
  170. $signString = '';
  171. foreach ($data as $key => $value) {
  172. if($key == 'sign' || $value == ''){
  173. continue;
  174. }
  175. if ($signString !== '') {
  176. $signString .= '&';
  177. }
  178. $signString .= $key . '=' . $value;
  179. }
  180. return $signString;
  181. }
  182. /**
  183. * 使用 RSA 私钥 + SHA512 签名数组
  184. *
  185. * @param array $data 要签名的数组
  186. * @param string $privateKey 私钥内容(PEM格式)
  187. * @return string Base64 编码签名字符串
  188. */
  189. function handleSign(array $data, string $privateKey): string
  190. {
  191. $signString = $this->handleSignData($data);
  192. $pkey = openssl_pkey_get_private($privateKey);
  193. if (!$pkey) {
  194. throw new Exception('无效的 RSA 私钥');
  195. }
  196. $signature = '';
  197. $success = openssl_sign($signString, $signature, $pkey, OPENSSL_ALGO_SHA512);
  198. openssl_free_key($pkey);
  199. if (!$success) {
  200. throw new Exception('签名失败');
  201. }
  202. // 6. 返回 Base64 编码后的签名
  203. return base64_encode($signature);
  204. }
  205. /**
  206. * 签名效验
  207. *
  208. * @param array $data
  209. * @param string $publicKey
  210. * @param string $signstr
  211. *
  212. * @return string
  213. */
  214. function handleSignVerify(array $data, string $publicKey, string $signstr): string
  215. {
  216. $signString = $this->handleSignData($data);
  217. $public_key_res = openssl_get_publickey($publicKey);
  218. $res = openssl_verify($signString, base64_decode($signstr), $public_key_res, OPENSSL_ALGO_SHA512);
  219. if($res == 1){
  220. return true;
  221. }
  222. return false;
  223. }
  224. }