Common.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. namespace app\phpPay\hpay\pay;
  3. class Common
  4. {
  5. // 商户号
  6. protected $sellerMid = '6888804045113';
  7. // 公钥文件
  8. protected $publicKeyPath = 'cert/sand.cer';
  9. // 私钥文件
  10. protected $privateKeyPath = 'cert/MID_RSA_PRIVATE_KEY_100211701160001_new.pfx';
  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. return array();
  58. }
  59. /*
  60. |--------------------------------------------------------------------------
  61. | step2. 请求
  62. |--------------------------------------------------------------------------
  63. */
  64. // curl请求接口
  65. public function request($apiName)
  66. {
  67. try {
  68. $apiMap = $this->apiMap();
  69. if (!isset($apiMap[$apiName])) {
  70. throw new \Exception('接口名错误');
  71. }
  72. $postData = $this->postData($apiMap[$apiName]['method']);
  73. $url = $this->apiUrl . $apiMap[$apiName]['url'];
  74. $ret = $this->httpPost($url, $postData);
  75. $retAry = $this->parseResult($ret); // 格式解析
  76. $verify = $this->verify($retAry['data'], $retAry['sign']); // 验签
  77. if (!$verify) {
  78. throw new \Exception('返回数据验签失败');
  79. }
  80. return $retAry;
  81. } catch (\Exception $e) {
  82. return $e->getMessage();
  83. }
  84. }
  85. // curl. 发送请求
  86. protected function httpPost($url, $params)
  87. {
  88. if (empty($url) || empty($params)) {
  89. throw new \Exception('请求参数错误');
  90. }
  91. $params = http_build_query($params);
  92. try {
  93. $ch = curl_init();
  94. curl_setopt($ch, CURLOPT_URL, $url);
  95. curl_setopt($ch, CURLOPT_POST, 1);
  96. curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
  97. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  98. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  99. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  100. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  101. $data = curl_exec($ch);
  102. $err = curl_error($ch);
  103. $errno = curl_errno($ch);
  104. if ($errno) {
  105. $msg = 'curl errInfo: ' . $err . ' curl errNo: ' . $errno;
  106. throw new \Exception($msg);
  107. }
  108. curl_close($ch);
  109. return $data;
  110. } catch (\Exception $e) {
  111. if ($ch) curl_close($ch);
  112. throw $e;
  113. }
  114. }
  115. // curl.解析返回数据
  116. protected function parseResult($result)
  117. {
  118. $arr = array();
  119. $response = urldecode($result);
  120. $arrStr = explode('&', $response);
  121. foreach ($arrStr as $str) {
  122. $p = strpos($str, "=");
  123. $key = substr($str, 0, $p);
  124. $value = substr($str, $p + 1);
  125. $arr[$key] = $value;
  126. }
  127. return $arr;
  128. }
  129. // 表单请求接口
  130. public function form($apiName)
  131. {
  132. $apiMap = $this->apiMap();
  133. if (!isset($apiMap[$apiName])) {
  134. throw new \Exception('接口名错误');
  135. }
  136. $postData = $this->postData($apiMap[$apiName]['method']);
  137. $url = $this->apiUrl . $apiMap[$apiName]['url'];
  138. $form = '<form action="' . $url . '" method="post">';
  139. foreach ($postData as $k => $v) {
  140. $form .= "{$k} <p><input type='text' name='{$k}' value='{$v}'></p>";
  141. }
  142. $form .= '<input type="submit" value="提交"></form>';
  143. return $form;
  144. }
  145. /*
  146. |--------------------------------------------------------------------------
  147. | step3.签名 + 验签
  148. |--------------------------------------------------------------------------
  149. */
  150. // 公钥
  151. private function publicKey()
  152. {
  153. try {
  154. $file = file_get_contents($this->publicKeyPath);
  155. if (!$file) {
  156. throw new \Exception('getPublicKey::file_get_contents ERROR');
  157. }
  158. $cert = chunk_split(base64_encode($file), 64, "\n");
  159. $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
  160. $res = openssl_pkey_get_public($cert);
  161. $detail = openssl_pkey_get_details($res);
  162. openssl_free_key($res);
  163. if (!$detail) {
  164. throw new \Exception('getPublicKey::openssl_pkey_get_details ERROR');
  165. }
  166. return $detail['key'];
  167. } catch (\Exception $e) {
  168. throw $e;
  169. }
  170. }
  171. // 私钥
  172. private function privateKey()
  173. {
  174. try {
  175. $file = file_get_contents($this->privateKeyPath);
  176. if (!$file) {
  177. throw new \Exception('getPrivateKey::file_get_contents');
  178. }
  179. if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) {
  180. throw new \Exception('getPrivateKey::openssl_pkcs12_read ERROR');
  181. }
  182. return $cert['pkey'];
  183. } catch (\Exception $e) {
  184. throw $e;
  185. }
  186. }
  187. // 私钥加签
  188. protected function sign($plainText)
  189. {
  190. $plainText = json_encode($plainText);
  191. try {
  192. $resource = openssl_pkey_get_private($this->privateKey());
  193. $result = openssl_sign($plainText, $sign, $resource);
  194. openssl_free_key($resource);
  195. if (!$result) throw new \Exception('sign error');
  196. return base64_encode($sign);
  197. } catch (\Exception $e) {
  198. throw $e;
  199. }
  200. }
  201. // 公钥验签
  202. public function verify($plainText, $sign)
  203. {
  204. $resource = openssl_pkey_get_public($this->publicKey());
  205. $result = openssl_verify($plainText, base64_decode($sign), $resource);
  206. openssl_free_key($resource);
  207. if (!$result) {
  208. throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign);
  209. }
  210. return $result;
  211. }
  212. }