XiTaiYouPay.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <?php
  2. namespace XiTaiYouPay;
  3. use XiTaiYouPay\util\RSAUtil;
  4. use XiTaiYouPay\util\ParameterUtil;
  5. use XiTaiYouPay\util\HttpRequestUtil;
  6. /**
  7. * 喜钛游 支付
  8. */
  9. class XiTaiYouPay
  10. {
  11. private $config; // 配置
  12. private $commonData = [
  13. 'app_id' => '',
  14. 'timestamp' => '',
  15. 'version' => '',
  16. 'charset' => '',
  17. 'method' => '',
  18. 'sign_type' => '',
  19. ];
  20. const SIGN_TYPE = "RSA2"; // 签名规则
  21. const VERSION = "1.0.1";
  22. const CHARSET = "UTF-8"; // 字符集
  23. const REQ_URL = "https://api.tengtren.com/"; // 支付请求地址
  24. const SUCCESS = "SUCCESS";
  25. const FAIL = "fail";
  26. private $rsa_helper;
  27. public function __construct(){
  28. $this->config = config('xty_pay');
  29. // dump($this->config['private_file'], $this->config['public_file']);
  30. $this->rsa_helper = new RSAUtil($this->config['private_file'], $this->config['public_file']);
  31. }
  32. /**
  33. * 初始化公共参数
  34. * @param string $type 请求类型
  35. *
  36. * @return array
  37. */
  38. public function initCommonData($type){
  39. if(!$type){
  40. return [];
  41. }
  42. switch ($type){
  43. case 'pay_h5':
  44. $method = 'trade.h5.pay';
  45. break;
  46. case 'order_query':
  47. $method = 'trade.order.query';
  48. break;
  49. case 'refund':
  50. $method = 'trade.order.refund';
  51. break;
  52. case 'refund_query':
  53. $method = 'trade.refund.query';
  54. break;
  55. }
  56. $this->commonData = [
  57. 'app_id' => $this->config['app_id'],
  58. 'timestamp' => ParameterUtil::getCurrentDateStr(),
  59. 'version' => self::VERSION,
  60. 'charset' => self::CHARSET,
  61. 'method' => $method,
  62. 'sign_type' => self::SIGN_TYPE,
  63. ];
  64. return $this->commonData;
  65. }
  66. // 发起H5支付
  67. public function payH5($pay_channel, $return_type, $data){
  68. try {
  69. $requestData = $this->initCommonData('pay_h5');
  70. $requestData['req_data'] = [
  71. "out_trade_no" => $data['out_trade_no'],
  72. "pay_channel" => $pay_channel, // 支付渠道: WX=微信, ALI=支付宝, UNI=银联
  73. "order_name" => $data['order_name'],
  74. "total_amount" => $data['total_amount'], // 金额(分)
  75. "order_desc" => $data['order_desc'],
  76. "notify_url" => $data['notify_url'], // 回调地址
  77. "return_type" => $return_type, // 返回类型: 1=返回跳转支付收银台 URL, 2=返回支付链接, 3=返回官方标准支付 URL, 4=返回支付宝 qrCode
  78. "return_url" => $data['return_url'], // 前端跳转地址
  79. // "limit_pay" => "1",
  80. "payer_client_ip" => $data['ip'],
  81. // "h5_info" => "{\"type\":\"Android\",\"app_name\": \"XXXXXX\",\"package_name\": \"com.tencent.tmgp.sgame\"}", // 微信H5场景信息
  82. "reserved" => $data['reserved']??"", // 附加数据
  83. ];
  84. $sign = $this->rsa_helper->createRSA2Sign(json_encode($requestData));
  85. // 请求的为JSON类型的数据
  86. $result = HttpRequestUtil::sendPost(self::REQ_URL, json_encode($requestData), $sign);
  87. // $result["responseData"] = {"resp_code":"SUCCESS","resp_data":"{\"order_no\":\"1010371122024090311352084041\",\"biz_code\":\"SUCCESS\",\"pay_data\":\"https://api.tengtren.com/wxH5Pay?orderNo=1010371122024090311352084041&payUrl=weixin%3A%2F%2Fdl%2Fbusiness%2F%3Ft%3DpYe3V25CZ3m&payScene=H5&appId=A17222309452323464&version=1.0.1\"}","resp_msg":"成功","timestamp":"20240903113520"}
  88. $resp_data_json = json_decode($result["responseData"], TRUE);
  89. if ("SUCCESS" == $resp_data_json["resp_code"]) {
  90. if ($this->rsa_helper->verifyRSA2Sign($result["responseData"], $result["signature"])) {
  91. $newRespData = json_decode($resp_data_json['resp_data'], true);
  92. if (!empty($newRespData['pay_data'])) {
  93. return ['code' => 200, 'msg' => "success", 'data' => $newRespData['pay_data']];
  94. }
  95. }
  96. }
  97. return ['code' => -100, 'msg' => "请求失败: " . $resp_data_json['resp_data'], 'data' => []];
  98. } catch (\Exception $e) {
  99. return ['code' => -110, 'msg' => "请求异常:".$e->getMessage(), 'data' => []];
  100. }
  101. }
  102. /**
  103. * 订单查询
  104. *
  105. * @param $order_no string 交易流水号
  106. * @param $out_trade_no string 商户订单号
  107. *
  108. * @return array
  109. */
  110. public function orderQuery($out_trade_no, $order_no = '')
  111. {
  112. try {
  113. $request_data = $this->initCommonData('order_query');
  114. $request_data['req_data'] = [
  115. "out_trade_no" => $out_trade_no,
  116. ];
  117. if(!empty($order_no)){
  118. $request_data['req_data']['order_no'] = $order_no;
  119. }
  120. $sign = $this->rsa_helper->createRSA2Sign(json_encode($request_data));
  121. // 请求的为JSON类型的数据
  122. $result = HttpRequestUtil::sendPost(self::REQ_URL, json_encode($request_data), $sign);
  123. $resp_data_json = json_decode($result["responseData"], TRUE);
  124. if ("SUCCESS" != $resp_data_json["resp_code"]) {
  125. return ['code' => -100, 'msg' => "订单查询失败:" . $resp_data_json['resp_data'], 'data' => []];
  126. }
  127. if ($this->rsa_helper->verifyRSA2Sign($result["responseData"], $result["signature"]) != true) {
  128. return ['code' => -101, 'msg' => "订单-签名解析失败: msg=" . $resp_data_json['resp_msg'], 'data' => []];
  129. }
  130. $resp_data = json_decode($resp_data_json["resp_data"], TRUE);
  131. if($resp_data['biz_code'] != "SUCCESS"){
  132. return ['code' => -103, 'msg' => "订单业务失败状态!", 'data' => []];
  133. }
  134. if($resp_data['trade_status'] != "SUCCESS"){
  135. return ['code' => -104, 'msg' => "订单已关闭或未支付!", 'data' => []];
  136. }
  137. return ['code' => 200, 'msg' => "success", 'data' => $resp_data];
  138. } catch (\Exception $e) {
  139. return ['code' => -110, 'msg' => "订单查询异常:".$e->getMessage(), 'data' => []];
  140. }
  141. }
  142. // 回调通知
  143. public function notify($bodyData)
  144. {
  145. // 回调验证走查询订单接口验证即可。
  146. }
  147. /**
  148. * 退款
  149. *
  150. * @param $out_trade_no
  151. * @param $out_refund_no
  152. * @param $refund_amount
  153. * @param $refund_reason
  154. *
  155. * @return array
  156. */
  157. public function refund($out_trade_no, $out_refund_no, $refund_amount, $refund_reason)
  158. {
  159. try {
  160. $queryResult = $this->orderQuery($out_trade_no);
  161. // dump($queryResult);
  162. if ($queryResult['code'] != 200) {
  163. return ['code' => -100, 'msg' => $queryResult['msg'], 'data' => []];
  164. }
  165. $request_data = $this->initCommonData('refund');
  166. $request_data['req_data'] = [
  167. "out_trade_no" => $out_trade_no, // 商户订单号
  168. // "order_no" => $order_no, // 交易流水号
  169. "out_refund_no" => $out_refund_no, // 商户退款单号
  170. "refund_amount" => formatYuanToFen($refund_amount), // 退款金额(分)
  171. "refund_reason" => $refund_reason, // 退款原因
  172. ];
  173. // dump('$request_data: ', $request_data);
  174. $sign = $this->rsa_helper->createRSA2Sign(json_encode($request_data));
  175. // 请求的为JSON类型的数据
  176. $result = HttpRequestUtil::sendPost(self::REQ_URL, json_encode($request_data), $sign);
  177. $respDataJson = json_decode($result["responseData"], TRUE);
  178. // dump($respDataJson);
  179. $respData = "";
  180. if(!empty($respDataJson)){
  181. $respData = json_decode($respDataJson['resp_data'], TRUE);
  182. }
  183. // dump($respDataJson, $respData, 'xxxxasd');
  184. /**
  185. * 退款成功返回样例:
  186. *
  187. * array(4) {
  188. * ["resp_code"]=>string(7) "SUCCESS"
  189. * ["resp_data"]=>string(313) "{"order_no":"1010317712026012015442715994","refund_ch_order_no":"300220260121000000015829065830","biz_code":"SUCCESS","out_trade_no":"WL17688950675041100742","refund_status":"PROCESSING","out_refund_no":"REF17689609400390212861","total_amount":1,"refund_amount":1,"refund_order_no":"1010417742026012110022036442"}"
  190. * ["resp_msg"]=>string(6) "成功"
  191. * ["timestamp"]=>string(14) "20260121100220"
  192. * }
  193. *
  194. * 退款请求失败样例:
  195. * array(4) {
  196. * ["resp_code"]=>string(7) "SUCCESS"
  197. * ["resp_data"]=>
  198. * string(130) "{"biz_code":"FAIL","error_msg":"请求参数错误:out_trade_no和order_no参数至少一个必填","error_code":"PARAMS_INVALID"}"
  199. * string(145) "{"biz_code":"FAIL","error_msg":"业务规则检查异常:退款金额不能大于可退金额","refund_status":"FAIL","error_code":"BIZ_INVALID"}"
  200. * string(149) "{"biz_code":"FAIL","error_msg":"频率限制:同一笔订单多次退款受限,请稍后再试","refund_status":"FAIL","error_code":"RATE_LIMITED"}"
  201. * ["resp_msg"]=>string(6) "成功"
  202. * ["timestamp"]=>string(14) "20260121100420"
  203. * }
  204. *
  205. */
  206. if (!empty($respData) && !empty($respDataJson) && !empty($respData["biz_code"]) && $respData["biz_code"] == "SUCCESS" && ($respData["refund_status"] == "SUCCESS" || $respData["refund_status"] == "PROCESSING")) {
  207. return ['code' => 200, 'msg' => "success", 'data' => []];
  208. }
  209. $errorMsg = $respDataJson['resp_msg'];
  210. if(!empty($respData['error_msg'])){
  211. $errorMsg = $respData['error_msg'];
  212. }
  213. return ['code' => -100, 'msg' => "退款失败:" . $errorMsg, 'data' => []];
  214. } catch (\Exception $e) {
  215. return ['code' => -110, 'msg' => $e->getMessage(), 'data' => []];
  216. }
  217. }
  218. // 退款查询
  219. public function refund_query($out_refund_no, $refund_order_no){
  220. try {
  221. $request_data = $this->initCommonData('refund_query');
  222. $request_data['req_data'] = [
  223. "out_refund_no" => $out_refund_no, // 商户退款单号
  224. "refund_order_no" => $refund_order_no, // 退款交易流水号
  225. ];
  226. // dump($request_data);
  227. $sign = $this->rsa_helper->createRSA2Sign(json_encode($request_data));
  228. // 请求的为JSON类型的数据
  229. $result = HttpRequestUtil::sendPost(self::REQ_URL, json_encode($request_data), $sign);
  230. // dump($result);
  231. $respDataJson = json_decode($result["responseData"], TRUE);
  232. $respData = "";
  233. if(!empty($respDataJson)){
  234. $respData = json_decode($respDataJson['resp_data'], TRUE);
  235. }
  236. // dump($respDataJson, $respData, 'xxxxasd');
  237. /**
  238. * 退款成功返回样例:
  239. *
  240. * array(4) {
  241. * ["resp_code"]=>string(7) "SUCCESS"
  242. * ["resp_data"]=>string(313) "{"order_no":"1010317712026012015442715994","refund_ch_order_no":"300220260121000000015829065830","biz_code":"SUCCESS","out_trade_no":"WL17688950675041100742","refund_status":"PROCESSING","out_refund_no":"REF17689609400390212861","total_amount":1,"refund_amount":1,"refund_order_no":"1010417742026012110022036442"}"
  243. * ["resp_msg"]=>string(6) "成功"
  244. * ["timestamp"]=>string(14) "20260121100220"
  245. * }
  246. *
  247. * 退款请求失败样例:
  248. * array(4) {
  249. * ["resp_code"]=>string(7) "SUCCESS"
  250. * ["resp_data"]=>
  251. * string(130) "{"biz_code":"FAIL","error_msg":"请求参数错误:out_trade_no和order_no参数至少一个必填","error_code":"PARAMS_INVALID"}"
  252. * string(145) "{"biz_code":"FAIL","error_msg":"业务规则检查异常:退款金额不能大于可退金额","refund_status":"FAIL","error_code":"BIZ_INVALID"}"
  253. * string(149) "{"biz_code":"FAIL","error_msg":"频率限制:同一笔订单多次退款受限,请稍后再试","refund_status":"FAIL","error_code":"RATE_LIMITED"}"
  254. * ["resp_msg"]=>string(6) "成功"
  255. * ["timestamp"]=>string(14) "20260121100420"
  256. * }
  257. *
  258. */
  259. if (!empty($respData) && !empty($respDataJson) && !empty($respData["biz_code"]) && $respData["biz_code"] == "SUCCESS" && ($respData["refund_status"] == "SUCCESS" || $respData["refund_status"] == "PROCESSING")) {
  260. return ['code' => 200, 'msg' => "success", 'data' => []];
  261. }
  262. $errorMsg = $respDataJson['resp_msg'];
  263. if(!empty($respData['error_msg'])){
  264. $errorMsg = $respData['error_msg'];
  265. }
  266. return ['code' => -100, 'msg' => "退款失败:" . $errorMsg, 'data' => []];
  267. } catch (\Exception $e) {
  268. return ['code' => -110, 'msg' => $e->getMessage(), 'data' => []];
  269. }
  270. }
  271. }