doge 1 hafta önce
ebeveyn
işleme
dd127ce63d

+ 6 - 1
app/Http/Controllers/admin/PaymentOrder.php

@@ -14,6 +14,7 @@ use App\Models\Wallet;
 use App\Models\Recharge;
 use App\Models\Recharge;
 use App\Models\Withdraw;
 use App\Models\Withdraw;
 use App\Services\BalanceLogService;
 use App\Services\BalanceLogService;
+use App\Services\Payment\ZimuPayService;
 
 
 class PaymentOrder extends Controller
 class PaymentOrder extends Controller
 {
 {
@@ -33,7 +34,11 @@ class PaymentOrder extends Controller
         //人工提现订单
         //人工提现订单
         $data['rgWithdraw'] = (int)PaymentOrderModel::where('status', 0)->where('type', 4)->where('created_at', '>', date('Y-m-d H:i:s', strtotime('-1 day')))->count();
         $data['rgWithdraw'] = (int)PaymentOrderModel::where('status', 0)->where('type', 4)->where('created_at', '>', date('Y-m-d H:i:s', strtotime('-1 day')))->count();
         //人民币提现订单
         //人民币提现订单
-        $data['rmbWithdraw'] = (int)PaymentOrderModel::where('status', 0)->where('type', 2)->where('created_at', '>', date('Y-m-d H:i:s', strtotime('-1 day')))->count();
+        $data['rmbWithdraw'] = (int)PaymentOrderModel::where('status', 0)
+            ->where('type', 2)
+            ->where('channel', '!=', ZimuPayService::CHANNEL_WITHDRAW)
+            ->where('created_at', '>', date('Y-m-d H:i:s', strtotime('-1 day')))
+            ->count();
         return $this->success($data);
         return $this->success($data);
     }
     }
 
 

+ 3 - 1
app/Http/Controllers/admin/Withdraw.php

@@ -15,6 +15,7 @@ use Exception;
 use App\Models\Withdraw as WithdrawModel;
 use App\Models\Withdraw as WithdrawModel;
 use App\Models\PaymentOrder;
 use App\Models\PaymentOrder;
 use App\Models\Config;
 use App\Models\Config;
+use App\Services\Payment\ZimuPayService;
 
 
 class Withdraw extends Controller
 class Withdraw extends Controller
 {
 {
@@ -58,7 +59,8 @@ class Withdraw extends Controller
             $params['type'] = $params['type'] ?? 2;
             $params['type'] = $params['type'] ?? 2;
             
             
             $query = PaymentOrder::join('users', 'users.member_id', '=', 'payment_orders.member_id')
             $query = PaymentOrder::join('users', 'users.member_id', '=', 'payment_orders.member_id')
-                        ->select("payment_orders.*", "users.first_name","users.admin_note as user_admin_note");
+                        ->select("payment_orders.*", "users.first_name","users.admin_note as user_admin_note")
+                        ->where('payment_orders.channel', '!=', ZimuPayService::CHANNEL_WITHDRAW);
                         
                         
             $where = PaymentOrderService::getWhere($params);
             $where = PaymentOrderService::getWhere($params);
             $count = $query->where($where)->count();
             $count = $query->where($where)->count();

+ 57 - 2
app/Http/Controllers/api/Wallet.php

@@ -369,8 +369,8 @@ class Wallet extends BaseController
             $params = request()->validate([
             $params = request()->validate([
                 'amount' => ['required', 'numeric', 'min:0.01'],
                 'amount' => ['required', 'numeric', 'min:0.01'],
                 'channel' => ['required', 'string', 'in:DF001,DF002,DF005,JDpay,jdpay,NOwithdraw,ZIMUwithdraw,ZIMUcash,rgtx'],
                 'channel' => ['required', 'string', 'in:DF001,DF002,DF005,JDpay,jdpay,NOwithdraw,ZIMUwithdraw,ZIMUcash,rgtx'],
-                'bank_name' => ['required', 'string'],
-                'account' => ['required', 'string'],
+                'bank_name' => ['nullable', 'string'],
+                'account' => ['nullable', 'string'],
                 'card_no' => ['required', 'string'],
                 'card_no' => ['required', 'string'],
                 'safe_word' => ['required'],
                 'safe_word' => ['required'],
             ]);
             ]);
@@ -401,6 +401,9 @@ class Wallet extends BaseController
             if ($check === false) {
             if ($check === false) {
                 throw new Exception(lang("不支持此提现方式"));
                 throw new Exception(lang("不支持此提现方式"));
             }
             }
+            if (!ZimuPayService::isWithdrawChannel($params['channel']) && (empty($params['bank_name']) || empty($params['account']))) {
+                throw new Exception(lang("提现信息不完整"));
+            }
 
 
             $amount = $params['amount'];
             $amount = $params['amount'];
             //校验提现金额的限制
             //校验提现金额的限制
@@ -408,6 +411,58 @@ class Wallet extends BaseController
                 throw new Exception(lang("提现金额超出限制"));
                 throw new Exception(lang("提现金额超出限制"));
             }
             }
             
             
