IndexController.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php
  2. namespace app\models\intracityService;
  3. use app\models\Option;
  4. use app\models\StoreMini;
  5. use app\utils\Wechat\WechatMini;
  6. use EasyWeChat\Factory;
  7. use EasyWeChat\Kernel\BaseClient;
  8. use app\utils\phpseclib\phpseclib\Crypt\RSA;
  9. use yii\base\Model;
  10. use app\models\WechatIntracityService;
  11. class IndexController extends Model
  12. {
  13. public $params;
  14. public $app;
  15. public $id;
  16. public $wechatService;
  17. public $store_id;
  18. public $base_uri = "https://api.weixin.qq.com/";
  19. private $param = [
  20. 'app_id' => '',
  21. 'url' => '',
  22. 'local_sym_key' => '',//API对称密钥
  23. 'local_sym_sn' => '',//API对称密钥编号
  24. 'local_sn' => '',//平台证书编号
  25. 'local_key' => '',
  26. 'local_certificate' => '',//平台证书
  27. 'store_id' => '',
  28. 'out_store_id' => '',
  29. ];
  30. private $key =[
  31. 'rsa_sn'=>'',
  32. 'rsa_private_key'=>''
  33. ];
  34. public function rules()
  35. {
  36. return [
  37. [['store_id'], 'integer']
  38. ];
  39. }
  40. public function getWechatApp($store_id)
  41. {
  42. $this->store_id = $store_id;
  43. $result = $this->initData($store_id);
  44. $this->param['local_sym_sn'] = $result['local_sym_sn'];
  45. $this->param['local_sym_key'] = $result['local_sym_key'];
  46. $this->key['rsa_sn'] = $result['rsa_sn'];
  47. $this->key['rsa_private_key'] = $result['rsa_private_key'];
  48. if (empty($this->param['local_sym_sn']) ||
  49. empty($this->param['local_sym_key']) ||
  50. empty($this->key['rsa_sn']) ||
  51. empty($this->key['rsa_private_key'])
  52. ) {
  53. $this->app = null;
  54. } else {
  55. $this->app = (new WechatMini())::getWechatConfig($this->store_id);
  56. }
  57. $storeMini = StoreMini::findOne(['is_use' => 1, 'store_id' => $store_id]);
  58. if ($storeMini) {
  59. $this->param['app_id'] = Option::get("platform_third_appid", 0, 'saas')['value'];
  60. } else {
  61. if ($this->app) {
  62. $this->param['app_id'] = $this->app->getConfig()['app_id'];
  63. }
  64. }
  65. }
  66. public function initData($store_id) {
  67. $keys = [
  68. 'local_sym_sn',
  69. 'local_sym_key',
  70. 'rsa_sn',
  71. 'rsa_private_key'
  72. ];
  73. // if (is_open_platform()) {
  74. // $store_id = 0;
  75. // }
  76. $storeMini = StoreMini::findOne(['is_use' => 1, 'store_id' => $store_id]);
  77. if ($storeMini) {
  78. $delivery_data = Option::get($keys, 0, 'delivery');
  79. } else {
  80. $delivery_data = Option::get($keys, $store_id, 'delivery');
  81. }
  82. if ($delivery_data) {
  83. $arr = [];
  84. foreach ($delivery_data as $value) {
  85. $index = array_search($value['name'], $keys);
  86. unset($keys[$index]);
  87. $arr[$value['name']] = $value['value'];
  88. }
  89. foreach ($keys as $key) {
  90. $arr[$key] = '';
  91. }
  92. $delivery_data = $arr;
  93. } else {
  94. $delivery_data = [
  95. 'local_sym_sn' => '',
  96. 'local_sym_key' => '',
  97. 'rsa_sn' => '',
  98. 'rsa_private_key' => ''
  99. ];
  100. }
  101. return $delivery_data;
  102. }
  103. public function getUrlData($store_id, $url_, $data = [], $type = 0) {
  104. $this->getWechatApp($store_id);
  105. $app = $this->app;
  106. try {
  107. if (empty($app)) {
  108. throw new \Exception();
  109. }
  110. $access_token = $app->access_token->getToken();
  111. } catch (\Exception $e) {
  112. return [
  113. 'code' => 1,
  114. 'msg' => '未获取到配置信息'
  115. ];
  116. }
  117. $accessToken = $access_token['authorizer_access_token'] ?? $access_token['access_token'];
  118. $url_ = $this->base_uri . $url_;
  119. $url = $url_ . "?access_token=" . $accessToken;
  120. $newRe = $this->getRequestParam($data, $url_, ['app_id' => $this->param['app_id']]);
  121. if (!$type) {
  122. $signature = $this->getSignature($newRe, $url_, ['app_id' => $this->param['app_id']]);
  123. $headerArray =['wechatmp_appid' => $this->param['app_id'], 'wechatmp_timeStamp'=>$newRe['ts'], 'wechatmp_signature'=>$signature];
  124. }
  125. $result = $this->curlPost($url, $newRe['reqData'] ?: '', $headerArray ?: []);
  126. $headers = $this->httpParseHeaders($result['header']);
  127. $result = json_decode($result['body'], true);
  128. if (!$type) {
  129. $result = $this->decryptToString($url_, $headers['Wechatmp-TimeStamp'], $result, $this->param['app_id']);
  130. }
  131. if ($result['errcode'] === 10000) {
  132. return retry(3, function () use ($store_id, $url_, $data, $type) {
  133. return $this->getUrlData($store_id, $url_, $data, $type);
  134. });
  135. }
  136. return $result;
  137. }
  138. //加密
  139. public function getRequestParam($req = [], $url_ = '', $params_data = [])
  140. {
  141. $param = $this->param;
  142. $time = time();
  143. //16位随机字符串
  144. $nonce = base64_encode(random_bytes(16));
  145. $addReq = ["_n" => $nonce, "_appid" => $params_data['app_id'], "_timestamp" => $time];
  146. $realReq = array_merge($addReq, $req);
  147. $realReq = json_encode($realReq);
  148. //额外参数
  149. $aad = $url_ . "|" . $params_data['app_id'] . "|" . $time . "|" . $param['local_sym_sn'];
  150. //12位随机字符
  151. $iv = random_bytes(12);
  152. $cipher = openssl_encrypt($realReq, "aes-256-gcm", base64_decode($param['local_sym_key']), OPENSSL_RAW_DATA, $iv, $tag, $aad);
  153. $iv = base64_encode($iv);
  154. $data = base64_encode($cipher);
  155. $authTag = base64_encode($tag);
  156. $reqData = ["iv" => $iv, "data" => $data, "authtag" => $authTag];
  157. return ['ts' => $time, 'reqData' => json_encode($reqData)];
  158. }
  159. //加签
  160. public function getSignature($newRe, $url_, $params_data = [])
  161. {
  162. $keys = $this->key;
  163. $time = $newRe['ts'];
  164. $url = $url_;
  165. $appId = $params_data['app_id'];
  166. $reqData = $newRe['reqData'];
  167. $payload = $url . "\n" . $appId . "\n" . $time . "\n" . $reqData;
  168. // mb_internal_encoding($payload);
  169. // $payload = mb_convert_encoding($payload, 'UTF-8', "auto");
  170. // $key = openssl_pkey_get_private($keys['key']);
  171. //
  172. // $signature = '';
  173. // openssl_sign($payload, $signature, $key, OPENSSL_ALGO_SHA256);
  174. // openssl_free_key($key);
  175. $signature = RSA::loadPrivateKey($keys['rsa_private_key'])
  176. ->withPadding(RSA::SIGNATURE_PSS)
  177. ->withHash('sha256')
  178. ->withMGFHash('sha256')
  179. ->sign($payload);
  180. return base64_encode($signature);
  181. }
  182. //请求
  183. public function curlPost($url, $field, $header)
  184. {
  185. $headerArray = array('Content-Type: application/json;charset=utf-8',
  186. 'Accept:application/json',
  187. 'Content-Length:' . strlen($field),
  188. 'Wechatmp-Appid:' . $header['wechatmp_appid'],
  189. 'Wechatmp-TimeStamp:' . $header['wechatmp_timeStamp'],
  190. 'Wechatmp-Signature:' . $header['wechatmp_signature']
  191. );
  192. $curl = curl_init();
  193. curl_setopt($curl, CURLOPT_HTTPHEADER, $headerArray);
  194. curl_setopt($curl, CURLOPT_URL, $url);
  195. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  196. curl_setopt($curl, CURLOPT_POST, 1);
  197. curl_setopt($curl, CURLOPT_POSTFIELDS, $field);
  198. //输出响应头部
  199. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  200. curl_setopt($curl, CURLOPT_HEADER, true);
  201. $str = curl_exec($curl);
  202. $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
  203. $headers = substr($str, 0, $headerSize);
  204. $body = substr($str, $headerSize);
  205. curl_close($curl);
  206. return ['body' => $body, 'header' => $headers];
  207. // $header_ = array('Content-Type: application/json;charset=utf-8',
  208. // 'Accept:application/json',
  209. // 'Content-Length:' . strlen($field),
  210. // 'Wechatmp-Appid:' . $header['wechatmp_appid'],
  211. // 'Wechatmp-TimeStamp:' . $header['wechatmp_timeStamp'],
  212. // 'Wechatmp-Signature:' . $header['wechatmp_signature']
  213. //
  214. // );
  215. // $ch = curl_init();
  216. // curl_setopt($ch, CURLOPT_POST, 1);
  217. // curl_setopt($ch, CURLOPT_URL, $url);
  218. // curl_setopt($ch, CURLOPT_POSTFIELDS, $field);
  219. // curl_setopt($ch, CURLOPT_HTTPHEADER, $header_);
  220. // ob_start();
  221. // curl_exec($ch);
  222. // $return_content = ob_get_contents();
  223. // ob_end_clean();
  224. // return $return_content;
  225. }
  226. //解析加密信息
  227. public function decryptToString($url, $ts, $result_, $app_id)
  228. {
  229. try {
  230. $param = $this->param;
  231. $message = $url . '|' . $app_id . '|' . $ts . '|' . $param['local_sym_sn'];
  232. $iv = base64_decode($result_['iv']);
  233. $data = base64_decode($result_['data']);
  234. $authTag = base64_decode($result_['authtag']);
  235. $result = openssl_decrypt($data, "aes-256-gcm", base64_decode($param['local_sym_key']), OPENSSL_RAW_DATA, $iv, $authTag, $message);
  236. if (!$result) {
  237. throw new \Exception(openssl_error_string(), 10000);
  238. }
  239. return json_decode($result, true);
  240. } catch (\Exception $e) {
  241. return [
  242. 'errcode' => $e->getCode() ?: 1,
  243. 'errmsg' => $e->getMessage()
  244. ];
  245. }
  246. // $config = self::$config;
  247. }
  248. /**
  249. * Name:解析头部信息
  250. * User: zcw
  251. * Date: 2023/7/14
  252. * Time: 10:28
  253. * @param $headerString
  254. * @return array
  255. */
  256. private function httpParseHeaders($headerString)
  257. {
  258. $headers = [];
  259. $lines = explode("\r\n", $headerString);
  260. foreach ($lines as $line) {
  261. $line = trim($line);
  262. if (!empty($line)) {
  263. $parts = explode(':', $line, 2);
  264. $key = trim($parts[0]);
  265. $value = isset($parts[1]) ? trim($parts[1]) : '';
  266. $headers[$key] = $value;
  267. }
  268. }
  269. return $headers;
  270. }
  271. }