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.

186 lines
5.8 KiB

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