From 2ea780365876d2df1557abf46761ae040c5035f3 Mon Sep 17 00:00:00 2001 From: "zhangf@suq.cn" Date: Thu, 18 Dec 2025 17:09:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(pay):=20=E5=AE=9E=E7=8E=B0=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=9B=9E=E8=B0=83=E4=B8=8E=E8=AE=A2=E5=8D=95=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增支付回调接口,支持微信和支付宝回调处理 - 创建支付订单模型及数据访问对象 - 实现订单支付成功后的状态更新逻辑 - 添加用户余额自动增加功能 - 实现唯一订单号生成功能,包含时间、机器标识和随机字符串 - 引入随机字符串工具类方法 - 修改支付工具类依赖注入,使用 PayService 处理回调 - 调整支付金额精度处理逻辑,支持调试模式小额测试 - 完善支付异常处理机制,增加订单不存在等校验逻辑 --- .../app/controller/v1/pay/PayController.php | 13 +- plugin/piadmin/app/dao/PayOrderDao.php | 16 +++ plugin/piadmin/app/model/PayOrder.php | 13 ++ plugin/piadmin/app/route/v1/pay.php | 2 +- plugin/piadmin/app/service/pay/PayService.php | 152 ++++++++++++++++++++- plugin/piadmin/app/utils/RandomUtils.php | 19 +++ plugin/piadmin/app/utils/pay/ali/AliPay.php | 5 +- plugin/piadmin/app/utils/pay/wechat/WechatPay.php | 5 +- 8 files changed, 212 insertions(+), 13 deletions(-) create mode 100644 plugin/piadmin/app/dao/PayOrderDao.php create mode 100644 plugin/piadmin/app/model/PayOrder.php diff --git a/plugin/piadmin/app/controller/v1/pay/PayController.php b/plugin/piadmin/app/controller/v1/pay/PayController.php index 2f21553..ec3b8ae 100644 --- a/plugin/piadmin/app/controller/v1/pay/PayController.php +++ b/plugin/piadmin/app/controller/v1/pay/PayController.php @@ -3,6 +3,7 @@ namespace plugin\piadmin\app\controller\v1\pay; use plugin\piadmin\app\base\BaseController; +use plugin\piadmin\app\exception\ApiException; use plugin\piadmin\app\service\pay\PayService; class PayController extends BaseController @@ -19,5 +20,15 @@ class PayController extends BaseController return success($qrurl); } - + /** + * 支付回调 + */ + public function notify(PayService $payService, $type) + { + $result = $payService->notify($type); + if (!empty($result)) { + return success(); + } + throw new ApiException('支付失败'); + } } \ No newline at end of file diff --git a/plugin/piadmin/app/dao/PayOrderDao.php b/plugin/piadmin/app/dao/PayOrderDao.php new file mode 100644 index 0000000..343fd06 --- /dev/null +++ b/plugin/piadmin/app/dao/PayOrderDao.php @@ -0,0 +1,16 @@ +middleware([ UserAuthorizationMiddleware::class, ]); diff --git a/plugin/piadmin/app/service/pay/PayService.php b/plugin/piadmin/app/service/pay/PayService.php index 49d80ad..669b908 100644 --- a/plugin/piadmin/app/service/pay/PayService.php +++ b/plugin/piadmin/app/service/pay/PayService.php @@ -3,25 +3,56 @@ namespace plugin\piadmin\app\service\pay; use plugin\piadmin\app\base\BaseService; +use plugin\piadmin\app\dao\PayOrderDao; +use plugin\piadmin\app\dao\UserDao; +use plugin\piadmin\app\exception\ApiException; use plugin\piadmin\app\utils\pay\ali\AliPay; use plugin\piadmin\app\utils\pay\wechat\WechatPay; +use plugin\piadmin\app\utils\RandomUtils; +use plugin\piadmin\app\utils\RequestUtils; +use support\Db; class PayService extends BaseService { + protected $payOrderDao; + protected $userDao; + + public function __construct() + { + $this->payOrderDao = app()->make(PayOrderDao::class); + $this->userDao = app()->make(UserDao::class); + } + public function getQrUrl($params) { + $userInfo = RequestUtils::getUserInfo(); + //创建订单记录 + $order_no = $this->generateOrderNo(); + $price = $params['price']; + if (env('APP_DEBUG')) { + $price = 0.01; + } + $this->payOrderDao->save([ + 'uid' => $userInfo['id'], + 'order_no' => $order_no, + 'desc' => '投稿点数充值', + 'order_time' => date('Y-m-d H:i:s'), + 'total_price' => $params['price'], + 'ip_address' => request()->getRealIp() + ]); + //微信订单封装 $wechatOrder = [ - 'out_trade_no' => time().'', - 'description' => 'subject-测试', + 'out_trade_no' => $order_no, + 'description' => '投稿点数充值', 'amount' => [ - 'total' => $params['price'] * 100, + 'total' => $price * 100, ], ]; - + //支付宝订单封装 $aliOrder = [ - 'out_trade_no' => time(), - 'total_amount' => $params['price'] . '', - 'subject' => 'yansongda 测试 - 01', + 'out_trade_no' => $order_no, + 'total_amount' => $price . '', + 'subject' => '投稿点数充值', ]; $wechatqrurl = WechatPay::getQrUrl($wechatOrder); $aliqrurl = AliPay::getQrUrl($aliOrder); @@ -31,4 +62,111 @@ class PayService extends BaseService ]; } + public function notify($type = 'wechat') + { + switch ($type) { + case 'wechat': + $result = WechatPay::notify(); + break; + case 'alipay': + $result = AliPay::notify(); + break; + default: + $result = []; + } + return $result; + } + + /** + * 订单支付回调 + * @param array $params + * @return array + */ + public function orderPayNotify(array $params): array + { + // 查订单 + $order = $this->payOrderDao->getOne([ + 'no' => $params['order_no'], + 'delete_time' => 0 + ]); + if (empty($order)) { + throw new ApiException('订单不存在'); + } + if ($order['status'] != 1) { + throw new ApiException('订单状态不需要处理'); + } + // 更新订单信息 + $updateOrderData = [ + 'payer_open_id' => $params['payer_open_id'] ?? '', + 'transaction_id' => $params['transaction_id'] ?? '', + 'pay_type' => $params['pay_type'] + ]; + if ($params['is_paid']) { + $updateOrderData['status'] = 3; + $updateOrderData['pay_price'] = $params['pay_total'] / 100; + $updateOrderData['pay_time'] = $params['success_time']->getTimestamp(); + $updateOrderData['pay_currency'] = $params['currency']; + } + // 落库 + Db::beginTransaction(); + try { + // 订单主表落库 + $this->dao->update(['id' => $order['id']], $updateOrderData); + //更改用户余额 + $this->userDao->update(['id' => $order['uid']], [ + 'balance' => Db::raw('balance+' . $params['pay_total'] / 100) + ]); + Db::commit(); + } catch (\Exception $exception) { + Db::rollback(); + } + return []; + } + + /** + * 生成唯一订单号 + * + * @param string $prefix 业务前缀(如支付方式、渠道标识) + * @param int $length 订单号总长度(默认32位,最小16位) + * @return string + */ + private function generateOrderNo(string $prefix = 'GEOCN', int $length = 32): string + { + // 验证参数 + if ($length < 16) { + throw new ApiException("订单号长度不能小于16位"); + } + + // 1. 基础时间部分(14位:年月日时分秒) + $time = date('YmdHis'); + + // 2. 随机字符串(至少2位) + $randomLength = $length - strlen($prefix) - strlen($time) - 2; + $randomLength = max($randomLength, 2); // 确保至少2位随机数 + $random = RandomUtils::generateRandomString($randomLength); + + // 3. 进程/机器标识(2位,避免集群环境冲突) + $machineId = self::getMachineId(); + + // 组合订单号 + $orderNo = $prefix . $time . $machineId . $random; + + // 截断或补位到指定长度 + return substr($orderNo, 0, $length); + } + + /** + * 获取机器/进程标识(2位) + * + * @return string + */ + private function getMachineId(): string + { + // 单机环境:使用进程ID的后两位 + // 集群环境:可改为服务器IP最后一段的哈希值 + $pid = getmypid() ?: 1; + $id = $pid % 100; + + return str_pad((string)$id, 2, '0', STR_PAD_LEFT); + } } \ No newline at end of file diff --git a/plugin/piadmin/app/utils/RandomUtils.php b/plugin/piadmin/app/utils/RandomUtils.php index 367aad0..8e6832a 100644 --- a/plugin/piadmin/app/utils/RandomUtils.php +++ b/plugin/piadmin/app/utils/RandomUtils.php @@ -47,4 +47,23 @@ class RandomUtils $hex_part = substr($hash, 0, 8); return hexdec($hex_part); } + + /** + * 生成指定长度的随机字符串 + * + * @param int $length + * @return string + */ + public static function generateRandomString(int $length): string + { + $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charLength = strlen($chars); + $random = ''; + + for ($i = 0; $i < $length; $i++) { + $random .= $chars[mt_rand(0, $charLength - 1)]; + } + + return $random; + } } \ No newline at end of file diff --git a/plugin/piadmin/app/utils/pay/ali/AliPay.php b/plugin/piadmin/app/utils/pay/ali/AliPay.php index ced3bb8..b283d17 100644 --- a/plugin/piadmin/app/utils/pay/ali/AliPay.php +++ b/plugin/piadmin/app/utils/pay/ali/AliPay.php @@ -1,6 +1,7 @@ make(OrderService::class); -// return $orderService->orderPayNotify($data); + $payService = app()->make(PayService::class); + return $payService->orderPayNotify($data); } public static function refund($order) diff --git a/plugin/piadmin/app/utils/pay/wechat/WechatPay.php b/plugin/piadmin/app/utils/pay/wechat/WechatPay.php index 343c46f..9ceb4d8 100644 --- a/plugin/piadmin/app/utils/pay/wechat/WechatPay.php +++ b/plugin/piadmin/app/utils/pay/wechat/WechatPay.php @@ -1,6 +1,7 @@ make(OrderService::class); -// return $orderService->orderPayNotify($data); + $payService = app()->make(PayService::class); + return $payService->orderPayNotify($data); } public static function refund($order)