扣款失败后,重试扣款

This commit is contained in:
陈俊宏 2024-07-08 15:26:18 +08:00
parent 6f331a22d5
commit ce08f7866d
9 changed files with 164 additions and 12 deletions

View File

@ -65,7 +65,7 @@ class AgreementPaySendSms extends Command
// 开始发送扣款短信 // 开始发送扣款短信
$res = AliSms::sendSms(['phone_numbers' => $collection->mobile], 3); $res = AliSms::sendSms(['phone_numbers' => $collection->mobile], 3);
$redis->set($cacheKey, true, 'ex', $cacheKeys['ttl']); $redis->set($cacheKey, true, 'ex', $cacheKeys['ttl']);
if ($res['code'] == "OK") { if ($res['code'] == 200) {
$this->successCount += 1; $this->successCount += 1;
$output->writeln("短信发送受理成功"); $output->writeln("短信发送受理成功");
} else { } else {

67
app/cmd/PayOrderRetry.php Normal file
View File

@ -0,0 +1,67 @@
<?php
namespace app\cmd;
use app\model\Order;
use app\model\Sign;
use app\service\AgreementService;
use think\Collection;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Log;
class PayOrderRetry extends Command
{
protected int $count = 0;
protected int $successCount = 0;
protected int $failedCount = 0;
protected function configure()
{
$this->setName('PayOrderRetry')->setDescription('重试扣款失败的订单');
}
/**
* 所有失败订单每天重试一次,重试到订单创建时间的月底。如果还是失败就进行解约操作
* 每天11点执行
* @param Input $input
* @param Output $output
* @return void
*/
protected function execute(Input $input, Output $output)
{
// 本月内的支付失败订单
// 获取本月的开始时间
$startOfMonth = new \DateTime('first day of this month 00:00:00');
$startOfMonthFormatted = $startOfMonth->format('Y-m-d H:i:s');
// 获取本月的结束时间
$endOfMonth = new \DateTime('last day of this month 23:59:59');
$endOfMonthFormatted = $endOfMonth->format('Y-m-d H:i:s');
Order::where(['pay_status' => Order::PAY_STATUS_FAIL])->whereBetweenTime('create_time', strtotime($startOfMonthFormatted), strtotime($endOfMonthFormatted))
->where(['is_retry' => Order::RETRY_STATUS_NO])
->field('id,order_number,agreement_id,user_id')->chunk(100, function (Collection $orderCollection) use (&$output) {
// 拉取支付失败的订单
foreach ($orderCollection as $order) {
// 查询签约状态是否取消
$signInfo = Sign::getByAgreementId($order->agreement_id);
if ($signInfo->isEmpty() || $signInfo->sign_status != Order::STATUS_SIGNED) {
continue;
}
$res = AgreementService::RetryPayOrder($order->order_number, $signInfo->agreement_id);
if ($res['respCode'] == 1000) {
$this->successCount += 1;
$output->writeln("受理成功,等待支付通知");
} else {
$this->failedCount += 1;
$output->writeln("用户" . $order->user_id . "扣款失败:" . $res['respMsg']);
}
}
});
Log::info(sprintf("扣款job执行情况执行成功累计 %s, 受理成功 %s, 失败 %s", $this->count, $this->successCount, $this->failedCount));
$output->writeln(sprintf("重试扣款失败的订单执行成功,累计 %s, 受理成功 %s, 失败 %s", $this->count, $this->successCount, $this->failedCount));
}
}

View File

@ -2,7 +2,6 @@
namespace app\front; namespace app\front;
use app\config\ResponseCode;
use app\service\CmbService; use app\service\CmbService;
use app\service\OrderService; use app\service\OrderService;
use app\service\RechargeService; use app\service\RechargeService;
@ -12,7 +11,11 @@ class Order extends Base
{ {
public function list(Request $request): \think\Response public function list(Request $request): \think\Response
{ {
if (empty($request->user_id)) {
return responseOk();
}
$params['user_id'] = $request->user_id; $params['user_id'] = $request->user_id;
$params['is_retry'] = \app\model\Order::RETRY_STATUS_NO;
return responseOk(app()->make(OrderService::class)->list($params)); return responseOk(app()->make(OrderService::class)->list($params));
} }

View File

@ -26,6 +26,9 @@ class Order extends BaseModel
const REFUND_STATUS_WAIT = 1; const REFUND_STATUS_WAIT = 1;
const REFUND_STATUS_SUCCESS = 2; const REFUND_STATUS_SUCCESS = 2;
const REFUND_STATUS_FAIL = 3; const REFUND_STATUS_FAIL = 3;
// 重试状态
const RETRY_STATUS_NO = 0; // 未重试
const RETRY_STATUS_YES = 1; // 已重试
const STATUS_TEXT = [ const STATUS_TEXT = [
self::STATUS_WAIT_SIGN => '待签约', self::STATUS_WAIT_SIGN => '待签约',

View File

@ -216,6 +216,68 @@ class AgreementService extends BaseService
return CmbHttpUtils::doPost($funcName, $requestParams); return CmbHttpUtils::doPost($funcName, $requestParams);
} }
/**
* 扣款失败,重试
*
* @param string $orderNumber
* @param string $agreementId
* @return array|mixed|string
*/
public static function RetryPayOrder(string $orderNumber, string $agreementId)
{
// 判断订单状态是否正确
$order = Order::getByOrderNumber($orderNumber);
if ($order->isEmpty() || $order->pay_status != Order::PAY_STATUS_FAIL || $order->is_retry == Order::RETRY_STATUS_YES) {
return ['respCode' => 1000, 'respMsg' => '处理成功'];
}
// 处理订单再次扣款
// 创建新的订单
$productId = $order->product_id;
$product = Product::getBySupplierProductId($productId);
$newOrder = [
'user_id' => $order->user_id,
'order_number' => StringUtil::makeOrderNumber(),
'account' => $order->account,
'type' => $order->type,
'product_id' => $order->product_id,
'price' => $order->price,
'bonus' => $order->bonus,
'agreement_id' => $order->agreement_id,
'order_status' => Order::STATUS_SIGNED
];
// 生成重试订单
Db::startTrans();
try {
// 创建新订单
Order::create($newOrder);
// 更新老订单
$order->is_retry = Order::RETRY_STATUS_YES;
$order->retry_order_num = $newOrder['order_number'];
$res = $order->save();
if (!$res) {
throw new \Exception("重试支付,更新老订单失败,订单号:" . $orderNumber);
}
Db::commit();
} catch (\Exception $e) {
Db::rollback();
Log::error("JobPayOrderRetry错误信息" . $e->getMessage());
return ['respCode' => 1001, 'respMsg' => $e->getMessage()];
}
// 扣款参数
$requestParams = [
'agreementId' => $agreementId,
'billNo' => $newOrder['order_number'],
'amount' => $newOrder['price'] * 100,
'notifyUrl' => config('cmb.pay_notify_url'),
'productName' => $product['name']
];
$funcName = 'agreementPay';
return CmbHttpUtils::doPost($funcName, $requestParams);
}
/** /**
* 协议扣款回调处理 * 协议扣款回调处理
* @param array $data * @param array $data
@ -252,6 +314,10 @@ class AgreementService extends BaseService
if ($encryptData['result'] == 2) { if ($encryptData['result'] == 2) {
RechargeService::rechargeOrder($orderNumber); // 支付成功,到直连天下充值 RechargeService::rechargeOrder($orderNumber); // 支付成功,到直连天下充值
} }
// 支付失败
if ($encryptData['result'] == 3 && BaseService::isLastDayOfMonth()) {
CmbService::releaseMerchant($order->agreement_id);
}
if ($res) { if ($res) {
return ['respCode' => 1000, 'respMsg' => '处理成功']; return ['respCode' => 1000, 'respMsg' => '处理成功'];
} else { } else {

View File

@ -59,4 +59,14 @@ class BaseService
//找不到方法,自动调用模型中基类方法 //找不到方法,自动调用模型中基类方法
return call_user_func_array([$this->model, $name], $arguments); return call_user_func_array([$this->model, $name], $arguments);
} }
public static function isLastDayOfMonth()
{
$date = date('Y-m-d');
// 获取该月的最后一天
$lastDayOfMonth = date('Y-m-t', strtotime($date));
// 比较给定日期和该月的最后一天
return $date === $lastDayOfMonth;
}
} }

View File

@ -167,7 +167,8 @@ class CmbService extends BaseService
if ($res['result'] == 2) { // 扣款成功 if ($res['result'] == 2) { // 扣款成功
RechargeService::rechargeOrder($orderNumber); RechargeService::rechargeOrder($orderNumber);
} }
if ($res['result'] == 3) { // 扣款失败 解约 // 月底不进行重试,失败直接解约
if ($res['result'] == 3 && BaseService::isLastDayOfMonth()) { // 扣款失败 解约
self::releaseMerchant($order->agreement_id); self::releaseMerchant($order->agreement_id);
} }
return true; return true;

View File

@ -25,7 +25,7 @@ class OrderService extends BaseService
public function list($params) public function list($params)
{ {
return $this->model->searchPages(['order_number', 'type', 'status', 'pay_status', 'create_at', 'user_id'], $params)->toArray(); return $this->model->searchPages(['order_number', 'type', 'status', 'create_at', 'user_id', 'is_retry'], $params)->toArray();
} }
/** /**

View File

@ -10,6 +10,8 @@ return [
'getPayOrder' => \app\cmd\GetPayOrder::class, 'getPayOrder' => \app\cmd\GetPayOrder::class,
'getRefundOrder' => \app\cmd\getRefundOrder::class, 'getRefundOrder' => \app\cmd\getRefundOrder::class,
'queryAgreeStatus' => \app\cmd\QueryAgreeStatus::class, 'queryAgreeStatus' => \app\cmd\QueryAgreeStatus::class,
'queryRechargeOrder' =>\app\cmd\QueryRechargeOrder::class 'queryRechargeOrder' =>\app\cmd\QueryRechargeOrder::class,
'payOrderRetry' =>\app\cmd\PayOrderRetry::class,
'agreementPaySendSms' =>\app\cmd\AgreementPaySendSms::class
], ],
]; ];