增加 微信支付
This commit is contained in:
parent
39918b3422
commit
d635a5a343
45
app/Console/Commands/TestCommand.php
Normal file
45
app/Console/Commands/TestCommand.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Balance;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Command\Command as CommandAlias;
|
||||
|
||||
class TestCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'test-command';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '测试用的命令。';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
|
||||
// 使用 SQL 语句,获取 Balance 中的今日收益(sum amount),然后 dd 为 sql
|
||||
$sql = Balance::query()
|
||||
->selectRaw('sum(amount) as amount')
|
||||
->where('created_at', '>=', today())
|
||||
->toSql();
|
||||
|
||||
|
||||
dd($sql);
|
||||
|
||||
|
||||
return CommandAlias::SUCCESS;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Exceptions\ChargeException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Balance;
|
||||
use App\Models\Module;
|
||||
@ -9,8 +10,13 @@
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use SimpleSoftwareIO\QrCode\Facades\QrCode;
|
||||
use Yansongda\LaravelPay\Facades\Pay;
|
||||
use Yansongda\Pay\Exception\ContainerException;
|
||||
use Yansongda\Pay\Exception\InvalidParamsException;
|
||||
|
||||
class BalanceController extends Controller
|
||||
{
|
||||
@ -30,26 +36,19 @@ public function store(Request $request)
|
||||
// 充值
|
||||
$this->validate($request, [
|
||||
'amount' => 'required|integer|min:0.1|max:10000',
|
||||
'payment' => 'required|in:wechat,alipay',
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$balance = new Balance();
|
||||
|
||||
|
||||
$data = [
|
||||
'user_id' => $user->id,
|
||||
'amount' => $request->input('amount'),
|
||||
'payment' => 'alipay',
|
||||
'payment' => $request->input('payment'),
|
||||
];
|
||||
|
||||
// if local
|
||||
// if (env('APP_ENV') == 'local') {
|
||||
// $data['payment'] = null;
|
||||
// $data['paid_at'] = now();
|
||||
// }
|
||||
|
||||
|
||||
$balance = $balance->create($data);
|
||||
|
||||
// 生成 18 位订单号
|
||||
@ -58,82 +57,220 @@ public function store(Request $request)
|
||||
|
||||
$balance->save();
|
||||
|
||||
// $balances['pay_url'] = route('balances.balances.show', ['balances' => $balances['order_id']]);
|
||||
|
||||
return redirect()->route('balances.balances.show', ['balances' => $balance->order_id]);
|
||||
return redirect()->route('balances.show', compact('balance'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示充值页面和状态(ajax)
|
||||
*/
|
||||
public function show(Balance $balance)
|
||||
public function show(Request $request, Balance $balance)
|
||||
{
|
||||
|
||||
if ($balance->paid_at !== null) {
|
||||
return $this->error('订单已支付');
|
||||
if ($request->ajax()) {
|
||||
return $this->success($balance);
|
||||
}
|
||||
|
||||
return view('balances.process', compact('balance'));
|
||||
} else {
|
||||
if ($request->ajax()) {
|
||||
return $this->success($balance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (now()->diffInDays($balance->created_at) > 1) {
|
||||
return $this->error('订单已失效');
|
||||
if ($request->ajax()) {
|
||||
return $this->forbidden($balance);
|
||||
}
|
||||
|
||||
return redirect()->route('index')->with('error', '订单已逾期。');
|
||||
}
|
||||
|
||||
$web = Pay::alipay()->web([
|
||||
'out_trade_no' => $balance->order_id,
|
||||
'total_amount' => $balance->amount,
|
||||
'subject' => config('app.display_name') . ' 充值',
|
||||
]);
|
||||
$balance->load('user');
|
||||
|
||||
return view('balances.pay', ['html' => (string)$web->getBody()]);
|
||||
$subject = config('app.display_name') . ' 充值';
|
||||
|
||||
$order = [
|
||||
'out_trade_no' => $balance->order_id,
|
||||
];
|
||||
|
||||
$code = QrCode::size(150);
|
||||
|
||||
if ($balance->payment === 'wechat') {
|
||||
$pay = $this->xunhu_wechat($balance, $subject);
|
||||
|
||||
$qr_code = $code->generate($pay['url']);
|
||||
} else {
|
||||
$order['subject'] = $subject;
|
||||
$order['total_amount'] = $balance->amount;
|
||||
|
||||
$pay = Pay::alipay()->scan($order);
|
||||
|
||||
$qr_code = $code->generate($pay->qr_code);
|
||||
}
|
||||
|
||||
if (!isset($qr_code)) {
|
||||
abort(500, '支付方式错误');
|
||||
}
|
||||
|
||||
return view('balances.pay', compact('balance', 'qr_code'));
|
||||
}
|
||||
|
||||
private function xunhu_wechat(Balance $balance, $subject = '支付')
|
||||
{
|
||||
$data = [
|
||||
'version' => '1.1',
|
||||
'lang' => 'zh-cn',
|
||||
'plugins' => config('app.name'),
|
||||
'appid' => config('pay.xunhu.app_id'),
|
||||
'trade_order_id' => $balance->order_id,
|
||||
'payment' => 'wechat',
|
||||
'type' => 'WAP',
|
||||
'wap_url' => config('app.url'),
|
||||
'wap_name' => config('app.display_name'),
|
||||
'total_fee' => $balance->amount,
|
||||
'title' => $subject,
|
||||
'time' => time(),
|
||||
'notify_url' => route('balances.notify', 'wechat'),
|
||||
'return_url' => route('balances.notify', 'wechat'),
|
||||
'callback_url' => route('balances.show', $balance),
|
||||
'modal' => null,
|
||||
'nonce_str' => str_shuffle(time()),
|
||||
];
|
||||
|
||||
$data['hash'] = $this->xunhu_hash($data);
|
||||
|
||||
$response = Http::post(config('pay.xunhu.gateway'), $data);
|
||||
|
||||
if (!$response->successful()) {
|
||||
abort(500, '支付网关错误');
|
||||
}
|
||||
|
||||
$response = $response->json();
|
||||
|
||||
$hash = $this->xunhu_hash($response);
|
||||
|
||||
if (!isset($response['hash']) || $response['hash'] !== $hash) {
|
||||
abort(500, '无法校验支付网关返回数据');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function xunhu_hash(array $arr)
|
||||
{
|
||||
ksort($arr);
|
||||
|
||||
$pre = [];
|
||||
foreach ($arr as $key => $data) {
|
||||
if (is_null($data) || $data === '') {
|
||||
continue;
|
||||
}
|
||||
if ($key == 'hash') {
|
||||
continue;
|
||||
}
|
||||
$pre[$key] = stripslashes($data);
|
||||
}
|
||||
|
||||
$arg = '';
|
||||
$qty = count($pre);
|
||||
$index = 0;
|
||||
|
||||
foreach ($pre as $key => $val) {
|
||||
$arg .= "$key=$val";
|
||||
if ($index++ < ($qty - 1)) {
|
||||
$arg .= "&";
|
||||
}
|
||||
}
|
||||
|
||||
return md5($arg . config('pay.xunhu.app_secret'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function notify(Request $request): View|JsonResponse
|
||||
public function notify(Request $request, $payment): View|JsonResponse
|
||||
{
|
||||
$this->validate($request, [
|
||||
'out_trade_no' => 'required',
|
||||
]);
|
||||
$is_paid = false;
|
||||
// $pay_amount = 0;
|
||||
|
||||
if ($payment === 'alipay') {
|
||||
$out_trade_no = $request->input('out_trade_no');
|
||||
} else if ($payment === 'wechat') {
|
||||
$out_trade_no = $request->input('trade_order_id');
|
||||
} else {
|
||||
abort(400, '支付方式错误');
|
||||
}
|
||||
|
||||
// 检测订单是否存在
|
||||
$balance = Balance::where('order_id', $request->out_trade_no)->with('user')->first();
|
||||
$balance = Balance::where('order_id', $out_trade_no)->with('user')->first();
|
||||
if (!$balance) {
|
||||
abort(404, '找不到订单。');
|
||||
}
|
||||
|
||||
// 检测订单是否已支付
|
||||
// if ($balance->paid_at !== null) {
|
||||
// // return $this->success('订单已支付');
|
||||
// return view('balances.process', compact('balance'));
|
||||
// }
|
||||
if ($balance->paid_at !== null) {
|
||||
if ($request->ajax()) {
|
||||
return $this->success($balance);
|
||||
}
|
||||
|
||||
return view('balances.process', compact('balance'));
|
||||
}
|
||||
|
||||
|
||||
// 处理验证
|
||||
if ($payment === 'alipay') {
|
||||
try {
|
||||
$pay = Pay::alipay()->callback();
|
||||
|
||||
} catch (ContainerException|InvalidParamsException $e) {
|
||||
abort(500, $e->getMessage());
|
||||
}
|
||||
|
||||
$is_paid = true;
|
||||
} else if ($payment === 'wechat') {
|
||||
if (!($request->filled('hash') || $request->filled('trade_order_id'))) {
|
||||
return $this->error('参数错误。');
|
||||
}
|
||||
|
||||
if ($request->filled('plugins') && $request->input('plugins') != config('app.name')) {
|
||||
return $this->error('插件不匹配。');
|
||||
}
|
||||
|
||||
$hash = $this->xunhu_hash($request->toArray());
|
||||
if ($request->input('hash') != $hash) {
|
||||
Log::debug('hash error', $request->toArray());
|
||||
}
|
||||
|
||||
if ($request->input('status') === 'OD') {
|
||||
$is_paid = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
abort(400, '支付方式错误。');
|
||||
}
|
||||
|
||||
if ($is_paid) {
|
||||
try {
|
||||
(new Transaction)->addAmount($balance->user_id, 'alipay', $balance->amount);
|
||||
|
||||
$balance->update([
|
||||
'paid_at' => now()
|
||||
]);
|
||||
|
||||
} catch (ChargeException $e) {
|
||||
abort(500, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($request->ajax()) {
|
||||
return $this->success($balance);
|
||||
}
|
||||
|
||||
return view('balances.process', compact('balance'));
|
||||
|
||||
// try {
|
||||
// $data = Pay::alipay()->callback();
|
||||
// } catch (InvalidResponseException $e) {
|
||||
// return $this->error('支付失败');
|
||||
// }
|
||||
|
||||
// // 检测 out_trade_no 是否为商户系统中创建的订单号
|
||||
// if ($data->out_trade_no != $balances->order_id) {
|
||||
// return $this->error('订单号不一致');
|
||||
// }
|
||||
|
||||
// if ((int) $data->total_amount != (int) $balances->amount) {
|
||||
// throw new ChargeException('金额不一致');
|
||||
// }
|
||||
|
||||
// // 验证 商户
|
||||
// if ($data['app_id'] != config('balances.alipay.default.app_id')) {
|
||||
// throw new ChargeException('商户不匹配');
|
||||
// }
|
||||
|
||||
//
|
||||
// if ((new \App\Jobs\CheckAndChargeBalance())->checkAndCharge($balance, true)) {
|
||||
// return view('pay_process');
|
||||
// } else {
|
||||
// abort(500, '支付失败');
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,6 +12,7 @@ class VerifyCsrfToken extends Middleware
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
// notify
|
||||
'balances/notify/*',
|
||||
];
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Yansongda\LaravelPay\Facades\Pay;
|
||||
use Yansongda\Pay\Exception\InvalidResponseException;
|
||||
|
||||
class CheckAndChargeBalance extends Job
|
||||
{
|
||||
@ -62,7 +61,7 @@ public function checkAndCharge(Balance $balance, $check = false): bool
|
||||
$balance->update([
|
||||
'paid_at' => now()
|
||||
]);
|
||||
} catch (InvalidResponseException|ChargeException $e) {
|
||||
} catch (ChargeException $e) {
|
||||
Log::error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Eloquent;
|
||||
use GeneaLabs\LaravelModelCaching\CachedBuilder;
|
||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo as BelongsToAlias;
|
||||
use Illuminate\Support\Carbon;
|
||||
use function auth;
|
||||
|
||||
/**
|
||||
@ -19,47 +21,47 @@
|
||||
* @property string $remaining_amount
|
||||
* @property string|null $paid_at
|
||||
* @property int|null $user_id
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\User|null $user
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance all($columns = [])
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance avg($column)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance cache(array $tags = [])
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance cachedValue(array $arguments, string $cacheKey)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance count($columns = '*')
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance disableCache()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance disableModelCaching()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance exists()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance flushCache(array $tags = [])
|
||||
* @property Carbon|null $created_at
|
||||
* @property Carbon|null $updated_at
|
||||
* @property-read User|null $user
|
||||
* @method static CachedBuilder|Balance all($columns = [])
|
||||
* @method static CachedBuilder|Balance avg($column)
|
||||
* @method static CachedBuilder|Balance cache(array $tags = [])
|
||||
* @method static CachedBuilder|Balance cachedValue(array $arguments, string $cacheKey)
|
||||
* @method static CachedBuilder|Balance count($columns = '*')
|
||||
* @method static CachedBuilder|Balance disableCache()
|
||||
* @method static CachedBuilder|Balance disableModelCaching()
|
||||
* @method static CachedBuilder|Balance exists()
|
||||
* @method static CachedBuilder|Balance flushCache(array $tags = [])
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance
|
||||
* getModelCacheCooldown(\Illuminate\Database\Eloquent\Model $instance)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance inRandomOrder($seed = '')
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance insert(array $values)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance isCachable()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance max($column)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance min($column)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance newModelQuery()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance newQuery()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance query()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance sum($column)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance thisUser()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance truncate()
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereAmount($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereCreatedAt($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereId($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereOrderId($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance wherePaidAt($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance wherePayment($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereRemainingAmount($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereTradeId($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereUpdatedAt($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance whereUserId($value)
|
||||
* @method static \GeneaLabs\LaravelModelCaching\CachedBuilder|Balance withCacheCooldownSeconds(?int $seconds = null)
|
||||
* @mixin \Eloquent
|
||||
* @method static CachedBuilder|Balance inRandomOrder($seed = '')
|
||||
* @method static CachedBuilder|Balance insert(array $values)
|
||||
* @method static CachedBuilder|Balance isCachable()
|
||||
* @method static CachedBuilder|Balance max($column)
|
||||
* @method static CachedBuilder|Balance min($column)
|
||||
* @method static CachedBuilder|Balance newModelQuery()
|
||||
* @method static CachedBuilder|Balance newQuery()
|
||||
* @method static CachedBuilder|Balance query()
|
||||
* @method static CachedBuilder|Balance sum($column)
|
||||
* @method static CachedBuilder|Balance thisUser()
|
||||
* @method static CachedBuilder|Balance truncate()
|
||||
* @method static CachedBuilder|Balance whereAmount($value)
|
||||
* @method static CachedBuilder|Balance whereCreatedAt($value)
|
||||
* @method static CachedBuilder|Balance whereId($value)
|
||||
* @method static CachedBuilder|Balance whereOrderId($value)
|
||||
* @method static CachedBuilder|Balance wherePaidAt($value)
|
||||
* @method static CachedBuilder|Balance wherePayment($value)
|
||||
* @method static CachedBuilder|Balance whereRemainingAmount($value)
|
||||
* @method static CachedBuilder|Balance whereTradeId($value)
|
||||
* @method static CachedBuilder|Balance whereUpdatedAt($value)
|
||||
* @method static CachedBuilder|Balance whereUserId($value)
|
||||
* @method static CachedBuilder|Balance withCacheCooldownSeconds(?int $seconds = null)
|
||||
* @mixin Eloquent
|
||||
*/
|
||||
class Balance extends Model
|
||||
{
|
||||
use HasFactory, Cachable;
|
||||
use Cachable;
|
||||
|
||||
protected $fillable = [
|
||||
'order_id',
|
||||
@ -70,6 +72,11 @@ class Balance extends Model
|
||||
'trade_id'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'paid_at' => 'datetime',
|
||||
'amount' => 'decimal:2',
|
||||
];
|
||||
|
||||
// route key
|
||||
public function getRouteKeyName(): string
|
||||
{
|
||||
|
@ -20,6 +20,7 @@
|
||||
"laravel/tinker": "^2.7",
|
||||
"php-mqtt/laravel-client": "^1.1",
|
||||
"pusher/pusher-php-server": "^7.2",
|
||||
"simplesoftwareio/simple-qrcode": "^4.2",
|
||||
"spiral/roadrunner": "^2.8.2",
|
||||
"symfony/psr-http-message-bridge": "^2.1",
|
||||
"yansongda/laravel-pay": "~3.2.0"
|
||||
|
189
composer.lock
generated
189
composer.lock
generated
@ -4,8 +4,68 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "4753398c786d748ab94e8601f781d93a",
|
||||
"content-hash": "3bd5ef4c5c796cdfc149a5ef57641d9a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
"version": "2.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Bacon/BaconQrCode.git",
|
||||
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
|
||||
"reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"dasprid/enum": "^1.0.3",
|
||||
"ext-iconv": "*",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phly/keep-a-changelog": "^2.1",
|
||||
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||
"spatie/phpunit-snapshot-assertions": "^4.2.9",
|
||||
"squizlabs/php_codesniffer": "^3.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-imagick": "to generate QR code images"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BaconQrCode\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "BaconQrCode is a QR code generator for PHP.",
|
||||
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||
"support": {
|
||||
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
||||
"source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
|
||||
},
|
||||
"time": "2022-12-07T17:46:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.10.1",
|
||||
@ -155,6 +215,59 @@
|
||||
],
|
||||
"time": "2022-04-01T19:23:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dasprid/enum",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DASPRiD/Enum.git",
|
||||
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
|
||||
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||
"squizlabs/php_codesniffer": "^3.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DASPRiD\\Enum\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP 7.1 enum implementation",
|
||||
"keywords": [
|
||||
"enum",
|
||||
"map"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/DASPRiD/Enum/issues",
|
||||
"source": "https://github.com/DASPRiD/Enum/tree/1.0.3"
|
||||
},
|
||||
"time": "2020-10-02T16:03:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dflydev/dot-access-data",
|
||||
"version": "v3.0.1",
|
||||
@ -4608,6 +4721,80 @@
|
||||
],
|
||||
"time": "2022-11-05T23:03:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplesoftwareio/simple-qrcode",
|
||||
"version": "4.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SimpleSoftwareIO/simple-qrcode.git",
|
||||
"reference": "916db7948ca6772d54bb617259c768c9cdc8d537"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SimpleSoftwareIO/simple-qrcode/zipball/916db7948ca6772d54bb617259c768c9cdc8d537",
|
||||
"reference": "916db7948ca6772d54bb617259c768c9cdc8d537",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"ext-gd": "*",
|
||||
"php": ">=7.2|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1",
|
||||
"phpunit/phpunit": "~9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-imagick": "Allows the generation of PNG QrCodes.",
|
||||
"illuminate/support": "Allows for use within Laravel."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"QrCode": "SimpleSoftwareIO\\QrCode\\Facades\\QrCode"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SimpleSoftwareIO\\QrCode\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simple Software LLC",
|
||||
"email": "support@simplesoftware.io"
|
||||
}
|
||||
],
|
||||
"description": "Simple QrCode is a QR code generator made for Laravel.",
|
||||
"homepage": "https://www.simplesoftware.io/#/docs/simple-qrcode",
|
||||
"keywords": [
|
||||
"Simple",
|
||||
"generator",
|
||||
"laravel",
|
||||
"qrcode",
|
||||
"wrapper"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/SimpleSoftwareIO/simple-qrcode/issues",
|
||||
"source": "https://github.com/SimpleSoftwareIO/simple-qrcode/tree/4.2.0"
|
||||
},
|
||||
"time": "2021-02-08T20:43:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spiral/core",
|
||||
"version": "3.4.0",
|
||||
|
@ -4,20 +4,23 @@
|
||||
|
||||
use Yansongda\Pay\Pay;
|
||||
|
||||
$secret_file = env('ALIPAY_APP_SECERT_CERT_PATH', config_path('secrets/alipayAppPriv.key'));
|
||||
if (!file_exists($secret_file)) {
|
||||
$secret_file = '';
|
||||
$alipay_app_private_key = env('ALIPAY_APP_SECERT_CERT_PATH', config_path('secrets/alipayAppPriv.key'));
|
||||
if (!file_exists($alipay_app_private_key)) {
|
||||
$alipay_app_private_key = '';
|
||||
} else {
|
||||
$secret_file = trim(file_get_contents($secret_file));
|
||||
$alipay_app_private_key = trim(file_get_contents($alipay_app_private_key));
|
||||
}
|
||||
|
||||
$wechat_pay_cert = env('WECHAT_PAY_CERT_PATH', config_path('secrets/wepay_cert.pem'));
|
||||
$wechat_pay_private_key = env('WECHAT_PAY_PRIVATE_KEY_PATH', config_path('secrets/wepay_key.pem'));
|
||||
|
||||
return [
|
||||
'alipay' => [
|
||||
'default' => [
|
||||
// 必填-支付宝分配的 app_id
|
||||
'app_id' => env('ALIPAY_APP_ID'),
|
||||
// 必填-应用私钥 字符串或路径
|
||||
'app_secret_cert' => $secret_file,
|
||||
'app_secret_cert' => $alipay_app_private_key,
|
||||
// 必填-应用公钥证书 路径
|
||||
'app_public_cert_path' => env('ALIPAY_APP_PUBLIC_CERT_PATH', config_path('secrets/appCertPublicKey.crt')),
|
||||
// 必填-支付宝公钥证书 路径
|
||||
@ -36,17 +39,17 @@
|
||||
'wechat' => [
|
||||
'default' => [
|
||||
// 必填-商户号,服务商模式下为服务商商户号
|
||||
'mch_id' => env('WECHAT_MENCENT_ID'),
|
||||
'mch_id' => env('WECHAT_MERCHANT_ID'),
|
||||
// 必填-商户秘钥
|
||||
'mch_secret_key' => '',
|
||||
'mch_secret_key' => env('WECHAT_V3_API_KEY'),
|
||||
// 必填-商户私钥 字符串或路径
|
||||
'mch_secret_cert' => '',
|
||||
'mch_secret_cert' => $wechat_pay_private_key,
|
||||
// 必填-商户公钥证书路径
|
||||
'mch_public_cert_path' => '',
|
||||
'mch_public_cert_path' => $wechat_pay_cert,
|
||||
// 必填
|
||||
'notify_url' => '',
|
||||
'notify_url' => env('WECHAT_PAY_CALLBACK_NOTIFY_URL'),
|
||||
// 选填-公众号 的 app_id
|
||||
'mp_app_id' => '',
|
||||
'mp_app_id' => env('WECHAT_MP_APP_ID'),
|
||||
// 选填-小程序 的 app_id
|
||||
'mini_app_id' => '',
|
||||
// 选填-app 的 app_id
|
||||
@ -63,10 +66,6 @@
|
||||
'sub_mini_app_id' => '',
|
||||
// 选填-服务商模式下,子商户id
|
||||
'sub_mch_id' => '',
|
||||
// 选填-微信公钥证书路径, optional,强烈建议 php-fpm 模式下配置此参数
|
||||
'wechat_public_cert_path' => [
|
||||
'45F59D4DABF31918AFCEC556D5D2C6E376675D57' => __DIR__ . '/Cert/wechatPublicKey.crt',
|
||||
],
|
||||
// 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE
|
||||
'mode' => Pay::MODE_NORMAL,
|
||||
],
|
||||
@ -87,6 +86,11 @@
|
||||
'notify_url' => '',
|
||||
],
|
||||
],
|
||||
'xunhu' => [
|
||||
'app_id' => env('XUNHU_PAY_APP_ID'),
|
||||
'app_secret' => env('XUNHU_PAY_APP_SECRET'),
|
||||
'gateway' => env('XUNHU_PAY_GATEWAY', 'https://api.xunhupay.com/payment/do.html'),
|
||||
],
|
||||
'http' => [ // optional
|
||||
'timeout' => 5.0,
|
||||
'connect_timeout' => 5.0,
|
||||
|
@ -3,16 +3,31 @@
|
||||
@section('title', '余额')
|
||||
|
||||
@section('content')
|
||||
<h2>余额</h2>
|
||||
<p>您的余额: {{ $balance }} 元 <small class="text-danger"><i class="bi bi-exclamation-circle"></i> 余额不可用于提现</small></p>
|
||||
<h1><small class="fs-5">余额</small> {{ $balance }} <small class="fs-5">元</small></h1>
|
||||
<p><small class="text-danger"><i class="bi bi-exclamation-circle"></i> 余额不可用于提现</small></p>
|
||||
|
||||
|
||||
<h2>充值余额</h2>
|
||||
<form name="charge" method="POST" target="_blank" action="{{ route('balances.store') }}"
|
||||
<form name="charge" method="POST" target="_blank" class="form-horizontal" action="{{ route('balances.store') }}"
|
||||
onsubmit="return confirm('请注意: 由于计费方式的特殊性,我们不支持退款,请合理充值。')">
|
||||
@csrf
|
||||
<input type="number" id="amount" name="amount" value="10" min="1" max="1000"/>元
|
||||
<button type="submit" class="btn btn-primary">充值</button>
|
||||
|
||||
<input type="radio" name="payment" value="wechat" checked> 微信支付
|
||||
<input type="radio" name="payment" value="alipay"> 支付宝
|
||||
|
||||
|
||||
<div class="input-group mt-2 mb-3 w-25">
|
||||
<button class="btn btn-outline-secondary" type="submit">充值</button>
|
||||
|
||||
|
||||
<input type="number" id="amount" name="amount" value="10" min="1" max="1000" class="form-control text-center"
|
||||
aria-label="充值金额">
|
||||
|
||||
<span class="input-group-text">元</span>
|
||||
|
||||
<button class="btn btn-secondary" type="submit">充值</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-2">
|
||||
|
@ -1,13 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@extends('layouts.app')
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>支付订单</title>
|
||||
</head>
|
||||
@section('title', '付款')
|
||||
|
||||
<body>
|
||||
{!! $html !!}
|
||||
</body>
|
||||
@section('content')
|
||||
|
||||
</html>
|
||||
<h1>莱云 支付处理器</h1>
|
||||
<p>此页面正在进行充值交易。</p>
|
||||
|
||||
<div id="pay">
|
||||
<p>您将要给 {{ $balance->user->name }} 充值 {{ $balance->amount }} 元。
|
||||
@if ($balance->user == auth()->user())
|
||||
这是您自己的账号。
|
||||
@else
|
||||
但是请看仔细,<span class="text-danger">这不是您自己的账号。</span>
|
||||
@endif
|
||||
|
||||
</p>
|
||||
|
||||
<p>如果您已知晓情况,请
|
||||
@php
|
||||
if ($balance->payment === 'alipay') {
|
||||
$payment = '支付宝';
|
||||
} elseif ($balance->payment === 'wechat') {
|
||||
$payment = '微信';
|
||||
} else {
|
||||
$payment = '相应的软件';
|
||||
}
|
||||
|
||||
@endphp
|
||||
使用"{{ $payment }}"扫描二维码。
|
||||
</p>
|
||||
|
||||
|
||||
{{ $qr_code }}
|
||||
</div>
|
||||
|
||||
<h3 class="text-success d-none" id="pay-success">您已成功完成支付。@auth
|
||||
我们将稍后带您去余额界面。
|
||||
@else
|
||||
您可以关闭此网页去通知 {{ $balance->user->name }} 了。
|
||||
@endauth</h3>
|
||||
<h3 class="text-danger d-none" id="pay-error">此支付出现了问题。@auth
|
||||
我们将稍后带您去余额界面。稍后请尽快联系我们。
|
||||
@else
|
||||
您可以让 {{ $balance->user->name }} 联系我们,或者您可以点击上方的"联系我们"按钮。
|
||||
@endauth</h3>
|
||||
|
||||
|
||||
<script>
|
||||
const inter = setInterval(function () {
|
||||
axios.get(location.href)
|
||||
.then(function (response) {
|
||||
if (response.data.paid_at) {
|
||||
document.getElementById('pay-success').classList.remove('d-none');
|
||||
document.getElementById('pay').classList.add('d-none');
|
||||
|
||||
clearInterval(inter);
|
||||
|
||||
@auth
|
||||
setTimeout(function () {
|
||||
location.href = '/balances';
|
||||
}, 3000);
|
||||
@endauth
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
document.getElementById('pay-error').classList.remove('d-none');
|
||||
document.getElementById('pay').classList.add('d-none');
|
||||
|
||||
@auth
|
||||
setTimeout(function () {
|
||||
location.href = '/balances';
|
||||
}, 3000);
|
||||
@endauth
|
||||
|
||||
clearInterval(inter);
|
||||
});
|
||||
}, 1500);
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
30
resources/views/contact.blade.php
Normal file
30
resources/views/contact.blade.php
Normal file
@ -0,0 +1,30 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<p>请注意,我们官方的控制面板只有一个,那就是
|
||||
<a target="_blank"
|
||||
href="https://dash.laecloud.com">dash.laecloud.com</a>。
|
||||
<br/>
|
||||
莱云 官方没有推出过任何其它形式的客户端(比如桌面客户端等)。我们不会其它形式的客户端做出任何技术支持,也不解答任何问题,也不负责任何损失。
|
||||
</p>
|
||||
|
||||
@auth
|
||||
<p>你好, {{ auth()->user()->name }}。
|
||||
<br/>
|
||||
如果您在使用一些服务方面遇到了问题,可以在"仪表盘"的菜单中的"工单",联系我们。
|
||||
<br/>
|
||||
"工单"是我们的客户支持系统,您可以在这里提交工单,我们会尽快处理。
|
||||
<br/>
|
||||
"工单"会根据您的服务,将您的工单投递到不同的部门,交给他们进行处理。
|
||||
</p>
|
||||
|
||||
@endauth
|
||||
|
||||
<p>
|
||||
您可以加入我们的 QQ 群: 769779712。
|
||||
<br/>
|
||||
请注意,我们不会在群里给任何不看《提问的智慧》的人解答问题。并且我们还会快速的把他们踢出群。
|
||||
</p>
|
||||
|
||||
@endsection
|
@ -39,10 +39,10 @@
|
||||
<!-- Left Side Of Navbar -->
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-auto" href="{{ route('index') }}">密钥</a>
|
||||
<a class="nav-link text-auto" href="{{ route('index') }}">密钥管理</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-auto" href="{{ route('balances.index') }}">余额</a>
|
||||
<a class="nav-link text-auto" href="{{ route('balances.index') }}">余额与充值</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-auto" href="{{ route('transfer') }}">转账</a>
|
||||
@ -55,6 +55,12 @@
|
||||
|
||||
<!-- Right Side Of Navbar -->
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<a class="nav-link" target="_blank"
|
||||
href="https://dash.laecloud.com">仪表盘</a>
|
||||
|
||||
<a class="nav-link"
|
||||
href="{{ route('contact') }}">联系我们</a>
|
||||
|
||||
@if (Auth::guard('admin')->check())
|
||||
|
||||
<li class="nav-item">
|
||||
|
@ -5,6 +5,16 @@
|
||||
use App\Http\Controllers\Web\TransferController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
||||
Route::get('/', [AuthController::class, 'index'])->name('index')->middleware('banned');
|
||||
|
||||
|
||||
Route::prefix('auth')->group(function () {
|
||||
Route::get('redirect', [AuthController::class, 'redirect'])->name('login');
|
||||
Route::get('callback', [AuthController::class, 'callback'])->name('callback');
|
||||
});
|
||||
|
||||
|
||||
Route::middleware(['auth', 'banned'])->group(function () {
|
||||
Route::view('banned', 'banned')->name('banned')->withoutMiddleware('banned');
|
||||
Route::post('logout', [AuthController::class, 'logout'])->name('logout')->withoutMiddleware('banned');
|
||||
@ -15,24 +25,18 @@
|
||||
|
||||
|
||||
Route::get('transactions', [BalanceController::class, 'transactions'])->name('transactions');
|
||||
Route::resource('balances', BalanceController::class);
|
||||
|
||||
Route::resource('balances', BalanceController::class)->except('show');
|
||||
Route::get('/balances/{balance}', [BalanceController::class, 'show'])->name('balances.show')->withoutMiddleware('auth');
|
||||
|
||||
Route::get('transfer', [TransferController::class, 'index'])->name('transfer');
|
||||
Route::post('transfer', [TransferController::class, 'transfer']);
|
||||
|
||||
|
||||
});
|
||||
|
||||
Route::prefix('auth')->group(function () {
|
||||
Route::get('redirect', [AuthController::class, 'redirect'])->name('login');
|
||||
Route::get('callback', [AuthController::class, 'callback'])->name('callback');
|
||||
});
|
||||
|
||||
Route::get('/', [AuthController::class, 'index'])->name('index')->middleware('banned');
|
||||
Route::view('contact', 'contact')->name('contact');
|
||||
|
||||
// 静态页面
|
||||
Route::view('not_verified', 'not_verified')->name('not_verified');
|
||||
Route::any('/balances/notify/{payment}', [BalanceController::class, 'notify'])->name('balances.notify');
|
||||
|
||||
Route::get('/balances/{balances}', [BalanceController::class, 'show'])->name('balances.balances.show');
|
||||
Route::get('/balances/alipay/notify', [BalanceController::class, 'notify'])->name('balances.alipay.notify');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user