Files
code-bang/backend/application/common/service/OrderService.php
T

773 lines
33 KiB
PHP
Raw Normal View History

2026-09-17 16:25:31 +08:00
<?php
namespace app\common\service;
use think\Cache;
use think\Db;
use think\Log;
2026-09-23 14:46:56 +08:00
use EasyWeChat\Pay\Message as PayMessage;
2026-09-17 16:25:31 +08:00
use app\common\model\Orders;
use app\common\traits\ServiceTrait;
use think\Exception;
class OrderService
{
use ServiceTrait;
/** @var Orders */
public static $Model = Orders::class;
const LOCK_ORDER_PREFIX = 'lock_order_cache:'; //重复创建订单锁
2026-09-23 15:03:52 +08:00
const DEFAULT_SAAS_ID = '1000'; //默认商家ID(fa_orders.saas_id 为 NOT NULL 且无默认值,商品无归属时兜底)
2026-09-17 16:25:31 +08:00
//订单状态
const ORDER_STATUS_NO = 0; //关闭
const ORDER_STATUS_WAIT_PAY = 10; //待付款
const ORDER_STATUS_WAIT_SHIP = 20; //已支付(待发货)
const ORDER_STATUS_SHIPPED = 30; //已发货(待收货)
const ORDER_STATUS_FAILED = 31; //交易失败
const ORDER_STATUS_TIMEOUT = 33; //订单超时
const ORDER_STATUS_FINISHED = 40; //已完成
const ORDER_STATUS_CANCEL = 50; //已取消
const ORDER_STATUS_REFUNDED = 60; //已退款
const ORDER_STATUS_MAP = [
self::ORDER_STATUS_NO => '订单关闭',
self::ORDER_STATUS_WAIT_PAY => '待支付',
self::ORDER_STATUS_WAIT_SHIP => '待发货',
self::ORDER_STATUS_SHIPPED => '已发货',
self::ORDER_STATUS_FAILED => '交易失败',
self::ORDER_STATUS_TIMEOUT => '订单超时',
self::ORDER_STATUS_CANCEL => '用户取消订单',
self::ORDER_STATUS_FINISHED => '交易完成',
self::ORDER_STATUS_REFUNDED => '已退款',
];
// 订单操作按钮
const BUTTON_PAY = 1; // 付款
const BUTTON_CANCEL = 2; // 取消订单
const BUTTON_CONFIRM = 3; // 确认收货
const BUTTON_APPLY_REFUND = 4; // 申请售后
const BUTTON_VIEW_REFUND = 5; // 查看退款
const BUTTON_COMMENT = 6; // 评价
const BUTTON_DELETE = 7; // 删除订单
const BUTTON_DELIVERY = 8; // 查看物流
const BUTTON_REBUY = 9; // 再次购买
const BUTTON_INVITE_GROUPON = 11; //邀请好友拼团
const BUTTON_MAP = [
self::BUTTON_PAY => '付款', // 付款
self::BUTTON_CANCEL => '取消订单', // 取消订单
self::BUTTON_CONFIRM => '确认收货', // 确认收货
self::BUTTON_APPLY_REFUND => '申请售后', // 申请售后
self::BUTTON_VIEW_REFUND => '查看退款', // 查看退款
self::BUTTON_COMMENT => '评价', // 评价
self::BUTTON_DELETE => '删除订单', // 删除订单
self::BUTTON_DELIVERY => '查看物流', // 查看物流
self::BUTTON_REBUY => '再次购买', // 再次购买
self::BUTTON_INVITE_GROUPON => '邀请好友拼团', //邀请好友拼团
];
const ORDER_TYPE_SOFTWARE = 0;//软件产品;
const ORDER_TYPE_FIRST_PAYMENT = 1;//首付款款订单;
const ORDER_TYPE_FINAL_PAYMENT = 2; //尾款订单;
const ORDER_TYPE_MAP = [
self::ORDER_TYPE_SOFTWARE => '软件产品',
self::ORDER_TYPE_FIRST_PAYMENT => '需求首付款',
self::ORDER_TYPE_FINAL_PAYMENT => '需求尾款',
];
/**
* 生成订单
* @param $requestData
* @return array
* @throws \Exception
*/
public static function createOrder($requestData)
{
try {
$user = $requestData['userInfo'] ?? [];
$userId = $user['id'] ?? 0;
$openid = $user['openid'] ?? '';
Log::info('创建订单数据-request:' . json_encode($requestData, JSON_UNESCAPED_UNICODE));
if (empty($requestData['goodsRequestList'])) {
throw new \Exception('订单数据错误');
}
//如果30秒内有重复数据,直接返回缓存订单,不再创建
$lockOrderKey = self::getLockOrderKey($userId, $requestData);
if ($lockOrderRes = Cache::get($lockOrderKey)) {
return json_decode($lockOrderRes, true);
}
$goodsRequestList = $requestData['goodsRequestList'];
$count = count($goodsRequestList);
if ($count == 0) {
Log::error('订单创建失败:商品数量不能为0');
throw new \Exception('订单创建失败:商品数量不能为0');
}
$total_amount = 0;
$total_quantity = 0;
$goodsDataList = [];
foreach ($goodsRequestList as $item) {
$quantity = max(1, $item['quantity']);
$total_quantity += $quantity;
$total_amount += $quantity * $item['price'];
$goodsDataList[] = [
'spu_id' => $item['spuId'],
'sku_id' => $item['sku_id'] ?? '',
'goods_name' => $item['goodsName'],
'goods_picture_url' => $item['primaryImage'],
'origin_price' => $item['price'],
'actual_price' => $item['price'],
'buy_quantity' => $quantity,
'item_total_amount' => $item['price'] * $quantity,
'item_discount_amount' => 0,
'item_payment_amount' => $item['price'] * $quantity,
'goods_payment_price' => $item['price'],
];
}
2026-09-23 15:03:52 +08:00
// saas_id 取商品归属商家,取不到时用默认值(fa_orders.saas_id 不允许为空且无默认值)
$goodsInfo = GoodsService::getOne(['spu_id' => $goodsRequestList[0]['spuId'] ?? '']);
$saasId = ($goodsInfo && !empty($goodsInfo->saas_id)) ? $goodsInfo->saas_id : self::DEFAULT_SAAS_ID;
$storeInfo = $requestData['storeInfoList'][0] ?? [];
2026-09-17 16:25:31 +08:00
$orderData = [
'user_id' => $userId,
2026-09-23 15:03:52 +08:00
'saas_id' => $saasId,
'store_id' => $storeInfo['storeId'] ?? ($goodsRequestList[0]['storeId'] ?? 1),
'store_name' => $storeInfo['storeName'] ?? '',
'parent_order_no' => '', // 单订单无父子关系,稍后回填为自身订单号
2026-09-17 16:25:31 +08:00
'order_no' => generate_order_sn(),
'order_status' => OrderService::ORDER_STATUS_WAIT_PAY,
'order_type' => 0,
'total_amount' => $total_amount,
'payment_amount' => $total_amount,
2026-09-23 15:03:52 +08:00
'goods_amount' => $total_amount,
2026-09-17 16:25:31 +08:00
'discount_amount' => 0,
2026-09-23 15:03:52 +08:00
'remark' => $storeInfo['remark'] ?? '',
2026-10-14 14:44:01 +08:00
'promoter_id' => PromotionService::getPromoterId(intval($userId)),
2026-09-17 16:25:31 +08:00
];
2026-09-23 15:03:52 +08:00
$orderData['parent_order_no'] = $orderData['order_no'];
2026-10-27 18:10:51 +08:00
VirtualPayService::rememberPayPlatform($orderData['order_no'], $requestData['payPlatform'] ?? '');
2026-09-17 16:25:31 +08:00
$existed = OrderService::getOne(['order_no' => $orderData['order_no']]);
if (!$existed) {
//创建订单及订单商品
$orderRes = self::$Model::create($orderData);
Log::info('创建订单数据:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
if (!$orderRes) {
Log::error('创建订单创建失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('订单创建失败');
}
$goodsRes = $orderRes->orderGoods()->saveAll($goodsDataList);
if (!$goodsRes) {
Log::error('创建订单产品失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('订单创建数据失败');
}
}
$orderData += [
'channel' => 'wechat',
'tradeNo' => $orderData['order_no'],
'interactId' => 0,
'transactionId' => 0,
2026-10-27 16:19:46 +08:00
'payInfo' => self::pay($orderData['order_no'], $total_amount, $openid),
2026-09-17 16:25:31 +08:00
];
2026-10-27 16:19:46 +08:00
if (!empty($orderData['payInfo']['paid'])) {
$orderData['order_status'] = self::ORDER_STATUS_WAIT_SHIP;
}
2026-09-17 16:25:31 +08:00
//存入缓存
Cache::set($lockOrderKey, json_encode($orderData), 30);
return $orderData;
} catch (Exception $e) {
throw new \Exception('订单数据保存失败:' . $e->getMessage());
}
}
/**
* 生成需求订单
* @param $requestData
* @throws \Exception
*/
public static function createDemandOrder($requestData)
{
try {
$demandId = $requestData['demandId'] ?? 0;
$formData = $requestData['formData'] ?? [];
$totalCost = $formData['totalCost'] ?? 0;
Log::info('创建需求订单数据-request:' . json_encode($requestData, JSON_UNESCAPED_UNICODE));
$demand = DemandService::getOne(['id' => $demandId]);
if (!$demand) {
throw new \Exception('该需求不存在');
}
$winBid = DemandBiddingService::getOne(['demand_id' => $demandId, 'bidding_status' => DemandBiddingService::STATUS_BIDDING_WIN]);
$orderInfo = self::where(['parent_order_no' => $demand->parent_order_no])->find();
$ratio = 0.5; //分期比率50%
if (!$orderInfo) {
2026-10-14 14:44:01 +08:00
$promoterId = PromotionService::getPromoterId(intval($demand->user_id));
2026-09-17 16:25:31 +08:00
$installments = 2;//分期数
for ($i = 1; $i <= $installments; $i++) {
$total_amount = $i == 1 ? $totalCost * $ratio : $totalCost * (1 - $ratio);
$goodsDataList = [[
'spu_id' => 0,
'sku_id' => 0,
'goods_name' => '需求签约-' . ($i == 1 ? '首付款' : '尾款') . $demand->parent_order_no,
'goods_picture_url' => 'https://soft-market.oss-cn-shenzhen.aliyuncs.com/images/contract.png',
'origin_price' => $total_amount,
'actual_price' => $total_amount,
'buy_quantity' => 1,
'item_total_amount' => $total_amount,
'item_discount_amount' => 0,
'item_payment_amount' => $total_amount,
'goods_payment_price' => $total_amount,
]];
$orderData = [
'user_id' => $demand->user_id,
2026-09-23 15:03:52 +08:00
'saas_id' => self::DEFAULT_SAAS_ID,
2026-09-17 16:25:31 +08:00
'store_id' => 1,
2026-09-23 15:03:52 +08:00
'store_name' => '',
2026-09-17 16:25:31 +08:00
'parent_order_no' => $demand->parent_order_no,
'order_no' => generate_order_sn(),
'order_status' => OrderService::ORDER_STATUS_WAIT_PAY,
'order_type' => $i,
'total_amount' => $total_amount,
'payment_amount' => $total_amount,
'goods_amount' => $total_amount,
'discount_amount' => 0,
'remark' => '',
2026-10-14 14:44:01 +08:00
'promoter_id' => $promoterId,
2026-09-17 16:25:31 +08:00
];
$existed = OrderService::getOne(['order_no' => $orderData['order_no']]);
if (!$existed) {
//创建订单及订单商品
$orderRes = self::$Model::create($orderData);
Log::info('创建需求订单数据:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
if (!$orderRes) {
Log::error('创建需求订单创建失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('需求订单创建失败');
}
$goodsRes = $orderRes->orderGoods()->saveAll($goodsDataList);
if (!$goodsRes) {
Log::error('创建需求订单产品失败:' . json_encode($orderData, JSON_UNESCAPED_UNICODE));
throw new \Exception('需求订单创建数据失败');
}
}
}
}
//查找签约记录,没有则创建
$contractInfo = ContractService::getOne(['demand_id' => $demand, 'bidding_id' => $winBid->id ?? 0]);
if (!$contractInfo) {
//创建合同数据
$firstContract = ContractService::getFirstContract($demandId);
$depositAmount = $firstContract ? $firstContract->deposit_amount : $formData['totalCost'] * $ratio;
ContractService::createData($demandId, $winBid->id ?? 0, $depositAmount, $formData);
}
return $contractInfo;
} catch (Exception $e) {
throw new \Exception('需求订单数据保存失败:' . $e->getMessage());
}
}
public static function getDemandOrder($parentOrderNo, $contractType)
{
$res = self::getOne(['parent_order_no' => $parentOrderNo, 'order_type' => $contractType], ['orderGoods', 'orderPayment']);
return $res;
}
public static function orderPay($requestData)
{
try {
$user = $requestData['userInfo'] ?? [];
$userId = $user['id'] ?? 0;
$openid = $user['openid'] ?? '';
$orderNo = $requestData['orderNo'] ?? '';
if (!$orderNo) {
throw new \Exception('订单号不存在');
}
$fields = 'user_id,store_id,order_no,order_status,order_type,total_amount,payment_amount,goods_amount,discount_amount,remark';
$orderInfo = OrderService::where(['order_no' => $orderNo, 'user_id' => $userId])->field($fields)->find();
if (!$orderInfo) {
throw new \Exception('该订单不存在');
}
$orderData = $orderInfo->toArray();
2026-10-27 18:10:51 +08:00
VirtualPayService::rememberPayPlatform($orderNo, $requestData['payPlatform'] ?? '');
2026-09-23 14:46:56 +08:00
Log::info('微信下单[发起支付] ' . json_encode([
'order_no' => $orderNo,
'user_id' => $userId,
'order_status' => $orderData['order_status'] ?? null,
'status_text' => self::ORDER_STATUS_MAP[$orderData['order_status'] ?? -1] ?? '未知',
'payment_amount' => $orderData['payment_amount'] ?? 0,
'total_amount' => $orderData['total_amount'] ?? 0,
], JSON_UNESCAPED_UNICODE));
2026-09-17 16:25:31 +08:00
$orderData += [
'channel' => 'wechat',
'tradeNo' => $orderData['order_no'],
'interactId' => 1,
'transactionId' => 0,
2026-10-27 16:19:46 +08:00
'payInfo' => self::pay($orderInfo->order_no, $orderInfo->payment_amount, $openid),
2026-09-17 16:25:31 +08:00
];
2026-10-27 16:19:46 +08:00
if (!empty($orderData['payInfo']['paid'])) {
$orderData['order_status'] = self::ORDER_STATUS_WAIT_SHIP;
}
Log::info('虚拟支付下单[下单成功] ' . json_encode([
2026-09-23 14:46:56 +08:00
'order_no' => $orderNo,
2026-10-27 16:19:46 +08:00
'paid' => $orderData['payInfo']['paid'] ?? 0,
'mode' => $orderData['payInfo']['mode'] ?? '',
2026-09-23 14:46:56 +08:00
], JSON_UNESCAPED_UNICODE));
2026-09-17 16:25:31 +08:00
return $orderData;
} catch (Exception $e) {
throw new \Exception('订单支付异常:' . $e->getMessage());
}
}
/**
* 订单操作
* @param $request
* @return mixed
* @throws \Exception
*/
public static function orderOperate($requestData, $userId)
{
$goodsInfo = $requestData['goodsInfo'] ?? [];
$type = $requestData['type'] ?? '';
switch ($type) {
case self::BUTTON_CONFIRM:
$res = self::confirmOrder($goodsInfo['id'] ?? 0, $userId);
break;
case self::BUTTON_CANCEL:
$res = self::cancelOrder($goodsInfo['id'] ?? 0, $userId);
break;
default:
throw new \Exception('非法操作');
}
return $res;
}
/**
* 取消订单
* @param $order_id
* @param $userId
* @return mixed
* @throws \Exception
*/
public static function cancelOrder($orderId, $userId = 0)
{
try {
$where = ['id' => $orderId, 'user_id' => $userId];
$order = self::getOne($where);
if (!$order) {
throw new \Exception('该订单不存在');
}
Db::startTrans();
if (!in_array($order->order_status, [OrderService::ORDER_STATUS_WAIT_PAY, OrderService::ORDER_STATUS_WAIT_SHIP])) {
throw new \Exception('该订单为' . OrderService::ORDER_STATUS_MAP[$order->order_status] . ',不能取消');
}
//修改订单状态
$upRes = self::where($where)->update(['order_status' => OrderService::ORDER_STATUS_CANCEL]);
if (!$upRes) {
throw new \Exception('更新订单状态失败');
}
//退款原路返回
// OrderRefund::dispatch(['order_sn' => $order->order_sn, 'actual_amount' => $order->actual_amount]);
//给用户发送出票失败结果通知
// SendMsg::dispatch(['order_id' => $order->id, 'user_id' => $order->user_id, 'template_id' => SubscribeMessageService::MSG_TEMP_ORDER_FAILED, 'desc' => SubscribeMessageService::ORDER_MSG_MAP['failed'], 'desc2' => SubscribeMessageService::ORDER_MSG_MAP['refund']]);
Db::commit();
$info = OrderService::getOne(['id' => $orderId]);
return $info;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
/**
* 确认收货
* @param $orderId
* @param $userId
* @return mixed
* @throws \Exception
*/
public static function confirmOrder($orderId, $userId = 0)
{
try {
$where = ['id' => $orderId, 'user_id' => $userId];
$order = self::getOne($where);
if (!$order) {
throw new \Exception('该订单不存在');
}
Db::startTrans();
if (!in_array($order->order_status, [OrderService::ORDER_STATUS_SHIPPED])) {
throw new \Exception('该订单为' . OrderService::ORDER_STATUS_MAP[$order->order_status] . ',不能确认收货');
}
//修改订单状态
$upRes = self::where($where)->update(['order_status' => OrderService::ORDER_STATUS_FINISHED]);
if (!$upRes) {
throw new \Exception('更新订单状态失败');
}
2026-09-30 14:56:56 +08:00
//需求订单(首付款/尾款):同步需求状态与关联订单
if (in_array($order->order_type, [self::ORDER_TYPE_FIRST_PAYMENT, self::ORDER_TYPE_FINAL_PAYMENT])) {
$demand = DemandService::getOne(['parent_order_no' => $order->parent_order_no]);
if ($demand && $demand->status == DemandService::STATUS_CONFIRM_RECEIPT) {
$demand->status = DemandService::STATUS_FINISHED;
$demand->settle_status = CommonService::ONGOING; //结算中
$saveRes = $demand->save();
if (!$saveRes) {
throw new \Exception('更新需求状态失败');
}
//同一需求下的关联订单全部改为已完成
$upRes = self::where(['parent_order_no' => $order->parent_order_no])->update(['order_status' => self::ORDER_STATUS_FINISHED]);
if (!$upRes) {
throw new \Exception('更新关联订单状态失败');
}
}
}
2026-10-14 14:44:01 +08:00
//交易完成:结算推广佣金
PromotionService::settleOrders($order->parent_order_no ? ['parent_order_no' => $order->parent_order_no] : ['id' => $order->id]);
2026-09-17 16:25:31 +08:00
Db::commit();
$info = OrderService::getOne(['id' => $orderId]);
return $info;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
/**
2026-09-21 14:57:50 +08:00
* 微信支付回调(EasyWeChat 6.x / 支付 v3: 自动验签+解密)
* @return \Psr\Http\Message\ResponseInterface
2026-09-17 16:25:31 +08:00
* @throws \Exception
*/
public static function payNotify()
{
2026-09-21 14:57:50 +08:00
$service = new EasyWeChatService();
2026-09-17 16:25:31 +08:00
2026-09-23 14:46:56 +08:00
return $service->handlePayNotify(function (PayMessage $message, $traceId = '') {
$prefix = '微信支付回调[业务] trace=' . $traceId . ' ';
2026-09-21 14:57:50 +08:00
$order_no = $message->out_trade_no ?? '';
2026-09-23 14:46:56 +08:00
$total = $message->amount['total'] ?? 0;
Log::info($prefix . '支付成功通知: ' . json_encode([
'order_no' => $order_no,
'transaction_id' => $message->transaction_id ?? '',
'trade_state' => $message->trade_state ?? '',
'trade_type' => $message->trade_type ?? '',
'amount_fen' => $total,
'amount_yuan' => sprintf('%.2f', $total / 100),
'success_time' => $message->success_time ?? '',
], JSON_UNESCAPED_UNICODE));
2026-09-21 14:57:50 +08:00
$orderInfo = OrderService::getOne(['order_no' => $order_no]);
if (!$orderInfo) {
2026-09-23 14:46:56 +08:00
Log::error($prefix . '订单不存在: ' . json_encode(['order_no' => $order_no], JSON_UNESCAPED_UNICODE));
2026-09-21 14:57:50 +08:00
throw new \Exception('获取订单数据失败');
2026-09-17 16:25:31 +08:00
}
2026-09-23 14:46:56 +08:00
Log::info($prefix . '订单当前状态: ' . json_encode([
'order_id' => $orderInfo->id,
'order_no' => $order_no,
'order_status' => $orderInfo->order_status,
'status_text' => self::ORDER_STATUS_MAP[$orderInfo->order_status] ?? '未知',
'order_type' => $orderInfo->order_type,
'parent_no' => $orderInfo->parent_order_no ?? '',
'payment_amount'=> $orderInfo->payment_amount,
], JSON_UNESCAPED_UNICODE));
2026-09-21 14:57:50 +08:00
if (($message->trade_state ?? '') != 'SUCCESS') {
2026-09-23 14:46:56 +08:00
Log::error($prefix . '支付状态异常: trade_state=' . ($message->trade_state ?? ''));
2026-09-21 14:57:50 +08:00
throw new \Exception('支付失败:' . ($message->trade_state ?? ''));
2026-09-17 16:25:31 +08:00
}
2026-10-27 16:19:46 +08:00
self::finishPaid(
$order_no,
(int)$total,
(string)($message->transaction_id ?? ''),
(string)($message->trade_type ?? 'JSAPI'),
strtotime((string)($message->success_time ?? '')) ?: time(),
$traceId
);
2026-09-23 14:46:56 +08:00
return true;
2026-09-21 14:57:50 +08:00
});
2026-09-17 16:25:31 +08:00
}
/**
* 获取缓存
* @param $userId
* @param $data array
* @return mixed
*/
public static function getLockOrderKey($userId, $data)
{
$key = self::LOCK_ORDER_PREFIX . md5($userId . json_encode($data));
return $key;
}
/**
2026-10-27 16:19:46 +08:00
* 支付成功入账。虚拟支付确认和微信支付回调共用,重复通知直接返回
* @param string $orderNo
* @param int $amountFen 实付金额,单位分
* @param string $transactionId
* @param string $payWay
* @param int $paidTime
* @param string $traceId
2026-10-27 18:10:51 +08:00
* @param string $payPlatform android|ios,空则用下单时记下的终端
2026-10-27 16:19:46 +08:00
* @return bool
* @throws \Exception
*/
2026-10-27 18:10:51 +08:00
public static function finishPaid($orderNo, $amountFen, $transactionId, $payWay, $paidTime, $traceId = '', $payPlatform = '')
2026-10-27 16:19:46 +08:00
{
2026-10-27 18:10:51 +08:00
$payPlatform = VirtualPayService::normalizePayPlatform($payPlatform);
if ($payPlatform === '') {
$payPlatform = VirtualPayService::payPlatformOf($orderNo);
}
if ($payPlatform === '') {
$payPlatform = 'android';
}
2026-10-27 16:19:46 +08:00
$prefix = '订单支付入账 trace=' . $traceId . ' ';
try {
2026-10-27 18:10:51 +08:00
Db::transaction(function () use ($orderNo, $amountFen, $transactionId, $payWay, $paidTime, $prefix, $payPlatform) {
2026-10-27 16:19:46 +08:00
$orderInfo = self::where('order_no', $orderNo)->lock(true)->find();
if (!$orderInfo) {
Log::error($prefix . '订单不存在: ' . $orderNo);
throw new \Exception('获取订单数据失败');
}
if ((int)$orderInfo->order_status !== self::ORDER_STATUS_WAIT_PAY) {
Log::info($prefix . '订单已处理过,跳过状态更新: ' . json_encode([
'order_no' => $orderNo,
'order_status' => $orderInfo->order_status,
], JSON_UNESCAPED_UNICODE));
return;
}
$checkMoney = sprintf('%.1f', $amountFen / 100) == sprintf('%.1f', $orderInfo->payment_amount);
if (!$checkMoney) {
Log::error($prefix . '金额校验失败: ' . json_encode([
'notify_amount' => sprintf('%.2f', $amountFen / 100),
'order_amount' => sprintf('%.2f', $orderInfo->payment_amount),
], JSON_UNESCAPED_UNICODE));
throw new \Exception('金额错误:' . sprintf('%.2f', $amountFen / 100) . '----' . sprintf('%.2f', $orderInfo->payment_amount));
}
$paymentData = [
'order_no' => $orderNo,
'pay_status' => 'SUCCESS',
'amount' => $amountFen / 100,
'currency' => 'CNY',
'pay_type' => '1',
'pay_way' => $payWay ?: 'XPAY',
2026-10-27 18:10:51 +08:00
'pay_platform' => $payPlatform,
2026-10-27 16:19:46 +08:00
'trace_no' => $transactionId,
'pay_time' => time(),
'pay_success_time' => $paidTime > 0 ? $paidTime : time(),
];
self::where('order_no', $orderNo)->update(['order_status' => self::ORDER_STATUS_WAIT_SHIP]);
OrderPaymentService::create($paymentData);
if ($orderInfo->order_type == self::ORDER_TYPE_FIRST_PAYMENT) {
DemandService::customerContractFinishedDeal($orderInfo->parent_order_no);
} elseif ($orderInfo->order_type == self::ORDER_TYPE_FINAL_PAYMENT) {
DemandService::customerWorkAcceptFinishedDeal($orderInfo->parent_order_no);
}
Log::info($prefix . '入账成功 ' . json_encode($paymentData, JSON_UNESCAPED_UNICODE));
});
} catch (\Exception $e) {
Log::error($prefix . '入账失败: ' . $e->getMessage());
throw $e;
}
return true;
}
/**
* 虚拟支付下单。上一笔现金单已经支付成功时直接入账,不再拉起收银台
* @param string $order_sn
* @param float|string $total_fee
* @param string $openid
2026-09-17 16:25:31 +08:00
* @param string $body
2026-10-27 16:19:46 +08:00
* @return array
2026-09-17 16:25:31 +08:00
* @throws \Exception
*/
public static function pay($order_sn, $total_fee, $openid, string $body = '软件产品服务')
{
if (!($body && $order_sn && $total_fee && $openid)) {
2026-10-27 16:19:46 +08:00
Log::error('虚拟支付下单[参数错误] ' . json_encode([
'body' => $body,
'order_sn' => $order_sn,
2026-09-23 14:46:56 +08:00
'total_fee' => $total_fee,
2026-10-27 16:19:46 +08:00
'openid' => $openid,
2026-09-23 14:46:56 +08:00
], JSON_UNESCAPED_UNICODE));
2026-09-17 16:25:31 +08:00
throw new \Exception('支付参数错误2');
}
2026-10-27 16:19:46 +08:00
$current = self::getOne(['order_no' => $order_sn]);
if ($current && !in_array((int)$current->order_status, [
self::ORDER_STATUS_WAIT_PAY,
self::ORDER_STATUS_WAIT_SHIP,
self::ORDER_STATUS_SHIPPED,
self::ORDER_STATUS_FINISHED,
], true)) {
throw new \Exception('订单状态不可支付');
}
if (VirtualPayService::settleIfPaid($order_sn, $openid)) {
return ['paid' => 1];
}
return VirtualPayService::createClientPay($order_sn, $total_fee, $openid);
}
2026-09-17 16:25:31 +08:00
2026-10-27 16:19:46 +08:00
/**
* 客户端支付成功后确认:查现金单、扣代币、更新订单
* @param string $orderNo
* @param int $userId
* @return array
* @throws \Exception
*/
2026-10-27 18:10:51 +08:00
public static function confirmVirtualPay($orderNo, $userId, $payPlatform = '')
2026-10-27 16:19:46 +08:00
{
2026-10-27 18:10:51 +08:00
VirtualPayService::rememberPayPlatform($orderNo, $payPlatform);
2026-10-27 16:19:46 +08:00
$order = self::getOne(['order_no' => $orderNo, 'user_id' => $userId]);
if (!$order) {
throw new \Exception('订单不存在');
}
if ((int)$order->order_status !== self::ORDER_STATUS_WAIT_PAY) {
return ['order_status' => (int)$order->order_status];
}
$openid = \app\common\model\User::where('id', $userId)->value('openid');
if (!$openid) {
throw new \Exception('用户未绑定微信');
}
if (!VirtualPayService::settleIfPaid($orderNo, $openid)) {
throw new \Exception('支付结果确认中,请稍后在订单里再次点击支付,不会重复扣款');
2026-09-17 16:25:31 +08:00
}
2026-10-27 16:19:46 +08:00
$fresh = self::getOne(['order_no' => $orderNo]);
2026-09-17 16:25:31 +08:00
2026-10-27 16:19:46 +08:00
return ['order_status' => (int)($fresh->order_status ?? self::ORDER_STATUS_WAIT_SHIP)];
2026-09-17 16:25:31 +08:00
}
/**
* 获取订单操作按钮
* @param $orderStatus
* @return array
*/
public static function getOrderButtons($orderStatus): array
{
$buttons = [];
if ($orderStatus == self::ORDER_STATUS_WAIT_PAY) {
$buttons[] = ['name' => self::BUTTON_MAP[OrderService::BUTTON_CANCEL], 'primary' => false, 'type' => OrderService::BUTTON_CANCEL];
$buttons[] = ['name' => self::BUTTON_MAP[OrderService::BUTTON_PAY], 'primary' => true, 'type' => OrderService::BUTTON_PAY];
}
if ($orderStatus == self::ORDER_STATUS_SHIPPED) {
$buttons[] = ['name' => self::BUTTON_MAP[OrderService::BUTTON_CONFIRM], 'primary' => true, 'type' => OrderService::BUTTON_CONFIRM];
}
return $buttons;
}
/**
* 获取订单统计
* @param $userId
* @return array[]
*/
public static function getOrderCount($userId): array
{
$orderCountList = [
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_WAIT_PAY],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_WAIT_SHIP],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_SHIPPED],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_FINISHED],
['orderNum' => 0, 'tabType' => OrderService::ORDER_STATUS_REFUNDED],
];
$countList = self::where(['user_id' => $userId, 'order_type' => self::ORDER_TYPE_SOFTWARE])->field('order_status, COUNT(*) as total')
->group('order_status')
->select();
foreach ($orderCountList as &$value) {
if ($value['tabType'] == OrderService::ORDER_STATUS_FINISHED) {
continue;
}
foreach ($countList as $item) {
if ($item->order_status == $value['tabType']) {
$value['orderNum'] = $item->total;
}
}
}
return $orderCountList;
}
/**
2026-10-14 17:21:51 +08:00
* 订单发货(只生成发货单并更新状态,发货通知在后台「订单发货管理」编辑并确认发货时发送)
2026-09-17 16:25:31 +08:00
* @param $id
* @return void
* @throws \Exception
*/
public static function orderShip($id)
{
2026-10-14 17:21:51 +08:00
Db::transaction(function () use ($id) {
2026-10-14 16:26:41 +08:00
//加锁读取,防止并发重复发货、重复分配激活码
$order = OrderService::where(['id' => $id])->lock(true)->find();
if (!$order) {
exception('订单不存在');
}
2026-09-17 16:25:31 +08:00
if ($order->order_status != OrderService::ORDER_STATUS_WAIT_SHIP) {
exception('订单状态异常,不能发货');
}
//查询激活码
$code = SoftCodeService::getAvailableCode();
//生成发货单
OrderShipService::createData($order->order_no, $order->orderGoods[0]->spu_id, $code);
$order->order_status = OrderService::ORDER_STATUS_SHIPPED;
//修改发货状态
$saveRes = $order->save();
if (!$saveRes) {
exception('更新发货状态失败');
}
2026-10-14 16:26:41 +08:00
});
2026-10-14 17:21:51 +08:00
}
2026-10-14 16:26:41 +08:00
2026-10-14 17:21:51 +08:00
/**
* 发送软件订单发货通知(订阅消息 + 站内消息),失败只记日志
* @param string $orderNo 订单号
* @return bool 是否发送成功
* @throws \Exception 订单不存在或不是已发货的软件订单
*/
public static function sendShipNotice($orderNo): bool
{
$order = self::getOne(['order_no' => $orderNo], ['orderGoods']);
if (!$order) {
exception('订单不存在');
}
if ($order->order_type != self::ORDER_TYPE_SOFTWARE) {
exception('仅软件订单支持发送发货通知');
}
if ($order->order_status != self::ORDER_STATUS_SHIPPED) {
exception('订单未发货,不能发送发货通知');
}
2026-10-14 16:26:41 +08:00
try {
2026-09-17 16:25:31 +08:00
$msgData = [
2026-10-14 16:26:41 +08:00
'page' => SubscribeMessageService::PAGE_ORDER_DETAIL,
'param' => ['orderNo' => $order->order_no],
2026-09-17 16:25:31 +08:00
'values' => [
$order->order_no,
mb_substr($order->orderGoods[0]->goods_name, 0, 20),
'已发货',
SubscribeMessageService::MSG_MAP[SubscribeMessageService::MSG_TEMP_ORDER_SHIPPED]['remark'],
]
];
(new EasyWeChatService())->sendMsg($order->user_id, SubscribeMessageService::MSG_TEMP_ORDER_SHIPPED, $msgData);
2026-10-14 16:26:41 +08:00
} catch (\Exception $e) {
Log::error(__CLASS__ . ' 订单发货通知发送失败:' . json_encode([
2026-10-14 17:21:51 +08:00
'order_no' => $orderNo,
2026-10-14 16:26:41 +08:00
'error' => $e->getMessage(),
], JSON_UNESCAPED_UNICODE));
2026-10-14 17:21:51 +08:00
return false;
2026-10-14 16:26:41 +08:00
}
2026-10-14 17:21:51 +08:00
return true;
2026-09-17 16:25:31 +08:00
}
}