Merge pull request #439 from v2board/dev

1.5.1
This commit is contained in:
tokumeikoi 2021-05-18 21:00:54 +09:00 committed by GitHub
commit 229a3022ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1148 additions and 807 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View 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()
]);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
]);
}
}

View 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')
]
]);
}
}

View File

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

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

View File

@ -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, '令牌有误');
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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格式不正确'
];
}
}

View 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' => '过滤条件参数有误',
];
}
}

View File

@ -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' => '过滤值不能为空'
];
}
}

View File

@ -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位'
];
}
}

View File

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

View File

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

View File

@ -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
View 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'];
}

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -236,5 +236,5 @@ return [
| The only modification by laravel config
|
*/
'version' => '1.5.0'
'version' => '1.5.1.1621339182281'
];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,5 +12,7 @@ window.settings = {
color: 'default'
},
// 背景
background_url: ''
background_url: '',
// crisp
crisp_id: ''
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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' => [

View File

@ -112,6 +112,9 @@ return [
'fetch' => [
'knowledge_not_exist' => '文章不存在',
'apple_id_must_be_plan' => '无有效订阅,无法使用本站提供的 AppleID'
],
'formatAccessData' => [
'no_access' => '你必须拥有有效的订阅才可以查看该区域的内容'
]
],
'invite' => [

View File

@ -20,7 +20,8 @@
},
verison: '{{$verison}}',
background_url: '{{$backgroun_url}}',
description: '{{$description}}'
description: '{{$description}}',
crisp_id: '{{$crisp_id}}'
}
</script>
</head>

View File

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