WxPay.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. /**
  3. * WxPay.php
  4. *
  5. * 微信小程序开发API接口
  6. * - 小程序码
  7. * - 安全内容
  8. * - 微信支付
  9. *
  10. */
  11. namespace echowx;
  12. use think\Exception;
  13. use echowx\utils\HttpCurl;
  14. use echowx\utils\Error;
  15. use echowx\utils\SHA1;
  16. use echowx\utils\Xml;
  17. class WxPay
  18. {
  19. // 开发者中心-配置项-AppID(应用ID)
  20. protected $appId;
  21. // 开发者中心-配置项-AppSecret(应用密钥)
  22. protected $appSecret;
  23. // 微信支付商户号,商户申请微信支付后,由微信支付分配的商户收款账号
  24. protected $payMchId;
  25. // API密钥,微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
  26. protected $payKey;
  27. // 微信支付回调地址
  28. protected $payNotifyUrl;
  29. /**
  30. * 设定配置项
  31. *
  32. * @param 微信小程序配置文件读取
  33. */
  34. public function __construct()
  35. {
  36. $this->appId = config('wxconfig.appId');
  37. $this->appSecret = config('wxconfig.appSecret');
  38. $this->payMchId = config('wxconfig.payMchId');
  39. $this->payKey = config('wxconfig.payKey');
  40. $this->payNotifyUrl = config('wxconfig.payNotifyUrl');
  41. }
  42. /**
  43. * 校验access_token是否过期
  44. * @param string $token
  45. * @return bool
  46. */
  47. public function valid_access_token($token)
  48. {
  49. return $token && isset($token['expires_in']) && ($token['expires_in'] > time() + 1200);
  50. }
  51. /**
  52. * 生成新的access_token
  53. * @return mixed
  54. */
  55. public function new_access_token()
  56. {
  57. $url = self::API_DOMAIN . 'cgi-bin/token?grant_type=client_credential&appid=' . $this->appId . '&secret=' . $this->appSecret;
  58. $res = HttpCurl::get($url, 'json');
  59. // 异常处理: 获取access_token网络错误
  60. if ($res === false) {
  61. @error_log('Http Get AccessToken Error.', 0);
  62. return false;
  63. }
  64. // 异常处理: access_token获取失败
  65. if (!isset($res['access_token'])) {
  66. @error_log('Get AccessToken Error: ' . json_encode($res), 0);
  67. return false;
  68. }
  69. $res['expires_in'] += time();
  70. $fp = fopen(dirname(__FILE__)."/json/access_token.json", "w");
  71. fwrite($fp, json_encode($res));
  72. fclose($fp);
  73. return $res;
  74. }
  75. /**
  76. * 获取access_token
  77. * @return string
  78. */
  79. public function get_access_token()
  80. {
  81. $token = json_decode(file_get_contents(dirname(__FILE__)."/json/access_token.json"), true);
  82. // 验证AccessToken是否有效
  83. if (!$this->valid_access_token($token)) {
  84. // 生成新的AccessToken
  85. $token = $this->new_access_token();
  86. if ($token === false) {
  87. return false;
  88. }
  89. }
  90. return $token['access_token'];
  91. }
  92. /**
  93. * WxPay - 统一下单 - 生成预订单
  94. * @param array $conf 配置数组
  95. * @return bool|mixed
  96. */
  97. public function wxpay_unified_order($conf = [])
  98. {
  99. // [必填]公众账号ID、应用ID
  100. $conf['appid'] = $this->appId;
  101. // [必填]商户号、
  102. $conf['mch_id'] = $this->payMchId;
  103. // 设备号
  104. // - device_info
  105. // [必填]nonce_str 随机字符串
  106. $conf['nonce_str'] = SHA1::get_random_str(32);
  107. // 签名类型
  108. $conf['sign_type'] = 'MD5';
  109. // [必填]商品描述
  110. // - body
  111. // 商品详情
  112. // - detail
  113. // 附加数据
  114. // - attach
  115. // [必填]商户订单号
  116. // - out_trade_no
  117. // 货币类型
  118. // - fee_type
  119. // [必填]总金额
  120. // - total_fee
  121. // [必填]终端IP
  122. $conf['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
  123. // 交易起始时间
  124. // - time_start
  125. // 交易结束时间
  126. // - time_expire
  127. // 商品标记
  128. // - goods_tag
  129. // [必填]通知地址
  130. $conf['notify_url'] = $this->payNotifyUrl;
  131. // [必填]交易类型
  132. $conf['trade_type'] = "JSAPI";
  133. // 指定支付方式
  134. // - limit_pay
  135. // 用户标识, trade_type=JSAPI时(即公众号支付),此参数必传
  136. // - openid
  137. // [必填]签名
  138. $conf['sign'] = SHA1::getSign2($conf, 'key='.$this->payKey);
  139. // 生成xml
  140. $xml = Xml::toXml($conf);
  141. // 调用接口
  142. $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
  143. try {
  144. $res = HttpCurl::post($url, $xml);
  145. libxml_disable_entity_loader(true);
  146. $datas = json_decode(json_encode(simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  147. if($datas['return_code'] == 'FAIL'){
  148. $result = array('status'=>40014,'data'=>$datas['return_msg']);
  149. return $result;
  150. }if($datas['result_code'] == 'FAIL'){
  151. $result = array('status'=>40014,'data'=>$datas['err_code'].":".$datas['err_code_des']);
  152. return $result;
  153. }
  154. //判断签名是否存在,及签名是否正确
  155. if(!array_key_exists('sign', $datas)){
  156. $result = array('status'=>40011,'data'=>'签名错误!');
  157. return $result;
  158. }
  159. $result = array('status'=>1,'data'=>$datas);
  160. return $result;
  161. } catch (\Exception $e) {
  162. $result = array('status'=>40011,'data'=>'下单失败,请稍后重试!');
  163. return $result;
  164. }
  165. }
  166. /**
  167. * WxPay - 获取jsapi支付的参数
  168. * 用于直接填入js函数作为参数
  169. * @param string $prepayId 预生成订单ID
  170. * @return string
  171. */
  172. public function get_jsapi_param($prepayId)
  173. {
  174. $input = [
  175. 'appId' => $this->appId,
  176. 'nonceStr' => SHA1::get_random_str(32),
  177. 'package' => 'prepay_id='.$prepayId,
  178. 'timeStamp' => (string)time(),
  179. 'signType' => 'MD5'
  180. ];
  181. $input['paySign'] = SHA1::getSign2($input, 'key='.$this->payKey);
  182. return $input;
  183. }
  184. /**
  185. * WxPay - 处理微信支付异步通知
  186. * 包含
  187. * @return array [是否支付成功, 异步通知的原始数据, 回复微信异步通知的数据]
  188. */
  189. public function notify()
  190. {
  191. $xml = file_get_contents('php://input');
  192. try {
  193. libxml_disable_entity_loader(true);
  194. $data = Xml::toArr($xml);
  195. if (! is_array($data)) {
  196. $result = array('status'=>40014,'data'=>'支付回调出错。');
  197. return $result;
  198. }
  199. // 格式是否正确
  200. if (! array_key_exists('return_code', $data)) {
  201. $result = array('status'=>40011,'data'=>'return_code is not set。');
  202. return $result;
  203. }
  204. // 是否支付成功
  205. if ($data['return_code'] != 'SUCCESS') {
  206. $result = array('status'=>40011,'data'=>'return_code is '.$data['return_code']);
  207. return $result;
  208. }
  209. // 签名是否正确
  210. $sign1 = SHA1::getSign2($data, 'key='.$this->payKey);
  211. if ($sign1 != $data['sign']) {
  212. $result = array('status'=>40011,'data'=>'签名验证失败。');
  213. return $result;
  214. }
  215. //返回结果
  216. $result = array('status'=>1,'data'=>$data);
  217. return $result;
  218. } catch (\Exception $e) {
  219. $result = array('status'=>40011,'data'=>'回调失败!');
  220. return $result;
  221. }
  222. }
  223. /**
  224. * WxPay - 回复微信异步通知
  225. * 包含
  226. * @param array $info 回复内容数组
  227. */
  228. public function reply_notify()
  229. {
  230. $data['return_code'] = 'SUCCESS';
  231. $data['return_msg'] = 'OK';
  232. $xml = Xml::toXml($data);
  233. echo $xml;
  234. die();
  235. }
  236. }