$customRechargeSn], true); if (empty($customRecharge)) { noticeUtil::push('充值订单,单号:' . $customRechargeSn . ',没有找到订单', '15280215347'); $msg = "没有找到充值订单 orderSn:{$customRechargeSn}"; Yii::info($msg); util::fail($msg); } if ($customRecharge->amount != $totalFee) { noticeUtil::push("充值订单,支付回调错误,零售订单金额与回调通知金额不一致 orderSn:{$customRechargeSn} {$customRecharge->amount} {$totalFee}", '15280215347'); $msg = "充值订单金额与回调通知金额不一致 orderSn:{$customRechargeSn} {$customRecharge->amount} {$totalFee}"; Yii::info($msg); util::fail($msg); } $amount = $totalFee; $id = $customRecharge->id; $customRecharge = self::getLockById($id); // 加锁后再判已付:防并发双入账,重复回调静默成功以便 NoticeController 回 SUCCESS if ((int)$customRecharge->payStatus === 1) { Yii::info("充值订单重复支付回调,已处理过,幂等跳过 orderSn:{$customRechargeSn}"); return true; } $payDate = date("Y-m-d H:i:s"); $customRecharge->payTime = $payDate; $customRecharge->payWay = $payWay; $customRecharge->payStatus = 1; $customRecharge->status = 1; $customRecharge->returnCode = $transactionId; $customRecharge->onlinePay = dict::getDict('onlinePay', 'yes'); $customRecharge->save(); $ghsRechargeId = $customRecharge->ghsRechargeId ?? 0; $ghsRecharge = GhsRechargeClass::getById($ghsRechargeId, true); if (empty($ghsRecharge)) { noticeUtil::push("充值订单,支付回调错误,没有找到对应单号 orderSn:{$customRechargeSn}", '15280215347'); $msg = "充值订单,支付回调错误,没有找到对应单号 orderSn:{$customRechargeSn}"; Yii::info($msg); util::fail($msg); } $ghsRecharge->payTime = $payDate; $ghsRecharge->payWay = $payWay; $ghsRecharge->payStatus = 1; $ghsRecharge->status = 1; $ghsRecharge->onlinePay = dict::getDict('onlinePay', 'yes'); $ghsRecharge->save(); // 充值金额,有多个地方要同步修改,请搜索关键词 custom_ghs_recharge、custom_recharge_ghs_clear_action // 合并后:入账后由 GhsRechargeSettleService::onlinePayFifoClear 按本次来款 FIFO 销账 $customRechargeId = $customRecharge->id ?? 0; $customId = $customRecharge->customId ?? 0; $ghsId = $customRecharge->ghsId ?? 0; $custom = CustomClass::getLockById($customId); $ghsInfo = GhsClass::getLockById($ghsId); AccountMoneyClass::ensureCustomMoneyReady($custom, true); AccountMoneyClass::ensureGhsMoneyReady($ghsInfo, true); $rechargeEvent = '线上充值'; $customMainId = $custom->mainId ?? 0; $customShopId = $custom->shopId ?? 0; $customName = $custom->name??''; $ghsMainId = $ghsInfo->mainId ?? 0; $ghsShopId = $ghsInfo->shopId ?? 0; $ghsName = $ghsInfo->name??''; $customStaffId = $ghsRecharge->staffId ?? 0; $customStaffName = $ghsRecharge->staffName ?? ''; // 入账前净余额:用于判断结账单是否纯本次线上来款销账(有历史正余额则不标 onlinePay) $balanceBeforeRecharge = bcadd((string)($custom->balance ?? '0'), '0', 2); $custom->balance = bcadd($custom->balance, $amount, 2); $custom->save(); $capitalType = dict::getDict('capitalType', 'customRechargeToGhs', 'id'); $fromType = dict::getDict('fromType', 'shop'); $cbData = [ 'customId' => $customId, 'customName'=>$customName, 'relateId' => $customRechargeId, 'onlinePay' => 2, 'ptStyle' => 2, 'capitalType' => $capitalType, 'amount' => $amount, 'balance' => $custom->balance, 'staffId' => 0, 'staffName' => '', 'io' => 1, 'side' => 1, 'payWay' => $payWay, 'fromType' => $fromType, 'event' => $rechargeEvent, 'mainId' => $ghsMainId, 'shopId' => $ghsShopId, 'sjId' => 0, 'remark' => '', ]; CustomBalanceChangeClass::add($cbData, true); $ghsInfo->balance = bcadd($ghsInfo->balance, $amount, 2); $ghsInfo->save(); $ghsRechargeId = $ghsRecharge->id ?? 0; $gbData = [ 'ghsId' => $ghsId, 'ghsName'=>$ghsName, 'relateId' => $ghsRechargeId, 'ptStyle' => 2, 'capitalType' => $capitalType, 'amount' => $amount, 'balance' => $ghsInfo->balance, 'staffId' => $customStaffId, 'staffName' => $customStaffName, 'io' => 1, 'side' => 1, 'onlinePay' => 2, 'payWay' => $payWay, 'fromType' => $fromType, 'event' => $rechargeEvent, 'sjId' => 0, 'mainId' => $customMainId, 'shopId' => $customShopId, 'remark' => '', ]; GhsBalanceChangeClass::add($gbData, true); $ghsRecharge->balance = $ghsInfo->balance; $ghsRecharge->save(); $customRecharge->balance = $custom->balance; $customRecharge->save(); if ($customRecharge->onlinePay == dict::getDict('onlinePay', 'yes')) { //供货商门店余额增加 $ghsShop = ShopClass::getById($ghsShopId, true); $ghsMain = MainClass::getLockById($ghsMainId); ShopClass::customRechargeAddBalance($ghsMain, $ghsShop, $customRecharge); } /** * 线上充值自动销账(详见 GhsRechargeSettleService::onlinePayFifoClear)。 * 前置:balance 已 += amount;本步用「本次实付 amount」作资金池,FIFO 销欠款订单。 * $balanceBeforeRecharge:入账前 balance,决定结账单标线上付还是余额付。 */ $ghsShop = ShopClass::getById($ghsShopId, true); if (!empty($ghsShop)) { GhsRechargeSettleService::onlinePayFifoClear( $custom, $ghsInfo, $ghsShop, $amount, $customRecharge, $ghsRecharge, $balanceBeforeRecharge ); } return true; } /** * 供货商后台:对已付款充值单退款(线上原路退 / 线下扣减余额)。 * * @param int $rechargeId xhCustomRecharge.id * @param object $shop 当前供货商门店 * @param object $staff 操作员工(记余额变动 staffId/staffName) */ public static function refundPaidRecharge($rechargeId, $shop, $staff) { $rechargeId = intval($rechargeId); if ($rechargeId <= 0) { util::fail('参数错误'); } $customRecharge = self::getLockById($rechargeId); if (empty($customRecharge)) { util::fail('没有找到充值记录'); } $shopMainId = intval($shop->mainId ?? 0); if (intval($customRecharge->mainId ?? 0) !== $shopMainId) { util::fail('没有权限操作该充值记录'); } if (intval($customRecharge->payStatus ?? 0) !== 1) { util::fail('仅已付款的充值可退款'); } if (self::isRechargeRefunded($customRecharge)) { util::fail('该充值已退款'); } $amount = bcadd((string)($customRecharge->amount ?? '0'), '0', 2); if (bccomp($amount, '0', 2) <= 0) { util::fail('充值金额有误'); } $customId = intval($customRecharge->customId ?? 0); $pair = GhsRechargeSettleService::lockAccountPair(['id' => $customId]); $custom = $pair['custom']; $ghs = $pair['ghs']; if (empty($custom)) { util::fail('没有找到客户'); } $onlinePayYes = intval(dict::getDict('onlinePay', 'yes')); $isOnline = intval($customRecharge->onlinePay ?? 0) === $onlinePayYes; $payWay = intval($customRecharge->payWay ?? -1); $wxPay = intval(dict::getDict('payWay', 'wxPay')); $aliPay = intval(dict::getDict('payWay', 'alipay')); $cashPay = intval(dict::getDict('payWay', 'cash')); // 线上退款:先调支付渠道原路退回(拉卡拉商户取充值单所属门店) if ($isOnline) { if ($payWay !== $wxPay && $payWay !== $aliPay) { util::fail('暂不支持该支付方式原路退款'); } $returnCode = trim((string)($customRecharge->returnCode ?? '')); if ($returnCode === '') { util::fail('缺少支付流水号,无法原路退款'); } $payShopId = intval($customRecharge->shopId ?? 0); $payShop = $payShopId > 0 ? ShopClass::getById($payShopId, true) : $shop; if (empty($payShop)) { util::fail('没有找到收款门店'); } self::refundOnlineByLakala($customRecharge, $payShop, $amount, $returnCode); } $staffId = intval($staff->id ?? 0); $staffName = (string)($staff->name ?? ''); $newBalance = CustomClass::deductPairBalanceForRefund($custom, $ghs, $amount); $capitalType = dict::getDict('capitalType', 'customRechargeRefund', 'id'); $fromType = dict::getDict('fromType', 'shop'); $orderSn = (string)($customRecharge->orderSn ?? ''); $customName = (string)($custom->name ?? ''); $ghsName = (string)($ghs->name ?? ($customRecharge->ghsName ?? '')); $event = '充值退款(操作人:' . $staffName . ')单号:' . $orderSn; $shopId = intval($shop->id ?? 0); $mainId = intval($shop->mainId ?? 0); $sjId = intval($shop->sjId ?? 0); $onlinePayVal = $isOnline ? $onlinePayYes : intval(dict::getDict('onlinePay', 'not')); CustomBalanceChangeClass::add([ 'customId' => $customId, 'customName' => $customName, 'relateId' => $rechargeId, 'onlinePay' => $onlinePayVal, 'ptStyle' => dict::getDict('ptStyle', 'ghs'), 'capitalType' => $capitalType, 'amount' => $amount, 'balance' => $newBalance, 'staffId' => $staffId, 'staffName' => $staffName, 'io' => 0, 'side' => 0, 'payWay' => $payWay, 'fromType' => $fromType, 'event' => $event, 'mainId' => $mainId, 'shopId' => $shopId, 'sjId' => $sjId, 'remark' => '', ], true); $customShopId = intval($custom->shopId ?? 0); $customShop = GhsShopClass::getById($customShopId, true); $ghsRechargeId = intval($customRecharge->ghsRechargeId ?? 0); GhsBalanceChangeClass::add([ 'ghsId' => intval($ghs->id ?? 0), 'ghsName' => $ghsName, 'relateId' => $ghsRechargeId, 'ptStyle' => dict::getDict('ptStyle', 'ghs'), 'capitalType' => $capitalType, 'amount' => $amount, 'balance' => $newBalance, 'staffId' => $staffId, 'staffName' => $staffName, 'io' => 0, 'side' => 0, 'onlinePay' => $onlinePayVal, 'payWay' => $payWay, 'fromType' => $fromType, 'event' => $event, 'sjId' => $customShop->sjId ?? 0, 'mainId' => $customShop->mainId ?? 0, 'shopId' => $customShopId, 'remark' => '', ], true); // 线上充值曾增加门店 xhMain 余额,退款时对称扣减(不操作平台资产 PtAsset) if ($isOnline) { $ghsShopId = intval($customRecharge->shopId ?? 0); $ghsShop = ShopClass::getById($ghsShopId, true); if (empty($ghsShop)) { util::fail('没有找到供货商门店'); } $ghsMain = MainClass::getLockById($ghsShop->mainId ?? 0); if (empty($ghsMain)) { util::fail('没有找到资产信息'); } $originCapitalType = dict::getDict('capitalType', 'customRechargeToGhs', 'id'); $rechargeType = intval($customRecharge->rechargeType ?? 0); if ($rechargeType === 1) { $originCapitalType = dict::getDict('capitalType', 'ghsHelpCustomRechargeReturn', 'id'); } ShopClass::customRechargeRefundReduceBalance($ghsMain, $ghsShop, $customRecharge, $originCapitalType, $staffName); } // 线下现金充值:扣减 xhMain.money if (!$isOnline && $payWay === $cashPay) { $m = MainClass::getById($mainId, true, 'id, money'); if (!empty($m)) { $moneyBefore = $m->money ?? 0; $m->money = bcsub((string)$moneyBefore, $amount, 2); $m->save(false, ['money']); ShopMoneyClass::addData([ 'amount' => $amount, 'balance' => $m->money, 'io' => 0, 'mainId' => $mainId, 'capitalType' => $capitalType, 'ptStyle' => dict::getDict('ptStyle', 'ghs'), 'event' => '客户(' . $customName . ')充值退款' . $amount . '元', 'remark' => '操作人:' . $staffName, ]); } } self::markRechargeRefunded($customRecharge); if ($ghsRechargeId > 0) { $ghsRecharge = GhsRechargeClass::getLockById($ghsRechargeId); if (!empty($ghsRecharge)) { self::markRechargeRefunded($ghsRecharge); $ghsRecharge->balance = $newBalance; $ghsRecharge->save(false); } } $customRecharge->balance = $newBalance; $customRecharge->save(false); return $customRecharge; } /** 充值记录是否已退款(兼容未加 refundStatus 列的环境) */ protected static function isRechargeRefunded($recharge) { if (empty($recharge)) { return true; } if (self::modelHasAttr($recharge, 'refundStatus')) { return intval($recharge->refundStatus ?? 0) === 1; } return false; } /** 标记充值单已退款 */ protected static function markRechargeRefunded($recharge) { if (empty($recharge) || !self::modelHasAttr($recharge, 'refundStatus')) { return; } $recharge->refundStatus = 1; $recharge->save(false, ['refundStatus']); } protected static function modelHasAttr($model, $attr) { return is_object($model) && method_exists($model, 'hasAttribute') && $model->hasAttribute($attr); } /** * 拉卡拉原路退款(微信/支付宝线上充值)。 */ protected static function refundOnlineByLakala($customRecharge, $shop, $amount, $returnCode) { $termNo = $shop->lklScanTermNo ?? ''; $payWay = intval($customRecharge->payWay ?? 0); if ($payWay === intval(dict::getDict('payWay', 'alipay'))) { $termNo = $shop->lklB2BTermNo ?? ''; } if (empty($shop->lklSjNo) || empty($termNo)) { util::fail('门店未配置拉卡拉商户信息'); } $merchantPrivateKeyPath = Yii::getAlias('@vendor/lakala') . '/production/api_private_key.pem'; $lklCertificatePath = Yii::getAlias('@vendor/lakala') . '/production/lkl-apigw-v1.cer'; $params = [ 'appid' => 'OP00002119', 'serial_no' => '018b08cfddbd', 'merchant_no' => $shop->lklSjNo, 'term_no' => $termNo, 'merchantPrivateKeyPath' => $merchantPrivateKeyPath, 'lklCertificatePath' => $lklCertificatePath, ]; $laResource = new Lakala($params); $refundSn = orderSn::getGhsCustomRechargeSn(); $refundFee = bcmul($amount, 100); $aliParams = [ 'refundSn' => $refundSn, 'orderSn' => $customRecharge->orderSn ?? '', 'refundAmount' => $refundFee, 'refundReason' => '充值退款', 'thirdNo' => $returnCode, ]; $response = $laResource->refund($aliParams); if (!isset($response['code']) || $response['code'] != 'BBS00000') { $errMsg = $response['msg'] ?? '退款失败'; util::fail('原路退款失败:' . $errMsg); } } }