+            if (ZimuPayService::isWithdrawChannel($params['channel'])) {
+                $serviceCharge = (new WithdrawService())->serviceCharge;
+                $real = bcsub($amount, $serviceCharge, 10);
+                $real = floatval($real);
+
+                if ($amount <= $serviceCharge) {
+                    throw new Exception(lang("提现不能少于") . "{$serviceCharge} USDT");
+                }
+
+                $wallet = WalletModel::where('member_id', $member_id)->first();
+                if (!$wallet) throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR);
+                $balance = $wallet->available_balance;
+                $rate = Config::where('field', 'exchange_rate_rmb')->first()->val ?? 1;
+                $exchange_rate_difference = Config::where('field', 'exchange_rate_difference')->first()->val ?? 0;
+                $rate = bcadd($rate, $exchange_rate_difference, 2);
+                $rate_usdt_amount = bcdiv($balance, $rate, 2);
+                $rate_rmb_amount = bcmul($amount, $rate, 2);
+                if ($amount > $rate_usdt_amount) {
+                    throw new Exception(lang("余额不足") . "{$serviceCharge} USDT");
+                }
+
+                DB::beginTransaction();
+                try {
+                    $beforeBalance = $wallet->available_balance;
+                    $afterBalance = bcsub($wallet->available_balance, $rate_rmb_amount, 2);
+                    $wallet->available_balance = $afterBalance;
+                    if (!$wallet->save()) throw new Exception('钱包更新失败!', HttpStatus::CUSTOM_ERROR);
+
+                    $withdraw = Withdraw::create([
+                        'member_id' => $member_id,
+                        'amount' => $amount,
+                        'service_charge' => $serviceCharge,
+                        'to_account' => $real,
+                        'address' => $params['card_no'],
+                        'exchange_rate' => $rate,
+                        'status' => 0,
+                        'after_balance' => $afterBalance,
+                        'channel' => ZimuPayService::CHANNEL_WITHDRAW,
+                        'order_no' => PaymentOrderService::createOrderNo('zw_', $member_id),
+                    ]);
+                    BalanceLogService::addLog($member_id, bcmul(($amount * -1), $rate, 2), $beforeBalance, $afterBalance, '提现', $withdraw->id, '');
+                    DB::commit();
+                    return $this->success($withdraw, '提交成功');
+                } catch (Exception $e) {
+                    DB::rollBack();
+                    if ($e->getCode() === HttpStatus::CUSTOM_ERROR) {
+                        return $this->error($e->getMessage());
+                    }
+                    throw $e;
+                }
+            }
+
             if ($params['channel'] == 'rgtx') {
             if ($params['channel'] == 'rgtx') {
                 
                 
                 DB::beginTransaction();
                 DB::beginTransaction();

+ 1 - 1
app/Models/Withdraw.php

@@ -8,7 +8,7 @@ class Withdraw extends BaseModel
 {
 {
 
 
     protected $table = 'withdraws';
     protected $table = 'withdraws';
-    protected $fillable = ['member_id', 'amount', 'service_charge', 'to_account', 'address', 'exchange_rate', 'status', 'after_balance', 'remark', 'admin_note', 'is_locked'];
+    protected $fillable = ['member_id', 'amount', 'service_charge', 'to_account', 'address', 'exchange_rate', 'status', 'after_balance', 'remark', 'admin_note', 'is_locked', 'channel', 'order_no', 'pay_no', 'pay_data', 'callback_data'];
     protected $hidden = [];
     protected $hidden = [];
 
 
     public function member(): BelongsTo
     public function member(): BelongsTo

+ 5 - 2
app/Services/PaymentOrderService.php

@@ -767,7 +767,7 @@ class PaymentOrderService extends BaseService
                         ZimuPayService::cashPayMethod((string)$order->bank_name)
                         ZimuPayService::cashPayMethod((string)$order->bank_name)
                     );
                     );
                 } else {
                 } else {
-                    $ret = ZimuPayService::withdraw($amount, $order->order_no, (string)$order->member_id, (string)$order->card_no, (string)$order->account);
+                    $ret = ZimuPayService::withdraw($amount, $order->order_no, (string)$order->member_id, (string)$order->card_no);
                 }
                 }
                 Log::channel('payment')->info('ZIMU提现/代付接口调用', [
                 Log::channel('payment')->info('ZIMU提现/代付接口调用', [
                     'order_id' => $order->id,
                     'order_id' => $order->id,
@@ -973,7 +973,7 @@ class PaymentOrderService extends BaseService
                         ZimuPayService::cashPayMethod((string)$bank_name)
                         ZimuPayService::cashPayMethod((string)$bank_name)
                     );
                     );
                 } else {
                 } else {
-                    $ret = ZimuPayService::withdraw($amount, $order_no, (string)$memberId, (string)$card_no, (string)$account);
+                    $ret = ZimuPayService::withdraw($amount, $order_no, (string)$memberId, (string)$card_no);
                 }
                 }
                 Log::channel('payment')->info('ZIMU提现/代付接口调用', [
                 Log::channel('payment')->info('ZIMU提现/代付接口调用', [
                     'order_no' => $order_no,
                     'order_no' => $order_no,
@@ -1119,6 +1119,9 @@ class PaymentOrderService extends BaseService
     {
     {
         if (ZimuPayService::getAppId() !== '' && ($params['appId'] ?? '') === ZimuPayService::getAppId()) {
         if (ZimuPayService::getAppId() !== '' && ($params['appId'] ?? '') === ZimuPayService::getAppId()) {
             $info = self::findOne(['order_no' => $params['mchOrderNo'] ?? '']);
             $info = self::findOne(['order_no' => $params['mchOrderNo'] ?? '']);
+            if (!$info) {
+                return WithdrawService::receiveZimuWithdraw($params);
+            }
             if (!$info || $info->type != self::TYPE_PAYOUT || !ZimuPayService::isPayoutChannel($info->channel)) {
             if (!$info || $info->type != self::TYPE_PAYOUT || !ZimuPayService::isPayoutChannel($info->channel)) {
                 Log::error('ZIMU提现/代付回调订单不存在或类型不匹配', [
                 Log::error('ZIMU提现/代付回调订单不存在或类型不匹配', [
                     'mch_order_no' => $params['mchOrderNo'] ?? '',
                     'mch_order_no' => $params['mchOrderNo'] ?? '',

+ 80 - 1
app/Services/WithdrawService.php

@@ -15,8 +15,11 @@ use App\Models\RoomUser;
 use App\Models\User;
 use App\Models\User;
 use App\Models\Wallet;
 use App\Models\Wallet;
 use App\Models\Withdraw;
 use App\Models\Withdraw;
+use App\Services\Payment\ZimuPayService;
 use Exception;
 use Exception;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 use LaravelLang\Publisher\Console\Add;
 use LaravelLang\Publisher\Console\Add;
 use Telegram\Bot\Api;
 use Telegram\Bot\Api;
 use Telegram\Bot\Exceptions\TelegramSDKException;
 use Telegram\Bot\Exceptions\TelegramSDKException;
@@ -642,7 +645,27 @@ class WithdrawService
         if (!$w) return 0;
         if (!$w) return 0;
         // 汇率
         // 汇率
         $rate = $w->exchange_rate ?? 1;
         $rate = $w->exchange_rate ?? 1;
-        if ($status == 1) {
+        if ($status == 1 && ($w->channel ?? '') === ZimuPayService::CHANNEL_WITHDRAW) {
+            $orderNo = $w->order_no ?: PaymentOrderService::createOrderNo('zw_', $w->member_id);
+            $ret = ZimuPayService::withdraw($w->to_account, $orderNo, (string)$w->member_id, (string)$w->address);
+            Log::channel('payment')->info('ZIMU钱包地址提现接口调用', [
+                'withdraw_id' => $w->id,
+                'order_no' => $orderNo,
+                'member_id' => $w->member_id,
+                'amount' => $w->to_account,
+                'address' => $w->address,
+                'response' => $ret,
+            ]);
+            if (($ret['code'] ?? -1) != 0) {
+                throw new Exception($ret['msg'] ?? 'ZIMU钱包地址提现失败', HttpStatus::CUSTOM_ERROR);
+            }
+
+            $w->order_no = $orderNo;
+            $w->pay_no = $ret['data']['orderNo'] ?? '';
+            $w->pay_data = json_encode($ret, JSON_UNESCAPED_UNICODE);
+            $w->status = 1;
+            $w->save();
+        } else if ($status == 1) {
             $w->status = 1;
             $w->status = 1;
             $w->save();
             $w->save();
         } else if ($status == 2) {
         } else if ($status == 2) {
@@ -677,6 +700,62 @@ class WithdrawService
         return 1;
         return 1;
     }
     }
 
 
+    public static function receiveZimuWithdraw(array $params): bool
+    {
+        $withdraw = Withdraw::where('order_no', $params['mchOrderNo'] ?? '')->first();
+        if (!$withdraw || ($withdraw->channel ?? '') !== ZimuPayService::CHANNEL_WITHDRAW) {
+            return false;
+        }
+        if (!ZimuPayService::verifyNotify($params)) {
+            Log::error('ZIMU钱包地址提现回调验签失败', [
+                'mch_order_no' => $params['mchOrderNo'] ?? '',
+                'signature' => ZimuPayService::notifySignatureDiagnostics($params),
+            ]);
+            return false;
+        }
+        if (bccomp(ZimuPayService::amount($withdraw->to_account), ZimuPayService::amount($params['amount'] ?? 0), 2) !== 0) {
+            Log::error('ZIMU钱包地址提现回调金额不一致', [
+                'mch_order_no' => $params['mchOrderNo'] ?? '',
+                'order_amount' => $withdraw->to_account,
+                'callback_amount' => $params['amount'] ?? null,
+            ]);
+            return false;
+        }
+
+        DB::transaction(function () use ($params, $withdraw) {
+            $order = Withdraw::where('id', $withdraw->id)->lockForUpdate()->first();
+            if (!$order || $order->status == 2) {
+                return;
+            }
+
+            $order->callback_data = json_encode($params, JSON_UNESCAPED_UNICODE);
+            if ((string)$params['status'] === ZimuPayService::WITHDRAW_STATUS_SUCCESS) {
+                $order->status = 1;
+                $order->save();
+            } elseif ((string)$params['status'] === ZimuPayService::WITHDRAW_STATUS_FAIL) {
+                if ($order->status != 2) {
+                    $order->status = 2;
+                    $order->remark = $params['msg'] ?? 'ZIMU钱包地址提现失败';
+                    $order->save();
+
+                    $wallet = Wallet::where('member_id', $order->member_id)->lockForUpdate()->first();
+                    if ($wallet) {
+                        $refundAmount = bcmul($order->amount, $order->exchange_rate ?: 1, 2);
+                        $beforeBalance = $wallet->available_balance;
+                        $afterBalance = bcadd($beforeBalance, $refundAmount, 10);
+                        $wallet->available_balance = $afterBalance;
+                        $wallet->save();
+                        BalanceLogService::addLog($order->member_id, $refundAmount, $beforeBalance, $afterBalance, '提现', $order->id, 'ZIMU钱包地址提现失败退款');
+                    }
+                }
+            } else {
+                $order->save();
+            }
+        }, 3);
+
+        return true;
+    }
+
 
 
     public static function batchReject()
     public static function batchReject()
     {
     {

+ 39 - 0
database/migrations/2026_06_09_130000_add_zimu_fields_to_withdraws.php

@@ -0,0 +1,39 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration {
+    public function up()
+    {
+        Schema::table('withdraws', function (Blueprint $table) {
+            if (!Schema::hasColumn('withdraws', 'channel')) {
+                $table->string('channel')->nullable()->default('')->comment('提现通道');
+            }
+            if (!Schema::hasColumn('withdraws', 'order_no')) {
+                $table->string('order_no')->nullable()->default('')->comment('商户订单号');
+            }
+            if (!Schema::hasColumn('withdraws', 'pay_no')) {
+                $table->string('pay_no')->nullable()->default('')->comment('三方订单号');
+            }
+            if (!Schema::hasColumn('withdraws', 'pay_data')) {
+                $table->text('pay_data')->nullable()->comment('三方下单响应');
+            }
+            if (!Schema::hasColumn('withdraws', 'callback_data')) {
+                $table->text('callback_data')->nullable()->comment('三方回调数据');
+            }
+        });
+    }
+
+    public function down()
+    {
+        Schema::table('withdraws', function (Blueprint $table) {
+            foreach (['channel', 'order_no', 'pay_no', 'pay_data', 'callback_data'] as $column) {
+                if (Schema::hasColumn('withdraws', $column)) {
+                    $table->dropColumn($column);
+                }
+            }
+        });
+    }
+};