添加虚拟支付
This commit is contained in:
@@ -168,8 +168,11 @@ class OrderService
|
||||
'tradeNo' => $orderData['order_no'],
|
||||
'interactId' => 0,
|
||||
'transactionId' => 0,
|
||||
'payInfo' => self::pay($orderData['order_no'], $total_amount, $openid), //统一下单接口
|
||||
'payInfo' => self::pay($orderData['order_no'], $total_amount, $openid),
|
||||
];
|
||||
if (!empty($orderData['payInfo']['paid'])) {
|
||||
$orderData['order_status'] = self::ORDER_STATUS_WAIT_SHIP;
|
||||
}
|
||||
//存入缓存
|
||||
Cache::set($lockOrderKey, json_encode($orderData), 30);
|
||||
|
||||
@@ -300,11 +303,15 @@ class OrderService
|
||||
'tradeNo' => $orderData['order_no'],
|
||||
'interactId' => 1,
|
||||
'transactionId' => 0,
|
||||
'payInfo' => self::pay($orderInfo->order_no, $orderInfo->total_amount, $openid), //统一下单接口
|
||||
'payInfo' => self::pay($orderInfo->order_no, $orderInfo->payment_amount, $openid),
|
||||
];
|
||||
Log::info('微信下单[下单成功] ' . json_encode([
|
||||
if (!empty($orderData['payInfo']['paid'])) {
|
||||
$orderData['order_status'] = self::ORDER_STATUS_WAIT_SHIP;
|
||||
}
|
||||
Log::info('虚拟支付下单[下单成功] ' . json_encode([
|
||||
'order_no' => $orderNo,
|
||||
'payInfo' => $orderData['payInfo'],
|
||||
'paid' => $orderData['payInfo']['paid'] ?? 0,
|
||||
'mode' => $orderData['payInfo']['mode'] ?? '',
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
return $orderData;
|
||||
@@ -471,84 +478,18 @@ class OrderService
|
||||
'payment_amount'=> $orderInfo->payment_amount,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
//金额校验(v3 通知金额单位为分)
|
||||
$checkMoney = sprintf('%.1f', $total / 100) == sprintf('%.1f', $orderInfo->payment_amount);
|
||||
if (!$checkMoney) {
|
||||
Log::error($prefix . '金额校验失败: ' . json_encode([
|
||||
'notify_amount' => sprintf('%.2f', $total / 100),
|
||||
'order_amount' => sprintf('%.2f', $orderInfo->payment_amount),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
throw new \Exception('金额错误:' . sprintf('%.2f', $total / 100) . '----' . sprintf('%.2f', $orderInfo->payment_amount));
|
||||
}
|
||||
if (($message->trade_state ?? '') != 'SUCCESS') {
|
||||
Log::error($prefix . '支付状态异常: trade_state=' . ($message->trade_state ?? ''));
|
||||
throw new \Exception('支付失败:' . ($message->trade_state ?? ''));
|
||||
}
|
||||
|
||||
//如果为待支付,才可更新为已支付。防止重复更新状态,导致重复发货。
|
||||
if ($orderInfo->order_status != self::ORDER_STATUS_WAIT_PAY) {
|
||||
Log::info($prefix . '订单已处理过,跳过状态更新(幂等): ' . json_encode([
|
||||
'order_no' => $order_no,
|
||||
'order_status' => $orderInfo->order_status,
|
||||
'status_text' => self::ORDER_STATUS_MAP[$orderInfo->order_status] ?? '未知',
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$paymentData = [
|
||||
'order_no' => $order_no,
|
||||
'pay_status' => 'SUCCESS',
|
||||
'amount' => $total / 100,
|
||||
'currency' => $message->amount['currency'] ?? 'CNY',
|
||||
'pay_type' => '1', // 1微信 2支付宝
|
||||
'pay_way' => $message->trade_type ?? 'JSAPI',
|
||||
'trace_no' => $message->transaction_id ?? '',
|
||||
'pay_time' => time(),
|
||||
'pay_success_time' => strtotime((string)($message->success_time ?? '')) ?: time(),
|
||||
];
|
||||
Log::info($prefix . '开始更新订单状态: ' . json_encode($paymentData, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
try {
|
||||
Db::transaction(function () use ($order_no, $paymentData, $orderInfo, $prefix) {
|
||||
$updateRows = self::where('order_no', $order_no)->update(['order_status' => self::ORDER_STATUS_WAIT_SHIP]);
|
||||
Log::info($prefix . '订单状态更新: order_no=' . $order_no
|
||||
. ' 10(待支付) -> ' . self::ORDER_STATUS_WAIT_SHIP . '(待发货) 影响行数=' . $updateRows);
|
||||
|
||||
$payment = OrderPaymentService::create($paymentData);
|
||||
Log::info($prefix . '支付流水写入: ' . json_encode([
|
||||
'payment_id' => $payment->id ?? 0,
|
||||
'order_no' => $payment->order_no ?? '',
|
||||
'pay_status' => $payment->pay_status ?? '',
|
||||
'amount' => $payment->amount ?? 0,
|
||||
'trace_no' => $payment->trace_no ?? '',
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
if ($orderInfo->order_type == self::ORDER_TYPE_FIRST_PAYMENT) {
|
||||
Log::info($prefix . '需求首付款订单,处理合同成交: parent_order_no=' . $orderInfo->parent_order_no);
|
||||
DemandService::customerContractFinishedDeal($orderInfo->parent_order_no);
|
||||
} elseif ($orderInfo->order_type == self::ORDER_TYPE_FINAL_PAYMENT) {
|
||||
Log::info($prefix . '需求尾款订单,处理验收结算: parent_order_no=' . $orderInfo->parent_order_no);
|
||||
DemandService::customerWorkAcceptFinishedDeal($orderInfo->parent_order_no);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
Log::error($prefix . '事务执行失败已回滚: msg=' . $e->getMessage()
|
||||
. ' file=' . $e->getFile() . ':' . $e->getLine());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// 复核:确认状态与支付流水真的落库了
|
||||
$newOrder = OrderService::getOne(['order_no' => $order_no]);
|
||||
$payment = OrderPaymentService::getOne(['order_no' => $order_no]);
|
||||
Log::info($prefix . '回调处理成功: ' . json_encode([
|
||||
'order_no' => $order_no,
|
||||
'new_order_status'=> $newOrder->order_status ?? null,
|
||||
'new_status_text' => self::ORDER_STATUS_MAP[$newOrder->order_status ?? -1] ?? '未知',
|
||||
'payment_id' => $payment->id ?? 0,
|
||||
'payment_status' => $payment->pay_status ?? '',
|
||||
'trace_no' => $payment->trace_no ?? '',
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
self::finishPaid(
|
||||
$order_no,
|
||||
(int)$total,
|
||||
(string)($message->transaction_id ?? ''),
|
||||
(string)($message->trade_type ?? 'JSAPI'),
|
||||
strtotime((string)($message->success_time ?? '')) ?: time(),
|
||||
$traceId
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
@@ -568,35 +509,131 @@ class OrderService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $order_sn
|
||||
* @param $total_fee
|
||||
* @param $openid
|
||||
* 支付成功入账。虚拟支付确认和微信支付回调共用,重复通知直接返回
|
||||
* @param string $orderNo
|
||||
* @param int $amountFen 实付金额,单位分
|
||||
* @param string $transactionId
|
||||
* @param string $payWay
|
||||
* @param int $paidTime
|
||||
* @param string $traceId
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function finishPaid($orderNo, $amountFen, $transactionId, $payWay, $paidTime, $traceId = '')
|
||||
{
|
||||
$prefix = '订单支付入账 trace=' . $traceId . ' ';
|
||||
try {
|
||||
Db::transaction(function () use ($orderNo, $amountFen, $transactionId, $payWay, $paidTime, $prefix) {
|
||||
$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',
|
||||
'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
|
||||
* @param string $body
|
||||
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function pay($order_sn, $total_fee, $openid, string $body = '软件产品服务')
|
||||
{
|
||||
if (!($body && $order_sn && $total_fee && $openid)) {
|
||||
Log::error('微信下单[参数错误] ' . json_encode([
|
||||
'body' => $body,
|
||||
'order_sn' => $order_sn,
|
||||
Log::error('虚拟支付下单[参数错误] ' . json_encode([
|
||||
'body' => $body,
|
||||
'order_sn' => $order_sn,
|
||||
'total_fee' => $total_fee,
|
||||
'openid' => $openid,
|
||||
'openid' => $openid,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
throw new \Exception('支付参数错误2');
|
||||
}
|
||||
|
||||
$EasyWeChatService = new EasyWeChatService();
|
||||
$payRes = $EasyWeChatService->unify($body, $order_sn, $total_fee, $openid);
|
||||
if (!($payRes && !empty($payRes['prepay_id']))) {
|
||||
Log::error('微信下单[统一下单失败] order_sn=' . $order_sn . ' res=' . json_encode($payRes, JSON_UNESCAPED_UNICODE));
|
||||
throw new \Exception('统一下单接口调用失败:' . ($payRes['message'] ?? ($payRes['errmsg'] ?? '未知错误')));
|
||||
$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];
|
||||
}
|
||||
Log::info('微信下单[prepay_id] order_sn=' . $order_sn . ' prepay_id=' . $payRes['prepay_id']);
|
||||
$payData = $EasyWeChatService->sdkConfig($payRes['prepay_id']);
|
||||
|
||||
return $payData;
|
||||
return VirtualPayService::createClientPay($order_sn, $total_fee, $openid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端支付成功后确认:查现金单、扣代币、更新订单
|
||||
* @param string $orderNo
|
||||
* @param int $userId
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function confirmVirtualPay($orderNo, $userId)
|
||||
{
|
||||
$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('支付结果确认中,请稍后在订单里再次点击支付,不会重复扣款');
|
||||
}
|
||||
$fresh = self::getOne(['order_no' => $orderNo]);
|
||||
|
||||
return ['order_status' => (int)($fresh->order_status ?? self::ORDER_STATUS_WAIT_SHIP)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user