BaseWechat.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <?php
  2. /**
  3. * @note: 微信支付
  4. * @author: jdzor <895947580@qq.com>
  5. * @date: 2019/3/15
  6. */
  7. namespace payment\wechat;
  8. class BaseWechat
  9. {
  10. const API_URL_PREFIX = 'https://api.mch.weixin.qq.com'; //接口API URL前缀
  11. protected $signType = 'MD5'; //签名方式
  12. protected $appId; //公众账号ID
  13. protected $mchId; //商户号
  14. protected $key; //支付密钥
  15. protected $sslcertPath; //证书路径
  16. protected $sslkeyPath; //证书路径
  17. protected $notifyUrl; //支付结果回调通知地址
  18. protected function __construct($config)
  19. {
  20. isset($config['appid']) || json_error('缺少参数:appid');
  21. isset($config['mch_id']) || json_error('缺少参数:mch_id');
  22. isset($config['key']) || json_error('缺少参数:key');
  23. $this->appId = $config['appid']; //支付、退款、企业付款使用
  24. $this->mchId = $config['mch_id'];
  25. $this->key = $config['key'];
  26. }
  27. //生成签名
  28. protected function makeSign($param)
  29. {
  30. //签名步骤一:按字典序排序数组参数
  31. ksort($param);
  32. $param = array_filter($param);
  33. $string = $this->toUrlParams($param);
  34. //签名步骤二:在string后加入KEY
  35. $string = $string . "&key=" . $this->key;
  36. //签名步骤三:MD5加密
  37. $string = md5($string);
  38. //签名步骤四:所有字符转为大写
  39. $result = strtoupper($string);
  40. return $result;
  41. }
  42. //参数拼接 url: key=value&key=value
  43. protected function toUrlParams($param)
  44. {
  45. $string = '';
  46. if ($param) {
  47. $array = [];
  48. foreach ($param as $key => $val) {
  49. $array[] = $key . '=' . $val;
  50. }
  51. $string = implode("&", $array);
  52. }
  53. return $string;
  54. }
  55. //输出xml字符
  56. protected function dataToXml($param)
  57. {
  58. if (!is_array($param) || count($param) <= 0) return false;
  59. $xml = "<xml>";
  60. foreach ($param as $key => $val) {
  61. if (is_numeric($val)) {
  62. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  63. } else {
  64. $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
  65. }
  66. }
  67. $xml .= "</xml>";
  68. return $xml;
  69. }
  70. //将xml转为array
  71. protected function xmlToArray($xml)
  72. {
  73. if (!$xml) return false;
  74. //禁止引用外部xml实体
  75. libxml_disable_entity_loader(true);
  76. $xmlString = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
  77. $array = json_decode(json_encode($xmlString), true);
  78. return $array;
  79. }
  80. //产生一个指定长度的随机字符串 不长于32位
  81. protected function getRandomString($length = 32)
  82. {
  83. $str = '';
  84. $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
  85. for ($i = 0; $i < $length; $i++) {
  86. $str .= $strPol[mt_rand(0, strlen($strPol) - 1)];
  87. }
  88. return $str;
  89. }
  90. //post方式提交xml到对应的接口url
  91. protected function postXmlCurl($xml, $url, $useCert = false, $second = 30)
  92. {
  93. $ch = curl_init();
  94. curl_setopt($ch, CURLOPT_TIMEOUT, $second);
  95. curl_setopt($ch, CURLOPT_URL, self::API_URL_PREFIX . $url);
  96. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  97. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //生产环境设置为2
  98. curl_setopt($ch, CURLOPT_HEADER, false); //设置header
  99. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //要求结果为字符串且输出到屏幕上
  100. if ($useCert) {
  101. //使用证书:cert 与 key 分别属于两个.pem文件
  102. curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
  103. curl_setopt($ch, CURLOPT_SSLCERT, $this->sslcertPath);
  104. curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
  105. curl_setopt($ch, CURLOPT_SSLKEY, $this->sslkeyPath);
  106. }
  107. //post提交方式
  108. curl_setopt($ch, CURLOPT_POST, true);
  109. curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
  110. if (!($data = curl_exec($ch))) $data = ['errno' => curl_errno($ch), 'error' => curl_error($ch)] + curl_getinfo($ch);
  111. curl_close($ch);
  112. return $data;
  113. }
  114. //get方式提交
  115. protected function getCurl($url)
  116. {
  117. $ch = curl_init();
  118. curl_setopt($ch, CURLOPT_URL, $url);
  119. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  120. curl_setopt($ch, CURLOPT_TIMEOUT, 60); //设置cURL允许执行的最长秒数
  121. curl_setopt($ch, CURLOPT_HEADER, false); //是否抓取头文件的信息,默认值为false,表示不显示响应头
  122. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  123. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  124. if (!($data = curl_exec($ch))) $data = ['errno' => curl_errno($ch), 'error' => curl_error($ch)] + curl_getinfo($ch);
  125. //转码
  126. $fromEncoding = 'auto'; //未知原编码,通过auto自动检测后,转换编码为utf-8
  127. if (is_array($data)) {
  128. $data = json_decode(mb_convert_encoding(json_encode($data), 'UTF-8', $fromEncoding), true);
  129. } else {
  130. $data = json_decode(mb_convert_encoding($data, 'UTF-8', $fromEncoding), true);
  131. }
  132. curl_close($ch);
  133. return $data;
  134. }
  135. }