release
parent
7cebf43fdf
commit
e17a2b9357
|
|
@ -51,7 +51,7 @@ class WalletToBankLogController extends AdminController
|
||||||
$grid->model()->orderBy('created_at', 'desc');
|
$grid->model()->orderBy('created_at', 'desc');
|
||||||
|
|
||||||
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
if ($actions->row->status == WalletToBankLogStatus::Pending && Admin::user()->can('dcat.admin.wallet_to_bank_logs.verify')) {
|
if (in_array($actions->row->status, [WalletToBankLogStatus::Pending, WalletToBankLogStatus::Failed]) && Admin::user()->can('dcat.admin.wallet_to_bank_logs.verify')) {
|
||||||
$actions->append(new WalletToBankLogVerify());
|
$actions->append(new WalletToBankLogVerify());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
namespace App\Admin\Forms;
|
namespace App\Admin\Forms;
|
||||||
|
|
||||||
|
use App\Enums\WalletToBankLogPayWay;
|
||||||
|
use App\Enums\WalletToBankLogStatus;
|
||||||
|
use App\Exceptions\BizException;
|
||||||
use App\Models\WalletLog;
|
use App\Models\WalletLog;
|
||||||
use App\Models\WalletToBankLog;
|
use App\Models\WalletToBankLog;
|
||||||
use App\Services\WalletService;
|
use App\Services\WalletService;
|
||||||
|
|
@ -34,15 +37,37 @@ class WalletToBankLogVerify extends Form implements LazyRenderable
|
||||||
*/
|
*/
|
||||||
public function handle(array $input)
|
public function handle(array $input)
|
||||||
{
|
{
|
||||||
$id = $this->payload['id'] ?? 0;
|
|
||||||
$log = WalletToBankLog::findOrFail($id);
|
|
||||||
try {
|
try {
|
||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
$log->update($input);
|
|
||||||
//如果拒绝,重新添加对应可提金额
|
$log = WalletToBankLog::lockForUpdate()->findOrFail($this->payload['id']);
|
||||||
if ($log->status == 2) {
|
|
||||||
$walletService = new WalletService();
|
if (! in_array($log->status, [WalletToBankLogStatus::Pending, WalletToBankLogStatus::Failed])) {
|
||||||
$walletService->changeBalance($log->user, $log->amount, WalletLog::ACTION_WITHDRAW_FAILED, '提现-失败', $log);
|
throw new BizException('提现记录状态异常');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input['status'] == 1) {
|
||||||
|
$log->pay_way = $input['pay_way'];
|
||||||
|
|
||||||
|
if ($log->pay_way === WalletToBankLogPayWay::Offline) {
|
||||||
|
$log->status = WalletToBankLogStatus::Success;
|
||||||
|
$log->pay_at = now();
|
||||||
|
} else {
|
||||||
|
$log->pay_sn = serial_number();
|
||||||
|
$log->status = WalletToBankLogStatus::Passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log->failed_reason = null;
|
||||||
|
} else {
|
||||||
|
$log->status = WalletToBankLogStatus::Refused;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log->remarks = $input['remarks'];
|
||||||
|
$log->save();
|
||||||
|
|
||||||
|
// 如果拒绝,退换扣除金额
|
||||||
|
if ($log->status === WalletToBankLogStatus::Refused) {
|
||||||
|
(new WalletService())->changeBalance($log->user, $log->amount, WalletLog::ACTION_WITHDRAW_FAILED, '提现-失败', $log);
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
@ -65,7 +90,6 @@ class WalletToBankLogVerify extends Form implements LazyRenderable
|
||||||
$id = $this->payload['id'] ?? 0;
|
$id = $this->payload['id'] ?? 0;
|
||||||
$log = WalletToBankLog::findOrFail($id);
|
$log = WalletToBankLog::findOrFail($id);
|
||||||
|
|
||||||
|
|
||||||
$this->text('bank_name')->value($log->bank_name)->disable();
|
$this->text('bank_name')->value($log->bank_name)->disable();
|
||||||
$this->text('bank_number')->value($log->bank_number)->disable();
|
$this->text('bank_number')->value($log->bank_number)->disable();
|
||||||
$this->text('username')->value($log->username)->disable();
|
$this->text('username')->value($log->username)->disable();
|
||||||
|
|
@ -75,11 +99,19 @@ class WalletToBankLogVerify extends Form implements LazyRenderable
|
||||||
$this->currency('service_amount')->symbol('¥')->value(bcdiv($log->service_amount, 100, 2))->disable();
|
$this->currency('service_amount')->symbol('¥')->value(bcdiv($log->service_amount, 100, 2))->disable();
|
||||||
$this->currency('account_amount')->symbol('¥')->value(bcdiv($log->account_amount, 100, 2))->disable();
|
$this->currency('account_amount')->symbol('¥')->value(bcdiv($log->account_amount, 100, 2))->disable();
|
||||||
|
|
||||||
$this->radio('status')->options([
|
if ($log->status === WalletToBankLogStatus::Failed) {
|
||||||
|
$this->textarea('failed_reason')->value($log->failed_reason ?: '')->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->radio('status')
|
||||||
|
->options([
|
||||||
1 => '通过',
|
1 => '通过',
|
||||||
2 => '拒绝',
|
2 => '拒绝',
|
||||||
])->default(1);
|
])
|
||||||
|
->default(1);
|
||||||
|
$this->select('pay_way')
|
||||||
|
->options(WalletToBankLogPayWay::texts())
|
||||||
|
->rules('required_if:status,1', ['required_if'=>'请选择支付方式']);
|
||||||
$this->text('remarks')->rules('required_if:status,2', ['required_if'=>'拒绝时需要填写备注']);
|
$this->text('remarks')->rules('required_if:status,2', ['required_if'=>'拒绝时需要填写备注']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\Bank;
|
||||||
|
use App\Enums\WalletToBankLogStatus;
|
||||||
|
use App\Exceptions\YeePayException;
|
||||||
|
use App\Models\WalletToBankLog;
|
||||||
|
use App\Services\YeePayService;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class WalletToBankCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'wallet-to-bank';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = '可提提现';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$yeePayService = new YeePayService(config('services.yeepay'));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
WalletToBankLog::where('status', WalletToBankLogStatus::Passed)->chunkById(1, function ($logs) use ($yeePayService) {
|
||||||
|
foreach ($logs as $log) {
|
||||||
|
try {
|
||||||
|
$result = $yeePayService->request('accountpay.behalf.Pay', [
|
||||||
|
'payerOutUserId' => '21102510220227100003' ?: config('services.yeepay.partner_id'),
|
||||||
|
'merchOrderNo' => $log->pay_sn,
|
||||||
|
'tradeName' => '余额提现',
|
||||||
|
'payeeUserName' => $log->username,
|
||||||
|
'bankCardNo' => $log->bank_number,
|
||||||
|
'bankCode' => Bank::tryFromBankName($log->bank_name)?->name,
|
||||||
|
'bankCardType' => 'DEBIT_CARD',
|
||||||
|
'amount' => bcdiv($log->account_amount, 100, 2),
|
||||||
|
'feeRole' => 'PAYER',
|
||||||
|
'tradeMemo' => '商城余额提现',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($result['orderStatus'] === 'SUCCESS') {
|
||||||
|
$log->update([
|
||||||
|
'status' => WalletToBankLogStatus::Success,
|
||||||
|
'pay_at' => now(),
|
||||||
|
'failed_reason' => null,
|
||||||
|
]);
|
||||||
|
} elseif ($result['orderStatus'] === 'FAIL') {
|
||||||
|
$log->update([
|
||||||
|
'status' => WalletToBankLogStatus::Failed,
|
||||||
|
'failed_reason' => '交易失败',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$log->update([
|
||||||
|
'status' => WalletToBankLogStatus::Paying,
|
||||||
|
'failed_reason' => null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (YeePayException $e) {
|
||||||
|
$log->update([
|
||||||
|
'status' => WalletToBankLogStatus::Failed,
|
||||||
|
'failed_reason' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sleep(60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Endpoint\Callback\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class YeePayNotifyController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
logger()->debug('yeepay notify', $request->input());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use App\Endpoint\Callback\Http\Controllers\AlipayController;
|
use App\Endpoint\Callback\Http\Controllers\AlipayController;
|
||||||
use App\Endpoint\Callback\Http\Controllers\Kuaidi100Controller;
|
use App\Endpoint\Callback\Http\Controllers\Kuaidi100Controller;
|
||||||
use App\Endpoint\Callback\Http\Controllers\WxpayController;
|
use App\Endpoint\Callback\Http\Controllers\WxpayController;
|
||||||
|
use App\Endpoint\Callback\Http\Controllers\YeePayNotifyController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
//快递100物流推送
|
//快递100物流推送
|
||||||
|
|
@ -13,3 +14,5 @@ Route::post('wxpay/{payment}/paid-notify', [WxpayController::class, 'paidNotify'
|
||||||
Route::post('wxpay/{payment}/order-refund-notify', [WxpayController::class, 'orderRefundedNotify'])->name('wxpay.order_refund_notify');
|
Route::post('wxpay/{payment}/order-refund-notify', [WxpayController::class, 'orderRefundedNotify'])->name('wxpay.order_refund_notify');
|
||||||
|
|
||||||
Route::post('alipay', AlipayController::class)->name('alipay.notify');
|
Route::post('alipay', AlipayController::class)->name('alipay.notify');
|
||||||
|
|
||||||
|
Route::post('yeepay-notify', YeePayNotifyController::class)->name('yeepay.notify');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum WalletToBankLogPayWay: int {
|
||||||
|
case Offline = 1;
|
||||||
|
case Behalf = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function color(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
static::Offline => '#5b69bc',
|
||||||
|
static::Behalf => '#21b978',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function text(): string
|
||||||
|
{
|
||||||
|
return static::texts()[$this->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function texts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
static::Offline->value => '线下',
|
||||||
|
static::Behalf->value => '代付',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,9 @@ enum WalletToBankLogStatus: int {
|
||||||
case Pending = 0;
|
case Pending = 0;
|
||||||
case Success = 1;
|
case Success = 1;
|
||||||
case Refused = 2;
|
case Refused = 2;
|
||||||
|
case Passed = 3; // 审核通过 - 等待付款
|
||||||
|
case Paying = 4; // 审核通过 - 付款中
|
||||||
|
case Failed = 9;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
|
|
@ -14,7 +17,10 @@ enum WalletToBankLogStatus: int {
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
static::Pending => '#5b69bc',
|
static::Pending => '#5b69bc',
|
||||||
|
static::Passed => '#dda451',
|
||||||
|
static::Paying => '#3085d6',
|
||||||
static::Success => '#21b978',
|
static::Success => '#21b978',
|
||||||
|
static::Failed => '#ea5455',
|
||||||
static::Refused => '#b3b9bf',
|
static::Refused => '#b3b9bf',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +40,10 @@ enum WalletToBankLogStatus: int {
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
static::Pending->value => '待处理',
|
static::Pending->value => '待处理',
|
||||||
|
static::Passed->value => '待付款',
|
||||||
|
static::Paying->value => '付款中',
|
||||||
static::Success->value => '成功',
|
static::Success->value => '成功',
|
||||||
|
static::Failed->value => '失败',
|
||||||
static::Refused->value => '拒绝',
|
static::Refused->value => '拒绝',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class YeePayException extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\WalletToBankLogPayWay;
|
||||||
use App\Enums\WalletToBankLogStatus;
|
use App\Enums\WalletToBankLogStatus;
|
||||||
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -14,6 +15,8 @@ class WalletToBankLog extends Model
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'status' => WalletToBankLogStatus::class,
|
'status' => WalletToBankLogStatus::class,
|
||||||
|
'pay_way' => WalletToBankLogPayWay::class,
|
||||||
|
'pay_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -31,6 +34,10 @@ class WalletToBankLog extends Model
|
||||||
'rate',
|
'rate',
|
||||||
'service_amount',
|
'service_amount',
|
||||||
'account_amount',
|
'account_amount',
|
||||||
|
'pay_sn',
|
||||||
|
'pay_way',
|
||||||
|
'pay_at',
|
||||||
|
'failed_reason',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Exceptions\YeePayException;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class YeePayService
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected array $config,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $service
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function request(string $service, array $data = []): array
|
||||||
|
{
|
||||||
|
$data = array_merge([
|
||||||
|
'requestNo' => Str::uuid()->getHex()->toString(),
|
||||||
|
'service' => $service,
|
||||||
|
'partnerId' => $this->config['partner_id'],
|
||||||
|
'signType' => 'MD5',
|
||||||
|
'notifyUrl' => route('yeepay.notify'),
|
||||||
|
], $data);
|
||||||
|
|
||||||
|
$data['sign'] = $this->sign($data);
|
||||||
|
|
||||||
|
$response = Http::asForm()->post($this->config['gateway_url'], $data);
|
||||||
|
|
||||||
|
$response->throw();
|
||||||
|
|
||||||
|
$result = $response->json();
|
||||||
|
|
||||||
|
if ($result['success'] && in_array($result['resultCode'], ['EXECUTE_SUCCESS', 'EXECUTE_PROCESSING'])) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = $result['resultDetail'] ?? $result['resultMessage'];
|
||||||
|
|
||||||
|
throw new YeePayException($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function sign(array $params): string
|
||||||
|
{
|
||||||
|
unset($params['sign']);
|
||||||
|
|
||||||
|
ksort($params);
|
||||||
|
|
||||||
|
$str = '';
|
||||||
|
|
||||||
|
foreach ($params as $key => $value) {
|
||||||
|
if (blank($value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($str !== '') {
|
||||||
|
$str .= '&';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
$value = json_encode($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$str .= "{$key}={$value}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$str .= $this->config['secret_key'];
|
||||||
|
|
||||||
|
return md5($str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,4 +30,10 @@ return [
|
||||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'yeepay' => [
|
||||||
|
'partner_id' => env('YEEPAY_PARTNER_ID'),
|
||||||
|
'secret_key' => env('YEEPAY_SECRET_KEY'),
|
||||||
|
'gateway_url' => env('YEEPAY_GATEWAY_URL'),
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\WalletToBankLogPayWay;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddPayColumnsToWalletToBankLogsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('wallet_to_bank_logs', function (Blueprint $table) {
|
||||||
|
$table->string('pay_sn')->nullable()->comment('支付单号');
|
||||||
|
$table->tinyInteger('pay_way')->default(WalletToBankLogPayWay::Offline->value)->comment('支付方式: 1 线下, 2 代付');
|
||||||
|
$table->timestamp('pay_at')->nullable()->comment('支付时间');
|
||||||
|
$table->text('failed_reason')->nullable()->comment('失败原因');
|
||||||
|
|
||||||
|
$table->unique('pay_sn');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('wallet_to_bank_logs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['pay_sn', 'pay_way', 'failed_reason', 'pay_at']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,9 @@ return [
|
||||||
'status' => '状态',
|
'status' => '状态',
|
||||||
'remarks' => '备注',
|
'remarks' => '备注',
|
||||||
'created_at'=> '申请时间',
|
'created_at'=> '申请时间',
|
||||||
|
'pay_way' => '支付方式',
|
||||||
|
'pay_at' => '支付时间',
|
||||||
|
'failed_reason' => '失败原因',
|
||||||
'user'=>[
|
'user'=>[
|
||||||
'phone' => '申请人',
|
'phone' => '申请人',
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue