UnsPayService.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: lts
  5. * Date: 2019-04-16
  6. * Time: 14:14
  7. */
  8. namespace app\main\service;
  9. use app\main\constants\ApiConstants;
  10. use GuzzleHttp\Client as Http;
  11. use GuzzleHttp\Exception\GuzzleException;
  12. use think\Exception;
  13. class UnsPayService
  14. {
  15. protected static $self = null;
  16. private $_key;
  17. function __construct($key)
  18. {
  19. $this->_key = $key;
  20. }
  21. public static function instance($key)
  22. {
  23. if (self::$self == null) {
  24. self::$self = new self($key);
  25. }
  26. return self::$self;
  27. }
  28. /**
  29. * 请求参数校验的key的顺序
  30. */
  31. const MAC_KEY_REQUEST_SORT = [
  32. 'accountId',
  33. 'orderId',
  34. 'commodity',
  35. 'amount',
  36. 'responseUrl',
  37. 'subject',
  38. 'returnPage',
  39. 'openId',
  40. 'version',
  41. 'limitPay',
  42. ];
  43. /**
  44. * 响应返回值校验的key的顺序
  45. */
  46. const MAC_KEY_RESPONSE_SORT = [
  47. 'result_code',
  48. 'result_msg',
  49. 'appId',
  50. 'timeStamp',
  51. 'nonceStr',
  52. 'mPackage',
  53. 'signType',
  54. 'paySign',
  55. 'orderId',
  56. 'transId',
  57. ];
  58. /**
  59. * 支付回调参数校验的key的顺序
  60. */
  61. const MAC_KEY_CALLBACK_SORT = [
  62. 'accountId',
  63. 'orderId',
  64. 'amount',
  65. 'result_code',
  66. 'result_msg',
  67. 'channel_refund_trade_no',
  68. ];
  69. /**
  70. * 构造银生宝下单mac参数
  71. * @param $params
  72. * @return string
  73. */
  74. public function makeMacParams(array $params)
  75. {
  76. $aResult = [];
  77. foreach (self::MAC_KEY_REQUEST_SORT as $key) {
  78. if (!empty($params[$key])) {
  79. $aResult[] = sprintf("%s=%s", $key, $params[$key]);
  80. }
  81. }
  82. $aResult[] = 'key=' . $this->_key;
  83. $strMac = implode('&', $aResult);
  84. LogService::info('strMac' . $strMac);
  85. $md5StrMac = md5($strMac);
  86. $md5StrMac = strtoupper($md5StrMac);
  87. return $md5StrMac;
  88. }
  89. /**
  90. * 构造银生宝回调mac参数
  91. * @param array $params
  92. * @return string
  93. */
  94. public function verifyCallbackMacParams(array $params)
  95. {
  96. if (empty($params['mac'])) {
  97. LogService::error('mac参数不存在,银生宝下单返回值校验失败!');
  98. return false;
  99. }
  100. $aResult = [];
  101. foreach (self::MAC_KEY_CALLBACK_SORT as $key) {
  102. if (!empty($params[$key])) {
  103. $aResult[] = sprintf("%s=%s", $key, $params[$key]);
  104. } else {
  105. $aResult[] = $key . '=';
  106. }
  107. }
  108. $aResult[] = 'key=' . $this->_key;
  109. $strMac = implode('&', $aResult);
  110. $md5StrMac = md5($strMac);
  111. $md5StrMac = strtoupper($md5StrMac);
  112. if ($md5StrMac != $params['mac']) {
  113. LogService::error('mac值有误,银生宝回调返回值校验失败!');
  114. return false;
  115. } else {
  116. return true;
  117. }
  118. }
  119. /**
  120. * 校验银生宝接口返回值mac
  121. * @param array $data
  122. * @return bool
  123. */
  124. private function verifyResponse(array $data)
  125. {
  126. if (empty($data['mac'])) {
  127. LogService::error('mac参数不存在,银生宝下单返回值校验失败!');
  128. return false;
  129. }
  130. $aResult = [];
  131. foreach (self::MAC_KEY_RESPONSE_SORT as $key) {
  132. if (!empty($data[$key])) {
  133. $aResult[] = sprintf("%s=%s", $key, $data[$key]);
  134. } else {
  135. $aResult[] = $key . '=';
  136. }
  137. }
  138. $aResult[] = 'key=' . $this->_key;
  139. $strMac = implode('&', $aResult);
  140. $md5StrMac = md5($strMac);
  141. $md5StrMac = strtoupper($md5StrMac);
  142. if ($data['mac'] != $md5StrMac) {
  143. LogService::error('mac值有误,银生宝下单返回值校验失败!');
  144. return false;
  145. } else {
  146. return true;
  147. }
  148. }
  149. public static function makeJsJsonParam($params)
  150. {
  151. $result = json_encode([
  152. 'appId' => $params['appId'],
  153. 'timeStamp' => $params['timeStamp'],
  154. 'nonceStr' => $params['nonceStr'],
  155. 'package' => $params['mPackage'],
  156. 'signType' => $params['signType'],
  157. 'paySign' => $params['paySign'],
  158. ]);
  159. return $result;
  160. }
  161. /**
  162. * 银生宝调用API接口
  163. * @param $data 发送到API的数据
  164. * @param $payInfo 支付信息
  165. * @param $goodsInfo 商品信息
  166. * @param $orderInfo 订单信息
  167. * @param int $loopIndex 循环调用API的次数
  168. * @return mixed|\Psr\Http\Message\ResponseInterface
  169. * @throws Exception
  170. */
  171. public function payUnsApi($data, $payInfo, $goodsInfo, $orderInfo, $loopIndex = 1)
  172. {
  173. $payUrl = ApiConstants::UNS_PAY_URL;
  174. $unsApiStartTime = microtime(true);
  175. try {
  176. $client = new Http([
  177. 'connect_timeout' => 10,
  178. 'timeout' => 30,
  179. 'http_errors' => true, //抛出异常 true是 false否
  180. 'verify' => false, //不验证ssl证书
  181. ]);
  182. $result = $client->post($payUrl, ['form_params' => $data]);
  183. $unsApiEndTime = microtime(true);
  184. $apiRunTime = round($unsApiEndTime - $unsApiStartTime, 3);
  185. if ($result->getStatusCode() != '200') {
  186. LogService::error(sprintf('unspay_create_order_fail!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s,errmessage:%s',
  187. $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'],
  188. $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'],
  189. $apiRunTime, json_encode(var_export($result, true), JSON_UNESCAPED_UNICODE)));
  190. LogService::error('银生宝平台订单创建失败!订单内容: ' . json_encode(var_export($data, true),
  191. JSON_UNESCAPED_UNICODE) . " 银生宝平台返回内容: " . json_encode(var_export($result, true),
  192. JSON_UNESCAPED_UNICODE));
  193. throw new Exception('订单创建失败,请重新下单!');
  194. } else {
  195. $result = json_decode($result->getBody()->getContents(), true);
  196. $verifyResponseResult = $this->verifyResponse($result);
  197. if ($result['result_code'] != '0000' || !$verifyResponseResult) {
  198. LogService::error(sprintf('unspay_create_order_fail!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s,errmessage:%s',
  199. $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'],
  200. $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'],
  201. $apiRunTime, $result['result_msg']));
  202. LogService::error('银生宝平台订单创建失败!订单内容:' . json_encode(var_export($data,
  203. true)) . " 银生宝平台返回错误码: " . $result['result_code']);
  204. throw new Exception('订单创建失败,请重新下单!');
  205. }
  206. }
  207. LogService::info(sprintf('unspay_create_order_success!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s',
  208. $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'],
  209. $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'],
  210. $apiRunTime));
  211. return $result;
  212. } catch (GuzzleException $exception) {
  213. $unsApiEndTime = microtime(true);
  214. $apiRunTime = round($unsApiEndTime - $unsApiStartTime, 3);
  215. LogService::error(sprintf('unspay_create_order_fail!wxpay_id:%s,wxpay_name:%s,mch_id:%s,channel_id:%s,user_id:%s,money:%s,good_id:%s,out_trade_no:%s,api_run_time:%s s,errmessage:%s',
  216. $payInfo['id'], $payInfo['name'], $payInfo['quartet_merchant_id'], $orderInfo['admin_id'],
  217. $orderInfo['user_id'], $goodsInfo['money'], $orderInfo['goods_id'], $orderInfo['out_trade_no'],
  218. $apiRunTime, $exception->getMessage()));
  219. LogService::error('银生宝平台订单创建失败!订单内容: ' . json_encode(var_export($data, true), JSON_UNESCAPED_UNICODE));
  220. LogService::error($exception->getMessage());
  221. LogService::error($exception->getTraceAsString());
  222. if ($loopIndex <= ApiConstants::LOOP_EXEC_API_COUNT) {
  223. $loopIndex++;
  224. return $this->payUnsApi($data, $payInfo, $goodsInfo, $orderInfo, $loopIndex);
  225. }
  226. throw new Exception('订单创建失败,请重新下单!');
  227. }
  228. }
  229. }