增加 微信支付

This commit is contained in:
iVampireSP.com 2022-12-28 00:24:41 +08:00
parent 39918b3422
commit d635a5a343
No known key found for this signature in database
GPG Key ID: 2F7B001CA27A8132
13 changed files with 645 additions and 140 deletions

View 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;
}
}

View File

@ -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, '支付失败');
// }
}
/**

View File

@ -12,6 +12,7 @@ class VerifyCsrfToken extends Middleware
* @var array<int, string>
*/
protected $except = [
//
// notify
'balances/notify/*',
];
}

View File

@ -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;
}

View File

@ -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
{

View File

@ -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
View File

@ -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",

View File

@ -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,

View File

@ -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">

View File

@ -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

View 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

View File

@ -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">

View File

@ -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');