Payment.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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\cashier;
  13. use app\api\model\Order as OrderModel;
  14. use app\api\model\Payment as PaymentModel;
  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\order\PaySuccess as OrderPaySuccesService;
  19. use app\api\service\order\source\Factory as OrderSourceFactory;
  20. use app\common\service\BaseService;
  21. use app\common\enum\Client as ClientEnum;
  22. use app\common\enum\OrderType as OrderTypeEnum;
  23. use app\common\enum\payment\Method as PaymentMethodEnum;
  24. use app\common\library\payment\Facade as PaymentFacade;
  25. use cores\exception\BaseException;
  26. /**
  27. * 订单付款服务类
  28. * Class Payment
  29. * @package app\api\controller
  30. */
  31. class Payment extends BaseService
  32. {
  33. // 提示信息
  34. private string $message = '';
  35. // 订单ID
  36. private int $orderId;
  37. // 订单信息
  38. private ?OrderModel $orderInfo;
  39. // 支付方式 (微信支付、支付宝)
  40. private string $method;
  41. // 下单的客户端
  42. private string $client;
  43. /**
  44. * 设置支付的订单ID
  45. * @param int $orderId 订单ID
  46. * @return $this
  47. */
  48. public function setOrderId(int $orderId): Payment
  49. {
  50. $this->orderId = $orderId;
  51. return $this;
  52. }
  53. /**
  54. * 设置当前支付方式
  55. * @param string $method 支付方式
  56. * @return $this
  57. */
  58. public function setMethod(string $method): Payment
  59. {
  60. $this->method = $method;
  61. return $this;
  62. }
  63. /**
  64. * 设置下单的客户端
  65. * @param string $client 客户端
  66. * @return $this
  67. */
  68. public function setClient(string $client): Payment
  69. {
  70. $this->client = $client;
  71. return $this;
  72. }
  73. /**
  74. * 获取支付订单的信息
  75. * @return array
  76. * @throws BaseException
  77. * @throws \think\db\exception\DataNotFoundException
  78. * @throws \think\db\exception\DbException
  79. * @throws \think\db\exception\ModelNotFoundException
  80. */
  81. public function orderInfo(): array
  82. {
  83. // 当期用户信息
  84. $userInfo = UserService::getCurrentLoginUser(true);
  85. // 根据指定客户端获取可用的支付方式
  86. $PaymentModel = new PaymentModel;
  87. $methods = $PaymentModel->getMethodsByClient($this->client);
  88. // 获取结算订单信息
  89. $OrderModel = new OrderModel;
  90. $orderInfo = $OrderModel->getUnpaidOrderDetail($this->orderId);
  91. return [
  92. 'order' => $orderInfo,
  93. 'personal' => $userInfo,
  94. 'paymentMethods' => $methods
  95. ];
  96. }
  97. /**
  98. * 确认订单支付事件
  99. * @param array $extra 附加数据
  100. * @return array[]
  101. * @throws BaseException
  102. * @throws \think\db\exception\DataNotFoundException
  103. * @throws \think\db\exception\DbException
  104. * @throws \think\db\exception\ModelNotFoundException
  105. */
  106. public function orderPay(array $extra = []): array
  107. {
  108. // 获取订单信息
  109. $this->orderInfo = OrderModel::getDetail($this->orderId);
  110. // 订单支付事件
  111. $this->orderPayEvent();
  112. // 构建第三方支付请求的参数
  113. $payment = $this->unifiedorder($extra);
  114. // 记录第三方交易信息
  115. $this->recordPaymentTrade($payment);
  116. // 返回结果
  117. return compact('payment');
  118. }
  119. /**
  120. * 查询订单是否支付成功 (仅限第三方支付订单)
  121. * @param string $outTradeNo 商户订单号
  122. * @return bool
  123. * @throws BaseException
  124. * @throws \think\db\exception\DataNotFoundException
  125. * @throws \think\db\exception\DbException
  126. * @throws \think\db\exception\ModelNotFoundException
  127. */
  128. public function tradeQuery(string $outTradeNo): bool
  129. {
  130. // 判断支付方式是否合法
  131. if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY, PaymentMethodEnum::PAYPAL, PaymentMethodEnum::STRIPE])) {
  132. return false;
  133. }
  134. // 获取支付方式的配置信息
  135. $options = $this->getPaymentConfig();
  136. // 构建支付模块
  137. $Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
  138. // 执行第三方支付查询API
  139. $result = $Payment->tradeQuery($outTradeNo);
  140. // 订单支付成功事件
  141. if (!empty($result) && $result['paySuccess']) {
  142. // 获取第三方交易记录信息
  143. $tradeInfo = PaymentTradeModel::detailByOutTradeNo($outTradeNo);
  144. // 订单支付成功事件
  145. $this->orderPaySucces($tradeInfo['order_no'], $tradeInfo['trade_id'], $result);
  146. }
  147. // 返回订单状态
  148. return $result ? $result['paySuccess'] : false;
  149. }
  150. /**
  151. * 记录第三方交易信息
  152. * @param array $payment 第三方支付数据
  153. * @throws BaseException
  154. * @throws \think\db\exception\DataNotFoundException
  155. * @throws \think\db\exception\DbException
  156. * @throws \think\db\exception\ModelNotFoundException
  157. */
  158. private function recordPaymentTrade(array $payment): void
  159. {
  160. if ($this->method != PaymentMethodEnum::BALANCE) {
  161. PaymentTradeModel::record($this->orderInfo, $this->method, $this->client, OrderTypeEnum::ORDER, $payment);
  162. }
  163. }
  164. /**
  165. * 返回消息提示
  166. * @return mixed
  167. */
  168. public function getMessage()
  169. {
  170. return $this->message;
  171. }
  172. /**
  173. * 订单支付事件
  174. * @return void
  175. * @throws BaseException
  176. */
  177. private function orderPayEvent(): void
  178. {
  179. // 验证当前订单是否允许支付
  180. $this->checkOrderStatusOnPay();
  181. // 余额支付
  182. if ($this->method == PaymentMethodEnum::BALANCE) {
  183. $this->orderPaySucces($this->orderInfo['order_no']);
  184. }
  185. }
  186. /**
  187. * 验证当前订单是否允许支付
  188. * @throws BaseException
  189. */
  190. private function checkOrderStatusOnPay()
  191. {
  192. $orderSource = OrderSourceFactory::getFactory($this->orderInfo['order_source']);
  193. if (!$orderSource->checkOrderStatusOnPay($this->orderInfo)) {
  194. throwError($orderSource->getError() ?: '当前订单状态不允许支付');
  195. }
  196. }
  197. /**
  198. * 构建第三方支付请求的参数
  199. * @param array $extra 附加数据
  200. * @return array
  201. * @throws BaseException
  202. * @throws \think\db\exception\DataNotFoundException
  203. * @throws \think\db\exception\DbException
  204. * @throws \think\db\exception\ModelNotFoundException
  205. */
  206. private function unifiedorder(array $extra = []): array
  207. {
  208. // 判断支付方式是否合法
  209. if (!in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY, PaymentMethodEnum::PAYPAL, PaymentMethodEnum::STRIPE])) {
  210. return [];
  211. }
  212. // 生成第三方交易订单号 (并非主订单号)
  213. $outTradeNo = OrderService::createOrderNo();
  214. // 获取支付方式的配置信息
  215. $options = $this->getPaymentConfig();
  216. // 整理下单接口所需的附加数据
  217. $extra = $this->extraAsUnify($extra);
  218. // 构建支付模块
  219. $Payment = PaymentFacade::store($this->method)->setOptions($options, $this->client);
  220. // 执行第三方支付下单API
  221. if (!$Payment->unify($outTradeNo, (string)$this->orderInfo['pay_price'], $extra)) {
  222. throwError('第三方支付下单API调用失败');
  223. }
  224. // 返回客户端需要的支付参数
  225. return $Payment->getUnifyResult();
  226. }
  227. /**
  228. * 获取支付方式的配置信息
  229. * @return mixed
  230. * @throws BaseException
  231. * @throws \think\db\exception\DataNotFoundException
  232. * @throws \think\db\exception\DbException
  233. * @throws \think\db\exception\ModelNotFoundException
  234. */
  235. private function getPaymentConfig()
  236. {
  237. $PaymentModel = new PaymentModel;
  238. $templateInfo = $PaymentModel->getPaymentInfo($this->method, $this->client, $this->getStoreId());
  239. return $templateInfo['template']['config'][$this->method];
  240. }
  241. /**
  242. * 整理下单接口所需的附加数据
  243. * @param array $extra
  244. * @return array
  245. * @throws BaseException
  246. */
  247. private function extraAsUnify(array $extra = []): array
  248. {
  249. // 微信支付时需要的附加数据
  250. if ($this->method === PaymentMethodEnum::WECHAT) {
  251. // 微信小程序端需要openid
  252. if (in_array($this->client, [ClientEnum::MP_WEIXIN])) {
  253. $extra['openid'] = $this->getWechatOpenid();
  254. }
  255. }
  256. // 支付宝支付时需要的附加数据
  257. if ($this->method === PaymentMethodEnum::ALIPAY) {
  258. }
  259. return $extra;
  260. }
  261. /**
  262. * 获取微信端的用户openid(仅微信小程序)
  263. * @return null
  264. * @throws BaseException
  265. */
  266. private function getWechatOpenid()
  267. {
  268. if (in_array($this->client, [ClientEnum::MP_WEIXIN])) {
  269. // 当前登录用户信息
  270. $useInfo = UserService::getCurrentLoginUser(true);
  271. if (!$useInfo['currentOauth'] || empty($useInfo['currentOauth']['oauth_id'])) {
  272. throwError('很抱歉,您当前不存在openid 无法发起微信支付');
  273. }
  274. // 当前第三方用户标识
  275. return $useInfo['currentOauth']['oauth_id'];
  276. }
  277. return null;
  278. }
  279. /**
  280. * 订单支付成功事件
  281. * @param string $orderNo 当前订单号
  282. * @param int|null $tradeId 第三方交易记录ID
  283. * @param array $paymentData 第三方支付成功返回的数据
  284. * @return void
  285. * @throws BaseException
  286. */
  287. private function orderPaySucces(string $orderNo, ?int $tradeId = null, array $paymentData = []): void
  288. {
  289. // 获取订单详情
  290. $service = new OrderPaySuccesService;
  291. // 订单支付成功业务处理
  292. $service->setOrderNo($orderNo)->setMethod($this->method)->setTradeId($tradeId)->setPaymentData($paymentData);
  293. if (!$service->handle()) {
  294. throwError($service->getError() ?: '订单支付失败');
  295. }
  296. $this->message = '恭喜您,订单已付款成功';
  297. }
  298. }