Payment.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2024 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\api\service\recharge;
  13. use app\api\model\Payment as PaymentModel;
  14. use app\api\model\recharge\Order as OrderModel;
  15. use app\api\model\PaymentTrade as PaymentTradeModel;
  16. use app\api\service\User as UserService;
  17. use app\api\service\Order as OrderService;
  18. use app\api\service\recharge\PaySuccess as RechargePaySuccesService;
  19. use app\common\service\BaseService;
  20. use app\common\enum\Client as ClientEnum;
  21. use app\common\enum\OrderType as OrderTypeEnum;
  22. use app\common\enum\payment\Method as PaymentMethodEnum;
  23. use app\common\library\payment\Facade as PaymentFacade;
  24. use cores\exception\BaseException;
  25. /**
  26. * 余额充值订单付款服务类
  27. * Class Payment
  28. * @package app\api\controller
  29. */
  30. class Payment extends BaseService
  31. {
  32. // 提示信息
  33. private string $message = '';
  34. // 订单信息
  35. private OrderModel $orderInfo;
  36. // 支付方式 (微信支付、支付宝)
  37. private string $method = '';
  38. // 下单的客户端
  39. private string $client = '';
  40. /**
  41. * 设置当前支付方式
  42. * @param string $method 支付方式
  43. * @return $this
  44. */
  45. public function setMethod(string $method): Payment
  46. {
  47. $this->method = $method;
  48. return $this;
  49. }
  50. /**
  51. * 设置下单的客户端
  52. * @param string $client 客户端
  53. * @return $this
  54. */
  55. public function setClient(string $client): Payment
  56. {
  57. $this->client = $client;
  58. return $this;
  59. }
  60. /**
  61. * 确认订单支付事件
  62. * @param int|null $planId 方案ID
  63. * @param string|null $customMoney 自定义金额
  64. * @param array $extra 附加数据
  65. * @return array[]
  66. * @throws BaseException
  67. * @throws \think\db\exception\DataNotFoundException
  68. * @throws \think\db\exception\DbException
  69. * @throws \think\db\exception\ModelNotFoundException
  70. */
  71. public function orderPay(?int $planId = null, string $customMoney = null, array $extra = []): array
  72. {
  73. // 创建余额订单信息
  74. $this->orderInfo = $this->createOrder($planId, $customMoney);
  75. // 构建第三方支付请求的参数
  76. $payment = $this->unifiedorder($extra);
  77. // 记录第三方交易信息
  78. $this->recordPaymentTrade($payment);
  79. // 返回结果
  80. return compact('payment');
  81. }
  82. /**
  83. * 创建充值订单
  84. * @param int|null $planId 方案ID
  85. * @param string|null $customMoney 自定义金额
  86. * @return OrderModel
  87. * @throws BaseException
  88. * @throws \think\db\exception\DataNotFoundException
  89. * @throws \think\db\exception\DbException
  90. * @throws \think\db\exception\ModelNotFoundException
  91. */
  92. private function createOrder(?int $planId = null, string $customMoney = null): OrderModel
  93. {
  94. $model = new OrderModel;
  95. if (!$model->createOrder($planId, $customMoney)) {
  96. throwError($model->getError() ?: '创建充值订单失败');
  97. }
  98. $model['order_id'] = (int)$model['order_id'];
  99. return $model;
  100. }
  101. /**
  102. * 查询订单是否支付成功 (仅限第三方支付订单)
  103. * @param string $outTradeNo 商户订单号
  104. * @return bool
  105. * @throws BaseException
  106. * @throws \think\db\exception\DataNotFoundException
  107. * @throws \think\db\exception\DbException
  108. * @throws \think\db\exception\ModelNotFoundException
  109. */
  110. public function tradeQuery(string $outTradeNo): bool
  111. {
  112. // 判断支付方式是否合法
  113. if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY])) {
  114. return false;
  115. }
  116. // 获取支付方式的配置信息
  117. $options = $this->getPaymentConfig();
  118. // 构建支付模块
  119. $Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
  120. // 执行第三方支付查询API
  121. $result = $Payment->tradeQuery($outTradeNo);
  122. // 订单支付成功事件
  123. if (!empty($result) && $result['paySuccess']) {
  124. // 获取第三方交易记录信息
  125. $tradeInfo = PaymentTradeModel::detailByOutTradeNo($outTradeNo);
  126. // 订单支付成功事件
  127. $this->orderPaySucces($tradeInfo['order_no'], $tradeInfo['trade_id'], $result);
  128. }
  129. // 返回订单状态
  130. return $result ? $result['paySuccess'] : false;
  131. }
  132. /**
  133. * 记录第三方交易信息
  134. * @param array $payment 第三方支付数据
  135. * @throws BaseException
  136. * @throws \think\db\exception\DataNotFoundException
  137. * @throws \think\db\exception\DbException
  138. * @throws \think\db\exception\ModelNotFoundException
  139. */
  140. private function recordPaymentTrade(array $payment): void
  141. {
  142. if ($this->method != PaymentMethodEnum::BALANCE) {
  143. PaymentTradeModel::record(
  144. $this->orderInfo,
  145. $this->method,
  146. $this->client,
  147. OrderTypeEnum::RECHARGE,
  148. $payment
  149. );
  150. }
  151. }
  152. /**
  153. * 返回消息提示
  154. * @return string
  155. */
  156. public function getMessage(): string
  157. {
  158. return $this->message;
  159. }
  160. /**
  161. * 构建第三方支付请求的参数
  162. * @param array $extra 附加数据
  163. * @return array
  164. * @throws BaseException
  165. * @throws \think\db\exception\DataNotFoundException
  166. * @throws \think\db\exception\DbException
  167. * @throws \think\db\exception\ModelNotFoundException
  168. */
  169. private function unifiedorder(array $extra = []): array
  170. {
  171. // 判断支付方式是否合法
  172. if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY])) {
  173. return [];
  174. }
  175. // 生成第三方交易订单号 (并非主订单号)
  176. $outTradeNo = OrderService::createOrderNo();
  177. // 获取支付方式的配置信息
  178. $options = $this->getPaymentConfig();
  179. // 整理下单接口所需的附加数据
  180. $extra = $this->extraAsUnify($extra);
  181. // 构建支付模块
  182. $Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
  183. // 执行第三方支付下单API
  184. if (!$Payment->unify($outTradeNo, (string)$this->orderInfo['pay_price'], $extra)) {
  185. throwError('第三方支付下单API调用失败');
  186. }
  187. // 返回客户端需要的支付参数
  188. return $Payment->getUnifyResult();
  189. }
  190. /**
  191. * 获取支付方式的配置信息
  192. * @return mixed
  193. * @throws BaseException
  194. * @throws \think\db\exception\DataNotFoundException
  195. * @throws \think\db\exception\DbException
  196. * @throws \think\db\exception\ModelNotFoundException
  197. */
  198. private function getPaymentConfig()
  199. {
  200. $PaymentModel = new PaymentModel;
  201. $templateInfo = $PaymentModel->getPaymentInfo($this->method, $this->client, $this->getStoreId());
  202. return $templateInfo['template']['config'][$this->method];
  203. }
  204. /**
  205. * 整理下单接口所需的附加数据
  206. * @param array $extra
  207. * @return array
  208. * @throws BaseException
  209. */
  210. private function extraAsUnify(array $extra = []): array
  211. {
  212. // 微信支付时需要的附加数据
  213. if ($this->method === PaymentMethodEnum::WECHAT) {
  214. // 微信小程序端需要openid
  215. if (in_array($this->client, [ClientEnum::MP_WEIXIN])) {
  216. $extra['openid'] = $this->getWechatOpenid();
  217. }
  218. }
  219. // 支付宝支付时需要的附加数据
  220. if ($this->method === PaymentMethodEnum::ALIPAY) {
  221. }
  222. return $extra;
  223. }
  224. /**
  225. * 获取微信端的用户openid(仅微信小程序)
  226. * @return null
  227. * @throws BaseException
  228. */
  229. private function getWechatOpenid()
  230. {
  231. if (in_array($this->client, [ClientEnum::MP_WEIXIN])) {
  232. // 当前登录用户信息
  233. $useInfo = UserService::getCurrentLoginUser(true);
  234. if (!$useInfo['currentOauth'] || empty($useInfo['currentOauth']['oauth_id'])) {
  235. throwError('很抱歉,您当前不存在openid 无法发起微信支付');
  236. }
  237. // 当前第三方用户标识
  238. return $useInfo['currentOauth']['oauth_id'];
  239. }
  240. return null;
  241. }
  242. /**
  243. * 订单支付成功事件
  244. * @param string $orderNo 当前订单号
  245. * @param int|null $tradeId 第三方交易记录ID
  246. * @param array $paymentData 第三方支付成功返回的数据
  247. * @return void
  248. * @throws BaseException
  249. */
  250. private function orderPaySucces(string $orderNo, ?int $tradeId = null, array $paymentData = []): void
  251. {
  252. // 获取订单详情
  253. $service = new RechargePaySuccesService;
  254. // 订单支付成功业务处理
  255. $service->setOrderNo($orderNo)->setMethod($this->method)->setTradeId($tradeId)->setPaymentData($paymentData);
  256. if (!$service->handle()) {
  257. throwError($service->getError() ?: '订单支付失败');
  258. }
  259. $this->message = '恭喜您,余额充值成功';
  260. }
  261. }