527 lines
26 KiB
PHP
527 lines
26 KiB
PHP
<?php
|
||
// +----------------------------------------------------------------------
|
||
// | 企油通 · 订单退款(7.17 获取支付凭证 + 7.16 司机额度调整)
|
||
// +----------------------------------------------------------------------
|
||
// | 规则(已与业务确认):退「剩余未加油金额」,每单只退一次
|
||
// | · 订单必须「发放成功」;一单可以分多次加油(7.10 逐笔累计到 used_amount)
|
||
// | · 退款金额 = 订单金额 − 已加油金额,固定不可指定;为 0 时不能退
|
||
// | · 额度在上游是按司机累计到凭证上的,退款只能「把凭证调低 X 元」。
|
||
// | 还要求凭证可用余额 ≥ 退款金额(额度可能被该司机其他订单用掉),否则直接拒绝。
|
||
// |
|
||
// | 状态(yoshop_partners_order.refund_status):
|
||
// | 0 未退款 -> 1 退款中 -> 2 已退款
|
||
// | -> 3 退款失败(上游明确拒绝 / 凭证不足,可重新发起)
|
||
// | -> 4 待人工确认(7.16 已发出但结果未知)
|
||
// |
|
||
// | ⚠️ 7.16 没有幂等键,同一请求发两次就调低两次。所以:
|
||
// | · 一旦走到 7.16,任何「不确定」都进 4,绝不自动重试
|
||
// | · 4 只能通过 confirm()(查上游证据)或 resolve()(人工判定)离开
|
||
// | · 退款单号写进 7.16 的 operation_reason,confirm() 优先在 7.13 流水里找它
|
||
// |
|
||
// | 并发:RedisLock 串行同一订单;状态跃迁是条件 UPDATE,affected=1 才退合作商余额。
|
||
// +----------------------------------------------------------------------
|
||
declare (strict_types=1);
|
||
|
||
namespace app\common\service;
|
||
|
||
use app\common\lock\RedisLock;
|
||
use app\common\model\qyt\Enterprise as EnterpriseModel;
|
||
use think\facade\Db;
|
||
|
||
class QytRefund
|
||
{
|
||
const NONE = 0;
|
||
const PROCESSING = 1;
|
||
const SUCCESS = 2;
|
||
const FAILED = 3;
|
||
const CONFIRM = 4;
|
||
|
||
const STATUS_TEXT = [
|
||
self::NONE => '未退款',
|
||
self::PROCESSING => '退款中',
|
||
self::SUCCESS => '已退款',
|
||
self::FAILED => '退款失败',
|
||
self::CONFIRM => '待人工确认',
|
||
];
|
||
|
||
// 失败分类,开放接口据此映射错误码
|
||
const REASON_NOT_FOUND = 'not_found';
|
||
const REASON_STATE = 'state';
|
||
const REASON_PROCESSING = 'processing';
|
||
const REASON_UPSTREAM = 'upstream';
|
||
|
||
/** 元金额比较容差 */
|
||
const EPS = 0.005;
|
||
|
||
// ================================================================
|
||
// 发起退款
|
||
// ================================================================
|
||
|
||
/**
|
||
* @param string $source admin / api
|
||
* @param string $operator 后台账号 或 合作商 appKey
|
||
*/
|
||
public static function apply(int $orderId, string $source, string $operator, string $reason = ''): array
|
||
{
|
||
$lock = new RedisLock('qyt:refund:' . $orderId, 120);
|
||
if (!$lock->acquireLock()) {
|
||
return self::result(false, self::PROCESSING, '该订单退款正在处理中,请勿重复提交', self::REASON_PROCESSING);
|
||
}
|
||
|
||
$order = null;
|
||
$refundId = 0;
|
||
$refundNo = '';
|
||
// before_adjust:还没调 7.16,出任何问题都可以安全地判失败
|
||
// after_adjust :7.16 已发出,出问题只能进人工确认
|
||
$stage = 'before_adjust';
|
||
|
||
try {
|
||
$order = Db::name('partners_order')->where('order_id', $orderId)->find();
|
||
if (!$order) {
|
||
return self::result(false, self::NONE, '订单不存在', self::REASON_NOT_FOUND);
|
||
}
|
||
|
||
$current = (int) $order['refund_status'];
|
||
if ($current === self::SUCCESS) {
|
||
// 幂等:已退过直接返回成功
|
||
return self::result(true, self::SUCCESS, '该订单已退款', '', self::orderData($order));
|
||
}
|
||
if ($current === self::PROCESSING || $current === self::CONFIRM) {
|
||
return self::result(false, $current,
|
||
$current === self::CONFIRM ? '该订单退款结果待人工确认,请勿重复发起' : '该订单退款正在处理中',
|
||
self::REASON_PROCESSING, self::orderData($order));
|
||
}
|
||
if (($err = self::checkRefundable($order)) !== '') {
|
||
return self::result(false, $current, $err, self::REASON_STATE, self::orderData($order));
|
||
}
|
||
$enterprise = EnterpriseModel::detail((int) $order['qyt_enterprise_id']);
|
||
if (!$enterprise) {
|
||
return self::result(false, $current, '本单绑定的企油通企业不存在,无法退款', self::REASON_STATE, self::orderData($order));
|
||
}
|
||
|
||
// 退款金额 = 剩余未加油金额(锁内重读订单后计算)
|
||
$amount = self::remainAmount($order);
|
||
|
||
// 抢占:只有 0/3 能进入退款中
|
||
$affected = Db::name('partners_order')
|
||
->where('order_id', $orderId)
|
||
->whereIn('refund_status', [self::NONE, self::FAILED])
|
||
->update(['refund_status' => self::PROCESSING, 'refund_text' => '退款中', 'update_time' => time()]);
|
||
if (!$affected) {
|
||
return self::result(false, $current, '订单状态已变化,请刷新后重试', self::REASON_PROCESSING, self::orderData($order));
|
||
}
|
||
|
||
$refundNo = 'RF' . $orderId . 'T' . date('YmdHis') . mt_rand(10, 99);
|
||
$refundId = (int) Db::name('qyt_refund')->insertGetId([
|
||
'refund_no' => $refundNo,
|
||
'order_id' => $orderId,
|
||
'partners_id' => (int) $order['partners_id'],
|
||
'qyt_enterprise_id' => (int) $order['qyt_enterprise_id'],
|
||
'source' => $source,
|
||
'operator' => mb_substr($operator, 0, 64),
|
||
'reason' => mb_substr($reason, 0, 200),
|
||
'amount' => $amount,
|
||
'status' => self::PROCESSING,
|
||
'store_id' => (int) $order['store_id'],
|
||
'create_time' => time(),
|
||
'update_time' => time(),
|
||
]);
|
||
|
||
$qyt = QytOil::forEnterprise($enterprise);
|
||
|
||
// 1) 7.17 找凭证(只读接口,失败不影响上游,可以放心判失败)
|
||
$vres = $qyt->getVoucherList((string) $order['mobile'], (string) $order['id_card'], 1, 50);
|
||
if ($vres['code'] !== '00') {
|
||
return self::finishFailed($order, $refundId, $refundNo,
|
||
'查询支付凭证失败:' . ($vres['message'] ?: '未知错误'), $vres);
|
||
}
|
||
$pick = self::pickVoucher((array) ($vres['data']['list'] ?? []), $amount);
|
||
if (!$pick['voucher']) {
|
||
return self::finishFailed($order, $refundId, $refundNo, $pick['message'], $vres);
|
||
}
|
||
$voucher = $pick['voucher'];
|
||
Db::name('qyt_refund')->where('refund_id', $refundId)->update([
|
||
'quota_id' => (string) $voucher['quota_id'],
|
||
'total_before' => round((float) ($voucher['total_amount'] ?? 0), 2),
|
||
'available_before' => round((float) ($voucher['available_amount'] ?? 0), 2),
|
||
'update_time' => time(),
|
||
]);
|
||
|
||
// 2) 7.16 调低额度
|
||
$stage = 'after_adjust';
|
||
$ares = $qyt->adjustVoucher(
|
||
$voucher['quota_id'],
|
||
'03',
|
||
$amount,
|
||
'订单退款 ' . $refundNo . ($reason !== '' ? ':' . $reason : ''),
|
||
(string) $order['waybill_no']
|
||
);
|
||
|
||
if (!empty($ares['transport_error'])) {
|
||
return self::finishConfirm($order, $refundId, $refundNo,
|
||
'退款请求已发出但结果未知(网络超时或响应异常),已转人工确认,切勿重复发起', $ares);
|
||
}
|
||
$success = $ares['code'] === '00' && !(isset($ares['data']['success']) && $ares['data']['success'] === false);
|
||
if (!$success) {
|
||
$msg = (string) ($ares['data']['message'] ?? '') ?: $ares['message'];
|
||
return self::finishFailed($order, $refundId, $refundNo, '企油通调额失败:' . ($msg ?: '未知错误'), $ares);
|
||
}
|
||
return self::finishSuccess($order, $refundId, $refundNo, $amount, '退款成功', $ares);
|
||
} catch (\Throwable $e) {
|
||
(new QytOil())->saveLog("退款异常 order={$orderId} stage={$stage}:" . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine());
|
||
if ($order && $refundId) {
|
||
return $stage === 'after_adjust'
|
||
? self::finishConfirm($order, $refundId, $refundNo, '退款过程发生异常且 7.16 可能已生效,已转人工确认:' . $e->getMessage(), [])
|
||
: self::finishFailed($order, $refundId, $refundNo, '退款过程发生异常:' . $e->getMessage(), []);
|
||
}
|
||
return self::result(false, self::NONE, '退款处理异常:' . $e->getMessage(), self::REASON_STATE);
|
||
} finally {
|
||
$lock->releaseLock();
|
||
}
|
||
}
|
||
|
||
// ================================================================
|
||
// 确认「待人工确认」的退款:向上游取证
|
||
// ================================================================
|
||
|
||
public static function confirm(int $orderId): array
|
||
{
|
||
$lock = new RedisLock('qyt:refund:' . $orderId, 120);
|
||
if (!$lock->acquireLock()) {
|
||
return self::result(false, self::CONFIRM, '该订单正在处理中,请稍后再试', self::REASON_PROCESSING);
|
||
}
|
||
try {
|
||
[$order, $refund, $enterprise, $err] = self::loadConfirmContext($orderId);
|
||
if ($err !== '') {
|
||
return self::result(false, (int) ($order['refund_status'] ?? self::NONE), $err, self::REASON_STATE);
|
||
}
|
||
$qyt = QytOil::forEnterprise($enterprise);
|
||
$amount = round((float) $refund['amount'], 2);
|
||
|
||
// 证据 1:7.13 钱包流水里带有退款单号的调额记录 —— 最可靠
|
||
$inWallet = false;
|
||
$wres = $qyt->driverWalletQuery((string) $order['mobile'], (string) $order['id_card'], 1, 50);
|
||
if ($wres['code'] === '00') {
|
||
foreach ((array) ($wres['data']['transactionDetails'] ?? []) as $t) {
|
||
if (strpos((string) ($t['remark'] ?? ''), (string) $refund['refund_no']) !== false) {
|
||
$inWallet = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 证据 2:7.17 同一张凭证的总额度 / 可用余额变化
|
||
$vres = $qyt->getVoucherList((string) $order['mobile'], (string) $order['id_card'], 1, 50);
|
||
$now = null;
|
||
if ($vres['code'] === '00') {
|
||
foreach ((array) ($vres['data']['list'] ?? []) as $v) {
|
||
if ((string) ($v['quota_id'] ?? '') === (string) $refund['quota_id']) {
|
||
$now = $v;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!$inWallet && $vres['code'] !== '00') {
|
||
return self::result(false, self::CONFIRM, '暂时无法向企油通取证:' . ($vres['message'] ?: '查询失败') . ',请稍后再试', self::REASON_UPSTREAM);
|
||
}
|
||
|
||
$totalNow = $now ? round((float) ($now['total_amount'] ?? 0), 2) : null;
|
||
$availNow = $now ? round((float) ($now['available_amount'] ?? 0), 2) : null;
|
||
if ($now) {
|
||
Db::name('qyt_refund')->where('refund_id', $refund['refund_id'])->update([
|
||
'total_after' => $totalNow, 'available_after' => $availNow, 'update_time' => time(),
|
||
]);
|
||
}
|
||
|
||
// 退款发起前后 10 分钟起,该司机在本企业是否有其他单在发放 / 发放成功(会干扰额度比对)
|
||
$grantedSince = Db::name('partners_order')
|
||
->where('id_card', $order['id_card'])
|
||
->where('qyt_enterprise_id', $order['qyt_enterprise_id'])
|
||
->where('order_id', '<>', $order['order_id'])
|
||
->whereIn('oil_status_zsy', [1, 2])
|
||
->where('update_time', '>=', (int) $refund['create_time'] - 600)
|
||
->count() > 0;
|
||
|
||
$verdict = self::judgeConfirm(
|
||
$inWallet,
|
||
(float) $refund['total_before'], $totalNow,
|
||
(float) $refund['available_before'], $availNow,
|
||
$amount,
|
||
$grantedSince
|
||
);
|
||
|
||
if ($verdict === 'applied') {
|
||
return self::finishSuccess($order, (int) $refund['refund_id'], (string) $refund['refund_no'], $amount,
|
||
'人工确认:企油通已调低额度,退款成功', $vres);
|
||
}
|
||
if ($verdict === 'not_applied') {
|
||
return self::finishFailed($order, (int) $refund['refund_id'], (string) $refund['refund_no'],
|
||
'确认结果:企油通未调低额度,退款未生效,可重新发起', $vres);
|
||
}
|
||
return self::result(false, self::CONFIRM,
|
||
'上游数据无法自动判定(期间可能有新的发放或加油消费),请到企油通后台核对该司机凭证后使用「人工判定」',
|
||
self::REASON_UPSTREAM, self::orderData($order, (string) $refund['refund_no']));
|
||
} catch (\Throwable $e) {
|
||
return self::result(false, self::CONFIRM, '确认过程异常:' . $e->getMessage(), self::REASON_STATE);
|
||
} finally {
|
||
$lock->releaseLock();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 人工判定「待人工确认」的退款(运营到企油通后台核对后操作)
|
||
*/
|
||
public static function resolve(int $orderId, bool $success, string $operator, string $remark): array
|
||
{
|
||
if (trim($remark) === '') {
|
||
return self::result(false, self::CONFIRM, '请填写判定依据', self::REASON_STATE);
|
||
}
|
||
$lock = new RedisLock('qyt:refund:' . $orderId, 120);
|
||
if (!$lock->acquireLock()) {
|
||
return self::result(false, self::CONFIRM, '该订单正在处理中,请稍后再试', self::REASON_PROCESSING);
|
||
}
|
||
try {
|
||
[$order, $refund, , $err] = self::loadConfirmContext($orderId);
|
||
if ($err !== '') {
|
||
return self::result(false, (int) ($order['refund_status'] ?? self::NONE), $err, self::REASON_STATE);
|
||
}
|
||
$text = "人工判定({$operator}):" . mb_substr($remark, 0, 100);
|
||
return $success
|
||
? self::finishSuccess($order, (int) $refund['refund_id'], (string) $refund['refund_no'], (float) $refund['amount'], $text, [])
|
||
: self::finishFailed($order, (int) $refund['refund_id'], (string) $refund['refund_no'], $text, []);
|
||
} finally {
|
||
$lock->releaseLock();
|
||
}
|
||
}
|
||
|
||
// ================================================================
|
||
// 纯函数(无副作用,便于单测)
|
||
// ================================================================
|
||
|
||
/**
|
||
* 退款前置条件,返回空串表示可以退
|
||
*/
|
||
public static function checkRefundable(array $order): string
|
||
{
|
||
if ((int) $order['oil_status_zsy'] !== 2 || (int) $order['oil_status'] !== 1) {
|
||
return '仅「发放成功」的订单可以退款';
|
||
}
|
||
if (self::remainAmount($order) <= self::EPS) {
|
||
return '订单金额已全部加油,没有可退金额';
|
||
}
|
||
if (trim((string) $order['id_card']) === '') {
|
||
return '订单缺少司机身份证号,无法查询支付凭证';
|
||
}
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* 剩余未加油金额(元)= 订单金额 − 已加油金额,不小于 0。这就是可退金额
|
||
*/
|
||
public static function remainAmount(array $order): float
|
||
{
|
||
return max(0.0, round((float) ($order['total_price'] ?? 0) - (float) ($order['used_amount'] ?? 0), 2));
|
||
}
|
||
|
||
/**
|
||
* 从 7.17 凭证列表里挑一张用于调低额度的凭证
|
||
* 规则:只看生效中的凭证,取可用余额最大的一张;它不够就拒绝(整单全额退,不拆多张凭证)
|
||
* @return array ['voucher' => array|null, 'message' => string]
|
||
*/
|
||
public static function pickVoucher(array $list, float $amount): array
|
||
{
|
||
$active = array_values(array_filter($list, function ($v) {
|
||
return is_array($v) && (string) ($v['status'] ?? '') === 'active';
|
||
}));
|
||
if (!$active) {
|
||
return ['voucher' => null, 'message' => '该司机在本企业下没有生效中的支付凭证,无法退款'];
|
||
}
|
||
usort($active, function ($a, $b) {
|
||
return (float) ($b['available_amount'] ?? 0) <=> (float) ($a['available_amount'] ?? 0);
|
||
});
|
||
$top = $active[0];
|
||
$avail = round((float) ($top['available_amount'] ?? 0), 2);
|
||
if ($avail + self::EPS < $amount) {
|
||
return ['voucher' => null, 'message' => sprintf(
|
||
'司机凭证可用余额不足:可用 %.2f 元,需退 %.2f 元(凭证额度按司机累计,可能已被该司机其他订单加油用掉)', $avail, $amount)];
|
||
}
|
||
return ['voucher' => $top, 'message' => ''];
|
||
}
|
||
|
||
/**
|
||
* 根据上游证据判定 7.16 是否已生效
|
||
* · 7.13 流水里找到退款单号 -> applied
|
||
* · 凭证总额度恰好减少了退款金额 -> applied
|
||
* (加油消费只动可用/已用,不动总额度;调额才动总额度)
|
||
* · 总额度没变 且 可用余额没有下降 -> not_applied
|
||
* · 其余(凭证找不到、期间有新发放或消费导致对不上) -> unknown,交人工
|
||
* @return string applied / not_applied / unknown
|
||
*/
|
||
public static function judgeConfirm(bool $inWallet, float $totalBefore, ?float $totalNow,
|
||
float $availBefore, ?float $availNow, float $amount,
|
||
bool $grantedSince = false): string
|
||
{
|
||
if ($inWallet) {
|
||
return 'applied';
|
||
}
|
||
if ($totalNow === null || $availNow === null) {
|
||
return 'unknown';
|
||
}
|
||
// $grantedSince:退款发起前后该司机在本企业有新的发放。此时额度比对不可信——
|
||
// 「调低 100 + 新发 100」看起来就是没变,若据此判「未生效」,运营重新发起会重复扣减。
|
||
if (abs(($totalBefore - $totalNow) - $amount) <= self::EPS) {
|
||
return $grantedSince ? 'unknown' : 'applied';
|
||
}
|
||
if (abs($totalNow - $totalBefore) <= self::EPS && $availNow + self::EPS >= $availBefore) {
|
||
return $grantedSince ? 'unknown' : 'not_applied';
|
||
}
|
||
return 'unknown';
|
||
}
|
||
|
||
// ================================================================
|
||
// 内部
|
||
// ================================================================
|
||
|
||
protected static function finishSuccess(array $order, int $refundId, string $refundNo, float $amount, string $text, array $res): array
|
||
{
|
||
$amount = round($amount, 2);
|
||
$won = false;
|
||
Db::transaction(function () use ($order, $refundId, $amount, $text, $res, &$won) {
|
||
$won = (bool) Db::name('partners_order')
|
||
->where('order_id', $order['order_id'])
|
||
->whereIn('refund_status', [self::PROCESSING, self::CONFIRM])
|
||
->update([
|
||
'refund_status' => self::SUCCESS,
|
||
'refund_amount' => $amount,
|
||
'refund_time' => time(),
|
||
'refund_text' => mb_substr($text, 0, 250),
|
||
'update_time' => time(),
|
||
]);
|
||
Db::name('qyt_refund')->where('refund_id', $refundId)->update([
|
||
'status' => self::SUCCESS,
|
||
'message' => mb_substr($text, 0, 250),
|
||
'response' => self::dumpRes($res),
|
||
'update_time' => time(),
|
||
]);
|
||
// 只有状态跃迁胜出者才原路退款,保证只退一次
|
||
// · 合作商单 -> 回补 partners.balance
|
||
// · 商城单 -> 退回下单用户余额(按实付折算)
|
||
if ($won) {
|
||
MallOil::giveBack($order, $amount, '订单退款');
|
||
}
|
||
});
|
||
|
||
if ($won) {
|
||
// 退款计算与加油推送之间有时间差:退款期间若又来了加油,已加油 + 已退款可能超过订单金额,告警人工核对
|
||
$fresh = Db::name('partners_order')->where('order_id', $order['order_id'])->field('total_price,used_amount')->find();
|
||
if ($fresh && (float) $fresh['used_amount'] + $amount > (float) $fresh['total_price'] + self::EPS) {
|
||
(new QytOil())->saveLog("【告警】退款后 已加油+已退款 超出订单金额 order={$order['order_id']}"
|
||
. " total={$fresh['total_price']} used={$fresh['used_amount']} refund={$amount}");
|
||
}
|
||
(new QueueService())->push('notify_queue', json_encode([
|
||
'order_id' => $order['order_id'],
|
||
'optType' => 'refund',
|
||
'retry_count' => 0,
|
||
], 320));
|
||
MallOil::syncGoods($order, 'refund');
|
||
(new QytOil())->saveLog("退款成功 order={$order['order_id']} refund_no={$refundNo} amount={$amount},已原路退款");
|
||
}
|
||
$order['refund_status'] = self::SUCCESS;
|
||
$order['refund_amount'] = $amount;
|
||
return self::result(true, self::SUCCESS, $text, '', self::orderData($order, $refundNo));
|
||
}
|
||
|
||
protected static function finishFailed(array $order, int $refundId, string $refundNo, string $text, array $res): array
|
||
{
|
||
Db::name('partners_order')
|
||
->where('order_id', $order['order_id'])
|
||
->whereIn('refund_status', [self::PROCESSING, self::CONFIRM])
|
||
->update(['refund_status' => self::FAILED, 'refund_text' => mb_substr($text, 0, 250), 'update_time' => time()]);
|
||
Db::name('qyt_refund')->where('refund_id', $refundId)->update([
|
||
'status' => self::FAILED, 'message' => mb_substr($text, 0, 250), 'response' => self::dumpRes($res), 'update_time' => time(),
|
||
]);
|
||
(new QytOil())->saveLog("退款失败 order={$order['order_id']} refund_no={$refundNo}:{$text}");
|
||
$order['refund_status'] = self::FAILED;
|
||
return self::result(false, self::FAILED, $text, self::REASON_UPSTREAM, self::orderData($order, $refundNo));
|
||
}
|
||
|
||
protected static function finishConfirm(array $order, int $refundId, string $refundNo, string $text, array $res): array
|
||
{
|
||
Db::name('partners_order')
|
||
->where('order_id', $order['order_id'])
|
||
->where('refund_status', self::PROCESSING)
|
||
->update(['refund_status' => self::CONFIRM, 'refund_text' => mb_substr($text, 0, 250), 'update_time' => time()]);
|
||
Db::name('qyt_refund')->where('refund_id', $refundId)->update([
|
||
'status' => self::CONFIRM, 'message' => mb_substr($text, 0, 250), 'response' => self::dumpRes($res), 'update_time' => time(),
|
||
]);
|
||
(new QytOil())->saveLog("【告警】退款结果未知 order={$order['order_id']} refund_no={$refundNo}:{$text}");
|
||
$order['refund_status'] = self::CONFIRM;
|
||
return self::result(false, self::CONFIRM, $text, self::REASON_PROCESSING, self::orderData($order, $refundNo));
|
||
}
|
||
|
||
/**
|
||
* @return array [order, refund, enterprise, errorMessage]
|
||
*/
|
||
protected static function loadConfirmContext(int $orderId): array
|
||
{
|
||
$order = Db::name('partners_order')->where('order_id', $orderId)->find();
|
||
if (!$order) {
|
||
return [[], [], null, '订单不存在'];
|
||
}
|
||
if ((int) $order['refund_status'] !== self::CONFIRM) {
|
||
return [$order, [], null, '只有「待人工确认」的退款需要确认,当前状态:' . (self::STATUS_TEXT[(int) $order['refund_status']] ?? '')];
|
||
}
|
||
$refund = Db::name('qyt_refund')->where('order_id', $orderId)->where('status', self::CONFIRM)
|
||
->order('refund_id', 'desc')->find();
|
||
if (!$refund) {
|
||
return [$order, [], null, '未找到待确认的退款流水'];
|
||
}
|
||
$enterprise = EnterpriseModel::detail((int) $order['qyt_enterprise_id']);
|
||
if (!$enterprise) {
|
||
return [$order, $refund, null, '本单绑定的企油通企业不存在'];
|
||
}
|
||
return [$order, $refund, $enterprise, ''];
|
||
}
|
||
|
||
protected static function orderData(array $order, string $refundNo = ''): array
|
||
{
|
||
if ($refundNo === '' && !empty($order['order_id'])) {
|
||
$refundNo = (string) Db::name('qyt_refund')->where('order_id', $order['order_id'])
|
||
->order('refund_id', 'desc')->value('refund_no');
|
||
}
|
||
return [
|
||
'oilCardNo' => (string) ($order['oilCardNo'] ?? ''),
|
||
'saleOrderNo' => (string) ($order['partners_order_no'] ?? ''),
|
||
'refundNo' => $refundNo,
|
||
// 已退款:实际退款金额;其余:当前剩余未加油金额(即本次会退的金额)
|
||
'refundAmount' => number_format((int) ($order['refund_status'] ?? 0) === self::SUCCESS
|
||
? (float) ($order['refund_amount'] ?? 0)
|
||
: self::remainAmount($order), 2, '.', ''),
|
||
'usedAmount' => number_format((float) ($order['used_amount'] ?? 0), 2, '.', ''),
|
||
'refundStatus' => (int) ($order['refund_status'] ?? 0),
|
||
];
|
||
}
|
||
|
||
protected static function result(bool $ok, int $status, string $message, string $reason = '', array $data = []): array
|
||
{
|
||
return [
|
||
'ok' => $ok,
|
||
'status' => $status,
|
||
'status_text' => self::STATUS_TEXT[$status] ?? '',
|
||
'message' => $message,
|
||
'reason' => $reason,
|
||
'data' => $data,
|
||
];
|
||
}
|
||
|
||
protected static function dumpRes(array $res): string
|
||
{
|
||
if (!$res) {
|
||
return '';
|
||
}
|
||
$keep = ['code' => $res['code'] ?? '', 'message' => $res['message'] ?? '',
|
||
'transport_error' => !empty($res['transport_error']), 'data' => QytOil::maskDeep($res['data'] ?? [])];
|
||
return mb_substr((string) json_encode($keep, JSON_UNESCAPED_UNICODE), 0, 60000);
|
||
}
|
||
}
|