WxPay.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <?php
  2. namespace addons\service\library;
  3. use think\Request;
  4. /*
  5. * 小程序微信支付
  6. */
  7. class WxPay {
  8. protected $userAppid ;
  9. protected $skillAppid;
  10. protected $shopAppid;
  11. protected $mch_id;
  12. protected $key;
  13. protected $openid;
  14. protected $out_trade_no;
  15. protected $body;
  16. protected $attach;
  17. protected $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
  18. public function __construct()
  19. {
  20. $payConfig = get_addon_config('epay');
  21. $serviceConfig = \app\api\model\service\ProjectConfigure::getProjectConfig();
  22. $this->userAppid = $serviceConfig['userappid'];
  23. $this->skillAppid = $serviceConfig['skillappid'];
  24. $this->shopAppid = $serviceConfig['shopappid'];
  25. $this->mch_id = $payConfig['wechat']['mch_id'];
  26. $this->key = $payConfig['wechat']['key'];
  27. }
  28. public function pay($data) {
  29. //统一下单接口
  30. $return = $this->wxApp($data);
  31. return $return;
  32. }
  33. private function wxApp($param) {
  34. //统一下单接口
  35. switch ($param['type'])
  36. {
  37. case 0:
  38. $this->appid = $this->userAppid;
  39. break;
  40. case 1:
  41. $this->appid = $this->skillAppid;
  42. break;
  43. case 2:
  44. $this->appid = $this->shopAppid;
  45. break;
  46. default :
  47. $this->appid = $this->userAppid;
  48. }
  49. $unifiedorder = $this->unifiedorder($param);
  50. $parameters = array(
  51. 'appId' => $this->appid,
  52. 'timeStamp' => (string) time(), //时间戳
  53. 'nonceStr' => $this->createNoncestr(), //随机串
  54. 'package' => 'prepay_id=' . $unifiedorder['prepay_id'], //数据包
  55. 'signType' => 'MD5'//签名方式
  56. );
  57. //签名
  58. $parameters['paySign'] = $this->getSign($parameters);
  59. return $parameters;
  60. }
  61. //统一下单接口
  62. private function unifiedorder($param) {
  63. $url = $this->url;
  64. $ip = request()->ip();
  65. $parameters = array(
  66. 'appid' => $this->appid, //小程序appid
  67. 'mch_id' => $this->mch_id, //商户号
  68. 'nonce_str' => $this->createNoncestr(),
  69. 'body' => $param['body'],
  70. 'out_trade_no'=> $param['out_trade_no'],
  71. 'total_fee' => $param['total_fee'],
  72. 'spbill_create_ip' => $ip,
  73. 'notify_url' => $param['notify_url'], //回调地址
  74. 'openid' => $param['openid'], //用户openid
  75. 'trade_type' => 'JSAPI'
  76. );
  77. //统一下单签名
  78. $parameters['sign'] = $this->getSign($parameters);
  79. $xmlData = $this->arrayToXml($parameters);
  80. $return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));
  81. return $return;
  82. }
  83. private static function postXmlCurl($xml, $url, $second = 30)
  84. {
  85. $ch = curl_init();
  86. //设置超时
  87. curl_setopt($ch, CURLOPT_TIMEOUT, $second);
  88. curl_setopt($ch, CURLOPT_URL, $url);
  89. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  90. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验
  91. //设置header
  92. curl_setopt($ch, CURLOPT_HEADER, FALSE);
  93. //要求结果为字符串且输出到屏幕上
  94. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  95. //post提交方式
  96. curl_setopt($ch, CURLOPT_POST, TRUE);
  97. curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
  98. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
  99. curl_setopt($ch, CURLOPT_TIMEOUT, 40);
  100. set_time_limit(0);
  101. //运行curl
  102. $data = curl_exec($ch);
  103. //返回结果
  104. if ($data) {
  105. curl_close($ch);
  106. return $data;
  107. } else {
  108. $error = curl_errno($ch);
  109. curl_close($ch);
  110. throw new WxPayException("curl出错,错误码:$error");
  111. }
  112. }
  113. //数组转换成xml
  114. private function arrayToXml($arr) {
  115. $xml = "<root>";
  116. foreach ($arr as $key => $val) {
  117. if (is_array($val)) {
  118. $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
  119. } else {
  120. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  121. }
  122. }
  123. $xml .= "</root>";
  124. return $xml;
  125. }
  126. //xml转换成数组
  127. public function xmlToArray($xml) {
  128. //禁止引用外部xml实体
  129. if (version_compare(PHP_VERSION, '8.0.0', '<')) {
  130. libxml_disable_entity_loader(true);
  131. }
  132. $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
  133. $val = json_decode(json_encode($xmlstring), true);
  134. return $val;
  135. }
  136. //作用:产生随机字符串,不长于32位
  137. private function createNoncestr($length = 32) {
  138. $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
  139. $str = "";
  140. for ($i = 0; $i < $length; $i++) {
  141. $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  142. }
  143. return $str;
  144. }
  145. //作用:生成签名
  146. public function getSign($Obj) {
  147. foreach ($Obj as $k => $v) {
  148. $Parameters[$k] = $v;
  149. }
  150. //签名步骤一:按字典序排序参数
  151. ksort($Parameters);
  152. $String = $this->formatBizQueryParaMap($Parameters, false);
  153. //签名步骤二:在string后加入KEY
  154. $String = $String . "&key=" . $this->key;
  155. //签名步骤三:MD5加密
  156. $String = md5($String);
  157. //签名步骤四:所有字符转为大写
  158. $result_ = strtoupper($String);
  159. return $result_;
  160. }
  161. ///作用:格式化参数,签名过程需要使用
  162. protected function formatBizQueryParaMap($paraMap, $urlencode) {
  163. $buff = "";
  164. ksort($paraMap);
  165. foreach ($paraMap as $k => $v) {
  166. if ($urlencode) {
  167. $v = urlencode($v);
  168. }
  169. $buff .= $k . "=" . $v . "&";
  170. }
  171. $reqPar = '';
  172. if (strlen($buff) > 0) {
  173. $reqPar = substr($buff, 0, strlen($buff) - 1);
  174. }
  175. return $reqPar;
  176. }
  177. }