LdysPay.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. /**
  3. * 聚合渠道相关公共业务逻辑类
  4. *
  5. */
  6. namespace app\common\logic;
  7. use app\service\PayService;
  8. use Exception;
  9. use GuzzleHttp\Client;
  10. use GuzzleHttp\Exception\GuzzleException;
  11. use LdzfPay\UmfPayService\UmfService;
  12. use think\Cache;
  13. use think\Config;
  14. ini_set('date.timezone','Asia/Shanghai');
  15. define("UMF_PAY_REQ_URL","https://pay.soopay.net/spay/pay/payservice.do");
  16. define("UMF_METHOD_GET","get");
  17. define("UMF_METHOD_POST","post");
  18. //子商户入网
  19. define("UMF_RESTPAY_AUTHORIZE","https://pay.soopay.net/spay_restPay/oauth/authorize");
  20. define("UMF_RESTPAY_ADDCHILDMERINFO","https://pay.soopay.net/spay_restPay/addChildMerInfo");
  21. define("UMF_RESTPAY_UPLOADCHILDFILE","https://pay.soopay.net/spay_restPay/uploadChildFile");
  22. define("UMF_RESTPAY_CHANGECHILDREBUT","https://pay.soopay.net/spay_restPay/changeChildRebut");
  23. define("UMF_RESTPAY_SELECTCHILDMERSTATE","https://pay.soopay.net/spay_restPay/selectChildMerState");
  24. class LdysPay {
  25. private $order_prefix = 'ldzf_order:';
  26. private $app_id, $secret;
  27. public function __construct(){
  28. $this->app_id = config('wxmp_config.app_id');
  29. $this->secret = config('wxmp_config.secret');
  30. }
  31. // 获取微信小程序支付订单参数
  32. public function getWxmpPay($pay_type, $data){
  33. if(empty($pay_type)){
  34. return ['code' => 100, 'data' => [], 'msg' => '缺少必要参数:pay_type'];
  35. }
  36. if(empty($data['title'])){
  37. return ['code' => 100, 'data' => [], 'msg' => '缺少必要参数:title'];
  38. }
  39. if(empty($data['order_id'])){
  40. return ['code' => 100, 'data' => [], 'msg' => '缺少必要参数:order_id'];
  41. }
  42. if(empty($data['amount'])){
  43. return ['code' => 100, 'data' => [], 'msg' => '缺少必要参数:amount'];
  44. }
  45. if(empty($data['open_id'])){
  46. return ['code' => 100, 'data' => [], 'msg' => '缺少必要参数:open_id'];
  47. }
  48. $orderInfo = Cache::store('redis')->get($this->order_prefix.$data['order_id']);
  49. if(!$orderInfo){
  50. return ['code' => 100, 'data' => [], 'msg' => '订单号有误,请重新支付!'];
  51. }
  52. // $data = [
  53. // 'title' => 'xxx',
  54. // 'amount' => 'xxx',
  55. // 'game_id' => 'xxx',
  56. // 'is_coin' => 'xxx',
  57. // ];
  58. $config = Config::get('ldys_pay');
  59. $orderInfo = json_decode($orderInfo, true);
  60. $notify_url = $config['notify'][$pay_type]['notify_url'];
  61. if (!empty($orderInfo['is_coin']) && $orderInfo['is_coin'] == 1) {
  62. $notify_url = $config['notify'][$pay_type]['notify_coin_url'];
  63. }
  64. $params = [
  65. "mer_id" => $config['mer_id'],
  66. "notify_url" => $notify_url,
  67. "amt_type" => "RMB",
  68. "goods_inf" => $data['title'],
  69. "order_id" => $data['order_id'],
  70. "mer_date" => date("Ymd"),
  71. "amount" => formatYuanToFen($data['amount']), // 分
  72. "user_ip" => getUserIp(),
  73. "open_id" => $data['open_id'],
  74. "app_id" => $config['app_id'],
  75. "scancode_type" => "WECHAT",
  76. "mer_flag" => "KMER",
  77. ];
  78. log_message('# '. $data['order_id'].' : ' . json_encode($params), 'log', LOG_PATH . 'three/ldys_pay/');
  79. $service = new UmfService($config['mer_id']);
  80. $res = $service->miniProgramPayMap($params);
  81. /**
  82. *
  83. * <pre>array(13) {
  84. * ["app_id"]=>
  85. * string(18) "wx117849d0c0632d22"
  86. * ["bank_seq"]=>
  87. * string(0) ""
  88. * ["mer_date"]=>
  89. * string(8) "20240425"
  90. * ["mer_id"]=>
  91. * string(5) "55282"
  92. * ["nonce_str"]=>
  93. * string(32) "7a9effbfe6324e089de9feb49c5f019f"
  94. * ["order_id"]=>
  95. * string(9) "633876442"
  96. * ["package"]=>
  97. * string(46) "prepay_id=wx25163615048261aa76cccf65807c9b0000"
  98. * ["pay_sign"]=>
  99. * string(344) "eHqSBYowy6LjbDYcHCcuh1GpY8K+mndcSU/WgWzmJlHWeUgefcix8yniZq4vVvgdODc2dZTwenPZa4hJhbp0Vw9x8uW6jb+GnlziJRZzj1mfer84UgoFh5iZy00mkBobe9VMdyZIFSbIhp41ICFpJulIFPqqrZ6T+kB4rw/kRkYNf7Px48w8XUWB2uGvLllUhjGQMdmnkULBO6x246GaXs2x6BFQbDxShOTX7KI48Whp6d4ZbJhLTSXUhN4N+6cKf7OH2jZNk/crBoQobQT4hhhUYnR44dIXimFsjLXCh+TQ5ZiNSw9xnj5Q7ER40ncl/5GSqectmtG4hCmrCv3xDg=="
  100. * ["ret_code"]=>
  101. * string(4) "0000"
  102. * ["ret_msg"]=>
  103. * string(12) "交易成功"
  104. * ["time_stamp"]=>
  105. * string(10) "1714034175"
  106. * ["trade_no"]=>
  107. * string(16) "3404251636078494"
  108. * ["weixin_sign_type"]=>
  109. * string(3) "RSA"
  110. * }
  111. */
  112. if ($res['ret_code'] == '00060780') {
  113. return ['code' => 110, 'data' => [], 'msg' => '订单已支付成功,无须再次支付'];
  114. }
  115. if ($res['ret_code'] != '0000') {
  116. // TODO: 钉钉通知
  117. return ['code' => 120, 'data' => [], 'msg' => $res['ret_msg']];
  118. }
  119. $res = [
  120. 'timeStamp' => $res['time_stamp'],
  121. 'nonceStr' => $res['nonce_str'],
  122. 'package' => $res['package'],
  123. 'signType' => $res['weixin_sign_type'],
  124. 'paySign' => $res['pay_sign'],
  125. ];
  126. return ['code' => 200, 'data' => $res, 'msg' => 'success'];
  127. }
  128. // 生成微信下单URL
  129. public function generateWxmpPayUrl($order_id, $game_id){
  130. // return ['error' => false, 'data' => 'https://wxaurl.cn/qmyZ8gwAjKv', 'msg' => 'success'];
  131. try {
  132. $access_token = $this->getWxToekn();
  133. $env_version = 'release';
  134. // if(config('app_status') == 'dev'){
  135. // $env_version = 'develop';
  136. // }
  137. $data = [
  138. 'path' => '/pages/index/pay',
  139. 'is_expire' => true,
  140. 'query' => "order_id={$order_id}&game_id={$game_id}",
  141. 'expire_type' => 0,
  142. 'expire_time' => time() + 300,
  143. // 'expire_type' => 1,
  144. // 'expire_interval' => 1,
  145. 'env_version' => $env_version, // 默认值"release"。要打开的小程序版本。正式版为 "release",体验版为"trial",开发版为"develop",仅在微信外打开时生效。
  146. ];
  147. $res = getHttpPost("https://api.weixin.qq.com/wxa/generate_urllink?access_token=" . $access_token, json_encode($data));
  148. $res = json_decode($res, true);
  149. if ($res['errcode'] !== 0 || empty($res['url_link'])) {
  150. return ['error' => true, 'data' => [], 'msg' => '001-1_LdysPay_生成微信URL失败'];
  151. }
  152. }catch (Exception $e) {
  153. // dump($e->getMessage(), $e->getFile(), $e->getLine());
  154. return ['error' => true, 'data' => [], 'msg' => '001-2_LdysPay_生成微信URL失败.Exception:' . $e->getMessage()];
  155. }
  156. return ['error' => false, 'data' => $res['url_link'], 'msg' => 'success'];
  157. }
  158. // 获取微信Token
  159. public function getWxToekn(){
  160. $cache_key = 'wx_token:'.$this->app_id;
  161. $token = Cache::get($cache_key);
  162. if(!$token){
  163. $res = getHttpGet("https://api.weixin.qq.com/cgi-bin/token", [
  164. 'grant_type' => 'client_credential',
  165. 'appid' => $this->app_id,
  166. 'secret' => $this->secret
  167. ]);
  168. $res = json_decode($res, true);
  169. Cache::set($cache_key, $res['access_token'], $res['expires_in']-30);
  170. $token = $res['access_token'];
  171. }
  172. return $token;
  173. }
  174. // 记录订单
  175. public function recordOrder($order_id, $data){
  176. $payUrl = '';
  177. try {
  178. $urlResult = $this->generateWxmpPayUrl($order_id, $data['game_id']);
  179. if($urlResult['error'] == true){
  180. return ['error' => true, 'data' => [], 'msg' => "001_".$urlResult['msg']];
  181. }
  182. $payUrl = $urlResult['data'];
  183. Cache::store('redis')->set($this->order_prefix.$order_id, json_encode($data), 300);
  184. }catch (Exception $e) {
  185. return ['error' => true, 'data' => [], 'msg' => '002_LdysPay_下单失败:'.$e->getMessage()];
  186. }
  187. return ['error' => false, 'data' => ['pay_url' => $payUrl], 'msg' => 'success'];
  188. }
  189. // 查询订单
  190. public function queryOrder($order_id){
  191. try {
  192. $result = Cache::store('redis')->get($this->order_prefix.$order_id);
  193. if(!$result){
  194. return ['error' => true, 'data' => [], 'msg' => '当前订单已过期或不存在!'];
  195. }
  196. }catch (Exception $e) {
  197. return ['error' => true, 'data' => [], 'msg' => '查询失败!'];
  198. }
  199. return ['error' => false, 'data' => json_decode($result, true), 'msg' => 'success'];;
  200. }
  201. // 回调处理
  202. public function notify(){
  203. $data = request()->only([
  204. "amount", "amt_type", "bank_mer_id", "bank_trace", "charset", "error_code", "media_type", "mer_date", "mer_id", "open_id", "order_id", "pay_date", "pay_seq", "pay_success_time", "pay_type", "service", "settle_date", "trade_no", "trade_state", "version", "sign", "sign_type"
  205. ]);
  206. $notifyParamsStr = http_build_query($data);
  207. $config = Config::get('ldys_pay');
  208. $service = new UmfService($config['mer_id']);
  209. $array = $service->notifyDataParserMap($notifyParamsStr);
  210. $mer2UmfPlatStr = $service->responseUMFMap($array, $config['private_file']);
  211. $resultStr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><HTML><HEAD><META NAME="MobilePayPlatform" CONTENT="' . $mer2UmfPlatStr . '" /></HEAD><BODY></BODY></HTML>';
  212. if ($array === false) {
  213. return ['error' => true, 'data' => ['result' => '', 'data' => $resultStr], 'msg' => '回调效验失败!'];
  214. }
  215. return ['error' => false, 'data' => $resultStr, 'msg' => 'success'];
  216. }
  217. public function getWxH5Pay($pay_type, $data)
  218. {
  219. try {
  220. $config = Config::get('ldys_pay');
  221. $notify_url = $config['notify'][$pay_type]['notify_url'];
  222. if (!empty($data['is_coin']) && $data['is_coin'] == 1) {
  223. $notify_url = $config['notify'][$pay_type]['notify_coin_url'];
  224. }
  225. // 预下载
  226. $params = array(
  227. "mer_id" => $config['mer_id'],
  228. "notify_url" => $notify_url,
  229. "order_id" => $data['orderid'],
  230. "mer_date" => date("Ymd"),
  231. "amount" => formatYuanToFen($data['real_amount']), // 分
  232. "user_ip" => getUserIp(),
  233. "goods_inf" => $data['productname'],
  234. "pay_type" => 2, // 支付类型: 1=支付宝, 2=微信支付, 3=微信云闪付
  235. "page_order_type" => "H5",
  236. "mer_flag" => "KMER",
  237. "consumer_id" => $data['userid'], // 消费者ID
  238. "app_id" => $config['app_id'],
  239. );
  240. $service = new UmfService($config['mer_id']);
  241. $result = $service->appOrderMap($params);
  242. if ($result['ret_code'] == '0000') {
  243. return ['code' => 200, 'data' => ['pay_url' => $result['page_link']], 'msg' => 'success'];
  244. }
  245. // TODO: 钉钉通知
  246. return ['code' => 110, 'data' => [], 'msg' => $result['ret_msg']];
  247. } catch (Exception $e) {
  248. return ['code' => 120, 'data' => [], 'msg' => '下单失败: ' . $e->getMessage()];
  249. }
  250. }
  251. }