You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
5.7 KiB

  1. <?php
  2. namespace App\Service\v3\Implementations;
  3. use App\Commons\Log;
  4. use App\Constants\v3\ErrorCode;
  5. use App\Constants\v3\LogLabel;
  6. use App\Constants\v3\OrderState;
  7. use App\Exception\ErrorCodeException;
  8. use App\Model\v3\OrderMain;
  9. use App\Model\v3\User;
  10. use App\Service\v3\Interfaces\GoodsActivityServiceInterface;
  11. use App\Service\v3\Interfaces\PaymentServiceInterface;
  12. use EasyWeChat\Factory;
  13. use GuzzleHttp\Exception\GuzzleException;
  14. use Hyperf\Guzzle\CoroutineHandler;
  15. use Hyperf\Di\Annotation\Inject;
  16. class PaymentService implements PaymentServiceInterface
  17. {
  18. /**
  19. * @Inject
  20. * @var Log
  21. */
  22. protected $log;
  23. /**
  24. * @Inject
  25. * @var GoodsActivityServiceInterface
  26. */
  27. protected $goodsActivityService;
  28. public function do($globalOrderId, $money, $userId, $notifyUrl)
  29. {
  30. try {
  31. $config = config('wxpay');
  32. $app = Factory::payment($config);
  33. $app['guzzle_handler'] = CoroutineHandler::class;
  34. // 待支付的,未超时(15min,900sec)的订单
  35. $orderMain = OrderMain::query()
  36. ->where(['state' => OrderState::UNPAID, 'global_order_id' => $globalOrderId, 'user_id' => $userId])
  37. ->where('created_at', '>=', (time()-900))
  38. ->first();
  39. if (empty($orderMain)) {
  40. throw new ErrorCodeException(ErrorCode::ORDER_NOT_AVAILABLE, '[支付订单号]'.$globalOrderId);
  41. }
  42. $payMoney = bcmul((string)$orderMain->money, 100, 0);
  43. if (env('APP_ENV') != 'prod') {
  44. $payMoney = 1;
  45. }
  46. $user = User::select('openid')->find($userId);
  47. $result = $app->order->unify([
  48. 'body' => '懒族生活',
  49. 'out_trade_no' => $orderMain->global_order_id,
  50. 'total_fee' => $payMoney,
  51. 'notify_url' => $notifyUrl,
  52. 'trade_type' => 'JSAPI',
  53. 'openid' => $user['openid'],
  54. ]);
  55. if ($result['return_code'] == 'FAIL') {
  56. throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, '[支付失败]'.$result['return_msg']);
  57. }
  58. // 返回支付参数给前端
  59. $parameters = [
  60. 'appId' => $result['appid'],
  61. 'timeStamp' => '' . time() . '',
  62. 'nonceStr' => uniqid(),
  63. 'package' => 'prepay_id=' . $result['prepay_id'],
  64. 'signType' => 'MD5'
  65. ];
  66. $parameters['paySign'] = $this->signture($parameters, $config['key']);
  67. $parameters['order_main_id'] = $orderMain->id;
  68. return $parameters;
  69. } catch (\Exception $e) {
  70. $this->log->event(LogLabel::ORDER_PAYMENT_LOG, ['payment_exception_msg' => $e->getMessage()]);
  71. throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, '[支付失败]'.$e->getMessage());
  72. }
  73. }
  74. public function check()
  75. {
  76. // TODO: Implement check() method.
  77. }
  78. public function undo()
  79. {
  80. // TODO: Implement undo() method.
  81. }
  82. /**
  83. * 企业付款到微信钱包
  84. * @param $money
  85. * @param $tradeNo
  86. * @param $openId
  87. * @param $userName
  88. * @param string $desc
  89. * @param string $checkName
  90. * @throws GuzzleException
  91. */
  92. public function payToWx($money, $tradeNo, $openId, $userName, $desc = '', $checkName = 'NO_CHECK')
  93. {
  94. $config = config('wxpay');
  95. $app = Factory::payment($config);
  96. $app['guzzle_handler'] = CoroutineHandler::class;
  97. $result = $app->transfer->toBalance([
  98. 'partner_trade_no' => $tradeNo, // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
  99. 'openid' => $openId,
  100. 'check_name' => $checkName, // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
  101. 're_user_name' => $userName, // 如果 check_name 设置为FORCE_CHECK,则必填用户真实姓名
  102. 'amount' => $money, // 企业付款金额,单位为分
  103. 'desc' => $desc, // 企业付款操作说明信息
  104. ]);
  105. if ($result['return_code'] != 'SUCCESS') {
  106. $this->log->event(LogLabel::PAY_TO_WX_FAIL_LOG, [
  107. 'exception_payToWx' => $result['return_msg'],
  108. 'result' => json_encode($result),
  109. 'desc' => $desc
  110. ]);
  111. throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, $result['return_msg']);
  112. }
  113. if ($result['result_code'] != 'SUCCESS') {
  114. $this->log->event(LogLabel::PAY_TO_WX_FAIL_LOG, [
  115. 'exception_payToWx' => $result['err_code_des'],
  116. 'result' => json_encode($result),
  117. 'desc' => $desc
  118. ]);
  119. throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, $result['err_code_des']);
  120. }
  121. return true;
  122. }
  123. /**
  124. * 支付参数加签
  125. * @param $parameters
  126. * @param $key
  127. * @return string
  128. */
  129. private function signture($parameters, $key)
  130. {
  131. // 按字典序排序参数
  132. ksort($parameters);
  133. // http_query
  134. $queryParams = $this->http_query($parameters);
  135. // 加入KEY
  136. $queryParams = $queryParams . "&key=" . $key;
  137. // MD5加密
  138. $queryParams = md5($queryParams);
  139. // 字符转为大写
  140. return strtoupper($queryParams);
  141. }
  142. /**
  143. * 参数转为http query字串
  144. * @param $parameters
  145. * @return string
  146. */
  147. private function http_query($parameters) {
  148. $http_query = [];
  149. foreach ($parameters as $key => $value) {
  150. $http_query[] = $key.'='.$value;
  151. }
  152. return implode('&', $http_query);
  153. }
  154. }