Common.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. namespace app\phpPay\wexin\pay;
  3. class Common
  4. {
  5. // 商户号
  6. protected $sellerMid = '6888804045113';
  7. // 公钥文件
  8. protected $publicKeyPath;
  9. // 私钥文件
  10. protected $privateKeyPath;
  11. // 私钥证书密码
  12. protected $privateKeyPwd = 'Nd123456';
  13. // 接口地址
  14. protected $apiUrl = 'https://cashier.sandpay.com.cn';
  15. // 产品id https://open.sandpay.com.cn/product/detail/43984//
  16. protected $productId = '';
  17. // 接入类型 1-普通商户接入 2-平台商户接入 3-核心企业商户接入
  18. protected $accessType = '1';
  19. // 渠道类型 07-互联网 08-移动端
  20. protected $channelType = '07';
  21. // 平台ID accessType为2时必填,在担保支付模式下填写核心商户号
  22. protected $plMid = '';
  23. // 请求报文体
  24. public $body;
  25. /*
  26. |--------------------------------------------------------------------------
  27. | step1.组装参数
  28. |--------------------------------------------------------------------------
  29. */
  30. // 参数
  31. protected function postData($method)
  32. {
  33. $data = array(
  34. 'head' => array(
  35. 'version' => '1.0',
  36. 'method' => $method,
  37. 'productId' => $this->productId,
  38. 'accessType' => $this->accessType,
  39. 'mid' => $this->sellerMid,
  40. 'plMid' => $this->plMid,
  41. 'channelType' => $this->channelType,
  42. 'reqTime' => date('YmdHis', time()),
  43. ),
  44. 'body' => $this->body,
  45. );
  46. $postData = array(
  47. 'charset' => 'utf-8',
  48. 'signType' => '01',
  49. 'data' => json_encode($data),
  50. 'sign' => $this->sign($data),
  51. );
  52. return $postData;
  53. }
  54. // 参数映射 继承类需要完善这个方法
  55. protected function apiMap()
  56. {
  57. $data =1;
  58. return array($data);
  59. }
  60. /*
  61. |--------------------------------------------------------------------------
  62. | step2. 请求
  63. |--------------------------------------------------------------------------
  64. */
  65. // curl请求接口
  66. public function request($apiName)
  67. {
  68. try {
  69. $apiMap = $this->apiMap();
  70. if (!isset($apiMap[$apiName])) {
  71. throw new \Exception('接口名错误');
  72. }
  73. $postData = $this->postData($apiMap[$apiName]['method']);
  74. $url = $this->apiUrl . $apiMap[$apiName]['url'];
  75. $ret = $this->httpPost($url, $postData);
  76. $retAry = $this->parseResult($ret); // 格式解析
  77. $verify = $this->verify($retAry['data'], $retAry['sign']); // 验签
  78. if (!$verify) {
  79. throw new \Exception('返回数据验签失败');
  80. }
  81. return $retAry;
  82. } catch (\Exception $e) {
  83. return $e->getMessage();
  84. }
  85. }
  86. // curl. 发送请求
  87. protected function httpPost($url, $params)
  88. {
  89. if (empty($url) || empty($params)) {
  90. throw new \Exception('请求参数错误');
  91. }
  92. $params = http_build_query($params);
  93. try {
  94. $ch = curl_init();
  95. curl_setopt($ch, CURLOPT_URL, $url);
  96. curl_setopt($ch, CURLOPT_POST, 1);
  97. curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
  98. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  99. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  100. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  101. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  102. $data = curl_exec($ch);
  103. $err = curl_error($ch);
  104. $errno = curl_errno($ch);
  105. if ($errno) {
  106. $msg = 'curl errInfo: ' . $err . ' curl errNo: ' . $errno;
  107. throw new \Exception($msg);
  108. }
  109. curl_close($ch);
  110. return $data;
  111. } catch (\Exception $e) {
  112. if ($ch) curl_close($ch);
  113. throw $e;
  114. }
  115. }
  116. // curl.解析返回数据
  117. protected function parseResult($result)
  118. {
  119. $arr = array();
  120. $response = urldecode($result);
  121. $arrStr = explode('&', $response);
  122. foreach ($arrStr as $str) {
  123. $p = strpos($str, "=");
  124. $key = substr($str, 0, $p);
  125. $value = substr($str, $p + 1);
  126. $arr[$key] = $value;
  127. }
  128. return $arr;
  129. }
  130. // 表单请求接口
  131. public function form($apiName)
  132. {
  133. $apiMap = $this->apiMap();
  134. if (!isset($apiMap[$apiName])) {
  135. throw new \Exception('接口名错误');
  136. }
  137. $postData = $this->postData($apiMap[$apiName]['method']);
  138. $url = $this->apiUrl . $apiMap[$apiName]['url'];
  139. $form = '<form action="' . $url . '" method="post">';
  140. foreach ($postData as $k => $v) {
  141. $form .= "{$k} <p><input type='text' name='{$k}' value='{$v}'></p>";
  142. }
  143. $form .= '<input type="submit" value="提交"></form>';
  144. return $form;
  145. }
  146. /*
  147. |--------------------------------------------------------------------------
  148. | step3.签名 + 验签
  149. |--------------------------------------------------------------------------
  150. */
  151. // 公钥
  152. private function publicKey()
  153. {
  154. try {
  155. $file = file_get_contents($this->publicKeyPath);
  156. if (!$file) {
  157. throw new \Exception('getPublicKey::file_get_contents ERROR');
  158. }
  159. $cert = chunk_split(base64_encode($file), 64, "\n");
  160. $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
  161. $res = openssl_pkey_get_public($cert);
  162. $detail = openssl_pkey_get_details($res);
  163. openssl_free_key($res);
  164. if (!$detail) {
  165. throw new \Exception('getPublicKey::openssl_pkey_get_details ERROR');
  166. }
  167. return $detail['key'];
  168. } catch (\Exception $e) {
  169. throw $e;
  170. }
  171. }
  172. // 私钥
  173. private function privateKey()
  174. {
  175. try {
  176. $file = file_get_contents($this->privateKeyPath);
  177. if (!$file) {
  178. throw new \Exception('getPrivateKey::file_get_contents');
  179. }
  180. if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) {
  181. throw new \Exception('getPrivateKey::openssl_pkcs12_read ERROR');
  182. }
  183. return $cert['pkey'];
  184. } catch (\Exception $e) {
  185. throw $e;
  186. }
  187. }
  188. // 私钥加签
  189. protected function sign($plainText)
  190. {
  191. $plainText = json_encode($plainText);
  192. try {
  193. $resource = openssl_pkey_get_private($this->privateKey());
  194. $result = openssl_sign($plainText, $sign, $resource);
  195. openssl_free_key($resource);
  196. if (!$result) throw new \Exception('sign error');
  197. return base64_encode($sign);
  198. } catch (\Exception $e) {
  199. throw $e;
  200. }
  201. }
  202. // 公钥验签
  203. public function verify($plainText, $sign)
  204. {
  205. $resource = openssl_pkey_get_public($this->publicKey());
  206. $result = openssl_verify($plainText, base64_decode($sign), $resource);
  207. openssl_free_key($resource);
  208. if (!$result) {
  209. throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign);
  210. }
  211. return $result;
  212. }
  213. }