WechatPay.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. /**
  3. * @note: 微信支付 App支付、小程序支付、扫码支付、H5支付
  4. * @author: jdzor <895947580@qq.com>
  5. * @date: 2019/1/18
  6. */
  7. namespace payment\wechat;
  8. /**
  9. * 文档地址: https://pay.weixin.qq.com/wiki/doc/api/index.html
  10. * 预下单方法:
  11. * $wechatPay = new WechatPay($config);
  12. * $info = $wechatPay->unifiedOrder($payData);
  13. * $info['return_code'] == 'FAIL' && json_error($info['return_msg'], -2);
  14. * $info['result_code'] == 'FAIL' && json_error($info['err_code_des'], -2);
  15. *
  16. * 1.扫码支付: $info['code_url'] 此url用于生成支付二维码,然后提供给用户进行扫码支付。注意:code_url的值并非固定,使用时按照URL格式转成二维码即可
  17. * 2.H5支付: $info['mweb_url'] mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟
  18. * 可拼接跳转链接: $info['mweb_url'].'&redirect_url='.urlencode($returnUrl);
  19. * 3.App支付: $wechatPay->getAppParam($info['prepay_id']); 将结果返回给App端即可
  20. * 4.Jsapi支付: $wechatPay->getJsapiParam($info['prepay_id']); 将结果返回给微信内H5支付或小程序端即可
  21. *
  22. * Class WechatPay
  23. * @package app\common\extend\wechat
  24. */
  25. class WechatPay extends BaseWechat
  26. {
  27. const UNIFIED_ORDER_URL = "/pay/unifiedorder"; //下单地址
  28. const ORDER_QUERY_URL = "/pay/orderquery"; //查询订单
  29. const CLOSE_ORDER_URL = "/pay/closeorder"; //关闭订单
  30. public static $tradeMap = [
  31. 'jsapi' => 'JSAPI', //小程序支付、公众号支付(微信内H5环境)
  32. 'app' => 'APP', //APP支付
  33. 'native' => 'NATIVE', //扫码支付
  34. 'mweb' => 'MWEB' //H5支付,非微信内H5环境
  35. ];
  36. private $openid; //公众号支付 用户openid
  37. private $wapUrl; //H5支付 WAP网站URL地址
  38. private $wapName; //H5支付 WAP网站名
  39. private $qrcodeTimeoutExpress; //扫码支付 二维码失效时间,单位:秒
  40. public function __construct($config)
  41. {
  42. parent::__construct($config);
  43. }
  44. //下单参数检测
  45. private function check($param)
  46. {
  47. isset($param['body']) || json_error('缺少参数:body');
  48. isset($param['out_trade_no']) || json_error('缺少参数:out_trade_no');
  49. isset($param['total_fee']) || json_error('缺少参数:total_fee');
  50. isset($param['trade_type']) || json_error('缺少参数:trade_type');
  51. isset($param['notify_url']) || json_error('缺少参数:notify_url');
  52. //不同支付类型时 参数检测
  53. if ($param['trade_type'] == self::$tradeMap['jsapi']) { //公众号支付
  54. isset($param['openid']) || json_error('缺少参数:openid');
  55. $this->openid = $param['openid'];
  56. } elseif ($param['trade_type'] == self::$tradeMap['native']) { //扫码支付
  57. isset($param['expire']) || json_error('缺少参数:expire');
  58. $this->qrcodeTimeoutExpress = $param['expire'];
  59. } elseif ($param['trade_type'] == self::$tradeMap['mweb']) { //H5支付
  60. isset($param['wap_url']) || json_error('缺少参数:wap_url');
  61. isset($param['wap_name']) || json_error('缺少参数:wap_name');
  62. $this->wapUrl = $param['wap_url'];
  63. $this->wapName = $param['wap_name'];
  64. }
  65. }
  66. //下单方法
  67. public function unifiedOrder($param)
  68. {
  69. $this->check($param);
  70. $data['appid'] = $this->appId;
  71. $data['mch_id'] = $this->mchId;
  72. $data['notify_url'] = $this->notifyUrl;
  73. $data['body'] = $param['body'];
  74. $data['out_trade_no'] = $param['out_trade_no'];
  75. $data['total_fee'] = $param['total_fee'];
  76. $data['trade_type'] = $param['trade_type'];
  77. $data['notify_url'] = $param['notify_url'];
  78. $data['nonce_str'] = $this->getRandomString();
  79. $data['spbill_create_ip'] = get_client_ip();
  80. $data['sign_type'] = $this->signType;
  81. //不同支付类型时 参数传入
  82. if ($param['trade_type'] == self::$tradeMap['jsapi']) {
  83. $data['openid'] = $this->openid;
  84. } elseif ($param['trade_type'] == self::$tradeMap['native']) {
  85. $data['time_start'] = date("YmdHis");
  86. $data['time_expire'] = date("YmdHis", time() + $this->qrcodeTimeoutExpress);
  87. } elseif ($param['trade_type'] == self::$tradeMap['mweb']) {
  88. $senseInfo = ['h5_info' => ['type' => 'Wap', 'wap_url' => $this->wapUrl, 'wap_name' => $this->wapName]];
  89. $data['scene_info'] = json_encode($senseInfo);
  90. }
  91. //获取签名数据
  92. $data['sign'] = $this->makeSign($data);
  93. $xml = $this->dataToXml($data);
  94. $response = $this->postXmlCurl($xml, self::UNIFIED_ORDER_URL);
  95. if (!$response) return false;
  96. $result = $this->xmlToArray($response);
  97. return $result;
  98. }
  99. //生成 APP端支付参数
  100. public function getAppParam($prepayId)
  101. {
  102. $data['appid'] = $this->appId;
  103. $data['partnerid'] = $this->mchId;
  104. $data['prepayid'] = $prepayId;
  105. $data['package'] = 'Sign=WXPay';
  106. $data['noncestr'] = $this->getRandomString();
  107. $data['timestamp'] = time();
  108. $data['sign_type'] = $this->signType;
  109. $data['sign'] = $this->makeSign($data);
  110. return $data;
  111. }
  112. //生成微信内H5支付、小程序端支付参数 注意参数大小写
  113. public function getJsapiParam($prepayId)
  114. {
  115. $data['appId'] = $this->appId;
  116. $data['nonceStr'] = $this->getRandomString();
  117. $data['package'] = 'prepay_id=' . $prepayId;
  118. $data['timeStamp'] = time();
  119. $data['signType'] = $this->signType;
  120. $data['paySign'] = $this->makeSign($data);
  121. return $data;
  122. }
  123. //查询订单
  124. public function queryOrder($outTradeNo)
  125. {
  126. $data['appid'] = $this->appId;
  127. $data['mch_id'] = $this->mchId;
  128. $data['nonce_str'] = $this->getRandomString();
  129. $data['out_trade_no'] = $outTradeNo;
  130. $data['sign_type'] = $this->signType;
  131. $data['sign'] = $this->makeSign($data);
  132. $xml = $this->dataToXml($data);
  133. $response = $this->postXmlCurl($xml, self::ORDER_QUERY_URL);
  134. if (!$response) return false;
  135. $result = $this->xmlToArray($response);
  136. return $result;
  137. }
  138. //关闭订单
  139. public function closeOrder($outTradeNo)
  140. {
  141. $data['appid'] = $this->appId;
  142. $data['mch_id'] = $this->mchId;
  143. $data['nonce_str'] = $this->getRandomString();
  144. $data['out_trade_no'] = $outTradeNo;
  145. $data['sign_type'] = $this->signType;
  146. $data['sign'] = $this->makeSign($data);
  147. $xml = $this->dataToXml($data);
  148. $response = $this->postXmlCurl($xml, self::CLOSE_ORDER_URL);
  149. if (!$response) return false;
  150. $result = $this->xmlToArray($response);
  151. return $result;
  152. }
  153. //获取支付结果通知数据
  154. public function getNotifyData()
  155. {
  156. //获取通知的数据
  157. $xml = file_get_contents('php://input');
  158. if (!$xml) return false;
  159. $data = $this->xmlToArray($xml);
  160. if (isset($data['return_code']) && $data['return_code'] == 'FAIL') return false;
  161. return $data;
  162. }
  163. //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。
  164. //通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒
  165. public function replyNotify()
  166. {
  167. $data['return_code'] = 'SUCCESS';
  168. $data['return_msg'] = 'OK';
  169. $xml = $this->dataToXml($data);
  170. echo $xml;
  171. die();
  172. }
  173. }