2025-09-23 16:43:18 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace app\common\controller;
|
|
|
|
|
|
|
|
|
|
|
|
use think\Db;
|
|
|
|
|
|
use app\common\util\PaymentUtil;
|
|
|
|
|
|
use think\facade\Config;
|
|
|
|
|
|
use think\facade\Env;
|
|
|
|
|
|
use think\facade\Log;
|
|
|
|
|
|
use think\facade\Request;
|
|
|
|
|
|
use app\common\model\Order;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 支付服务(内部调用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
class PaymentService
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 下单
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param array $order
|
|
|
|
|
|
* - out_trade_no: string 商户订单号(必填)
|
|
|
|
|
|
* - total_fee: int 金额(分,必填)
|
|
|
|
|
|
* - body: string 商品描述(必填)
|
|
|
|
|
|
* - notify_url: string 异步通知地址(可覆盖配置)
|
|
|
|
|
|
* - attach: string 附加数据(可选)
|
|
|
|
|
|
* - time_expire: string 订单失效时间(可选)
|
|
|
|
|
|
* - client_ip: string 终端IP(可选)
|
|
|
|
|
|
* - sign_type: string MD5/RSA_1_256/RSA_1_1(可选,默认MD5)
|
|
|
|
|
|
* - pay_type: string 支付场景,如 JSAPI/APP/H5(可选)
|
|
|
|
|
|
* @return array
|
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function createOrder(array $order)
|
|
|
|
|
|
{
|
2025-09-24 14:09:27 +08:00
|
|
|
|
Log::info('支付创建');
|
2025-09-23 16:43:18 +08:00
|
|
|
|
$params = [
|
|
|
|
|
|
'service' => 'unified.trade.native',
|
|
|
|
|
|
'sign_type' => PaymentUtil::SIGN_TYPE_MD5,
|
|
|
|
|
|
'mch_id' => Env::get('payment.mchId'),
|
|
|
|
|
|
'out_trade_no' => $order['orderNo'],
|
|
|
|
|
|
'body' => $order['goodsName'] ?? '',
|
|
|
|
|
|
'total_fee' => $order['money'] ?? 0,
|
|
|
|
|
|
'mch_create_ip' => Request::ip(),
|
2025-09-23 17:42:23 +08:00
|
|
|
|
'notify_url' => Env::get('payment.notify_url','127.0.0.1'),
|
2025-09-23 16:43:18 +08:00
|
|
|
|
'nonce_str' => PaymentUtil::generateNonceStr(),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
Db::startTrans();
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 过滤空值签名
|
|
|
|
|
|
$secret = Env::get('payment.key');
|
|
|
|
|
|
$params['sign_type'] = 'MD5';
|
|
|
|
|
|
$params['sign'] = PaymentUtil::generateSign($params, $secret, 'MD5');
|
|
|
|
|
|
|
|
|
|
|
|
$url = Env::get('payment.url');
|
|
|
|
|
|
if (empty($url)) {
|
|
|
|
|
|
throw new \Exception('支付网关地址未配置');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//创建订单
|
|
|
|
|
|
Order::create([
|
|
|
|
|
|
'mchId' => $params['mch_id'],
|
|
|
|
|
|
'companyId' => $order['companyId'],
|
|
|
|
|
|
'userId' => $order['userId'],
|
|
|
|
|
|
'orderType' => $order['orderType'] ?? 1,
|
|
|
|
|
|
'status' => 0,
|
|
|
|
|
|
'goodsId' => $order['goodsId'],
|
|
|
|
|
|
'goodsName' => $order['goodsName'],
|
|
|
|
|
|
'money' => $order['money'],
|
|
|
|
|
|
'orderNo' => $order['orderNo'],
|
|
|
|
|
|
'ip' => Request::ip(),
|
|
|
|
|
|
'nonceStr' => $params['nonce_str'],
|
|
|
|
|
|
'createTime' => time(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// XML POST 请求
|
|
|
|
|
|
$xmlBody = $this->arrayToXml($params);
|
|
|
|
|
|
$response = $this->postXml($url, $xmlBody);
|
|
|
|
|
|
$parsed = $this->parseXmlOrRaw($response);
|
|
|
|
|
|
|
|
|
|
|
|
if ($parsed['status'] == 0 && $parsed['result_code'] == 0) {
|
|
|
|
|
|
Db::commit();
|
|
|
|
|
|
return json(['code' => 200, 'msg' => '订单创建成功','data' => $parsed['code_url']]);
|
|
|
|
|
|
}else{
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
return json(['code' => 500, 'msg' => '订单创建失败:' . $parsed['err_msg']]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
return json(['code' => 500, 'msg' => '订单创建失败:' . $e->getMessage()]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* POST 请求(x-www-form-urlencoded)
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function httpPost(string $url, array $params, array $headers = [])
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!function_exists('requestCurl')) {
|
|
|
|
|
|
throw new \RuntimeException('requestCurl 未定义');
|
|
|
|
|
|
}
|
|
|
|
|
|
return requestCurl($url, $params, 'POST', $headers, 'dataBuild');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析响应
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function parseResponse($response)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ($response === '' || $response === null) {
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
$decoded = json_decode($response, true);
|
|
|
|
|
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
|
|
|
|
return $decoded;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (strpos($response, '=') !== false && strpos($response, '&') !== false) {
|
|
|
|
|
|
$arr = [];
|
|
|
|
|
|
foreach (explode('&', $response) as $pair) {
|
|
|
|
|
|
if ($pair === '') continue;
|
|
|
|
|
|
$kv = explode('=', $pair, 2);
|
|
|
|
|
|
$arr[$kv[0]] = $kv[1] ?? '';
|
|
|
|
|
|
}
|
|
|
|
|
|
return $arr;
|
|
|
|
|
|
}
|
|
|
|
|
|
return $response;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 以 XML 方式 POST(text/xml)
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function postXml(string $url, string $xml)
|
|
|
|
|
|
{
|
|
|
|
|
|
$ch = curl_init();
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
|
|
|
|
'Content-Type: text/xml; charset=UTF-8'
|
|
|
|
|
|
]);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
|
|
|
|
$res = curl_exec($ch);
|
|
|
|
|
|
curl_close($ch);
|
|
|
|
|
|
return $res;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 数组转 XML(按 ASCII 升序,字符串走 CDATA)
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function arrayToXml(array $data): string
|
|
|
|
|
|
{
|
|
|
|
|
|
// 过滤空值
|
|
|
|
|
|
$filtered = [];
|
|
|
|
|
|
foreach ($data as $k => $v) {
|
|
|
|
|
|
if ($v === '' || $v === null) continue;
|
|
|
|
|
|
$filtered[$k] = $v;
|
|
|
|
|
|
}
|
|
|
|
|
|
ksort($filtered, SORT_STRING);
|
|
|
|
|
|
|
|
|
|
|
|
$xml = '<xml>';
|
|
|
|
|
|
foreach ($filtered as $key => $value) {
|
|
|
|
|
|
if (is_numeric($value)) {
|
|
|
|
|
|
$xml .= "<{$key}>{$value}</{$key}>";
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$xml .= "<{$key}><![CDATA[{$value}]]></{$key}>";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
$xml .= '</xml>';
|
|
|
|
|
|
return $xml;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析 XML 响应
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function parseXmlOrRaw($response)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!is_string($response) || $response === '') {
|
|
|
|
|
|
return $response;
|
|
|
|
|
|
}
|
|
|
|
|
|
libxml_use_internal_errors(true);
|
|
|
|
|
|
$xml = simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA);
|
|
|
|
|
|
if ($xml !== false) {
|
|
|
|
|
|
$json = json_encode($xml, JSON_UNESCAPED_UNICODE);
|
|
|
|
|
|
return json_decode($json, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
return $response;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 支付结果异步通知
|
|
|
|
|
|
* - 威富通回调为 XML;需校验签名与业务字段并更新订单
|
|
|
|
|
|
* - 回应:成功回"success",失败回"fail"
|
|
|
|
|
|
* @return void
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function notify()
|
|
|
|
|
|
{
|
2025-09-23 17:42:23 +08:00
|
|
|
|
Log::info('支付结果异步通知');
|
2025-09-23 16:43:18 +08:00
|
|
|
|
$rawBody = file_get_contents('php://input');
|
|
|
|
|
|
Log::info('[SwiftPass][notify] raw: ' . $rawBody);
|
|
|
|
|
|
|
|
|
|
|
|
$payload = $this->parseXmlOrRaw($rawBody);
|
|
|
|
|
|
if (!is_array($payload) || empty($payload)) {
|
|
|
|
|
|
Log::error('[SwiftPass][notify] parse fail');
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 基础字段
|
|
|
|
|
|
$status = (string)($payload['status'] ?? '');
|
|
|
|
|
|
$resultCode = (string)($payload['result_code'] ?? '');
|
|
|
|
|
|
$outTradeNo = (string)($payload['out_trade_no'] ?? '');
|
|
|
|
|
|
$totalFee = (int)($payload['total_fee'] ?? 0);
|
|
|
|
|
|
$mchIdNotify = (string)($payload['mch_id'] ?? '');
|
|
|
|
|
|
$signInNotify = (string)($payload['sign'] ?? '');
|
|
|
|
|
|
$signType = (string)($payload['sign_type'] ?? 'MD5');
|
|
|
|
|
|
|
|
|
|
|
|
if ($status !== '0' || $resultCode !== '0') {
|
|
|
|
|
|
Log::warning('[SwiftPass][notify] business not success', $payload);
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验签
|
|
|
|
|
|
$secret = Env::get('payment.key');
|
|
|
|
|
|
if (empty($secret)) {
|
|
|
|
|
|
Log::error('[SwiftPass][notify] payment.key not configured');
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$verifyData = $payload;
|
|
|
|
|
|
unset($verifyData['sign']);
|
|
|
|
|
|
$calcSign = PaymentUtil::generateSign($verifyData, $secret, $signType ?: 'MD5');
|
|
|
|
|
|
if (strcasecmp($calcSign, $signInNotify) !== 0) {
|
|
|
|
|
|
Log::error('[SwiftPass][notify] sign mismatch', [
|
|
|
|
|
|
'calc' => $calcSign,
|
|
|
|
|
|
'recv' => $signInNotify,
|
|
|
|
|
|
]);
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 校验商户号
|
|
|
|
|
|
$mchIdConfig = Env::get('payment.mchId');
|
|
|
|
|
|
if (!empty($mchIdConfig) && $mchIdConfig !== $mchIdNotify) {
|
|
|
|
|
|
Log::error('[SwiftPass][notify] mch_id mismatch', [
|
|
|
|
|
|
'config' => $mchIdConfig,
|
|
|
|
|
|
'notify' => $mchIdNotify,
|
|
|
|
|
|
]);
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 业务处理:更新订单
|
|
|
|
|
|
Db::startTrans();
|
|
|
|
|
|
try {
|
|
|
|
|
|
/** @var Order|null $order */
|
|
|
|
|
|
$order = Order::where('orderNo', $outTradeNo)->lock(true)->find();
|
|
|
|
|
|
if (!$order) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
Log::error('[SwiftPass][notify] order not found', ['out_trade_no' => $outTradeNo]);
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 金额校验(单位:分)
|
|
|
|
|
|
if ((int)$order['money'] !== $totalFee) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
Log::error('[SwiftPass][notify] amount mismatch', [
|
|
|
|
|
|
'order_money' => (int)$order['money'],
|
|
|
|
|
|
'notify_fee' => $totalFee,
|
|
|
|
|
|
]);
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 幂等:已支付直接返回成功
|
|
|
|
|
|
if ((int)$order['status'] === 1) {
|
|
|
|
|
|
Db::commit();
|
|
|
|
|
|
echo 'success';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$transactionId = (string)($payload['transaction_id'] ?? '');
|
|
|
|
|
|
$timeEndStr = (string)($payload['time_end'] ?? '');
|
|
|
|
|
|
$paidAt = $this->parsePayTime($timeEndStr) ?: time();
|
|
|
|
|
|
|
|
|
|
|
|
$order->save([
|
|
|
|
|
|
'status' => 1,
|
|
|
|
|
|
'transactionId' => $transactionId,
|
|
|
|
|
|
'payTime' => $paidAt,
|
|
|
|
|
|
'updateTime' => time(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
Db::commit();
|
|
|
|
|
|
echo 'success';
|
|
|
|
|
|
return;
|
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
Log::error('[SwiftPass][notify] exception: ' . $e->getMessage());
|
|
|
|
|
|
echo 'fail';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析威富通时间(yyyyMMddHHmmss)为时间戳
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function parsePayTime(string $timeEnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ($timeEnd === '') {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 期望格式:20250102153045
|
|
|
|
|
|
if (preg_match('/^\\d{14}$/', $timeEnd) !== 1) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
$dt = \DateTime::createFromFormat('YmdHis', $timeEnd, new \DateTimeZone('Asia/Shanghai'));
|
|
|
|
|
|
return $dt ? $dt->getTimestamp() : 0;
|
|
|
|
|
|
}
|
2025-09-24 14:09:27 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询订单(威富通 unified.trade.query)
|
|
|
|
|
|
* - 入参:商户订单号或平台交易号
|
|
|
|
|
|
* - 出参:统一 JSON 格式,包含交易状态与关键信息
|
|
|
|
|
|
* @param array $query
|
|
|
|
|
|
* - out_trade_no: string 商户订单号(与 transaction_id 二选一)
|
|
|
|
|
|
* - transaction_id: string 平台交易号(与 out_trade_no 二选一)
|
|
|
|
|
|
* @return \think\response\Json
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function queryOrder(array $query)
|
|
|
|
|
|
{
|
|
|
|
|
|
$outTradeNo = $query['out_trade_no'] ?? ($query['orderNo'] ?? '');
|
|
|
|
|
|
$transactionId = $query['transaction_id'] ?? '';
|
|
|
|
|
|
|
|
|
|
|
|
if ($outTradeNo === '' && $transactionId === '') {
|
|
|
|
|
|
return json(['code' => 422, 'msg' => '缺少查询参数:out_trade_no 或 transaction_id']);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$params = [
|
|
|
|
|
|
'service' => 'unified.trade.query',
|
|
|
|
|
|
'mch_id' => Env::get('payment.mchId'),
|
|
|
|
|
|
'out_trade_no' => $outTradeNo ?: null,
|
|
|
|
|
|
'nonce_str' => PaymentUtil::generateNonceStr(),
|
|
|
|
|
|
'sign_type' => 'MD5',
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤空值后签名
|
|
|
|
|
|
$secret = Env::get('payment.key');
|
|
|
|
|
|
if (empty($secret)) {
|
|
|
|
|
|
return json(['code' => 500, 'msg' => '支付密钥未配置']);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$filtered = [];
|
|
|
|
|
|
foreach ($params as $k => $v) {
|
|
|
|
|
|
if ($v === '' || $v === null) continue;
|
|
|
|
|
|
$filtered[$k] = $v;
|
|
|
|
|
|
}
|
|
|
|
|
|
$filtered['sign'] = PaymentUtil::generateSign($filtered, $secret, $filtered['sign_type']);
|
|
|
|
|
|
|
|
|
|
|
|
$url = Env::get('payment.url');
|
|
|
|
|
|
if (empty($url)) {
|
|
|
|
|
|
return json(['code' => 500, 'msg' => '支付网关地址未配置']);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 请求网关
|
|
|
|
|
|
$xmlBody = $this->arrayToXml($filtered);
|
|
|
|
|
|
$response = $this->postXml($url, $xmlBody);
|
|
|
|
|
|
$parsed = $this->parseXmlOrRaw($response);
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_array($parsed)) {
|
|
|
|
|
|
return json(['code' => 500, 'msg' => '响应解析失败', 'data' => $response]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (($parsed['status'] ?? '') !== '0') {
|
|
|
|
|
|
return json(['code' => 500, 'msg' => '通信失败:' . ($parsed['message'] ?? 'unknown')]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (($parsed['result_code'] ?? '') !== '0') {
|
|
|
|
|
|
return json(['code' => 200, 'msg' => '业务失败', 'data' => [
|
|
|
|
|
|
'err_code' => $parsed['err_code'] ?? '',
|
|
|
|
|
|
'err_msg' => $parsed['err_msg'] ?? '',
|
|
|
|
|
|
]]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$tradeState = $parsed['trade_state'] ?? '';
|
|
|
|
|
|
$resp = [
|
|
|
|
|
|
'trade_state' => $tradeState,
|
|
|
|
|
|
'trade_state_desc'=> $parsed['trade_state_desc'] ?? '',
|
|
|
|
|
|
'transaction_id' => $parsed['transaction_id'] ?? '',
|
|
|
|
|
|
'out_trade_no' => $parsed['out_trade_no'] ?? $outTradeNo,
|
|
|
|
|
|
'total_fee' => isset($parsed['total_fee']) ? (int)$parsed['total_fee'] : null,
|
|
|
|
|
|
'time_end' => $parsed['time_end'] ?? '',
|
|
|
|
|
|
'buyer_logon_id' => $parsed['buyer_logon_id'] ?? '',
|
|
|
|
|
|
'bank_type' => $parsed['bank_type'] ?? '',
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 若已支付,同步本地订单
|
|
|
|
|
|
if ($tradeState === 'SUCCESS' && ($resp['out_trade_no'] ?? '') !== '') {
|
|
|
|
|
|
Db::startTrans();
|
|
|
|
|
|
try {
|
|
|
|
|
|
/** @var Order|null $order */
|
|
|
|
|
|
$order = Order::where('orderNo', $resp['out_trade_no'])->lock(true)->find();
|
|
|
|
|
|
if ($order) {
|
|
|
|
|
|
$paidAt = $this->parsePayTime($resp['time_end'] ?? '') ?: time();
|
|
|
|
|
|
if ((int)$order['status'] !== 1) {
|
|
|
|
|
|
$order->save([
|
|
|
|
|
|
'status' => 1,
|
|
|
|
|
|
'transactionId' => $resp['transaction_id'] ?? '',
|
|
|
|
|
|
'payTime' => $paidAt,
|
|
|
|
|
|
'updateTime' => time(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Db::commit();
|
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
Log::error('[SwiftPass][query] update order exception: ' . $e->getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return json(['code' => 200, 'msg' => '查询成功', 'data' => $resp]);
|
|
|
|
|
|
}
|
2025-09-23 16:43:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|