mirror of
https://github.com/v2board/v2board.git
synced 2024-11-10 17:49:11 +08:00
commit
229a3022ac
@ -49,7 +49,7 @@ class CheckCommission extends Command
|
||||
if ((int)config('v2board.commission_auto_check_enable', 1)) {
|
||||
Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('updated_at', '<=', strtotime('-3 day', time()))
|
||||
->update([
|
||||
'commission_status' => 1
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\PaymentService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Test extends Command
|
||||
@ -37,5 +38,7 @@ class Test extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$paymentService = new PaymentService('MGate');
|
||||
var_dump($paymentService->form());
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use App\Models\StatOrder;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class V2BoardStatistics extends Command
|
||||
class V2boardStatistics extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
@ -52,11 +52,11 @@ class V2BoardStatistics extends Command
|
||||
$startAt = strtotime('-1 day', $endAt);
|
||||
$builder = Order::where('created_at', '>=', $startAt)
|
||||
->where('created_at', '<', $endAt)
|
||||
->whereIn('status', [3, 4]);
|
||||
->whereNotIn('status', [0, 2]);
|
||||
$orderCount = $builder->count();
|
||||
$orderAmount = $builder->sum('total_amount');
|
||||
$builder = $builder->where('commission_balance', '!=', NULL)
|
||||
->whereIn('commission_status', [1, 2]);
|
||||
$builder = $builder->where('commission_balance', '!=', 0)
|
||||
->where('commission_status', 0);
|
||||
$commissionCount = $builder->count();
|
||||
$commissionAmount = $builder->sum('commission_balance');
|
||||
$data = [
|
||||
|
@ -66,7 +66,8 @@ class ConfigController extends Controller
|
||||
'email_gmail_limit_enable' => config('v2board.email_gmail_limit_enable', 0),
|
||||
'recaptcha_enable' => (int)config('v2board.recaptcha_enable', 0),
|
||||
'recaptcha_key' => config('v2board.recaptcha_key'),
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key')
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
|
||||
'tos_url' => config('v2board.tos_url')
|
||||
],
|
||||
'subscribe' => [
|
||||
'plan_change_enable' => (int)config('v2board.plan_change_enable', 1),
|
||||
@ -110,7 +111,9 @@ class ConfigController extends Controller
|
||||
'frontend_theme_header' => config('v2board.frontend_theme_header', 'dark'),
|
||||
'frontend_theme_color' => config('v2board.frontend_theme_color', 'default'),
|
||||
'frontend_background_url' => config('v2board.frontend_background_url'),
|
||||
'frontend_admin_path' => config('v2board.frontend_admin_path', 'admin')
|
||||
'frontend_admin_path' => config('v2board.frontend_admin_path', 'admin'),
|
||||
'frontend_customer_service_method' => config('v2board.frontend_customer_service_method', 0),
|
||||
'frontend_customer_service_id' => config('v2board.frontend_customer_service_id'),
|
||||
],
|
||||
'server' => [
|
||||
'server_token' => config('v2board.server_token'),
|
||||
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\OrderAssign;
|
||||
use App\Http\Requests\Admin\OrderUpdate;
|
||||
use App\Http\Requests\Admin\OrderFetch;
|
||||
use App\Services\OrderService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
@ -15,25 +16,36 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
private function filter(Request $request, &$builder)
|
||||
{
|
||||
if ($request->input('filter')) {
|
||||
foreach ($request->input('filter') as $filter) {
|
||||
if ($filter['key'] === 'email') {
|
||||
$user = User::where('email', "%{$filter['value']}%")->first();
|
||||
if (!$user) continue;
|
||||
$builder->where('user_id', $user->id);
|
||||
continue;
|
||||
}
|
||||
if ($filter['condition'] === '模糊') {
|
||||
$filter['condition'] = 'like';
|
||||
$filter['value'] = "%{$filter['value']}%";
|
||||
}
|
||||
$builder->where($filter['key'], $filter['condition'], $filter['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function fetch(OrderFetch $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$orderModel = Order::orderBy('created_at', 'DESC');
|
||||
if ($request->input('trade_no')) {
|
||||
$orderModel->where('trade_no', $request->input('trade_no'));
|
||||
}
|
||||
if ($request->input('is_commission')) {
|
||||
$orderModel->where('invite_user_id', '!=', NULL);
|
||||
$orderModel->whereIn('status', [3, 4]);
|
||||
$orderModel->whereNotIn('status', [0, 2]);
|
||||
$orderModel->where('commission_balance', '>', 0);
|
||||
}
|
||||
if ($request->input('id')) {
|
||||
$orderModel->where('id', $request->input('id'));
|
||||
}
|
||||
if ($request->input('user_id')) {
|
||||
$orderModel->where('user_id', $request->input('user_id'));
|
||||
}
|
||||
$this->filter($request, $orderModel);
|
||||
$total = $orderModel->count();
|
||||
$res = $orderModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
|
78
app/Http/Controllers/Admin/PaymentController.php
Normal file
78
app/Http/Controllers/Admin/PaymentController.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Services\PaymentService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Payment;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
public function getPaymentMethods()
|
||||
{
|
||||
$methods = [];
|
||||
foreach (glob(base_path('app//Payments') . '/*.php') as $file) {
|
||||
array_push($methods, pathinfo($file)['filename']);
|
||||
}
|
||||
return response([
|
||||
'data' => $methods
|
||||
]);
|
||||
}
|
||||
|
||||
public function fetch()
|
||||
{
|
||||
$payments = Payment::all();
|
||||
foreach ($payments as $k => $v) {
|
||||
$payments[$k]['notify_url'] = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}");
|
||||
}
|
||||
return response([
|
||||
'data' => $payments
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPaymentForm(Request $request)
|
||||
{
|
||||
$paymentService = new PaymentService($request->input('payment'), $request->input('id'));
|
||||
return response([
|
||||
'data' => $paymentService->form()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$payment = Payment::find($request->input('id'));
|
||||
if (!$payment) abort(500, '支付方式不存在');
|
||||
try {
|
||||
$payment->update($request->input());
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '更新失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
if (!Payment::create([
|
||||
'name' => $request->input('name'),
|
||||
'payment' => $request->input('payment'),
|
||||
'config' => $request->input('config'),
|
||||
'uuid' => Helper::guid()
|
||||
])) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
$payment = Payment::find($request->input('id'));
|
||||
if (!$payment) abort(500, '支付方式不存在');
|
||||
return response([
|
||||
'data' => $payment->delete()
|
||||
]);
|
||||
}
|
||||
}
|
@ -2,13 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerTrojanSort;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Models\User;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -24,6 +20,7 @@ class ManageController extends Controller
|
||||
$serverService->getV2rayServers(),
|
||||
$serverService->getTrojanServers()
|
||||
);
|
||||
$serverService->mergeData($servers);
|
||||
$tmp = array_column($servers, 'sort');
|
||||
array_multisort($tmp, SORT_ASC, $servers);
|
||||
return response([
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerShadowsocksSave;
|
||||
use App\Http\Requests\Admin\ServerShadowsocksSort;
|
||||
use App\Http\Requests\Admin\ServerShadowsocksUpdate;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Utils\CacheKey;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ShadowsocksController extends Controller
|
||||
{
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerTrojanSave;
|
||||
use App\Http\Requests\Admin\ServerTrojanSort;
|
||||
use App\Http\Requests\Admin\ServerTrojanUpdate;
|
||||
use App\Services\ServerService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerTrojan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TrojanController extends Controller
|
||||
{
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerV2raySave;
|
||||
use App\Http\Requests\Admin\ServerV2raySort;
|
||||
use App\Http\Requests\Admin\ServerV2rayUpdate;
|
||||
use App\Services\ServerService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class V2rayController extends Controller
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ class StatController extends Controller
|
||||
'data' => [
|
||||
'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
@ -35,16 +35,16 @@ class StatController extends Controller
|
||||
->count(),
|
||||
'commission_pendding_total' => Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->where('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('commission_balance', '>', 0)
|
||||
->count(),
|
||||
'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d')))
|
||||
->where('created_at', '<', time())
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount')
|
||||
]
|
||||
]);
|
||||
@ -99,12 +99,13 @@ class StatController extends Controller
|
||||
'server_id',
|
||||
'server_type',
|
||||
'u',
|
||||
'd'
|
||||
'd',
|
||||
DB::raw('(u+d) as total')
|
||||
])
|
||||
->where('record_at', '>=', $timestamp)
|
||||
->where('record_type', 'd')
|
||||
->limit(10)
|
||||
->orderBy('record_at', 'DESC')
|
||||
->orderBy('total', 'DESC')
|
||||
->get()
|
||||
->toArray();
|
||||
foreach ($statistics as $k => $v) {
|
||||
@ -113,7 +114,7 @@ class StatController extends Controller
|
||||
$statistics[$k]['server_name'] = $server['name'];
|
||||
}
|
||||
}
|
||||
$statistics[$k]['total'] = ($v['u'] + $v['d']) / 1073741824;
|
||||
$statistics[$k]['total'] = $statistics[$k]['total'] / 1073741824;
|
||||
}
|
||||
array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
|
||||
return response([
|
||||
|
@ -265,4 +265,30 @@ class UserController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function setInviteUser(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|integer',
|
||||
'invite_user' => 'required',
|
||||
], [
|
||||
'user_id.required' => '用户ID不能为空',
|
||||
'user_id.integer' => '用户ID参数有误',
|
||||
'invite_user.required' => '邀请人不能为空'
|
||||
]);
|
||||
|
||||
$user = User::find($request->input('user_id'));
|
||||
if (!$user) abort(500, '用户不存在');
|
||||
if (strpos($request->input('invite_user'), '@') !== -1) {
|
||||
$inviteUser = User::where('email', $request->input('invite_user'))->first();
|
||||
} else {
|
||||
$inviteUser = User::find($request->input('invite_user'));
|
||||
}
|
||||
if (!$inviteUser) abort(500, '邀请人不存在');
|
||||
$user->invite_user_id = $inviteUser->id;
|
||||
|
||||
return response([
|
||||
'data' => $user->save()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
18
app/Http/Controllers/Guest/CommController.php
Normal file
18
app/Http/Controllers/Guest/CommController.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Utils\Dict;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class CommController extends Controller
|
||||
{
|
||||
public function config()
|
||||
{
|
||||
return response([
|
||||
'data' => [
|
||||
'tos_url' => config('v2board.tos_url')
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Services\OrderService;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Order;
|
||||
use Library\Epay;
|
||||
use Omnipay\Omnipay;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Library\BitpayX;
|
||||
use Library\MGate;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function alipayNotify(Request $request)
|
||||
{
|
||||
if (!(int)config('v2board.alipay_enable')) {
|
||||
die('fail');
|
||||
}
|
||||
// Log::info('alipayNotifyData: ' . json_encode($_POST));
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
$gateway->setPrivateKey(config('v2board.alipay_privkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey(config('v2board.alipay_pubkey')); // 可以是路径,也可以是密钥内容
|
||||
$request = $gateway->completePurchase();
|
||||
$request->setParams($_POST); //Optional
|
||||
try {
|
||||
/** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
|
||||
$response = $request->send();
|
||||
|
||||
if ($response->isPaid()) {
|
||||
/**
|
||||
* Payment is successful
|
||||
*/
|
||||
if (!$this->handle($_POST['out_trade_no'], $_POST['trade_no'])) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
|
||||
die('success'); //The response should be 'success' only
|
||||
} else {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
die('fail');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
die('fail');
|
||||
}
|
||||
}
|
||||
|
||||
public function stripeNotify(Request $request)
|
||||
{
|
||||
// Log::info('stripeNotifyData: ' . json_encode($request->input()));
|
||||
|
||||
if (!(int)config('v2board.stripe_alipay_enable') && !(int)config('v2board.stripe_wepay_enable')) {
|
||||
die('fail');
|
||||
}
|
||||
\Stripe\Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
config('v2board.stripe_webhook_key')
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
die('success');
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
if (!$tradeNo) {
|
||||
abort(500, 'trade no is not found in metadata');
|
||||
}
|
||||
if (!$this->handle($tradeNo, $object->balance_transaction)) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
}
|
||||
|
||||
public function bitpayXNotify(Request $request)
|
||||
{
|
||||
if (!(int)config('v2board.bitpayx_enable')) {
|
||||
die('fail');
|
||||
}
|
||||
$inputString = file_get_contents('php://input', 'r');
|
||||
// Log::info('bitpayXNotifyData: ' . $inputString);
|
||||
$inputStripped = str_replace(array("\r", "\n", "\t", "\v"), '', $inputString);
|
||||
$inputJSON = json_decode($inputStripped, true); //convert JSON into array
|
||||
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'status' => $inputJSON['status'],
|
||||
'order_id' => $inputJSON['order_id'],
|
||||
'merchant_order_id' => $inputJSON['merchant_order_id'],
|
||||
'price_amount' => $inputJSON['price_amount'],
|
||||
'price_currency' => $inputJSON['price_currency'],
|
||||
'pay_amount' => $inputJSON['pay_amount'],
|
||||
'pay_currency' => $inputJSON['pay_currency'],
|
||||
'created_at_t' => $inputJSON['created_at_t']
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($inputJSON['merchant_order_id']);
|
||||
if (!$bitpayX->verify($strToSign, $inputJSON['token'])) {
|
||||
abort(500, 'sign error');
|
||||
}
|
||||
if ($params['status'] !== 'PAID') {
|
||||
abort(500, 'order is not paid');
|
||||
}
|
||||
if (!$this->handle($params['merchant_order_id'], $params['order_id'])) {
|
||||
abort(500, 'order process fail');
|
||||
}
|
||||
die(json_encode([
|
||||
'status' => 200
|
||||
]));
|
||||
}
|
||||
|
||||
public function mgateNotify(Request $request)
|
||||
{
|
||||
if (!(int)config('v2board.mgate_enable')) {
|
||||
die('fail');
|
||||
}
|
||||
$mgate = new MGate(config('v2board.mgate_url'), config('v2board.mgate_app_id'), config('v2board.mgate_app_secret'));
|
||||
if (!$mgate->verify($request->input())) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
if (!$this->handle($request->input('out_trade_no'), $request->input('trade_no'))) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
public function epayNotify(Request $request)
|
||||
{
|
||||
if (!(int)config('v2board.epay_enable')) {
|
||||
die('fail');
|
||||
}
|
||||
$epay = new Epay(config('v2board.epay_url'), config('v2board.epay_pid'), config('v2board.epay_key'));
|
||||
if (!$epay->verify($request->input())) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
if (!$this->handle($request->input('out_trade_no'), $request->input('trade_no'))) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function handle($tradeNo, $callbackNo)
|
||||
{
|
||||
$order = Order::where('trade_no', $tradeNo)->first();
|
||||
if ($order->status === 1) return true;
|
||||
if (!$order) {
|
||||
abort(500, 'order is not found');
|
||||
}
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->success($callbackNo)) {
|
||||
return false;
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$message = sprintf(
|
||||
"💰成功收款%s元\n———————————————\n订单号:%s",
|
||||
$order->total_amount / 100,
|
||||
$order->trade_no
|
||||
);
|
||||
$telegramService->sendMessageWithAdmin($message);
|
||||
return true;
|
||||
}
|
||||
}
|
49
app/Http/Controllers/Guest/PaymentController.php
Normal file
49
app/Http/Controllers/Guest/PaymentController.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
public function notify($method, $uuid, Request $request)
|
||||
{
|
||||
try {
|
||||
$paymentService = new PaymentService($method, null, $uuid);
|
||||
$verify = $paymentService->notify($request->input());
|
||||
if (!$verify) abort(500, 'verify error');
|
||||
if (!$this->handle($verify['trade_no'], $verify['callback_no'])) {
|
||||
abort(500, 'handle error');
|
||||
}
|
||||
die('success');
|
||||
} catch (\Exception $e) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
}
|
||||
|
||||
private function handle($tradeNo, $callbackNo)
|
||||
{
|
||||
$order = Order::where('trade_no', $tradeNo)->first();
|
||||
if (!$order) {
|
||||
abort(500, 'order is not found');
|
||||
}
|
||||
if ($order->status === 1) return true;
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->success($callbackNo)) {
|
||||
return false;
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$message = sprintf(
|
||||
"💰成功收款%s元\n———————————————\n订单号:%s",
|
||||
$order->total_amount / 100,
|
||||
$order->trade_no
|
||||
);
|
||||
$telegramService->sendMessageWithAdmin($message);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -131,7 +131,8 @@ class AuthController extends Controller
|
||||
}
|
||||
|
||||
$data = [
|
||||
'token' => $user->token
|
||||
'token' => $user->token,
|
||||
'auth_data' => base64_encode("{$user->email}:{$user->password}")
|
||||
];
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
@ -202,7 +203,10 @@ class AuthController extends Controller
|
||||
|
||||
public function getQuickLoginUrl(Request $request)
|
||||
{
|
||||
$user = User::where('token', $request->input('token'))->first();
|
||||
$authData = explode(':', base64_decode($request->input('auth_data')));
|
||||
$user = User::where('email', $authData[0])
|
||||
->where('password', $authData[1])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, '令牌有误');
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ class DeepbworkController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
@ -67,6 +67,7 @@ class PoseidonController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
|
@ -70,6 +70,7 @@ class ShadowsocksTidalabController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
@ -71,6 +71,7 @@ class TrojanTidalabController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Dict;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -19,4 +20,16 @@ class CommController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function getStripePublicKey(Request $request)
|
||||
{
|
||||
$payment = Payment::where('id', $request->input('id'))
|
||||
->where('payment', 'StripeCredit')
|
||||
->first();
|
||||
if (!$payment) abort(500, 'payment is not found');
|
||||
$config = json_decode($payment->config, true);
|
||||
return response([
|
||||
'data' => $config['stripe_pk_live']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,14 @@ class KnowledgeController extends Controller
|
||||
if (!$knowledge) abort(500, __('user.knowledge.fetch.knowledge_not_exist'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$userService = new UserService();
|
||||
$appleId = $userService->isAvailable($user) ? config('v2board.apple_id') : __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$appleIdPassword = $userService->isAvailable($user) ? config('v2board.apple_id_password') : __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
if ($userService->isAvailable($user)) {
|
||||
$appleId = config('v2board.apple_id');
|
||||
$appleIdPassword = config('v2board.apple_id_password');
|
||||
} else {
|
||||
$appleId = __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$appleIdPassword = __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$this->formatAccessData($knowledge['body']);
|
||||
}
|
||||
$subscribeUrl = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
$knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
|
||||
$knowledge['body'] = str_replace('{{appleId}}', $appleId, $knowledge['body']);
|
||||
@ -51,4 +57,13 @@ class KnowledgeController extends Controller
|
||||
'data' => $knowledges
|
||||
]);
|
||||
}
|
||||
|
||||
private function formatAccessData(&$body)
|
||||
{
|
||||
function getBetween($input, $start, $end){$substr = substr($input, strlen($start)+strpos($input, $start),(strlen($input) - strpos($input, $end))*(-1));return $substr;}
|
||||
$accessData = getBetween($body, '<!--access start-->', '<!--access end-->');
|
||||
if ($accessData) {
|
||||
$body = str_replace($accessData, '<div class="v2board-no-access">'. __('user.knowledge.formatAccessData.no_access') .'</div>', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\OrderSave;
|
||||
use App\Models\Payment;
|
||||
use App\Services\CouponService;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@ -40,7 +42,7 @@ class OrderController extends Controller
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
'data' => $order->makeHidden(['id', 'user_id'])
|
||||
]);
|
||||
}
|
||||
|
||||
@ -175,71 +177,20 @@ class OrderController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
switch ($method) {
|
||||
// return type => 0: QRCode / 1: URL / 2: No action
|
||||
case 0:
|
||||
// alipayF2F
|
||||
if (!(int)config('v2board.alipay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->alipayF2F($tradeNo, $order->total_amount)
|
||||
]);
|
||||
case 2:
|
||||
// stripeAlipay
|
||||
if (!(int)config('v2board.stripe_alipay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->stripeAlipay($order)
|
||||
]);
|
||||
case 3:
|
||||
// stripeWepay
|
||||
if (!(int)config('v2board.stripe_wepay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->stripeWepay($order)
|
||||
]);
|
||||
case 4:
|
||||
// bitpayX
|
||||
if (!(int)config('v2board.bitpayx_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->bitpayX($order)
|
||||
]);
|
||||
case 5:
|
||||
if (!(int)config('v2board.mgate_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->mgate($order)
|
||||
]);
|
||||
case 6:
|
||||
if (!(int)config('v2board.stripe_card_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 2,
|
||||
'data' => $this->stripeCard($order, $request->input('token'))
|
||||
]);
|
||||
case 7:
|
||||
if (!(int)config('v2board.epay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->epay($order)
|
||||
]);
|
||||
default:
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
$payment = Payment::find($method);
|
||||
if (!$payment || $payment->enable !== 1) abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
$paymentService = new PaymentService($payment->payment, $payment->id);
|
||||
$result = $paymentService->pay([
|
||||
'trade_no' => $tradeNo,
|
||||
'total_amount' => $order->total_amount,
|
||||
'user_id' => $order->user_id,
|
||||
'stripe_token' => $request->input('token')
|
||||
]);
|
||||
$order->update(['payment_id' => $method]);
|
||||
return response([
|
||||
'type' => $result['type'],
|
||||
'data' => $result['data']
|
||||
]);
|
||||
}
|
||||
|
||||
public function check(Request $request)
|
||||
@ -258,65 +209,15 @@ class OrderController extends Controller
|
||||
|
||||
public function getPaymentMethod()
|
||||
{
|
||||
$data = [];
|
||||
if ((int)config('v2board.alipay_enable')) {
|
||||
$alipayF2F = new \StdClass();
|
||||
$alipayF2F->name = '支付宝';
|
||||
$alipayF2F->method = 0;
|
||||
$alipayF2F->icon = 'alipay';
|
||||
array_push($data, $alipayF2F);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_alipay_enable')) {
|
||||
$stripeAlipay = new \StdClass();
|
||||
$stripeAlipay->name = '支付宝';
|
||||
$stripeAlipay->method = 2;
|
||||
$stripeAlipay->icon = 'alipay';
|
||||
array_push($data, $stripeAlipay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_wepay_enable')) {
|
||||
$stripeWepay = new \StdClass();
|
||||
$stripeWepay->name = '微信';
|
||||
$stripeWepay->method = 3;
|
||||
$stripeWepay->icon = 'wechat';
|
||||
array_push($data, $stripeWepay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.bitpayx_enable')) {
|
||||
$bitpayX = new \StdClass();
|
||||
$bitpayX->name = config('v2board.bitpayx_name', '在线支付');
|
||||
$bitpayX->method = 4;
|
||||
$bitpayX->icon = 'wallet';
|
||||
array_push($data, $bitpayX);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.mgate_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = config('v2board.mgate_name', '在线支付');
|
||||
$obj->method = 5;
|
||||
$obj->icon = 'wallet';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_card_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = '信用卡';
|
||||
$obj->method = 6;
|
||||
$obj->icon = 'card';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.epay_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = config('v2board.epay_name', '在线支付');
|
||||
$obj->method = 7;
|
||||
$obj->icon = 'wallet';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
$methods = Payment::select([
|
||||
'id',
|
||||
'name',
|
||||
'payment'
|
||||
])
|
||||
->where('enable', 1)->get();
|
||||
|
||||
return response([
|
||||
'data' => $data
|
||||
'data' => $methods
|
||||
]);
|
||||
}
|
||||
|
||||
@ -342,157 +243,4 @@ class OrderController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
private function alipayF2F($tradeNo, $totalAmount)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
$gateway->setPrivateKey(config('v2board.alipay_privkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey(config('v2board.alipay_pubkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setNotifyUrl(url('/api/v1/guest/order/alipayNotify'));
|
||||
$request = $gateway->purchase();
|
||||
$request->setBizContent([
|
||||
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
|
||||
'out_trade_no' => $tradeNo,
|
||||
'total_amount' => $totalAmount / 100
|
||||
]);
|
||||
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
|
||||
$response = $request->send();
|
||||
$result = $response->getAlipayResponse();
|
||||
if ($result['code'] !== '10000') {
|
||||
abort(500, $result['sub_msg']);
|
||||
}
|
||||
// 获取收款二维码内容
|
||||
return $response->getQrCode();
|
||||
}
|
||||
|
||||
private function stripeAlipay($order)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'alipay',
|
||||
'statement_descriptor' => $order->trade_no,
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
]);
|
||||
if (!$source['redirect']['url']) {
|
||||
abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
|
||||
}
|
||||
return $source['redirect']['url'];
|
||||
}
|
||||
|
||||
private function stripeWepay($order)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeWepay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'wechat',
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
]);
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, __('user.order.stripeWepay.gateway_request_failed'));
|
||||
}
|
||||
return $source['wechat']['qr_code_url'];
|
||||
}
|
||||
|
||||
private function stripeCard($order, string $token)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeCard.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
try {
|
||||
$charge = \Stripe\Charge::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => $currency,
|
||||
'source' => $token,
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, __('user.order.stripeCard.was_problem'));
|
||||
}
|
||||
info($charge);
|
||||
if (!$charge->paid) {
|
||||
abort(500, __('user.order.stripeCard.deduction_failed'));
|
||||
}
|
||||
return $charge->paid;
|
||||
}
|
||||
|
||||
private function bitpayX($order)
|
||||
{
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'merchant_order_id' => $order->trade_no,
|
||||
'price_amount' => $order->total_amount / 100,
|
||||
'price_currency' => 'CNY',
|
||||
'title' => '支付单号:' . $order->trade_no,
|
||||
'description' => '充值:' . $order->total_amount / 100 . ' 元',
|
||||
'callback_url' => url('/api/v1/guest/order/bitpayXNotify'),
|
||||
'success_url' => config('v2board.app_url', env('APP_URL')) . '/#/order',
|
||||
'cancel_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($params['merchant_order_id']);
|
||||
$params['token'] = $bitpayX->sign($strToSign);
|
||||
$result = $bitpayX->mprequest($params);
|
||||
// Log::info('bitpayXSubmit: ' . json_encode($result));
|
||||
return isset($result['payment_url']) ? $result['payment_url'] : false;
|
||||
}
|
||||
|
||||
private function mgate($order)
|
||||
{
|
||||
$mgate = new MGate(config('v2board.mgate_url'), config('v2board.mgate_app_id'), config('v2board.mgate_app_secret'));
|
||||
$result = $mgate->pay([
|
||||
'app_id' => config('v2board.mgate_app_id'),
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'total_amount' => $order->total_amount,
|
||||
'notify_url' => url('/api/v1/guest/order/mgateNotify'),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function epay($order)
|
||||
{
|
||||
$epay = new Epay(config('v2board.epay_url'), config('v2board.epay_pid'), config('v2board.epay_key'));
|
||||
return $epay->pay([
|
||||
'money' => $order->total_amount / 100,
|
||||
'name' => $order->trade_no,
|
||||
'notify_url' => url('/api/v1/guest/order/epayNotify'),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order',
|
||||
'out_trade_no' => $order->trade_no
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,11 @@ class User
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->input('access_token')) {
|
||||
$user = \App\Models\User::where('token', $request->input('access_token'))->first();
|
||||
if ($request->input('auth_data')) {
|
||||
$authData = explode(':', base64_decode($request->input('auth_data')));
|
||||
$user = \App\Models\User::where('password', $authData[1])
|
||||
->where('email', $authData[0])
|
||||
->first();
|
||||
if ($user) {
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
|
@ -41,6 +41,7 @@ class ConfigSave extends FormRequest
|
||||
'recaptcha_enable' => 'in:0,1',
|
||||
'recaptcha_key' => '',
|
||||
'recaptcha_site_key' => '',
|
||||
'tos_url' => 'nullable|url',
|
||||
// subscribe
|
||||
'plan_change_enable' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1',
|
||||
@ -87,6 +88,8 @@ class ConfigSave extends FormRequest
|
||||
'frontend_theme_color' => 'in:default,darkblue,black',
|
||||
'frontend_background_url' => 'nullable|url',
|
||||
'frontend_admin_path' => '',
|
||||
'frontend_customer_service_method' => '',
|
||||
'frontend_customer_service_id' => '',
|
||||
// tutorial
|
||||
'apple_id' => 'nullable|email',
|
||||
'apple_id_password' => '',
|
||||
@ -118,7 +121,9 @@ class ConfigSave extends FormRequest
|
||||
// illiteracy prompt
|
||||
return [
|
||||
'app_url.url' => '站点URL格式不正确,必须携带http(s)://',
|
||||
'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://'
|
||||
'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://',
|
||||
'server_token.min' => '通讯密钥长度必须大于16位',
|
||||
'tos_url.url' => '服务条款URL格式不正确'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
32
app/Http/Requests/Admin/OrderFetch.php
Normal file
32
app/Http/Requests/Admin/OrderFetch.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderFetch extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'filter.*.key' => 'required|in:email,trade_no,status,commission_status,user_id,invite_user_id',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
|
||||
'filter.*.value' => ''
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'filter.*.key.required' => '过滤键不能为空',
|
||||
'filter.*.key.in' => '过滤键参数有误',
|
||||
'filter.*.condition.required' => '过滤条件不能为空',
|
||||
'filter.*.condition.in' => '过滤条件参数有误',
|
||||
];
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ class UserFetch extends FormRequest
|
||||
{
|
||||
return [
|
||||
'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
|
||||
'filter.*.value' => 'required'
|
||||
];
|
||||
}
|
||||
@ -23,6 +23,11 @@ class UserFetch extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'filter.*.key.required' => '过滤键不能为空',
|
||||
'filter.*.key.in' => '过滤键参数有误',
|
||||
'filter.*.condition.required' => '过滤条件不能为空',
|
||||
'filter.*.condition.in' => '过滤条件参数有误',
|
||||
'filter.*.value.required' => '过滤值不能为空'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class UserUpdate extends FormRequest
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email',
|
||||
'password' => 'nullable',
|
||||
'password' => 'nullable|min:8',
|
||||
'transfer_enable' => 'numeric',
|
||||
'expired_at' => 'nullable|integer',
|
||||
'banned' => 'required|in:0,1',
|
||||
@ -57,7 +57,8 @@ class UserUpdate extends FormRequest
|
||||
'u.integer' => '上行流量格式不正确',
|
||||
'd.integer' => '下行流量格式不正确',
|
||||
'balance.integer' => '余额格式不正确',
|
||||
'commission_balance.integer' => '佣金格式不正确'
|
||||
'commission_balance.integer' => '佣金格式不正确',
|
||||
'password.min' => '密码长度最小8位'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ class AdminRoute
|
||||
$router->post('/user/sendMail', 'Admin\\UserController@sendMail');
|
||||
$router->post('/user/ban', 'Admin\\UserController@ban');
|
||||
$router->post('/user/resetSecret', 'Admin\\UserController@resetSecret');
|
||||
$router->post('/user/setInviteUser', 'Admin\\UserController@setInviteUser');
|
||||
// StatOrder
|
||||
$router->get ('/stat/getOverride', 'Admin\\StatController@getOverride');
|
||||
$router->get ('/stat/getServerLastRank', 'Admin\\StatController@getServerLastRank');
|
||||
@ -98,6 +99,12 @@ class AdminRoute
|
||||
$router->post('/knowledge/show', 'Admin\\KnowledgeController@show');
|
||||
$router->post('/knowledge/drop', 'Admin\\KnowledgeController@drop');
|
||||
$router->post('/knowledge/sort', 'Admin\\KnowledgeController@sort');
|
||||
// Payment
|
||||
$router->get ('/payment/fetch', 'Admin\\PaymentController@fetch');
|
||||
$router->get ('/payment/getPaymentMethods', 'Admin\\PaymentController@getPaymentMethods');
|
||||
$router->post('/payment/getPaymentForm', 'Admin\\PaymentController@getPaymentForm');
|
||||
$router->post('/payment/save', 'Admin\\PaymentController@save');
|
||||
$router->post('/payment/drop', 'Admin\\PaymentController@drop');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,12 @@ class GuestRoute
|
||||
], function ($router) {
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Guest\\PlanController@fetch');
|
||||
// Order
|
||||
$router->post('/order/alipayNotify', 'Guest\\OrderController@alipayNotify');
|
||||
$router->post('/order/stripeNotify', 'Guest\\OrderController@stripeNotify');
|
||||
$router->post('/order/bitpayXNotify', 'Guest\\OrderController@bitpayXNotify');
|
||||
$router->post('/order/mgateNotify', 'Guest\\OrderController@mgateNotify');
|
||||
$router->post('/order/epayNotify', 'Guest\\OrderController@epayNotify');
|
||||
// Telegram
|
||||
$router->post('/telegram/webhook', 'Guest\\TelegramController@webhook');
|
||||
// Payment
|
||||
$router->match(['get', 'post'], '/payment/notify/{method}/{uuid}', 'Guest\\PaymentController@notify');
|
||||
// Comm
|
||||
$router->get ('/comm/config', 'Guest\\CommController@config');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ class UserRoute
|
||||
$router->get ('/telegram/getBotInfo', 'User\\TelegramController@getBotInfo');
|
||||
// Comm
|
||||
$router->get ('/comm/config', 'User\\CommController@config');
|
||||
$router->Post('/comm/getStripePublicKey', 'User\\CommController@getStripePublicKey');
|
||||
// Knowledge
|
||||
$router->get ('/knowledge/fetch', 'User\\KnowledgeController@fetch');
|
||||
$router->get ('/knowledge/getCategory', 'User\\KnowledgeController@getCategory');
|
||||
|
12
app/Models/Payment.php
Normal file
12
app/Models/Payment.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Payment extends Model
|
||||
{
|
||||
protected $table = 'v2_payment';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
}
|
96
app/Payments/AlipayF2F.php
Normal file
96
app/Payments/AlipayF2F.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Omnipay\Omnipay;
|
||||
|
||||
class AlipayF2F {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'app_id' => [
|
||||
'label' => '支付宝APPID',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'private_key' => [
|
||||
'label' => '支付宝私钥',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'public_key' => [
|
||||
'label' => '支付宝公钥',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setNotifyUrl($order['notify_url']);
|
||||
$request = $gateway->purchase();
|
||||
$request->setBizContent([
|
||||
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'total_amount' => $order['total_amount'] / 100
|
||||
]);
|
||||
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
|
||||
$response = $request->send();
|
||||
$result = $response->getAlipayResponse();
|
||||
if ($result['code'] !== '10000') {
|
||||
abort(500, $result['sub_msg']);
|
||||
}
|
||||
return [
|
||||
'type' => 0, // 0:qrcode 1:url
|
||||
'data' => $response->getQrCode()
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
|
||||
$request = $gateway->completePurchase();
|
||||
$request->setParams($_POST); //Optional
|
||||
try {
|
||||
/** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
|
||||
$response = $request->send();
|
||||
if ($response->isPaid()) {
|
||||
/**
|
||||
* Payment is successful
|
||||
*/
|
||||
return [
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'callback_no' => $params['trade_no']
|
||||
];
|
||||
} else {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
69
app/Payments/EPay.php
Normal file
69
app/Payments/EPay.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Payments;
|
||||
|
||||
class EPay {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'url' => [
|
||||
'label' => 'URL',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'pid' => [
|
||||
'label' => 'PID',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'key' => [
|
||||
'label' => 'KEY',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$params = [
|
||||
'money' => $order['total_amount'] / 100,
|
||||
'name' => $order['trade_no'],
|
||||
'notify_url' => $order['notify_url'],
|
||||
'return_url' => $order['return_url'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'pid' => $this->config['pid']
|
||||
];
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->config['key'];
|
||||
$params['sign'] = md5($str);
|
||||
$params['sign_type'] = 'MD5';
|
||||
return [
|
||||
'type' => 1, // 0:qrcode 1:url
|
||||
'data' => $this->config['url'] . '/submit.php?' . http_build_query($params)
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
unset($params['sign_type']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->config['key'];
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'callback_no' => $params['trade_no']
|
||||
];
|
||||
}
|
||||
}
|
90
app/Payments/MGate.php
Normal file
90
app/Payments/MGate.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use \Curl\Curl;
|
||||
|
||||
class MGate {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'mgate_url' => [
|
||||
'label' => 'API地址',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'mgate_app_id' => [
|
||||
'label' => 'APPID',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'mgate_app_secret' => [
|
||||
'label' => 'AppSecret',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$params = [
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'total_amount' => $order['total_amount'],
|
||||
'notify_url' => $order['notify_url'],
|
||||
'return_url' => $order['return_url']
|
||||
];
|
||||
$params['app_id'] = $this->config['mgate_app_id'];
|
||||
ksort($params);
|
||||
$str = http_build_query($params) . $this->config['mgate_app_secret'];
|
||||
$params['sign'] = md5($str);
|
||||
$curl = new Curl();
|
||||
$curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
|
||||
$result = $curl->response;
|
||||
if (!$result) {
|
||||
abort(500, '网络异常');
|
||||
}
|
||||
if ($curl->error) {
|
||||
if (isset($result->errors)) {
|
||||
$errors = (array)$result->errors;
|
||||
abort(500, $errors[array_keys($errors)[0]][0]);
|
||||
}
|
||||
if (isset($result->message)) {
|
||||
abort(500, $result->message);
|
||||
}
|
||||
abort(500, '未知错误');
|
||||
}
|
||||
$curl->close();
|
||||
if (!isset($result->data->trade_no)) {
|
||||
abort(500, '接口请求失败');
|
||||
}
|
||||
return [
|
||||
'type' => 1, // 0:qrcode 1:url
|
||||
'data' => $result->data->pay_url
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = http_build_query($params) . $this->config['mgate_app_secret'];
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'callback_no' => $params['trade_no']
|
||||
];
|
||||
}
|
||||
}
|
114
app/Payments/StripeAlipay.php
Normal file
114
app/Payments/StripeAlipay.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Stripe\Source;
|
||||
use Stripe\Stripe;
|
||||
|
||||
class StripeAlipay {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'currency' => [
|
||||
'label' => '货币单位',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_sk_live' => [
|
||||
'label' => 'SK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_webhook_key' => [
|
||||
'label' => 'WebHook密钥签名',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$currency = $this->config['currency'];
|
||||
$exchange = $this->exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
$source = Source::create([
|
||||
'amount' => floor($order['total_amount'] * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'alipay',
|
||||
'statement_descriptor' => $order['trade_no'],
|
||||
'metadata' => [
|
||||
'user_id' => $order['user_id'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => $order['return_url']
|
||||
]
|
||||
]);
|
||||
if (!$source['redirect']['url']) {
|
||||
abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
|
||||
}
|
||||
return [
|
||||
'type' => 1,
|
||||
'data' => $source['redirect']['url']
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
$this->config['stripe_webhook_key']
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
return [
|
||||
'trade_no' => $tradeNo,
|
||||
'callback_no' => $object->balance_transaction
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
}
|
121
app/Payments/StripeCredit.php
Normal file
121
app/Payments/StripeCredit.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Stripe\Source;
|
||||
use Stripe\Stripe;
|
||||
|
||||
class StripeCredit {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'currency' => [
|
||||
'label' => '货币单位',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_sk_live' => [
|
||||
'label' => 'SK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_pk_live' => [
|
||||
'label' => 'PK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_webhook_key' => [
|
||||
'label' => 'WebHook密钥签名',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
info($order);
|
||||
$currency = $this->config['currency'];
|
||||
$exchange = $this->exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeCard.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$charge = \Stripe\Charge::create([
|
||||
'amount' => floor($order['total_amount'] * $exchange),
|
||||
'currency' => $currency,
|
||||
'source' => $order['stripe_token'],
|
||||
'metadata' => [
|
||||
'user_id' => $order['user_id'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'identifier' => ''
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
info($e);
|
||||
abort(500, __('user.order.stripeCard.was_problem'));
|
||||
}
|
||||
if (!$charge->paid) {
|
||||
abort(500, __('user.order.stripeCard.deduction_failed'));
|
||||
}
|
||||
return [
|
||||
'type' => 2,
|
||||
'data' => $charge->paid
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
$this->config['stripe_webhook_key']
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
return [
|
||||
'trade_no' => $tradeNo,
|
||||
'callback_no' => $object->balance_transaction
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
}
|
114
app/Payments/StripeWepay.php
Normal file
114
app/Payments/StripeWepay.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Stripe\Source;
|
||||
use Stripe\Stripe;
|
||||
|
||||
class StripeWepay {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'currency' => [
|
||||
'label' => '货币单位',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_sk_live' => [
|
||||
'label' => 'SK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_webhook_key' => [
|
||||
'label' => 'WebHook密钥签名',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$currency = $this->config['currency'];
|
||||
$exchange = $this->exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
$source = Source::create([
|
||||
'amount' => floor($order['total_amount'] * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'wechat',
|
||||
'statement_descriptor' => $order['trade_no'],
|
||||
'metadata' => [
|
||||
'user_id' => $order['user_id'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => $order['return_url']
|
||||
]
|
||||
]);
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, __('user.order.stripeWepay.gateway_request_failed'));
|
||||
}
|
||||
return [
|
||||
'type' => 0,
|
||||
'data' => $source['wechat']['qr_code_url']
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
$this->config['stripe_webhook_key']
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
return [
|
||||
'trade_no' => $tradeNo,
|
||||
'callback_no' => $object->balance_transaction
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
}
|
@ -141,7 +141,7 @@ class OrderService
|
||||
private function haveValidOrder(User $user)
|
||||
{
|
||||
return Order::where('user_id', $user->id)
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->first();
|
||||
}
|
||||
|
||||
|
54
app/Services/PaymentService.php
Normal file
54
app/Services/PaymentService.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Models\Payment;
|
||||
|
||||
class PaymentService
|
||||
{
|
||||
public function __construct($method, $id = NULL, $uuid = NULL)
|
||||
{
|
||||
$this->method = $method;
|
||||
$this->class = '\\App\\Payments\\' . $this->method;
|
||||
if (!class_exists($this->class)) abort(500, 'gate is not found');
|
||||
if ($id) $payment = Payment::find($id)->toArray();
|
||||
if ($uuid) $payment = Payment::where('uuid', $uuid)->first()->toArray();
|
||||
$this->config = [];
|
||||
if (isset($payment)) {
|
||||
$this->config = json_decode($payment['config'], true);
|
||||
$this->config['enable'] = $payment['enable'];
|
||||
$this->config['id'] = $payment['id'];
|
||||
$this->config['uuid'] = $payment['uuid'];
|
||||
};
|
||||
$this->payment = new $this->class($this->config);
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
if (!$this->config['enable']) abort(500, 'gate is not enable');
|
||||
return $this->payment->notify($params);
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
return $this->payment->pay([
|
||||
'notify_url' => url("/api/v1/guest/payment/notify/{$this->method}/{$this->config['uuid']}"),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order/' . $order['trade_no'],
|
||||
'trade_no' => $order['trade_no'],
|
||||
'total_amount' => $order['total_amount'],
|
||||
'user_id' => $order['user_id'],
|
||||
'stripe_token' => $order['stripe_token']
|
||||
]);
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$form = $this->payment->form();
|
||||
$keys = array_keys($form);
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->config[$key])) $form[$key]['value'] = $this->config[$key];
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
}
|
@ -298,13 +298,6 @@ class ServerService
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server[$i]['parent_id']));
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server[$i]['id']));
|
||||
}
|
||||
$server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
@ -327,13 +320,6 @@ class ServerService
|
||||
$server[$i]['ruleSettings'] = json_decode($server[$i]['ruleSettings']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server[$i]['parent_id']));
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server[$i]['id']));
|
||||
}
|
||||
$server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
@ -347,14 +333,29 @@ class ServerService
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['parent_id']));
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['id']));
|
||||
}
|
||||
$server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
|
||||
public function mergeData(&$servers)
|
||||
{
|
||||
foreach ($servers as $k => $v) {
|
||||
$serverType = strtoupper($servers[$k]['type']);
|
||||
$servers[$k]['online'] = Cache::get(CacheKey::get("SERVER_{$serverType}_ONLINE_USER", $servers[$k]['parent_id'] ? $servers[$k]['parent_id'] : $servers[$k]['id']));
|
||||
if ($servers[$k]['parent_id']) {
|
||||
$servers[$k]['last_check_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_CHECK_AT", $servers[$k]['parent_id']));
|
||||
$servers[$k]['last_push_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_PUSH_AT", $servers[$k]['parent_id']));
|
||||
} else {
|
||||
$servers[$k]['last_check_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_CHECK_AT", $servers[$k]['id']));
|
||||
$servers[$k]['last_push_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_PUSH_AT", $servers[$k]['id']));
|
||||
}
|
||||
if ((time() - 300) >= $servers[$k]['last_check_at']) {
|
||||
$servers[$k]['available_status'] = 0;
|
||||
} else if ((time() - 300) >= $servers[$k]['last_push_at']) {
|
||||
$servers[$k]['available_status'] = 1;
|
||||
} else {
|
||||
$servers[$k]['available_status'] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,13 @@ class CacheKey
|
||||
'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间',
|
||||
'SERVER_V2RAY_ONLINE_USER' => '节点在线用户',
|
||||
'SERVER_V2RAY_LAST_CHECK_AT' => '节点最后检查时间',
|
||||
'SERVER_V2RAY_LAST_PUSH_AT' => '节点最后推送时间',
|
||||
'SERVER_TROJAN_ONLINE_USER' => 'trojan节点在线用户',
|
||||
'SERVER_TROJAN_LAST_CHECK_AT' => 'trojan节点最后检查时间',
|
||||
'SERVER_TROJAN_LAST_PUSH_AT' => 'trojan节点最后推送时间',
|
||||
'SERVER_SHADOWSOCKS_ONLINE_USER' => 'ss节点在线用户',
|
||||
'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
|
||||
'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
|
||||
'TEMP_TOKEN' => '临时令牌',
|
||||
'LAST_SEND_EMAIL_REMIND_TRAFFIC'
|
||||
];
|
||||
|
@ -25,7 +25,7 @@ class Helper
|
||||
|
||||
public static function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangeratesapi.io/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
|
@ -36,9 +36,9 @@ class URLSchemes
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => $server['port'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $user['uuid'],
|
||||
"aid" => $server['alter_id'],
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
|
@ -236,5 +236,5 @@ return [
|
||||
| The only modification by laravel config
|
||||
|
|
||||
*/
|
||||
'version' => '1.5.0'
|
||||
'version' => '1.5.1.1621339182281'
|
||||
];
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- Adminer 4.7.8 MySQL dump
|
||||
-- Adminer 4.8.0 MySQL 5.7.29 dump
|
||||
|
||||
SET NAMES utf8;
|
||||
SET time_zone = '+00:00';
|
||||
@ -14,7 +14,7 @@ CREATE TABLE `failed_jobs` (
|
||||
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@ -41,8 +41,8 @@ CREATE TABLE `v2_invite_code` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`code` char(32) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`pv` int(11) NOT NULL DEFAULT 0,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`pv` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -57,7 +57,7 @@ CREATE TABLE `v2_knowledge` (
|
||||
`title` varchar(255) NOT NULL COMMENT '標題',
|
||||
`body` text NOT NULL COMMENT '內容',
|
||||
`sort` int(11) DEFAULT NULL COMMENT '排序',
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '顯示',
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '顯示',
|
||||
`created_at` int(11) NOT NULL COMMENT '創建時間',
|
||||
`updated_at` int(11) NOT NULL COMMENT '更新時間',
|
||||
PRIMARY KEY (`id`)
|
||||
@ -70,7 +70,7 @@ CREATE TABLE `v2_mail_log` (
|
||||
`email` varchar(64) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`template_name` varchar(255) NOT NULL,
|
||||
`error` text DEFAULT NULL,
|
||||
`error` text,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -96,6 +96,7 @@ CREATE TABLE `v2_order` (
|
||||
`user_id` int(11) NOT NULL,
|
||||
`plan_id` int(11) NOT NULL,
|
||||
`coupon_id` int(11) DEFAULT NULL,
|
||||
`payment_id` int(11) DEFAULT NULL,
|
||||
`type` int(11) NOT NULL COMMENT '1新购2续费3升级',
|
||||
`cycle` varchar(255) NOT NULL,
|
||||
`trade_no` varchar(36) NOT NULL,
|
||||
@ -105,26 +106,41 @@ CREATE TABLE `v2_order` (
|
||||
`surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
|
||||
`refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
|
||||
`balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
|
||||
`surplus_order_ids` text DEFAULT NULL COMMENT '折抵订单',
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0待支付1开通中2已取消3已完成4已折抵',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0待确认1发放中2有效3无效',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT 0,
|
||||
`surplus_order_ids` text COMMENT '折抵订单',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_payment`;
|
||||
CREATE TABLE `v2_payment` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`uuid` char(32) NOT NULL,
|
||||
`payment` varchar(16) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_plan`;
|
||||
CREATE TABLE `v2_plan` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` int(11) NOT NULL,
|
||||
`transfer_enable` int(11) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`renew` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`content` text DEFAULT NULL,
|
||||
`renew` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`content` text,
|
||||
`month_price` int(11) DEFAULT NULL,
|
||||
`quarter_price` int(11) DEFAULT NULL,
|
||||
`half_year_price` int(11) DEFAULT NULL,
|
||||
@ -148,18 +164,18 @@ CREATE TABLE `v2_server` (
|
||||
`host` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`tags` varchar(255) DEFAULT NULL,
|
||||
`rate` varchar(11) NOT NULL,
|
||||
`network` text NOT NULL,
|
||||
`alter_id` int(11) NOT NULL DEFAULT 1,
|
||||
`settings` text DEFAULT NULL,
|
||||
`rules` text DEFAULT NULL,
|
||||
`networkSettings` text DEFAULT NULL,
|
||||
`tlsSettings` text DEFAULT NULL,
|
||||
`ruleSettings` text DEFAULT NULL,
|
||||
`dnsSettings` text DEFAULT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`alter_id` int(11) NOT NULL DEFAULT '1',
|
||||
`settings` text,
|
||||
`rules` text,
|
||||
`networkSettings` text,
|
||||
`tlsSettings` text,
|
||||
`ruleSettings` text,
|
||||
`dnsSettings` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
@ -206,7 +222,7 @@ CREATE TABLE `v2_server_shadowsocks` (
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`cipher` varchar(255) NOT NULL,
|
||||
`show` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`show` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
@ -225,9 +241,9 @@ CREATE TABLE `v2_server_trojan` (
|
||||
`host` varchar(255) NOT NULL COMMENT '主机名',
|
||||
`port` int(11) NOT NULL COMMENT '连接端口',
|
||||
`server_port` int(11) NOT NULL COMMENT '服务端口',
|
||||
`allow_insecure` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否允许不安全',
|
||||
`allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全',
|
||||
`server_name` varchar(255) DEFAULT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否显示',
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
@ -276,7 +292,7 @@ CREATE TABLE `v2_ticket` (
|
||||
`last_reply_user_id` int(11) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`level` tinyint(1) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0:已开启 1:已关闭',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -303,27 +319,27 @@ CREATE TABLE `v2_user` (
|
||||
`email` varchar(64) NOT NULL,
|
||||
`password` varchar(64) NOT NULL,
|
||||
`password_algo` char(10) DEFAULT NULL,
|
||||
`balance` int(11) NOT NULL DEFAULT 0,
|
||||
`balance` int(11) NOT NULL DEFAULT '0',
|
||||
`discount` int(11) DEFAULT NULL,
|
||||
`commission_rate` int(11) DEFAULT NULL,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT 0,
|
||||
`t` int(11) NOT NULL DEFAULT 0,
|
||||
`u` bigint(20) NOT NULL DEFAULT 0,
|
||||
`d` bigint(20) NOT NULL DEFAULT 0,
|
||||
`transfer_enable` bigint(20) NOT NULL DEFAULT 0,
|
||||
`banned` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`is_staff` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`t` int(11) NOT NULL DEFAULT '0',
|
||||
`u` bigint(20) NOT NULL DEFAULT '0',
|
||||
`d` bigint(20) NOT NULL DEFAULT '0',
|
||||
`transfer_enable` bigint(20) NOT NULL DEFAULT '0',
|
||||
`banned` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_staff` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`last_login_at` int(11) DEFAULT NULL,
|
||||
`last_login_ip` int(11) DEFAULT NULL,
|
||||
`uuid` varchar(36) NOT NULL,
|
||||
`group_id` int(11) DEFAULT NULL,
|
||||
`plan_id` int(11) DEFAULT NULL,
|
||||
`remind_expire` tinyint(4) DEFAULT 1,
|
||||
`remind_traffic` tinyint(4) DEFAULT 1,
|
||||
`remind_expire` tinyint(4) DEFAULT '1',
|
||||
`remind_traffic` tinyint(4) DEFAULT '1',
|
||||
`token` char(32) NOT NULL,
|
||||
`remarks` text DEFAULT NULL,
|
||||
`expired_at` bigint(20) DEFAULT 0,
|
||||
`remarks` text,
|
||||
`expired_at` bigint(20) DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
@ -331,4 +347,4 @@ CREATE TABLE `v2_user` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- 2021-01-21 14:25:17
|
||||
-- 2021-05-06 16:14:04
|
||||
|
@ -394,3 +394,20 @@ DROP `enable`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `remarks` text COLLATE 'utf8_general_ci' NULL AFTER `token`;
|
||||
|
||||
CREATE TABLE `v2_payment` (
|
||||
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`payment` varchar(16) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
) COLLATE 'utf8mb4_general_ci';
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `payment_id` int(11) NULL AFTER `coupon_id`;
|
||||
|
||||
ALTER TABLE `v2_payment`
|
||||
ADD `uuid` char(32) NOT NULL AFTER `id`;
|
||||
|
2
init.sh
2
init.sh
@ -1,3 +1,3 @@
|
||||
wget https://getcomposer.org/download/1.9.0/composer.phar
|
||||
wget https://getcomposer.org/download/2.0.13/composer.phar
|
||||
php composer.phar install -vvv
|
||||
php artisan v2board:install
|
||||
|
@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
class BitpayX
|
||||
{
|
||||
private $bitpayxAppSecret;
|
||||
private $bitpayxGatewayUri;
|
||||
|
||||
/**
|
||||
* 签名初始化
|
||||
* @param merKey 签名密钥
|
||||
*/
|
||||
public function __construct($bitpayxAppSecret)
|
||||
{
|
||||
$this->bitpayxAppSecret = $bitpayxAppSecret;
|
||||
$this->bitpayxGatewayUri = 'https://api.mugglepay.com/v1/';
|
||||
}
|
||||
|
||||
public function prepareSignId($tradeno)
|
||||
{
|
||||
$data_sign = array();
|
||||
$data_sign['merchant_order_id'] = $tradeno;
|
||||
$data_sign['secret'] = $this->bitpayxAppSecret;
|
||||
$data_sign['type'] = 'FIAT';
|
||||
ksort($data_sign);
|
||||
return http_build_query($data_sign);
|
||||
}
|
||||
|
||||
public function sign($data)
|
||||
{
|
||||
return strtolower(md5(md5($data) . $this->bitpayxAppSecret));
|
||||
}
|
||||
|
||||
public function verify($data, $signature)
|
||||
{
|
||||
$mySign = $this->sign($data);
|
||||
return $mySign === $signature;
|
||||
}
|
||||
|
||||
public function mprequest($data)
|
||||
{
|
||||
$headers = array('content-type: application/json', 'token: ' . $this->bitpayxAppSecret);
|
||||
$curl = curl_init();
|
||||
$url = $this->bitpayxGatewayUri . 'orders';
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
$data_string = json_encode($data);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return json_decode($data, true);
|
||||
}
|
||||
|
||||
public function mpcheckout($orderId, $data)
|
||||
{
|
||||
$headers = array('content-type: application/json', 'token: ' . $this->bitpayxAppSecret);
|
||||
$curl = curl_init();
|
||||
$url = $this->bitpayxGatewayUri . 'orders/' . $orderId . '/checkout';
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
$data_string = json_encode($data);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return json_decode($data, true);
|
||||
}
|
||||
|
||||
public function refund($merchantTradeNo)
|
||||
{
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buildHtml($params, $method = 'post', $target = '_self')
|
||||
{
|
||||
// var_dump($params);exit;
|
||||
$html = "<form id='submit' name='submit' action='" . $this->gatewayUri . "' method='$method' target='$target'>";
|
||||
foreach ($params as $key => $value) {
|
||||
$html .= "<input type='hidden' name='$key' value='$value'/>";
|
||||
}
|
||||
$html .= "</form><script>document.forms['submit'].submit();</script>";
|
||||
return $html;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
class Epay
|
||||
{
|
||||
private $pid;
|
||||
private $key;
|
||||
private $url;
|
||||
|
||||
public function __construct($url, $pid, $key)
|
||||
{
|
||||
$this->pid = $pid;
|
||||
$this->key = $key;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function pay($params)
|
||||
{
|
||||
$params['pid'] = $this->pid;
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->key;
|
||||
$params['sign'] = md5($str);
|
||||
$params['sign_type'] = 'MD5';
|
||||
return $this->url . '/submit.php?' . http_build_query($params);
|
||||
}
|
||||
|
||||
public function verify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
unset($params['sign_type']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->key;
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
use \Curl\Curl;
|
||||
|
||||
class MGate
|
||||
{
|
||||
private $appId;
|
||||
private $appSecret;
|
||||
private $url;
|
||||
|
||||
public function __construct($url, $appId, $appSecret)
|
||||
{
|
||||
$this->appId = $appId;
|
||||
$this->appSecret = $appSecret;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function pay($params)
|
||||
{
|
||||
ksort($params);
|
||||
$str = http_build_query($params) . $this->appSecret;
|
||||
$params['sign'] = md5($str);
|
||||
$curl = new Curl();
|
||||
$curl->post($this->url . '/v1/gateway/fetch', http_build_query($params));
|
||||
$result = $curl->response;
|
||||
if (!$result) {
|
||||
abort(500, '网络异常');
|
||||
}
|
||||
if ($curl->error) {
|
||||
if (isset($result->errors)) {
|
||||
$errors = (array)$result->errors;
|
||||
abort(500, $errors[array_keys($errors)[0]][0]);
|
||||
}
|
||||
if (isset($result->message)) {
|
||||
abort(500, $result->message);
|
||||
}
|
||||
abort(500, '未知错误');
|
||||
}
|
||||
$curl->close();
|
||||
if (!isset($result->data->trade_no)) {
|
||||
abort(500, '接口请求失败');
|
||||
}
|
||||
return $result->data->pay_url;
|
||||
}
|
||||
|
||||
public function verify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = http_build_query($params) . $this->appSecret;
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
2
public/assets/admin/components.async.js
vendored
2
public/assets/admin/components.async.js
vendored
File diff suppressed because one or more lines are too long
16
public/assets/admin/components.chunk.css
vendored
16
public/assets/admin/components.chunk.css
vendored
File diff suppressed because one or more lines are too long
2
public/assets/admin/umi.js
vendored
2
public/assets/admin/umi.js
vendored
File diff suppressed because one or more lines are too long
2
public/assets/admin/vendors.async.js
vendored
2
public/assets/admin/vendors.async.js
vendored
File diff suppressed because one or more lines are too long
2
public/assets/user/components.async.js
vendored
2
public/assets/user/components.async.js
vendored
File diff suppressed because one or more lines are too long
4
public/assets/user/env.example.js
vendored
4
public/assets/user/env.example.js
vendored
@ -12,5 +12,7 @@ window.settings = {
|
||||
color: 'default'
|
||||
},
|
||||
// 背景
|
||||
background_url: ''
|
||||
background_url: '',
|
||||
// crisp
|
||||
crisp_id: ''
|
||||
}
|
||||
|
2
public/assets/user/umi.css
vendored
2
public/assets/user/umi.css
vendored
File diff suppressed because one or more lines are too long
2
public/assets/user/umi.js
vendored
2
public/assets/user/umi.js
vendored
File diff suppressed because one or more lines are too long
2
public/assets/user/vendors.async.js
vendored
2
public/assets/user/vendors.async.js
vendored
File diff suppressed because one or more lines are too long
@ -104,7 +104,7 @@ return [
|
||||
],
|
||||
'stripeCard' => [
|
||||
'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
|
||||
'was_problem' => 'Oops, there's a problem... Please refresh the page and try again later',
|
||||
'was_problem' => "Oops, there's a problem... Please refresh the page and try again later",
|
||||
'deduction_failed' => 'Payment failed. Please check your credit card information'
|
||||
]
|
||||
],
|
||||
@ -112,6 +112,9 @@ return [
|
||||
'fetch' => [
|
||||
'knowledge_not_exist' => 'Article does not exist',
|
||||
'apple_id_must_be_plan' => 'No active subscription. Unable to use our provided Apple ID'
|
||||
],
|
||||
'formatAccessData' => [
|
||||
'no_access' => 'You must have a valid subscription to view content in this area'
|
||||
]
|
||||
],
|
||||
'invite' => [
|
||||
|
@ -112,6 +112,9 @@ return [
|
||||
'fetch' => [
|
||||
'knowledge_not_exist' => '文章不存在',
|
||||
'apple_id_must_be_plan' => '无有效订阅,无法使用本站提供的 AppleID'
|
||||
],
|
||||
'formatAccessData' => [
|
||||
'no_access' => '你必须拥有有效的订阅才可以查看该区域的内容'
|
||||
]
|
||||
],
|
||||
'invite' => [
|
||||
|
@ -20,7 +20,8 @@
|
||||
},
|
||||
verison: '{{$verison}}',
|
||||
background_url: '{{$backgroun_url}}',
|
||||
description: '{{$description}}'
|
||||
description: '{{$description}}',
|
||||
crisp_id: '{{$crisp_id}}'
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -26,7 +26,8 @@ Route::get('/', function (Request $request) {
|
||||
'theme_color' => config('v2board.frontend_theme_color', 'default'),
|
||||
'backgroun_url' => config('v2board.frontend_background_url'),
|
||||
'verison' => config('app.version'),
|
||||
'description' => config('v2board.app_description', 'V2Board is best')
|
||||
'description' => config('v2board.app_description', 'V2Board is best'),
|
||||
'crisp_id' => config('v2board.frontend_customer_service_method') === 'crisp' ? config('v2board.frontend_customer_service_id') : ''
|
||||
]);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user