RequestUtil.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. namespace common\components\payment\joinpay\util;
  3. use common\helpers\FileHelper;
  4. use Yii;
  5. use Exception;
  6. use common\components\payment\joinpay\exceptions\SDKException;
  7. use common\components\payment\joinpay\entity\Request;
  8. use common\components\payment\joinpay\entity\SecretKey;
  9. use common\components\payment\joinpay\entity\Response;
  10. /**
  11. * 汇聚扫码支付
  12. * Class RequestUtil
  13. * @package common\components\payment\joinpay\util
  14. */
  15. class RequestUtil
  16. {
  17. /**
  18. * POST请求
  19. * @param $url
  20. * @param $params
  21. * @param bool $contentType
  22. * @return bool|mixed|string
  23. */
  24. public static function http_post($url, $params,$contentType=false)
  25. {
  26. if (function_exists('curl_init')) {
  27. // curl方式
  28. $oCurl = curl_init();
  29. if (stripos($url, 'https://') !== FALSE) {
  30. curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
  31. curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
  32. }
  33. $string = $params;
  34. if (is_array($params)) {
  35. $aPOST = array();
  36. foreach ($params as $key => $val) {
  37. $aPOST[] = $key . '=' . urlencode($val);
  38. }
  39. $string = join('&', $aPOST);
  40. }
  41. curl_setopt($oCurl, CURLOPT_URL, $url);
  42. curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
  43. curl_setopt($oCurl, CURLOPT_POST, TRUE);
  44. //$contentType json处理
  45. if($contentType){
  46. $headers = array(
  47. "Content-type: application/json;charset='utf-8'",
  48. );
  49. curl_setopt($oCurl, CURLOPT_HTTPHEADER, $headers);
  50. curl_setopt($oCurl, CURLOPT_POSTFIELDS, json_encode($params));
  51. }else{
  52. curl_setopt($oCurl, CURLOPT_POSTFIELDS, $string);
  53. }
  54. $response = curl_exec($oCurl);
  55. curl_close($oCurl);
  56. return $response;
  57. } elseif (function_exists('stream_context_create')) {
  58. // php5.3以上
  59. $opts = array(
  60. 'http' => array(
  61. 'method' => 'POST',
  62. 'header' => 'Content-type: application/x-www-form-urlencoded',
  63. 'content' => http_build_query($params),
  64. )
  65. );
  66. $_opts = stream_context_get_params(stream_context_get_default());
  67. $context = stream_context_create(array_merge_recursive($_opts['options'], $opts));
  68. return file_get_contents($url, false, $context);
  69. } else {
  70. return false;
  71. }
  72. }
  73. /**
  74. * 生成签名
  75. * @param $params
  76. * @param $key
  77. * @param string $encryptType
  78. * @return string
  79. */
  80. public static function hmacRequest($params, $key, $encryptType = "1")
  81. {
  82. if ("1" == $encryptType) {
  83. $str = implode("", $params) . $key;
  84. return md5($str);
  85. } else {
  86. $private_key = openssl_pkey_get_private($key);
  87. $params = implode("", $params);
  88. openssl_sign($params, $sign, $private_key, OPENSSL_ALGO_MD5);
  89. openssl_free_key($private_key);
  90. $sign = base64_encode($sign);
  91. return $sign;
  92. }
  93. }
  94. /**
  95. * 发起交易请求
  96. * 说明:
  97. * 1、此方法不会对商户私钥做任何处理,理论上不会造成私钥泄漏,如果商户觉得把私钥传递到此方法不安全,可以在调用本方法前
  98. * 自行调用SignUtil的相关方法为请求参数生成签名,自行调用RSAUtil的相关方法对sec_key进行加解密。
  99. * 2、发起请求时,如果SecretKey.reqSignKey不为空,当前方法会自动为请求参数生成签名、得到系统响应之后也会自动对响应
  100. * 数据进行验签,签名或验签失败会抛出 SDKException 异常
  101. * 3、发送请求时,如果Request中的sec_key和SecretKey.secKeyEncryptKey都不为空,会自动给sec_key加密
  102. * 4、得到响应时,如果Response中的sec_key和SecretKey.secKeyDecryptKey都不为空,会自动给sec_key解密
  103. *
  104. * @param string $url
  105. * @param Request $request
  106. * @param SecretKey $secretKey
  107. * @return Response
  108. * @throws SDKException
  109. * @throws \ReflectionException
  110. */
  111. public static function doRequest(string $url, Request $request, SecretKey $secretKey){
  112. //1.参数校验
  113. self::requestParamValid($request, $secretKey);
  114. if(! is_string($request->getData())){
  115. $request->setData(json_encode($request->getData(), JSON_UNESCAPED_UNICODE));
  116. }
  117. //2.添加签名
  118. if($secretKey->getReqSignKey()){
  119. $signStr = SignUtil::getSortedString($request, false);
  120. $signStr = SignUtil::sign($signStr, $request->getSignType(), $secretKey->getReqSignKey());
  121. $request->setSign($signStr);
  122. }else if(! $request->getSign()){
  123. throw new SDKException(SDKException::BIZ_ERROR, "请对请求参数添加签名!");
  124. }
  125. //3.如果存在sec_key,则对其进行rsa加密
  126. if($request->getSecKey() && $secretKey->getSecKeyEncryptKey()){
  127. $request->setSecKey(RSAUtil::encrypt($request->getSecKey(), $secretKey->getSecKeyEncryptKey()));
  128. }
  129. \Yii::error('$request ='.json_encode($request));
  130. //4.发起http请求
  131. $respJson = HttpUtil::postJsonSync($url, json_encode($request, JSON_UNESCAPED_UNICODE));
  132. if(! $respJson){
  133. throw new SDKException(SDKException::BIZ_ERROR, "请求完成,但响应信息为空");
  134. }
  135. \Yii::error('resp ='.json_encode($respJson));
  136. //5.响应结果转换
  137. $response = new Response();
  138. try{
  139. ObjectUtil::fillProperties($response, $respJson);
  140. }catch (Exception $e){
  141. throw new SDKException(SDKException::BIZ_ERROR, "请求完成,但响应信息转换失败: " . $e->getMessage() . ",respJson = " . $respJson);
  142. }
  143. $isOk = false;
  144. try{
  145. $signStr = SignUtil::getSortedString($response, false);
  146. $isOk = SignUtil::verify($signStr, $response->getSign(), $response->getSignType(), $secretKey->getRespVerifyKey());
  147. }catch (Exception $e){
  148. throw new SDKException(SDKException::BIZ_ERROR, "请求完成,但响应信息验签异常: " . $e->getMessage() . ",respJson = " . $respJson);
  149. }
  150. if($isOk !== true){
  151. throw new SDKException(SDKException::BIZ_ERROR, "响应信息验签不通过! respJson = " . $respJson);
  152. }
  153. if($response->getSecKey() && $secretKey->getSecKeyDecryptKey()){
  154. $secKey = RSAUtil::decrypt($response->getSecKey(), $secretKey->getSecKeyDecryptKey());
  155. $response->setSecKey($secKey);
  156. }
  157. return $response;
  158. }
  159. /**
  160. * 简单参数校验
  161. * @param Request $request
  162. * @param SecretKey $secretKey
  163. * @throws Exception
  164. */
  165. private static function requestParamValid(Request $request, SecretKey $secretKey){
  166. if(! $request){
  167. throw new SDKException(SDKException::PARAM_ERROR, "request不能为空");
  168. }else if(! $request->getMethod()){
  169. throw new SDKException(SDKException::PARAM_ERROR, "Request.method不能为空");
  170. }else if(! $request->getVersion()){
  171. throw new SDKException(SDKException::PARAM_ERROR, "Request.version不能为空");
  172. }else if(! $request->getData()){
  173. throw new SDKException(SDKException::PARAM_ERROR, "Request.data不能为空");
  174. }else if(! $request->getSignType()){
  175. throw new SDKException(SDKException::PARAM_ERROR, "Request.sign_type不能为空");
  176. }else if(! $request->getMchNo()){
  177. throw new SDKException(SDKException::PARAM_ERROR, "Request.mch_no不能为空");
  178. }
  179. if(! $secretKey){
  180. throw new SDKException(SDKException::PARAM_ERROR, "secretKey不能为空");
  181. }else if(! $secretKey->getRespVerifyKey()){
  182. throw new SDKException(SDKException::PARAM_ERROR, "SecretKey.respVerifyKey不能为空");
  183. }
  184. }
  185. }