Micro.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. namespace app\ais\event;
  3. use app\common\model\SystemMemberPayment;
  4. use EasyWeChat\Factory;
  5. class Micro
  6. {
  7. protected static $mch_id; //商户号
  8. protected static $mch_key; //32位密钥
  9. protected static $mch_private_key; //商户私钥
  10. protected static $wechat_serial_no; // 平台证书序列号
  11. protected static $serial_no = '275E949C1475DFCE8DDBEB5AC195D9DFABF48906'; // 商户证书序列号
  12. protected static $apiv3_key = '37Qx18rqjVVAWPXnuMSsL7hqbsxMFLSr'; // APIv3 密钥
  13. protected static $wechat_cert; // 平台证书
  14. //初始化配置
  15. public static function config(int $miniapp_id,$flag = false){
  16. $config = SystemMemberPayment::config($miniapp_id,'wepay');
  17. if(empty($config)) return;
  18. self::$mch_id = $config['mch_id'];
  19. self::$mch_key = $config['key'];
  20. self::$mch_private_key = self::getPrivateKey($config['key_path']);
  21. //如果需要,ture为获取平台证书
  22. if($flag){
  23. $data = [
  24. // 必要配置
  25. 'mch_id' => $config['mch_id'], // 服务商的商户号
  26. 'key' => $config['key'], // API 密钥
  27. 'apiv3_key' => self::$apiv3_key, // APIv3 密钥
  28. 'cert_path' => $config['cert_path'], // XXX: 绝对路径!!!!
  29. 'key_path' => $config['key_path'], // XXX: 绝对路径!!!!
  30. ];
  31. $response = Factory::microMerchant($data)->certficates->get();
  32. self::$wechat_cert = $response['certificates'];
  33. self::$wechat_serial_no = $response['serial_no'];
  34. }
  35. }
  36. //获取商户私钥
  37. public static function getPrivateKey($key){
  38. return openssl_get_privatekey(file_get_contents($key));
  39. }
  40. //签名
  41. public static function sign($url,$http_mothod,$timestamp,$nonce,$body,$mch_private_key,$merchant_id,$serial_no){
  42. $url_parts = parse_url($url);
  43. $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
  44. $message =
  45. $http_mothod . "\n".
  46. $canonical_url . "\n".
  47. $timestamp . "\n".
  48. $nonce . "\n".
  49. $body . "\n";
  50. openssl_sign($message,$raw_sign,$mch_private_key,'sha256WithRSAEncryption');
  51. $sign = base64_encode($raw_sign);
  52. $token = sprintf('mchid="%s",nonce_str="%s",signature="%s",timestamp="%d",serial_no="%s"', $merchant_id,$nonce,$sign,$timestamp,$serial_no);
  53. return $token;
  54. }
  55. //发送请求
  56. protected static function curl($url,$data = [],$sign,$method = "POST"){
  57. //设置header头
  58. $header = [
  59. 'Accept:application/json',
  60. 'User-Agent:'.self::$mch_id,
  61. 'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign,
  62. 'Content-Type:application/json',
  63. 'Wechatpay-Serial:' . self::$wechat_serial_no ?? self::$serial_no,
  64. ];
  65. $curl = curl_init();
  66. curl_setopt($curl,CURLOPT_URL,$url);
  67. curl_setopt($curl,CURLOPT_HTTPHEADER,$header);
  68. curl_setopt($curl,CURLOPT_HEADER,false);
  69. curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
  70. curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
  71. if($method == "POST"){
  72. curl_setopt($curl,CURLOPT_POST,true);
  73. curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
  74. }
  75. $result = curl_exec($curl);
  76. curl_close($curl);
  77. return $result;
  78. }
  79. //加密
  80. private static function getEncrypt($str){
  81. //$str是待加密字符串
  82. $encrypted = '';
  83. if (openssl_public_encrypt($str,$encrypted,self::$wechat_cert,OPENSSL_PKCS1_OAEP_PADDING)) {
  84. //base64编码
  85. $sign = base64_encode($encrypted);
  86. } else {
  87. throw new \Exception('encrypt failed');
  88. }
  89. return $sign;
  90. }
  91. //随机字符串
  92. public static function nonce_str(){
  93. return date('YmdHis',time().rand(10000,99999));
  94. }
  95. /**
  96. * 上传图片到微信服务器
  97. * @param integer $appid 来源小程序
  98. * @return void
  99. */
  100. public static function uploadMedia(int $miniapp_id,string $filename){
  101. self::config($miniapp_id);
  102. $url = 'https://api.mch.weixin.qq.com/v3/merchant/media/upload';
  103. $fi = new \finfo(FILEINFO_MIME_TYPE);
  104. $mime_type = $fi->file($filename);
  105. $data['filename'] = pathinfo($filename, PATHINFO_BASENAME);
  106. $meta['filename'] = pathinfo($filename, PATHINFO_BASENAME);
  107. $meta['sha256'] = hash_file('sha256', $filename);
  108. $boundary = uniqid(); //分割符号
  109. $sign = self::sign($url,'POST',time(),self::nonce_str(),json_encode($meta),self::$mch_private_key,self::$mch_id,self::$serial_no);//$http_method要大写
  110. $header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
  111. $header[] = 'Accept:application/json';
  112. $header[] = 'Authorization:WECHATPAY2-SHA256-RSA2048 '.$sign;
  113. $header[] = 'Content-Type:multipart/form-data;boundary='.$boundary;
  114. $boundaryStr = "--{$boundary}\r\n";
  115. $out = $boundaryStr;
  116. $out .= 'Content-Disposition: form-data; name="meta"'."\r\n";
  117. $out .= 'Content-Type: application/json'."\r\n";
  118. $out .= "\r\n";
  119. $out .= json_encode($meta)."\r\n";
  120. $out .= $boundaryStr;
  121. $out .= 'Content-Disposition: form-data; name="file"; filename="'.$data['filename'].'"'."\r\n";
  122. $out .= 'Content-Type: '.$mime_type.';'."\r\n";
  123. $out .= "\r\n";
  124. $out .= file_get_contents($filename)."\r\n";
  125. $out .= "--{$boundary}--\r\n";
  126. $ch = curl_init();
  127. curl_setopt($ch, CURLOPT_URL, $url);
  128. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  129. //避免https 的ssl验证
  130. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  131. curl_setopt($ch, CURLOPT_SSLVERSION, false);
  132. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  133. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  134. curl_setopt($ch, CURLOPT_POST, true);
  135. curl_setopt($ch, CURLOPT_POSTFIELDS, $out);
  136. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  137. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  138. // 模拟来源
  139. curl_setopt($ch, CURLOPT_REFERER, "");
  140. $response = curl_exec($ch);
  141. if ($error = curl_error($ch)) {
  142. return $error;
  143. }
  144. curl_close($ch);
  145. return $response;
  146. }
  147. //进件
  148. public static function submit($miniapp_id,$params){
  149. self::config($miniapp_id,true);
  150. $params['contact_info']['contact_name'] = self::getEncrypt($params['contact_info']['contact_name']);
  151. $params['contact_info']['contact_id_number'] = self::getEncrypt($params['contact_info']['contact_id_number']);
  152. $params['contact_info']['mobile_phone'] = self::getEncrypt($params['contact_info']['mobile_phone']);
  153. $params['contact_info']['contact_email'] = self::getEncrypt($params['contact_info']['contact_email']);
  154. $params['bank_account_info']['account_number'] = self::getEncrypt($params['bank_account_info']['account_number']);
  155. $params['bank_account_info']['account_name'] = self::getEncrypt($params['bank_account_info']['account_name']);
  156. $params['subject_info']['identity_info']['id_card_info']['id_card_name'] = self::getEncrypt($params['subject_info']['identity_info']['id_card_info']['id_card_name']);
  157. $params['subject_info']['identity_info']['id_card_info']['id_card_number'] = self::getEncrypt($params['subject_info']['identity_info']['id_card_info']['id_card_number']);
  158. if($params['subject_info']['identity_info']['owner'] == 1){
  159. $params['subject_info']['identity_info']['owner'] = true;
  160. }else{
  161. $params['subject_info']['identity_info']['owner'] = false;
  162. }
  163. $url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/';
  164. $sign = self::sign($url, 'POST', time(), self::nonce_str(), json_encode($params), self::$mch_private_key, self::$mch_id, self::$serial_no);
  165. $result = self::curl($url, json_encode($params), $sign);
  166. return json_decode($result,true);
  167. }
  168. //查询进件
  169. public static function query($miniapp_id,$no){
  170. self::config($miniapp_id);
  171. $url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/' . $no;
  172. $sign = self::sign($url, 'GET', time(), self::nonce_str(), '', self::$mch_private_key, self::$mch_id, self::$serial_no);
  173. $result = self::curl($url, '', $sign, 'GET');
  174. return json_decode($result,true);
  175. }
  176. //修改结算帐号
  177. public static function modify($miniapp_id,$params){
  178. self::config($miniapp_id,true);
  179. $params['account_number'] = self::getEncrypt($params['account_number']);
  180. $url = sprintf('https://api.mch.weixin.qq.com/v3/apply4sub/sub_merchants/%s/modify-settlement', $params['sub_mchid']);
  181. $sign = self::sign($url, 'POST', time(), self::nonce_str(), json_encode($params), self::$mch_private_key, self::$mch_id, self::$serial_no);
  182. $result = self::curl($url, json_encode($params), $sign);
  183. return json_decode($result,true);
  184. }
  185. //查询结算账户
  186. public static function settlement($miniapp_id,$sub_mchid){
  187. self::config($miniapp_id);
  188. $url = sprintf('https://api.mch.weixin.qq.com/v3/apply4sub/sub_merchants/%s/settlement', $sub_mchid);
  189. $sign = self::sign($url, 'GET', time(), self::nonce_str(), '', self::$mch_private_key, self::$mch_id, self::$serial_no);
  190. $result = self::curl($url, '', $sign, 'GET');
  191. return json_decode($result,true);
  192. }
  193. }