瀏覽代碼

Add Changeable Currency & centralized currency exchange Api

- 添加国际化货币;
- 添加统一化汇率查询API;
BrettonYe 2 年之前
父節點
當前提交
503b64e555
共有 52 個文件被更改,包括 927 次插入638 次删除
  1. 211 0
      app/Components/CurrencyExchange.php
  2. 42 36
      app/Components/Helpers.php
  3. 2 2
      app/Http/Controllers/Admin/CouponController.php
  4. 38 38
      app/Http/Controllers/Admin/SystemController.php
  5. 4 3
      app/Http/Controllers/User/AffiliateController.php
  6. 10 3
      app/Http/Controllers/UserController.php
  7. 15 4
      app/Models/Goods.php
  8. 11 0
      app/Models/Order.php
  9. 6 0
      app/Models/Payment.php
  10. 0 14
      app/Models/ProductsPool.php
  11. 6 0
      app/Models/ReferralApply.php
  12. 11 0
      app/Models/ReferralLog.php
  13. 11 5
      app/Models/User.php
  14. 2 8
      app/Notifications/PaymentReceived.php
  15. 6 4
      app/Observers/UserObserver.php
  16. 1 1
      app/Payments/Library/Gateway.php
  17. 22 25
      app/Payments/PayPal.php
  18. 21 32
      app/Payments/Stripe.php
  19. 1 1
      app/Services/CouponService.php
  20. 14 1
      config/common.php
  21. 2 0
      config/services.php
  22. 1 1
      config/version.php
  23. 0 36
      database/migrations/2020_08_21_145711_create_products_pool_table.php
  24. 18 0
      database/migrations/2023_01_04_210048_currency_internationalization.php
  25. 1 1
      resources/lang/en/notification.php
  26. 4 4
      resources/lang/en/user.php
  27. 1 1
      resources/lang/zh_CN.json
  28. 1 1
      resources/lang/zh_CN/notification.php
  29. 4 4
      resources/lang/zh_CN/user.php
  30. 3 3
      resources/views/admin/aff/detail.blade.php
  31. 1 1
      resources/views/admin/aff/index.blade.php
  32. 173 150
      resources/views/admin/config/system.blade.php
  33. 6 2
      resources/views/admin/coupon/create.blade.php
  34. 1 1
      resources/views/admin/coupon/index.blade.php
  35. 3 3
      resources/views/admin/coupon/show.blade.php
  36. 1 1
      resources/views/admin/layouts.blade.php
  37. 1 1
      resources/views/admin/logs/callback.blade.php
  38. 2 2
      resources/views/admin/logs/order.blade.php
  39. 1 1
      resources/views/admin/shop/index.blade.php
  40. 6 2
      resources/views/admin/shop/info.blade.php
  41. 2 2
      resources/views/components/payment/detail.blade.php
  42. 2 2
      resources/views/components/system/select.blade.php
  43. 2 2
      resources/views/components/ticket/reply.blade.php
  44. 118 114
      resources/views/user/buy.blade.php
  45. 3 3
      resources/views/user/components/payment/default.blade.php
  46. 3 3
      resources/views/user/components/payment/manual.blade.php
  47. 2 2
      resources/views/user/invoiceDetail.blade.php
  48. 1 1
      resources/views/user/invoices.blade.php
  49. 14 2
      resources/views/user/layouts.blade.php
  50. 5 5
      resources/views/user/referral.blade.php
  51. 110 110
      resources/views/user/services.blade.php
  52. 1 0
      routes/user.php

+ 211 - 0
app/Components/CurrencyExchange.php

@@ -0,0 +1,211 @@
+<?php
+
+namespace App\Components;
+
+use Cache;
+use Http;
+use Log;
+
+class CurrencyExchange
+{
+    /**
+     * @param  string  $target  target Currency
+     * @param  int|float|false  $amount  exchange amount
+     * @param  string  $base  Base Currency
+     * @return false|float amount in target currency
+     */
+    public static function convert(string $target, $amount = false, string $base = 'default')
+    {
+        if ($base === 'default') {
+            $base = sysConfig('standard_currency', 'CNY');
+        }
+        $cacheKey = "Currency_{$base}_{$target}_ExRate";
+        $isStored = Cache::has($cacheKey);
+        $rate = $isStored ? Cache::get($cacheKey) : false;
+        $case = 0;
+
+        while ($case < 7 && $rate === false) {
+            switch ($case) {
+                case 0:
+                    $rate = self::exchangerateApi($base, $target);
+                    break;
+                case 1:
+                    $rate = self::k780($base, $target);
+                    break;
+                case 2:
+                    $rate = self::it120($base, $target);
+                    break;
+                case 3:
+                    $rate = self::exchangerate($base, $target);
+                    break;
+                case 4:
+                    $rate = self::fixer($base, $target);
+                    break;
+                case 5:
+                    $rate = self::currencyData($base, $target);
+                    break;
+                case 6:
+                    $rate = self::exchangeRatesData($base, $target);
+                    break;
+                default:
+                    break;
+            }
+            $case++;
+        }
+
+        if ($rate !== false) {
+            if (! $isStored) {
+                Cache::put($cacheKey, $rate, Day);
+            }
+            if ($amount === false) {
+                return $rate;
+            }
+
+            return round(($amount * $rate), 2);
+        }
+
+        return false;
+    }
+
+    private static function exchangerateApi($base, $target)
+    { // Reference: https://www.exchangerate-api.com/docs/php-currency-api
+        $response = Http::retry(3)->get('https://open.er-api.com/v6/latest/'.$base); // https://v6.exchangerate-api.com/v6/{{your token}}/latest/USD
+        if ($response->ok()) {
+            $response = $response->json();
+
+            if ($response['result'] === 'success') {
+                return $response['rates'][$target];
+            }
+            Log::emergency('[CurrencyExchange]exchangerateApi exchange failed with following message: '.$response['error-type']);
+        } else {
+            Log::emergency('[CurrencyExchange]exchangerateApi request failed '.var_export($response, true));
+        }
+
+        return false;
+    }
+
+    private static function k780($base, $target)
+    { // Reference: https://www.nowapi.com/api/finance.rate
+        $response = Http::retry(3)->get("https://sapi.k780.com/?app=finance.rate&scur={$base}&tcur={$target}&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json");
+        if ($response->ok()) {
+            $response = $response->json();
+
+            if ($response['success'] === '1') {
+                return $response['result']['rate'];
+            }
+            Log::emergency('[CurrencyExchange]Nowapi exchange failed with following message: '.$response['msg']);
+        } else {
+            Log::emergency('[CurrencyExchange]Nowapi request failed'.var_export($response, true));
+        }
+
+        return false;
+    }
+
+    private static function it120($base, $target)
+    { // Reference: https://www.it120.cc/help/fnun8g.html
+        $response = Http::retry(3)->get("https://api.it120.cc/gooking/forex/rate?fromCode={$target}&toCode={$base}");
+        if ($response->ok()) {
+            $response = $response->json();
+
+            if ($response['code'] === 0) {
+                return $response['data']['rate'];
+            }
+            Log::emergency('[CurrencyExchange]it120 exchange failed with following message: '.$response['msg']);
+        } else {
+            Log::emergency('[CurrencyExchange]it120 request failed'.var_export($response, true));
+        }
+
+        return false;
+    }
+
+    private static function exchangerate($base, $target)
+    { // Reference: https://exchangerate.host/#/
+        $response = file_get_contents("https://api.exchangerate.host/latest?symbols={$target}&base={$base}");
+        if (false !== $response) {
+            $response = json_decode($response, true);
+
+            if ($response['success'] === true) {
+                if ($response['base'] === $base) {
+                    return $response['rates'][$target];
+                }
+
+                Log::emergency('[CurrencyExchange]exchangerate exchange failed with following message: Get un-supported base currency '.$response['base']);
+            } else {
+                Log::emergency('[CurrencyExchange]exchangerate exchange failed with following message: '.$response['error-type']);
+            }
+        } else {
+            Log::emergency('[CurrencyExchange]exchangerate request failed');
+        }
+
+        return false;
+    }
+
+    private static function fixer($base, $target)
+    { // Reference: https://apilayer.com/marketplace/fixer-api
+        // RATE LIMIT: 100 Requests / Monthly
+        if (! config('services.apiLayer')) {
+            return false;
+        }
+
+        $response = Http::retry(3)->withHeaders(['apikey' => config('services.apiLayer')])->get("https://api.apilayer.com/fixer/latest?symbols={$target}&base={$base}");
+        if ($response->ok()) {
+            $response = $response->json();
+
+            if ($response['success'] === true) {
+                return $response['rates'][$target];
+            }
+
+            Log::emergency('[CurrencyExchange]Fixer exchange failed with following message: '.$response['error']['type'] ?? '');
+        } else {
+            Log::emergency('[CurrencyExchange]Fixer request failed'.var_export($response, true));
+        }
+
+        return false;
+    }
+
+    private static function currencyData($base, $target)
+    { // Reference: https://apilayer.com/marketplace/currency_data-api
+        // RATE LIMIT: 100 Requests / Monthly
+        if (! config('services.apiLayer')) {
+            return false;
+        }
+
+        $response = Http::retry(3)->withHeaders(['apikey' => config('services.apiLayer')])
+            ->get("https://api.apilayer.com/currency_data/live?source={$base}&currencies={$target}");
+        if ($response->ok()) {
+            $response = $response->json();
+
+            if ($response['success'] === true) {
+                return $response['quotes'][$base.$target];
+            }
+            Log::emergency('[CurrencyExchange]Currency Data exchange failed with following message: '.$response['error']['info'] ?? '');
+        } else {
+            Log::emergency('[CurrencyExchange]Currency Data request failed'.var_export($response, true));
+        }
+
+        return false;
+    }
+
+    private static function exchangeRatesData($base, $target)
+    { // Reference: https://apilayer.com/marketplace/exchangerates_data-api
+        // RATE LIMIT: 250 Requests / Monthly
+        if (! config('services.apiLayer')) {
+            return false;
+        }
+
+        $response = Http::retry(3)->withHeaders(['apikey' => config('services.apiLayer')])
+            ->get("https://api.apilayer.com/exchangerates_data/latest?symbols={$target}&base={$base}");
+        if ($response->ok()) {
+            $response = $response->json();
+
+            if ($response['success'] === true) {
+                return $response['rates'][$target];
+            }
+            Log::emergency('[CurrencyExchange]Exchange Rates Data exchange failed with following message: '.$response['error']['message'] ?? '');
+        } else {
+            Log::emergency('[CurrencyExchange]Exchange Rates Data request failed'.var_export($response, true));
+        }
+
+        return false;
+    }
+}

+ 42 - 36
app/Components/Helpers.php

@@ -16,32 +16,25 @@ use Str;
 
 class Helpers
 {
-    // 不生成的端口
-    private static $denyPorts = [
-        1068, 1109, 1434, 3127, 3128, 3129, 3130, 3332, 4444, 5554, 6669, 8080, 8081, 8082, 8181, 8282, 9996, 17185, 24554, 35601, 60177, 60179,
-    ];
+    private static $denyPorts = [1068, 1109, 1434, 3127, 3128, 3129, 3130, 3332, 4444, 5554, 6669, 8080, 8081, 8082, 8181, 8282, 9996, 17185, 24554, 35601, 60177, 60179]; // 不生成的端口
 
-    // 加密方式
     public static function methodList()
-    {
+    { // 加密方式
         return SsConfig::type(1)->get();
     }
 
-    // 协议
     public static function protocolList()
-    {
+    { // 协议
         return SsConfig::type(2)->get();
     }
 
-    // 混淆
     public static function obfsList()
-    {
+    { // 混淆
         return SsConfig::type(3)->get();
     }
 
-    // 生成用户的订阅码
     public static function makeSubscribeCode(): string
-    {
+    { // 生成用户的订阅码
         $code = Str::random();
         if (UserSubscribe::whereCode($code)->exists()) {
             $code = self::makeSubscribeCode();
@@ -81,9 +74,8 @@ class Helpers
         ]);
     }
 
-    // 获取一个有效端口
     public static function getPort(): int
-    {
+    { // 获取一个有效端口
         if (sysConfig('is_rand_port')) {
             $port = self::getRandPort();
         } else {
@@ -98,41 +90,22 @@ class Helpers
         return $port;
     }
 
-    // 获取一个随机端口
-    private static function getRandPort(): int
-    {
-        $port = random_int(sysConfig('min_port'), sysConfig('max_port'));
-        $exists_port = array_merge(
-            User::where('port', '<>', 0)->pluck('port')->toArray(),
-            self::$denyPorts
-        );
-
-        while (in_array($port, $exists_port, true)) {
-            $port = random_int(sysConfig('min_port'), sysConfig('max_port'));
-        }
-
-        return $port;
-    }
-
-    // 获取默认加密方式
     public static function getDefaultMethod(): string
-    {
+    { // 获取默认加密方式
         $config = SsConfig::default()->type(1)->first();
 
         return $config->name ?? 'aes-256-cfb';
     }
 
-    // 获取默认协议
     public static function getDefaultProtocol(): string
-    {
+    { // 获取默认协议
         $config = SsConfig::default()->type(2)->first();
 
         return $config->name ?? 'origin';
     }
 
-    // 获取默认混淆
     public static function getDefaultObfs(): string
-    {
+    { // 获取默认混淆
         $config = SsConfig::default()->type(3)->first();
 
         return $config->name ?? 'plain';
@@ -283,4 +256,37 @@ class Helpers
 
         $user->update(['last_login' => time()]); // 更新登录信息
     }
+
+    /**
+     * Get price with money symbol in the user's preferred currency.
+     *
+     * @param  int|float  $amount  price
+     * @return string
+     */
+    public static function getPriceTag($amount): string
+    {
+        $currentCurrency = session('currency');
+        $standard = sysConfig('standard_currency');
+        $currencyLib = array_column(config('common.currency'), 'symbol', 'code');
+        if (! empty($currentCurrency) && isset($currencyLib[$currentCurrency]) && $currentCurrency !== $standard) {
+            return $currencyLib[$currentCurrency].CurrencyExchange::convert($currentCurrency, $amount);
+        }
+
+        return $currencyLib[$standard].$amount;
+    }
+
+    private static function getRandPort(): int
+    {  // 获取一个随机端口
+        $port = random_int(sysConfig('min_port'), sysConfig('max_port'));
+        $exists_port = array_merge(
+            User::where('port', '<>', 0)->pluck('port')->toArray(),
+            self::$denyPorts
+        );
+
+        while (in_array($port, $exists_port, true)) {
+            $port = random_int(sysConfig('min_port'), sysConfig('max_port'));
+        }
+
+        return $port;
+    }
 }

+ 2 - 2
app/Http/Controllers/Admin/CouponController.php

@@ -144,7 +144,7 @@ class CouponController extends Controller
             $spreadsheet->setActiveSheetIndex(0);
             $sheet = $spreadsheet->getActiveSheet();
             $sheet->setTitle('抵用券');
-            $sheet->fromArray(['名称', '使用次数', '有效期', '券码', '金额()', '权重', '使用限制']);
+            $sheet->fromArray(['名称', '使用次数', '有效期', '券码', '金额('.array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')].')', '权重', '使用限制']);
             foreach ($voucherList as $k => $vo) {
                 $dateRange = $vo->start_time.' ~ '.$vo->end_time;
                 $sheet->fromArray([$vo->name, $vo->usable_times ?? '无限制', $dateRange, $vo->sn, $vo->value, $vo->priority, json_encode($vo->limit)], null, 'A'.($k + 2));
@@ -166,7 +166,7 @@ class CouponController extends Controller
             $spreadsheet->setActiveSheetIndex(2);
             $sheet = $spreadsheet->getActiveSheet();
             $sheet->setTitle('充值券');
-            $sheet->fromArray(['名称', '有效期', '券码', '金额()']);
+            $sheet->fromArray(['名称', '有效期', '券码', '金额('.array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')].')']);
             foreach ($refillList as $k => $vo) {
                 $dateRange = $vo->start_time.' ~ '.$vo->end_time;
                 $sheet->fromArray([$vo->name, $dateRange, $vo->sn, $vo->value], null, 'A'.($k + 2));

+ 38 - 38
app/Http/Controllers/Admin/SystemController.php

@@ -31,44 +31,6 @@ class SystemController extends Controller
         return view('admin.config.system', array_merge(['payments' => $this->getPayment(), 'captcha' => $this->getCaptcha()], Config::pluck('value', 'name')->toArray()));
     }
 
-    private function getPayment() // 获取已经完成配置的支付渠道
-    {
-        if (sysConfig('f2fpay_app_id') && sysConfig('f2fpay_private_key') && sysConfig('f2fpay_public_key')) {
-            $payment[] = 'f2fpay';
-        }
-        if (sysConfig('codepay_url') && sysConfig('codepay_id') && sysConfig('codepay_key')) {
-            $payment[] = 'codepay';
-        }
-        if (sysConfig('epay_url') && sysConfig('epay_mch_id') && sysConfig('epay_key')) {
-            $payment[] = 'epay';
-        }
-        if (sysConfig('payjs_mch_id') && sysConfig('payjs_key')) {
-            $payment[] = 'payjs';
-        }
-        if (sysConfig('bitpay_secret')) {
-            $payment[] = 'bitpayx';
-        }
-        if (sysConfig('paypal_username') && sysConfig('paypal_password') && sysConfig('paypal_secret')) {
-            $payment[] = 'paypal';
-        }
-        if (sysConfig('stripe_public_key') && sysConfig('stripe_secret_key')) {
-            $payment[] = 'stripe';
-        }
-        if (sysConfig('paybeaver_app_id') && sysConfig('paybeaver_app_secret')) {
-            $payment[] = 'paybeaver';
-        }
-        if (sysConfig('theadpay_mchid') && sysConfig('theadpay_key')) {
-            $payment[] = 'theadpay';
-        }
-
-        return $payment ?? [];
-    }
-
-    private function getCaptcha()
-    {
-        return sysConfig('captcha_secret') && sysConfig('captcha_key');
-    }
-
     public function setExtend(Request $request): RedirectResponse  // 设置涉及到上传的设置
     {
         if ($request->hasAny(['website_home_logo', 'website_home_logo'])) { // 首页LOGO
@@ -224,4 +186,42 @@ class SystemController extends Controller
 
         return Response::json(['status' => 'success', 'message' => '发送成功,请查看手机是否收到推送消息']);
     }
+
+    private function getPayment() // 获取已经完成配置的支付渠道
+    {
+        if (sysConfig('f2fpay_app_id') && sysConfig('f2fpay_private_key') && sysConfig('f2fpay_public_key')) {
+            $payment[] = 'f2fpay';
+        }
+        if (sysConfig('codepay_url') && sysConfig('codepay_id') && sysConfig('codepay_key')) {
+            $payment[] = 'codepay';
+        }
+        if (sysConfig('epay_url') && sysConfig('epay_mch_id') && sysConfig('epay_key')) {
+            $payment[] = 'epay';
+        }
+        if (sysConfig('payjs_mch_id') && sysConfig('payjs_key')) {
+            $payment[] = 'payjs';
+        }
+        if (sysConfig('bitpay_secret')) {
+            $payment[] = 'bitpayx';
+        }
+        if (sysConfig('paypal_username') && sysConfig('paypal_password') && sysConfig('paypal_secret')) {
+            $payment[] = 'paypal';
+        }
+        if (sysConfig('stripe_public_key') && sysConfig('stripe_secret_key')) {
+            $payment[] = 'stripe';
+        }
+        if (sysConfig('paybeaver_app_id') && sysConfig('paybeaver_app_secret')) {
+            $payment[] = 'paybeaver';
+        }
+        if (sysConfig('theadpay_mchid') && sysConfig('theadpay_key')) {
+            $payment[] = 'theadpay';
+        }
+
+        return $payment ?? [];
+    }
+
+    private function getCaptcha()
+    {
+        return sysConfig('captcha_secret') && sysConfig('captcha_key');
+    }
 }

+ 4 - 3
app/Http/Controllers/User/AffiliateController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers\User;
 
+use App\Components\Helpers;
 use App\Http\Controllers\Controller;
 use App\Models\Order;
 use App\Models\ReferralApply;
@@ -24,9 +25,9 @@ class AffiliateController extends Controller
         return view('user.referral', [
             'referral_traffic'  => flowAutoShow(sysConfig('referral_traffic') * MB),
             'referral_percent'  => sysConfig('referral_percent'),
-            'referral_money'    => sysConfig('referral_money'),
+            'referral_money'    => Helpers::getPriceTag(sysConfig('referral_money')),
             'totalAmount'       => ReferralLog::uid()->sum('commission') / 100,
-            'canAmount'         => ReferralLog::uid()->whereStatus(0)->sum('commission') / 100,
+            'canAmount'         => Helpers::getPriceTag(ReferralLog::uid()->whereStatus(0)->sum('commission') / 100),
             'aff_link'          => UserService::getInstance()->inviteURI(),
             'referralLogList'   => ReferralLog::uid()->with('invitee:id,username')->latest()->paginate(10, ['*'], 'log_page'),
             'referralApplyList' => ReferralApply::uid()->latest()->paginate(10, ['*'], 'apply_page'),
@@ -52,7 +53,7 @@ class AffiliateController extends Controller
         $commission /= 100;
         if ($commission < sysConfig('referral_money')) {
             return Response::json([
-                'status' => 'fail', 'title' => trans('user.referral.failed'), 'message' => trans('user.referral.msg.unfulfilled', ['amount' => sysConfig('referral_money')]),
+                'status' => 'fail', 'title' => trans('user.referral.failed'), 'message' => trans('user.referral.msg.unfulfilled', ['amount' => Helpers::getPriceTag(sysConfig('referral_money'))]),
             ]);
         }
 

+ 10 - 3
app/Http/Controllers/UserController.php

@@ -206,7 +206,7 @@ class UserController extends Controller
         $user = auth()->user();
         // 余额充值商品,只取10个
         $renewOrder = Order::userActivePlan($user->id)->first();
-        $renewPrice = $renewOrder->goods ?? 0;
+        $renewPrice = $renewOrder->goods->renew ?? 0;
         // 有重置日时按照重置日为标准,否则就以过期日为标准
         $dataPlusDays = $user->reset_time ?? $user->expired_at;
 
@@ -224,7 +224,7 @@ class UserController extends Controller
         return view('user.services', [
             'chargeGoodsList' => Goods::type(3)->whereStatus(1)->orderBy('price')->get(),
             'goodsList'       => $goodsList,
-            'renewTraffic'    => $renewPrice->renew ?? 0,
+            'renewTraffic'    => $renewPrice ? Helpers::getPriceTag($renewPrice) : 0,
             'dataPlusDays'    => $dataPlusDays > date('Y-m-d') ? $dataPlusDays->diffInDays() : 0,
         ]);
     }
@@ -418,7 +418,7 @@ class UserController extends Controller
         $data = [
             'name'  => $ret->name,
             'type'  => $ret->type,
-            'value' => $ret->value,
+            'value' => $ret->type === 2 ? $ret->value : Helpers::getPriceTag($ret->value),
         ];
 
         return Response::json(['status' => 'success', 'data' => $data, 'message' => trans('common.applied', ['attribute' => trans('user.coupon.attribute')])]);
@@ -522,4 +522,11 @@ class UserController extends Controller
 
         return Response::json(['status' => 'fail', 'message' => trans('user.recharge').trans('common.failed')]);
     }
+
+    public function switchCurrency(string $code)// 切换语言
+    {
+        Session::put('currency', $code);
+
+        return Redirect::back();
+    }
 }

+ 15 - 4
app/Models/Goods.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Components\Helpers;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Database\Eloquent\SoftDeletes;
@@ -37,6 +38,11 @@ class Goods extends Model
         $this->attributes['price'] = $value * 100;
     }
 
+    public function getPriceTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->price);
+    }
+
     public function getRenewAttribute($value)
     {
         return $value / 100;
@@ -47,18 +53,23 @@ class Goods extends Model
         $this->attributes['renew'] = $value * 100;
     }
 
-    public function getTrafficLabelAttribute()
+    public function getRenewTagAttribute(): string
     {
-        return flowAutoShow($this->attributes['traffic'] * MB);
+        return Helpers::getPriceTag($this->renew);
     }
 
-    public function getSpeedLimitAttribute($value)
+    public function getTrafficLabelAttribute()
     {
-        return $value / Mbps;
+        return flowAutoShow($this->attributes['traffic'] * MB);
     }
 
     public function setSpeedLimitAttribute($value)
     {
         return $this->attributes['speed_limit'] = $value * Mbps;
     }
+
+    public function getSpeedLimitAttribute($value)
+    {
+        return $value / Mbps;
+    }
 }

+ 11 - 0
app/Models/Order.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Components\Helpers;
 use Auth;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -150,6 +151,11 @@ class Order extends Model
         return $this->attributes['origin_amount'] = $value * 100;
     }
 
+    public function getOriginAmountTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->origin_amount);
+    }
+
     public function getAmountAttribute($value)
     {
         return $value / 100;
@@ -160,6 +166,11 @@ class Order extends Model
         return $this->attributes['amount'] = $value * 100;
     }
 
+    public function getAmountTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->amount);
+    }
+
     // 支付渠道
     public function getPayTypeLabelAttribute(): string
     {

+ 6 - 0
app/Models/Payment.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Components\Helpers;
 use Auth;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -54,6 +55,11 @@ class Payment extends Model
         return $this->attributes['amount'] = $value * 100;
     }
 
+    public function getAmountTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->amount);
+    }
+
     // 订单状态
     public function getStatusLabelAttribute(): string
     {

+ 0 - 14
app/Models/ProductsPool.php

@@ -1,14 +0,0 @@
-<?php
-
-namespace App\Models;
-
-use Illuminate\Database\Eloquent\Model;
-
-/**
- * 产品名称池.
- */
-class ProductsPool extends Model
-{
-    protected $table = 'products_pool';
-    protected $guarded = [];
-}

+ 6 - 0
app/Models/ReferralApply.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Components\Helpers;
 use Auth;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -60,6 +61,11 @@ class ReferralApply extends Model
         $this->attributes['amount'] = $value * 100;
     }
 
+    public function getAmountTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->amount);
+    }
+
     public function getStatusLabelAttribute(): string
     {
         switch ($this->attributes['status']) {

+ 11 - 0
app/Models/ReferralLog.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Components\Helpers;
 use Auth;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -54,6 +55,16 @@ class ReferralLog extends Model
         $this->attributes['commission'] = $value * 100;
     }
 
+    public function getAmountTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->amount);
+    }
+
+    public function getCommissionTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->commission);
+    }
+
     public function getStatusLabelAttribute(): string
     {
         switch ($this->attributes['status']) {

+ 11 - 5
app/Models/User.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Components\Helpers;
 use Hash;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -177,6 +178,16 @@ class User extends Authenticatable
         return $this->attributes['credit'] / 100;
     }
 
+    public function setCreditAttribute($value)
+    {
+        return $this->attributes['credit'] = $value * 100;
+    }
+
+    public function getCreditTagAttribute(): string
+    {
+        return Helpers::getPriceTag($this->credit);
+    }
+
     public function getTransferEnableFormattedAttribute()
     {
         return flowAutoShow($this->attributes['transfer_enable']);
@@ -192,11 +203,6 @@ class User extends Authenticatable
         return $this->attributes['password'] = Hash::make($password);
     }
 
-    public function setCreditAttribute($value)
-    {
-        return $this->attributes['credit'] = $value * 100;
-    }
-
     public function setSpeedLimitAttribute($value)
     {
         return $this->attributes['speed_limit'] = $value * Mbps;

+ 2 - 8
app/Notifications/PaymentReceived.php

@@ -30,7 +30,7 @@ class PaymentReceived extends Notification implements ShouldQueue
     {
         return (new MailMessage)
             ->subject(__('Payment Received'))
-            ->line(__('Payment for #:sn has been received! Total amount: ¥:amount.', ['sn' => $this->sn, 'amount' => $this->amount]))
+            ->line(__('Payment for #:sn has been received! Total amount: :amount.', ['sn' => $this->sn, 'amount' => $this->amount]))
             ->action(__('Invoice Detail'), route('invoiceInfo', $this->sn));
     }
 
@@ -45,15 +45,9 @@ class PaymentReceived extends Notification implements ShouldQueue
     // todo: 需要重新审视发送对象
     public function toTelegram($notifiable)
     {
-        $message = sprintf(
-            "💰成功收款¥ %s\n———————————————\n订单号:%s",
-            $this->amount,
-            $this->sn
-        );
-
         return TelegramMessage::create()
             ->to($notifiable->telegram_user_id)
             ->token(sysConfig('telegram_token'))
-            ->content($message);
+            ->content('💰'.__('Payment for #:sn has been received! Total amount: :amount.', ['sn' => $this->sn, 'amount' => $this->amount]));
     }
 }

+ 6 - 4
app/Observers/UserObserver.php

@@ -36,11 +36,13 @@ class UserObserver
                     }
                 } else {
                     // 权限修改,消除重叠的部分
-                    if ($oldAllowNodes->isNotEmpty() && $oldAllowNodes->diff($allowNodes)->isNotEmpty()) {
-                        delUser::dispatch($user->id, $oldAllowNodes->diff($allowNodes));
+                    $old = $oldAllowNodes->diff($allowNodes);
+                    if ($oldAllowNodes->isNotEmpty() && $old->isNotEmpty()) {
+                        delUser::dispatch($user->id, $old->diff($allowNodes));
                     }
-                    if ($allowNodes->diff($oldAllowNodes)->isNotEmpty()) {
-                        addUser::dispatch($user->id, $allowNodes->diff($oldAllowNodes));
+                    $new = $allowNodes->diff($oldAllowNodes);
+                    if ($new->isNotEmpty()) {
+                        addUser::dispatch($user->id, $new);
                     }
                 }
             } elseif (Arr::hasAny($changes, ['port', 'passwd', 'speed_limit'])) {

+ 1 - 1
app/Payments/Library/Gateway.php

@@ -75,7 +75,7 @@ abstract class Gateway
         if ($payment) {
             $ret = $payment->order->complete();
             if ($ret) {
-                $payment->user->notify(new PaymentReceived($payment->order->sn, $payment->amount));
+                $payment->user->notify(new PaymentReceived($payment->order->sn, $payment->amount_tag));
             }
 
             return $ret;

+ 22 - 25
app/Payments/PayPal.php

@@ -2,11 +2,11 @@
 
 namespace App\Payments;
 
+use App\Components\CurrencyExchange;
 use App\Models\Payment;
 use App\Payments\Library\Gateway;
 use Auth;
 use Exception;
-use Http;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
@@ -16,7 +16,6 @@ use Srmklive\PayPal\Services\ExpressCheckout;
 class PayPal extends Gateway
 {
     protected $provider;
-    protected $exChange;
 
     public function __construct()
     {
@@ -39,8 +38,6 @@ class PayPal extends Gateway
             'validate_ssl'   => true,
         ];
         $this->provider->setApiCredentials($config);
-        $response = Http::timeout(15)->get('http://api.k780.com/?app=finance.rate&scur=USD&tcur=CNY&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4');
-        $this->exChange = $response->json()['result']['rate'] ?? 7;
     }
 
     public function purchase($request): JsonResponse
@@ -66,27 +63,6 @@ class PayPal extends Gateway
         }
     }
 
-    protected function getCheckoutData($trade_no, $amount): array
-    {
-        $amount = 0.3 + ceil($amount / $this->exChange * 100) / 100;
-
-        return [
-            'invoice_id' => $trade_no,
-            'items' => [
-                [
-                    'name' => sysConfig('subject_name') ?: sysConfig('website_name'),
-                    'price' => $amount,
-                    'desc' => 'Description for'.(sysConfig('subject_name') ?: sysConfig('website_name')),
-                    'qty' => 1,
-                ],
-            ],
-            'invoice_description' => $trade_no,
-            'return_url' => route('paypal.checkout'),
-            'cancel_url' => route('invoice'),
-            'total' => $amount,
-        ];
-    }
-
     public function getCheckout(Request $request)
     {
         $token = $request->get('token');
@@ -134,4 +110,25 @@ class PayPal extends Gateway
         }
         exit('fail');
     }
+
+    protected function getCheckoutData($trade_no, $amount): array
+    {
+        $amount = 0.3 + CurrencyExchange::convert('USD', $amount);
+
+        return [
+            'invoice_id'          => $trade_no,
+            'items'               => [
+                [
+                    'name'  => sysConfig('subject_name') ?: sysConfig('website_name'),
+                    'price' => $amount,
+                    'desc'  => 'Description for'.(sysConfig('subject_name') ?: sysConfig('website_name')),
+                    'qty'   => 1,
+                ],
+            ],
+            'invoice_description' => $trade_no,
+            'return_url'          => route('paypal.checkout'),
+            'cancel_url'          => route('invoice'),
+            'total'               => $amount,
+        ];
+    }
 }

+ 21 - 32
app/Payments/Stripe.php

@@ -6,7 +6,6 @@ use App\Models\Payment;
 use App\Payments\Library\Gateway;
 use Auth;
 use Exception;
-use Http;
 use Illuminate\Http\JsonResponse;
 use Log;
 use Response;
@@ -29,19 +28,9 @@ class Stripe extends Gateway
         $payment = $this->creatNewPayment(Auth::id(), $request->input('id'), $request->input('amount'));
 
         if ($type == 1 || $type == 3) {
-            $stripe_currency = sysConfig('stripe_currency');
-            $response = Http::get('https://api.exchangerate-api.com/v4/latest/'.strtoupper($stripe_currency));
-            if (! $response->ok()) {
-                Log::warning('【Stripe】错误: 获取汇率失败!');
-                $payment->delete();
-
-                return Response::json(['status' => 'fail', 'message' => '获取汇率失败!']);
-            }
-            $response = $response->json();
-            $price_exchanged = bcdiv((float) $payment->amount, $response['rates']['CNY'], 10);
             $source = Source::create([
-                'amount'               => floor($price_exchanged * 100),
-                'currency'             => $stripe_currency,
+                'amount'               => ceil($payment->amount * 100),
+                'currency'             => strtolower(sysConfig('standard_currency')),
                 'type'                 => $type == 1 ? 'alipay' : 'wechat',
                 'statement_descriptor' => $payment->trade_no,
                 'metadata'             => [
@@ -63,31 +52,31 @@ class Stripe extends Gateway
                 $payment->update(['qr_code' => 1, 'url' => $source['wechat']['qr_code_url']]);
 
                 return Response::json(['status' => 'success', 'data' => $payment->trade_no, 'message' => '创建订单成功!']);
-            } else {
-                if (! $source['redirect']['url']) {
-                    Log::warning('创建订单错误:未知错误');
-                    $payment->failed();
+            }
 
-                    return response()->json(['code' => 0, 'msg' => '创建订单失败:未知错误']);
-                }
-                $payment->update(['url' => $source['redirect']['url']]);
+            if (! $source['redirect']['url']) {
+                Log::warning('创建订单错误:未知错误');
+                $payment->failed();
 
-                return Response::json(['status' => 'success', 'url' => $source['redirect']['url'], 'message' => '创建订单成功!']);
+                return response()->json(['code' => 0, 'msg' => '创建订单失败:未知错误']);
             }
-        } else {
-            $data = $this->getCheckoutSessionData($payment->trade_no, $payment->amount, $type);
+            $payment->update(['url' => $source['redirect']['url']]);
 
-            try {
-                $session = Session::create($data);
+            return Response::json(['status' => 'success', 'url' => $source['redirect']['url'], 'message' => '创建订单成功!']);
+        }
 
-                $url = route('stripe.checkout', ['session_id' => $session->id]);
-                $payment->update(['url' => $url]);
+        $data = $this->getCheckoutSessionData($payment->trade_no, $payment->amount, $type);
 
-                return Response::json(['status' => 'success', 'url' => $url, 'message' => '创建订单成功!']);
-            } catch (Exception $e) {
-                Log::error('【Stripe】错误: '.$e->getMessage());
-                exit;
-            }
+        try {
+            $session = Session::create($data);
+
+            $url = route('stripe.checkout', ['session_id' => $session->id]);
+            $payment->update(['url' => $url]);
+
+            return Response::json(['status' => 'success', 'url' => $url, 'message' => '创建订单成功!']);
+        } catch (Exception $e) {
+            Log::error('【Stripe】错误: '.$e->getMessage());
+            exit;
         }
     }
 

+ 1 - 1
app/Services/CouponService.php

@@ -63,7 +63,7 @@ class CouponService
         }
 
         if (isset($coupon->limit['minimum']) && $goods->price < $coupon->limit['minimum']) {
-            return $this->failedReturn(trans('user.coupon.error.unmet'), trans('user.coupon.error.minimum', ['amount' => $coupon->limit['minimum']]));
+            return $this->failedReturn(trans('user.coupon.error.unmet'), trans('user.coupon.error.minimum', ['amount' => Helpers::getPriceTag($coupon->limit['minimum'])]));
         }
 
         if (isset($coupon->limit['users']['black']) && in_array($this->user->id, $coupon->limit['users']['black'], true)) {

+ 14 - 1
config/common.php

@@ -75,6 +75,19 @@ return [
 
     'language' => [
         'zh_CN' => ['简体中文', 'cn'],
-        'en'    => ['English', 'gb'],
+        'en'    => ['English', 'us'],
+    ],
+
+    'currency' => [
+        'cn' => ['name' => '人民币', 'code' => 'CNY', 'symbol' => '¥'],
+        'us' => ['name' => 'US Dollar', 'code' => 'USD', 'symbol' => '$'],
+        'gb' => ['name' => 'Pound sterling', 'code' => 'GBP', 'symbol' => '£'],
+        'hk' => ['name' => '港元', 'code' => 'HKD', 'symbol' => 'HK$'],
+        'sg' => ['name' => 'Singapore dollar', 'code' => 'SGD', 'symbol' => 'S$'],
+        'eu' => ['name' => 'Euro', 'code' => 'EUR', 'symbol' => '€'],
+        'jp' => ['name' => '日本円', 'code' => 'JPY', 'symbol' => '¥'],
+        'kr' => ['name' => '대한민국 원', 'code' => 'KRW', 'symbol' => '₩'],
+        'ca' => ['name' => 'Canadian dollar', 'code' => 'CAD', 'symbol' => 'C$'],
+        'tw' => ['name' => '新臺幣', 'code' => 'TWD', 'symbol' => 'NT$'],
     ],
 ];

+ 2 - 0
config/services.php

@@ -82,4 +82,6 @@ return [
     'baidu' => [
         'app_ak' => env('BAIDU_APP_AK'),
     ],
+
+    'apiLayer' => env('API_LAYER_API_KEY'),
 ];

+ 1 - 1
config/version.php

@@ -2,5 +2,5 @@
 
 return [
     'name' => 'ProxyPanel',
-    'number' => '2.7.c',
+    'number' => '2.7.d',
 ];

+ 0 - 36
database/migrations/2020_08_21_145711_create_products_pool_table.php

@@ -1,36 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\Schema;
-
-class CreateProductsPoolTable extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('products_pool', function (Blueprint $table) {
-            $table->increments('id');
-            $table->string('name')->comment('名称');
-            $table->unsignedInteger('min_amount')->default(0)->comment('适用最小金额,单位分');
-            $table->unsignedInteger('max_amount')->default(0)->comment('适用最大金额,单位分');
-            $table->boolean('status')->default(1)->comment('状态:0-未启用、1-已启用');
-            $table->dateTime('created_at')->comment('创建时间');
-            $table->dateTime('updated_at')->comment('最后更新时间');
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('products_pool');
-    }
-}

+ 18 - 0
database/migrations/2023_01_04_210048_currency_internationalization.php

@@ -0,0 +1,18 @@
+<?php
+
+use App\Models\Config;
+use Illuminate\Database\Migrations\Migration;
+
+class CurrencyInternationalization extends Migration
+{
+    public function up()
+    {
+        Schema::dropIfExists('products_pool');
+        Config::whereName('stripe_currency')->update(['name' => 'standard_currency', 'value' => strtoupper(Config::find('stripe_currency')->value ?? 'CNY')]);
+    }
+
+    public function down()
+    {
+        Config::whereName('standard_currency')->update(['name' => 'stripe_currency']);
+    }
+}

+ 1 - 1
resources/lang/en/notification.php

@@ -4,7 +4,7 @@ return [
     'attribute'               => 'NOTIFICATIONS',
     'new'                     => ':num new message|:num new messages',
     'empty'                   => 'No new message',
-    'payment_received'        => 'Order has paid, Amount:¥:amount! Click me to view the detail',
+    'payment_received'        => 'Order has paid, Amount::amount! Click me to view the detail',
     'account_expired'         => 'Account Going to Expired',
     'account_expired_content' => 'Your account will be expired after【:days】days. For your server experience, please renew your account ahead of time.',
     'account_expired_blade'   => 'Account will be expired after【:days】days, Please renew',

+ 4 - 4
resources/lang/en/user.php

@@ -98,7 +98,7 @@ return [
             'inactive' => 'Coupon Inactive yet',
             'wait'     => 'The Event will begin until :time, Please wait',
             'unmet'    => 'Conditions of use are not met',
-            'minimum'  => 'The minimum requirement of this coupon is ¥:amount',
+            'minimum'  => 'The minimum requirement of this coupon is :amount',
             'overused' => 'You can only use this coupon once|You can only use this coupon :times times',
             'users'    => 'This account is not eligible for promotions',
             'services' => 'This item is not qualify for this discount, please check the promotion terms',
@@ -116,14 +116,14 @@ return [
     'reset_data'          => [
         ''          => 'Reset Data',
         'required'  => 'Required',
-        'cost_tips' => 'This following action will cost ¥:amount!',
+        'cost_tips' => 'This following action will cost :amount!',
         'lack'      => 'No enough Credit!',
         'logs'      => 'User purchase reset data',
         'success'   => 'Reset Successfully',
     ],
     'referral'            => [
         'link'       => 'Referral link',
-        'total'      => 'Total rebate ¥:amount (:total times), you can apply for cash withdrawal when it reach  ¥:money.',
+        'total'      => 'Total rebate :amount (:total times), you can apply for cash withdrawal when it reaches :money.',
         'amount'     => 'Spend amount',
         'commission' => 'Rebate amount',
         'logs'       => 'Commission records',
@@ -132,7 +132,7 @@ return [
         'msg'        => [
             'account'     => 'Account has expired, please purchase to active service first',
             'applied'     => 'There is an application exist, please wait for the previous application to be processed',
-            'unfulfilled' => 'Minimal amount to created application is ¥:amount',
+            'unfulfilled' => 'A minimal amount to created application is :amount',
             'wait'        => 'Please wait for the administrator to review',
             'error'       => 'Rebate order creation failed, please try later or notify the administrator',
         ],

+ 1 - 1
resources/lang/zh_CN.json

@@ -7,7 +7,7 @@
   "If your problem has not been solved, Feel free to open other one.": "如果您的问题尚未解决,请另开工单解决。",
   "Invoice Detail": "订单明细",
   "Nodes Daily Report": "线路每日流量报告",
-  "Payment for #:sn has been received! Total amount: ¥:amount.": "您成功支付了订单#:sn,总金额为 ¥:amount。",
+  "Payment for #:sn has been received! Total amount: :amount.": "您成功支付了订单#:sn,总金额为 :amount。",
   "Payment Received": "账单付款成功",
   "Please click the button below to verify your email address.": "请点击下面按钮验证您的 E-mail:",
   "Please click the button below to reset your password.": "请点击下面按钮重置您的密码:",

+ 1 - 1
resources/lang/zh_CN/notification.php

@@ -4,7 +4,7 @@ return [
     'attribute'               => '通知',
     'new'                     => ':num条新消息',
     'empty'                   => '目前未收到新消息',
-    'payment_received'        => '订单支付成功,金额:¥:amount,查看详情',
+    'payment_received'        => '订单支付成功,金额::amount,查看详情',
     'account_expired'         => '账号过期提醒',
     'account_expired_content' => '您的账号将在【:days】天后过期,为了确保您可以继续正常使用我们的服务,请及时续费。',
     'account_expired_blade'   => '账号将于【:days天】后过期,请及时续费',

+ 4 - 4
resources/lang/zh_CN/user.php

@@ -98,7 +98,7 @@ return [
             'inactive' => '优惠券尚未生效',
             'wait'     => '活动将于:time生效,请耐心等待!',
             'unmet'    => '使用条件未满足',
-            'minimum'  => '本券最低使用金额为 ¥:amount',
+            'minimum'  => '本券最低使用金额为 :amount',
             'overused' => '本券只能使用 :times 次',
             'users'    => '帐户不符合促销条件',
             'services' => '商品不符合折扣条件,请查看促销条款',
@@ -116,14 +116,14 @@ return [
     'reset_data'          => [
         ''          => '重置流量',
         'required'  => '需要',
-        'cost_tips' => '本次重置流量将扣除余额 ¥:amount!',
+        'cost_tips' => '本次重置流量将扣除余额 :amount!',
         'lack'      => '余额不足,请充值余额',
         'logs'      => '用户自行重置流量',
         'success'   => '重置成功',
     ],
     'referral'            => [
         'link'       => '推广链接',
-        'total'      => '合计返利 ¥ :amount( :total 次),满 ¥ :money 可以申请提现。',
+        'total'      => '合计返利 :amount( :total 次),满 :money 可以申请提现。',
         'amount'     => '消费金额',
         'commission' => '返利金额',
         'logs'       => '佣金记录',
@@ -132,7 +132,7 @@ return [
         'msg'        => [
             'account'     => '账号已过期,请先购买服务吧',
             'applied'     => '已存在申请,请等待之前的申请处理完',
-            'unfulfilled' => '满¥ :amount才可以提现,继续努力吧',
+            'unfulfilled' => '满 :amount 才可以提现,继续努力吧',
             'wait'        => '请等待管理员审核',
             'error'       => '返利单建立失败,请稍后尝试或通知管理员',
         ],

+ 3 - 3
resources/views/admin/aff/detail.blade.php

@@ -22,7 +22,7 @@
                         <thead class="thead-default">
                         <tr>
                             <th colspan="6">
-                                申请单ID:{{$referral->id}} | 申请人:{{$referral->user->username}} | 申请提现金额:¥{{$referral->amount}} | 申请时间:{{$referral->created_at}}
+                                申请单ID:{{$referral->id}} | 申请人:{{$referral->user->username}} | 申请提现金额:{{$referral->amount_tag}} | 申请时间:{{$referral->created_at}}
                             </th>
                         </tr>
                         <tr>
@@ -49,8 +49,8 @@
                                         {{$commission->order->goods->name}}
                                     @endcan
                                 </td>
-                                <td> ¥{{$commission->amount}} </td>
-                                <td> ¥{{$commission->commission}} </td>
+                                <td> {{$commission->amount_tag}} </td>
+                                <td> {{$commission->commission_tag}} </td>
                                 <td> {{$commission->created_at}} </td>
                             </tr>
                         @endforeach

+ 1 - 1
resources/views/admin/aff/index.blade.php

@@ -57,7 +57,7 @@
                                     @endcan
                                 @endif
                             </td>
-                            <td> ¥{{$apply->amount}} </td>
+                            <td> {{$apply->amount_tag}} </td>
                             <td>
                                 @if($apply->status === -1)
                                     <span class="badge badge-lg badge-danger"> 驳 回 </span>

+ 173 - 150
resources/views/admin/config/system.blade.php

@@ -63,6 +63,8 @@
                         <x-system.tab-pane id="webSetting" :active="true">
                             <x-system.input title="网站名称" :value="$website_name" code="website_name" help="发邮件时展示"/>
                             <x-system.input title="网站地址" :value="$website_url" code="website_url" help="生成重置密码、在线支付必备" type="url"/>
+                            <x-system.select title="本位货币" code="standard_currency" :list="array_column(config('common.currency'), 'code', 'name')"
+                                             help="网站中涉及金钱部分的默认货币"/>
                             <x-system.input title="苹果账号" :value="$AppStore_id" code="AppStore_id" help="iOS软件设置教程中使用的苹果账号" type="email"/>
                             <x-system.input title="苹果密码" :value="$AppStore_password" code="AppStore_password" help="iOS软件设置教程中使用的苹果密码" type="password"/>
                             <x-system.input title="管理员邮箱" :value="$webmaster_email" code="webmaster_email" help="错误提示时会提供管理员邮箱作为联系方式" type="email"/>
@@ -94,12 +96,15 @@
                             <x-system.switch title="用户注册" code="is_register" :check="$is_register" help="关闭后无法注册"/>
                             <x-system.select title="第三方登录平台" code="oauth_path" help="请在.ENV中添加设置,再在此处开启平台" multiple="1"
                                              :list="array_flip(config('common.oauth.labels'))"/>
-                            <x-system.select title="账号类型" help="规范站点用户账号的类型,默认为电子邮箱" code="username_type" :list="['电子邮箱'=> 'email', '手机号码' => 'numeric', '任意用户名' => 'string']"/>
+                            <x-system.select title="账号类型" help="规范站点用户账号的类型,默认为电子邮箱" code="username_type"
+                                             :list="['电子邮箱'=> 'email', '手机号码' => 'numeric', '任意用户名' => 'string']"/>
                             <x-system.select title="邀请注册" code="is_invite_register" :list="['关闭' => '', '可选'=> 1, '必须' => 2]"/>
-                            <x-system.select title="激活账号" code="is_activate_account" :list="['关闭' => '', '注册前激活'=> 1, '注册后激活' => 2]" help="启用后用户需要通过邮件来激活账号"/>
+                            <x-system.select title="激活账号" code="is_activate_account" :list="['关闭' => '', '注册前激活'=> 1, '注册后激活' => 2]"
+                                             help="启用后用户需要通过邮件来激活账号"/>
                             <x-system.select title="重置密码" code="password_reset_notification" :list="['关闭' => '', '邮箱'=> 'mail']" help="启用后用户可以重置密码"/>
                             <x-system.switch title="免费邀请码" code="is_free_code" :check="$is_free_code" help="关闭后免费邀请码不可见"/>
-                            <x-system.input title="邀请链接 用户信息字符化" :value="$aff_salt" code="aff_salt" help="留空时,邀请链接将显示用户ID;填入任意英文/数字 即可对用户链接ID进行加密"/>
+                            <x-system.input title="邀请链接 用户信息字符化" :value="$aff_salt" code="aff_salt"
+                                            help="留空时,邀请链接将显示用户ID;填入任意英文/数字 即可对用户链接ID进行加密"/>
                             <x-system.switch title="随机端口" code="is_rand_port" :check="$is_rand_port" help="注册、添加用户时随机生成端口"/>
                             <x-system.input-limit title="端口范围" code="min_port" hcode="max_port" :value="$min_port" min="1000" max="$('#max_port').val()"
                                                   :hvalue="$max_port" hmin="$('#min_port').val()" hmax="65535" help="端口范围:1000 - 65535"/>
@@ -112,18 +117,21 @@
                             <x-system.input-limit title="激活账号次数" code="active_times" :value="$active_times" help="24小时内可以通过邮件激活账号次数"/>
                             <x-system.input-limit title="同IP注册限制" code="register_ip_limit" :value="$register_ip_limit" help="同IP在24小时内允许注册数量,为0/留空时不限制"/>
                             <x-system.input-limit title="用户-邀请码有效期" code="user_invite_days" :value="$user_invite_days" min="1" unit="天" help="用户自行生成邀请的有效期"/>
-                            <x-system.input-limit title="管理员-邀请码有效期" code="admin_invite_days" :value="$admin_invite_days" min="1" unit="天" help="管理员生成邀请码的有效期"/>
+                            <x-system.input-limit title="管理员-邀请码有效期" code="admin_invite_days" :value="$admin_invite_days" min="1" unit="天"
+                                                  help="管理员生成邀请码的有效期"/>
                         </x-system.tab-pane>
                         <x-system.tab-pane id="node">
                             <x-system.input title="节点订阅地址" :value="$subscribe_domain" code="subscribe_domain" help="(推荐)防止面板域名被DNS投毒后无法正常订阅,需带http://或https://"
                                             :holder="'默认为 '.$website_url" type="url"/>
                             <x-system.input-limit title="订阅节点数" code="subscribe_max" :value="$subscribe_max" help="客户端订阅时取得几个节点,为0/留空时返回全部节点"/>
                             <x-system.switch title="随机订阅" code="rand_subscribe" :check="$rand_subscribe" help="启用后,订阅时将随机返回节点信息,否则按节点排序返回"/>
-                            <x-system.switch title="高级订阅" code="is_custom_subscribe" :check="$is_custom_subscribe" help="启用后,订阅信息顶部将显示过期时间、剩余流量(只支持个别客户端)"/>
+                            <x-system.switch title="高级订阅" code="is_custom_subscribe" :check="$is_custom_subscribe"
+                                             help="启用后,订阅信息顶部将显示过期时间、剩余流量(只支持个别客户端)"/>
                             <x-system.input title="授权/后端访问域名" :value="$web_api_url" code="web_api_url" help="例:https://demo.proxypanel.cf" type="url"/>
                             <x-system.input title="V2Ray授权" :value="$v2ray_license" code="v2ray_license"/>
                             <x-system.input title="Trojan授权" :value="$trojan_license" code="trojan_license"/>
-                            <x-system.input title="V2Ray TLS配置" :value="$v2ray_tls_provider" code="v2ray_tls_provider" help="后端自动签发/载入TLS证书时用(节点的设置值优先级高于此处)"/>
+                            <x-system.input title="V2Ray TLS配置" :value="$v2ray_tls_provider" code="v2ray_tls_provider"
+                                            help="后端自动签发/载入TLS证书时用(节点的设置值优先级高于此处)"/>
                         </x-system.tab-pane>
                         <x-system.tab-pane id="extend">
                             <x-system.select title="DDNS模式" code="ddns_mode" help="添加/编辑/删除节点的【域名、ipv4、ipv6】时,自动更新对应内容至DNS服务商"
@@ -133,7 +141,8 @@
                             <x-system.input title="DNS服务商Secret" :value="$ddns_secret" code="ddns_secret"/>
                             <hr class="col-lg-12">
                             <x-system.select title="验证码模式" code="is_captcha"
-                                             :list="['关闭' => '', '普通验证码' => 1, '极验Geetest' => 2, 'Google reCaptcha' => 3, 'hCaptcha' => 4]" help="启用后 登录/注册 需要进行验证码认证"/>
+                                             :list="['关闭' => '', '普通验证码' => 1, '极验Geetest' => 2, 'Google reCaptcha' => 3, 'hCaptcha' => 4]"
+                                             help="启用后 登录/注册 需要进行验证码认证"/>
                             <x-system.input title="验证码 Key" :value="$captcha_key" code="captcha_key"
                                             help='浏览<a href="https://proxypanel.gitbook.io/wiki/captcha" target="_blank">设置指南</a>来设置'/>
                             <x-system.input title="验证码 Secret/ID" :value="$captcha_secret" code="captcha_secret"/>
@@ -146,11 +155,14 @@
                         </x-system.tab-pane>
                         <x-system.tab-pane id="promo">
                             <x-system.switch title="推广功能" code="referral_status" :check="$referral_status" help="关闭后用户不可见,但是不影响其正常邀请返利"/>
-                            <x-system.select title="返利模式" code="referral_type" :list="['关闭' => '', '首购返利' => 1, '循环返利' => 2]" help="切换模式后旧数据不变,新的返利按新的模式计算"/>
+                            <x-system.select title="返利模式" code="referral_type" :list="['关闭' => '', '首购返利' => 1, '循环返利' => 2]"
+                                             help="切换模式后旧数据不变,新的返利按新的模式计算"/>
                             <x-system.input-limit title="注册送流量" code="referral_traffic" :value="$referral_traffic" unit="MB" help="根据推广链接、邀请码注册则赠送相应的流量"/>
                             <x-system.input-limit title="返利比例" code="referral_percent" :value="$referral_percent * 100" max="100" unit="%"
                                                   help="根据推广链接注册的账号每笔消费推广人可以分成的比例 "/>
-                            <x-system.input-limit title="提现限制" code="referral_money" :value="$referral_money" unit="元" help="满多少元才可以申请提现"/>
+                            <x-system.input-limit title="提现限制" code="referral_money" :value="$referral_money"
+                                                  unit="{{array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')]}}"
+                                                  help="满多少元才可以申请提现"/>
                         </x-system.tab-pane>
                         <x-system.tab-pane id="notify">
                             <x-system.input-test title="ServerChan SCKEY" :value="$server_chan_key" code="server_chan_key" help='启用ServerChan,请务必填入本值(<a href=https://sc.ftqq.com
@@ -168,21 +180,28 @@
                                     target=_blank>申请 Token</a>)' holder="请到ServerChan申请" test="pushPlus"/>
                             <x-system.input title="钉钉自定义机器人 Access Token" :value="$dingTalk_access_token" code="dingTalk_access_token" holder="自定义机器人的WebHook中的access_token"
                                             help="可以阅读<a href=https://open.dingtalk.com/document/group/custom-robot-access#title-jfe-yo9-jl2 target=_blank>钉钉手册</a>查阅步骤"/>
-                            <x-system.input-test title="钉钉自定义机器人 密钥" :value="$dingTalk_secret" code="dingTalk_secret" help='可选填!开启机器人[加签]就是必填项目!' holder="自定义机器人加签后出现的的密钥" test="dingTalk"/>
+                            <x-system.input-test title="钉钉自定义机器人 密钥" :value="$dingTalk_secret" code="dingTalk_secret" help='可选填!开启机器人[加签]就是必填项目!'
+                                                 holder="自定义机器人加签后出现的的密钥" test="dingTalk"/>
                             <x-system.input title="微信企业ID" :value="$wechat_cid" code="wechat_cid" holder="填入微信企业ID -> 再点击更新"
                                             help="获取<a href=https://work.weixin.qq.com/wework_admin/frame#profile target=_blank>我的企业</a>中的企业ID"/>
                             <x-system.input title="微信企业应用ID" :value="$wechat_aid" code="wechat_aid" holder="应用的AgentId"
                                             help="在<a href=https://work.weixin.qq.com/wework_admin/frame#apps arget=_blank>应用管理</a>自建中创建应用 - AgentId"/>
-                            <x-system.input-test title="微信企业应用密钥" :value="$wechat_secret" code="wechat_secret" help='应用的Secret(可能需要下载企业微信才能查看)' holder="应用的Secret" test="weChat"/>
-                            <x-system.input title="微信企业应用TOKEN" :value="$wechat_token" code="wechat_token" help="{{'应用管理->应用->设置API接收->TOKEN,URL设置:'.route('wechat.verify')}}"/>
-                            <x-system.input title="微信企业应用EncodingAESKey" :value="$wechat_encodingAESKey" code="wechat_encodingAESKey" help='应用管理->应用->设置API接收->EncodingAESKey'/>
+                            <x-system.input-test title="微信企业应用密钥" :value="$wechat_secret" code="wechat_secret" help='应用的Secret(可能需要下载企业微信才能查看)'
+                                                 holder="应用的Secret" test="weChat"/>
+                            <x-system.input title="微信企业应用TOKEN" :value="$wechat_token" code="wechat_token"
+                                            help="{{'应用管理->应用->设置API接收->TOKEN,URL设置:'.route('wechat.verify')}}"/>
+                            <x-system.input title="微信企业应用EncodingAESKey" :value="$wechat_encodingAESKey" code="wechat_encodingAESKey"
+                                            help='应用管理->应用->设置API接收->EncodingAESKey'/>
                             <x-system.input-test title="TG酱Token" :value="$tg_chat_token" code="tg_chat_token" help='启用TG酱,请务必填入本值(<a href=https://t.me/realtgchat_bot
                                     target=_blank>申请 Token</a>)' holder="请到Telegram申请" test="tgChat"/>
                             <hr class="col-10"/>
-                            <x-system.select title="账号过期通知" code="account_expire_notification" help="通知用户账号即将到期" multiple="1" :list="['邮箱' => 'mail', '站内通知' => 'database']"/>
-                            <x-system.input-limit title="过期警告阈值" code="expire_days" :value="$expire_days" unit="元" help="【账号过期通知】开始阈值,每日通知用户"/>
-                            <x-system.select title="流量耗尽通知" code="data_exhaust_notification" help="通知用户流量即将耗尽" multiple="1" :list="['邮箱' => 'mail', '站内通知' => 'database']"/>
-                            <x-system.input-limit title="流量警告阈值" code="traffic_warning_percent" :value="$traffic_warning_percent" unit="%" help="【流量耗尽通知】开始阈值,每日通知用户"/>
+                            <x-system.select title="账号过期通知" code="account_expire_notification" help="通知用户账号即将到期" multiple="1"
+                                             :list="['邮箱' => 'mail', '站内通知' => 'database']"/>
+                            <x-system.input-limit title="过期警告阈值" code="expire_days" :value="$expire_days" unit="天" help="【账号过期通知】开始阈值,每日通知用户"/>
+                            <x-system.select title="流量耗尽通知" code="data_exhaust_notification" help="通知用户流量即将耗尽" multiple="1"
+                                             :list="['邮箱' => 'mail', '站内通知' => 'database']"/>
+                            <x-system.input-limit title="流量警告阈值" code="traffic_warning_percent" :value="$traffic_warning_percent" unit="%"
+                                                  help="【流量耗尽通知】开始阈值,每日通知用户"/>
                             <x-system.select title="节点离线提醒" code="node_offline_notification" help="每10分钟检测节点离线并提醒管理员" multiple="1"
                                              :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'PushDeer' => 'pushDear', '爱语飞飞' => 'iYuu', 'Telegram' =>
                                              'telegram', '钉钉' => 'dingTalk', '微信企业' => 'weChat', 'TG酱' => 'tgChat', 'PushPlus' => 'pushPlus']"/>
@@ -215,10 +234,14 @@
                             <x-system.select title="流量异常通知" code="data_anomaly_notification" help="1小时内流量超过异常阈值通知超管" multiple="1"
                                              :list="['邮箱' => 'mail', 'Bark' => 'bark', 'ServerChan' => 'serverChan', 'PushDeer' => 'pushDear', '爱语飞飞' => 'iYuu', 'Telegram' => 'telegram', '钉钉' => 'dingTalk', '微信企业' => 'weChat', 'TG酱' =>
                                              'tgChat', 'PushPlus' => 'pushPlus']"/>
-                            <x-system.input-limit title="流量异常阈值" code="traffic_ban_value" :value="$traffic_ban_value" min="1" unit="GB" help="1小时内超过该值,则触发自动封号"/>
-                            <x-system.input-limit title="封号时长" code="traffic_ban_time" :value="$traffic_ban_time" unit="分钟" help="触发流量异常导致用户被封禁的时长,到期后自动解封"/>
-                            <x-system.switch title="端口回收机制" code="auto_release_port" :check="$auto_release_port" help="被封禁/过期{{config('tasks.release_port')}}天的账号端口自动释放"/>
-                            <x-system.switch title="过期自动封禁" code="is_ban_status" :check="$is_ban_status" help="(慎重)封禁整个账号会重置账号的所有数据且会导致用户无法登录,不开启状态下只封禁用户代理"/>
+                            <x-system.input-limit title="流量异常阈值" code="traffic_ban_value" :value="$traffic_ban_value" min="1" unit="GB"
+                                                  help="1小时内超过该值,则触发自动封号"/>
+                            <x-system.input-limit title="封号时长" code="traffic_ban_time" :value="$traffic_ban_time" unit="分钟"
+                                                  help="触发流量异常导致用户被封禁的时长,到期后自动解封"/>
+                            <x-system.switch title="端口回收机制" code="auto_release_port" :check="$auto_release_port"
+                                             help="被封禁/过期{{config('tasks.release_port')}}天的账号端口自动释放"/>
+                            <x-system.switch title="过期自动封禁" code="is_ban_status" :check="$is_ban_status"
+                                             help="(慎重)封禁整个账号会重置账号的所有数据且会导致用户无法登录,不开启状态下只封禁用户代理"/>
                             <x-system.select title="节点使用报告" code="node_daily_notification" help="报告各节点流量昨日消耗情况" multiple="1"
                                              :list="['邮箱' => 'mail', 'ServerChan' => 'serverChan', 'PushDeer' => 'pushDear', '爱语飞飞' => 'iYuu', 'Telegram' => 'telegram', '钉钉' => 'dingTalk', '微信企业' => 'weChat', 'TG酱' =>
                                              'tgChat', 'PushPlus' => 'pushPlus']"/>
@@ -305,7 +328,8 @@
                                     <div class="form-group col-lg-6 d-flex">
                                         <label class="col-md-3 col-form-label">PayPal</label>
                                         <div class="col-md-7">
-                                            使用商家账号登录<a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty" target="_blank">API凭证申请页</a>, 同意并获取设置信息
+                                            使用商家账号登录<a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty" target="_blank">API凭证申请页</a>,
+                                            同意并获取设置信息
                                         </div>
                                     </div>
                                     <x-system.input title="API用户名" :value="$paypal_username" code="paypal_username"/>
@@ -321,8 +345,6 @@
                                     <x-system.input title="Public Key" :value="$stripe_public_key" code="stripe_public_key"/>
                                     <x-system.input title="Secret Key" :value="$stripe_secret_key" code="stripe_secret_key"/>
                                     <x-system.input title="WebHook Signing secret" :value="$stripe_signing_secret" code="stripe_signing_secret"/>
-                                    <x-system.select title="货币" code="stripe_currency"
-                                                     :list="['HKD(港币)' => 'hkd', 'USD(美元)' => 'usd', 'SGD(新币)' => 'sgd', 'EUR(欧元)' => 'eur', 'GBP(英镑)' => 'gbp', 'JPY(日元)' => 'jpy', 'CAD(加元)' => 'cad']"/>
                                 </x-system.tab-pane>
                                 <x-system.tab-pane id="PayBeaver">
                                     <div class="form-group col-lg-6 d-flex">
@@ -436,150 +458,151 @@
     <script src="/assets/custom/jump-tab.js"></script>
     <script src="/assets/global/js/Plugin/dropify.js"></script>
     <script>
-        $(document).ready(function() {
-            $('#forbid_mode').selectpicker('val', '{{$forbid_mode}}');
-            $('#username_type').selectpicker('val', '{{$username_type ?? 'email'}}');
-            $('#is_invite_register').selectpicker('val', '{{$is_invite_register}}');
-            $('#is_activate_account').selectpicker('val', '{{$is_activate_account}}');
-            $('#ddns_mode').selectpicker('val', '{{$ddns_mode}}');
-            $('#is_captcha').selectpicker('val', '{{$is_captcha}}');
-            $('#referral_type').selectpicker('val', '{{$referral_type}}');
-            $('#is_email_filtering').selectpicker('val', '{{$is_email_filtering}}');
-            $('#is_AliPay').selectpicker('val', '{{$is_AliPay}}');
-            $('#is_QQPay').selectpicker('val', '{{$is_QQPay}}');
-            $('#is_WeChatPay').selectpicker('val', '{{$is_WeChatPay}}');
-            $('#is_otherPay').selectpicker('val', {!! $is_otherPay !!});
-            $('#oauth_path').selectpicker('val', {!! $oauth_path !!});
-            $('#account_expire_notification').selectpicker('val', {!! $account_expire_notification !!});
-            $('#data_anomaly_notification').selectpicker('val', {!! $data_anomaly_notification !!});
-            $('#data_exhaust_notification').selectpicker('val', {!! $data_exhaust_notification !!});
-            $('#node_blocked_notification').selectpicker('val', {!! $node_blocked_notification !!});
-            $('#node_daily_notification').selectpicker('val', {!! $node_daily_notification !!});
-            $('#node_offline_notification').selectpicker('val', {!! $node_offline_notification !!});
-            $('#password_reset_notification').selectpicker('val', '{{$password_reset_notification}}');
-            $('#payment_confirm_notification').selectpicker('val', '{{$payment_confirm_notification}}');
-            $('#payment_received_notification').selectpicker('val', {!! $payment_received_notification !!});
-            $('#ticket_closed_notification').selectpicker('val', {!! $ticket_closed_notification !!});
-            $('#ticket_created_notification').selectpicker('val', {!! $ticket_created_notification !!});
-            $('#ticket_replied_notification').selectpicker('val', {!! $ticket_replied_notification !!});
+      $(document).ready(function() {
+        $('#forbid_mode').selectpicker('val', '{{$forbid_mode}}');
+        $('#username_type').selectpicker('val', '{{$username_type ?? 'email'}}');
+        $('#is_invite_register').selectpicker('val', '{{$is_invite_register}}');
+        $('#is_activate_account').selectpicker('val', '{{$is_activate_account}}');
+        $('#ddns_mode').selectpicker('val', '{{$ddns_mode}}');
+        $('#is_captcha').selectpicker('val', '{{$is_captcha}}');
+        $('#referral_type').selectpicker('val', '{{$referral_type}}');
+        $('#is_email_filtering').selectpicker('val', '{{$is_email_filtering}}');
+        $('#is_AliPay').selectpicker('val', '{{$is_AliPay}}');
+        $('#is_QQPay').selectpicker('val', '{{$is_QQPay}}');
+        $('#is_WeChatPay').selectpicker('val', '{{$is_WeChatPay}}');
+        $('#standard_currency').selectpicker('val', '{{$standard_currency}}');
+        $('#is_otherPay').selectpicker('val', {!! $is_otherPay !!});
+        $('#oauth_path').selectpicker('val', {!! $oauth_path !!});
+        $('#account_expire_notification').selectpicker('val', {!! $account_expire_notification !!});
+        $('#data_anomaly_notification').selectpicker('val', {!! $data_anomaly_notification !!});
+        $('#data_exhaust_notification').selectpicker('val', {!! $data_exhaust_notification !!});
+        $('#node_blocked_notification').selectpicker('val', {!! $node_blocked_notification !!});
+        $('#node_daily_notification').selectpicker('val', {!! $node_daily_notification !!});
+        $('#node_offline_notification').selectpicker('val', {!! $node_offline_notification !!});
+        $('#password_reset_notification').selectpicker('val', '{{$password_reset_notification}}');
+        $('#payment_confirm_notification').selectpicker('val', '{{$payment_confirm_notification}}');
+        $('#payment_received_notification').selectpicker('val', {!! $payment_received_notification !!});
+        $('#ticket_closed_notification').selectpicker('val', {!! $ticket_closed_notification !!});
+        $('#ticket_created_notification').selectpicker('val', {!! $ticket_created_notification !!});
+        $('#ticket_replied_notification').selectpicker('val', {!! $ticket_replied_notification !!});
 
-            // Get all options within select
-            disablePayment(document.getElementById('is_AliPay').getElementsByTagName('option'));
-            disablePayment(document.getElementById('is_QQPay').getElementsByTagName('option'));
-            disablePayment(document.getElementById('is_WeChatPay').getElementsByTagName('option'));
-            disablePayment(document.getElementById('is_otherPay').getElementsByTagName('option'));
+        // Get all options within select
+        disablePayment(document.getElementById('is_AliPay').getElementsByTagName('option'));
+        disablePayment(document.getElementById('is_QQPay').getElementsByTagName('option'));
+        disablePayment(document.getElementById('is_WeChatPay').getElementsByTagName('option'));
+        disablePayment(document.getElementById('is_otherPay').getElementsByTagName('option'));
 
-            @if (!$captcha)
-            disableCaptcha(document.getElementById('is_captcha').getElementsByTagName('option'));
-            @endif
+          @if (!$captcha)
+          disableCaptcha(document.getElementById('is_captcha').getElementsByTagName('option'));
+          @endif
 
-        });
-
-        function disablePayment(op) {
-            for (let i = 1; i < op.length; i++) {
-                @json($payments).
-                includes(op[i].value)
-                    ? op[i].disabled = false
-                    : op[i].disabled = true;
-            }
-        }
-
-        function disableCaptcha(op) {
-            for (let i = 2; i < op.length; i++) {
-                op[i].disabled = true;
-            }
-        }
+      });
 
-        // 系统设置更新
-        function systemUpdate(systemItem, value) {
-            @can('admin.system.update')
-            $.post('{{route('admin.system.update')}}', {_token: '{{csrf_token()}}', name: systemItem, value: value}, function(ret) {
-                if (ret.status === 'success') {
-                    swal.fire({title: ret.message, icon: 'success', timer: 1500, showConfirmButton: false});
-                } else {
-                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-            });
-            @else
-            swal.fire({title: '您没有权限修改系统参数!', icon: 'error', timer: 1500, showConfirmButton: false});
-            @endcan
+      function disablePayment(op) {
+        for (let i = 1; i < op.length; i++) {
+            @json($payments).
+          includes(op[i].value)
+              ? op[i].disabled = false
+              : op[i].disabled = true;
         }
+      }
 
-        // 正常input更新
-        function update(systemItem) {
-            systemUpdate(systemItem, $('#' + systemItem).val());
+      function disableCaptcha(op) {
+        for (let i = 2; i < op.length; i++) {
+          op[i].disabled = true;
         }
+      }
 
-        // 需要检查限制的更新
-        function updateFromInput(systemItem, lowerBound = false, upperBound = false) {
-            let value = parseInt($('#' + systemItem).val());
-            if (lowerBound !== false && value < lowerBound) {
-                swal.fire({title: '不能小于' + lowerBound, icon: 'warning', timer: 1500, showConfirmButton: false});
-            } else if (upperBound !== false && value > upperBound) {
-                swal.fire({title: '不能大于' + upperBound, icon: 'warning', timer: 1500, showConfirmButton: false});
+      // 系统设置更新
+      function systemUpdate(systemItem, value) {
+          @can('admin.system.update')
+          $.post('{{route('admin.system.update')}}', {_token: '{{csrf_token()}}', name: systemItem, value: value}, function(ret) {
+            if (ret.status === 'success') {
+              swal.fire({title: ret.message, icon: 'success', timer: 1500, showConfirmButton: false});
             } else {
-                systemUpdate(systemItem, value);
+              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
             }
-        }
+          });
+          @else
+          swal.fire({title: '您没有权限修改系统参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+          @endcan
+      }
 
-        // 其他项更新选择
-        function updateFromOther(inputType, systemItem) {
-            let input = $('#' + systemItem);
-            switch (inputType) {
-                case 'select':
-                    input.on('changed.bs.select', function() {
-                        systemUpdate(systemItem, $(this).val());
-                    });
-                    break;
-                case 'multiSelect':
-                    input.on('changed.bs.select', function() {
-                        systemUpdate(systemItem, $(this).val().join(','));
-                    });
-                    break;
-                case 'switch':
-                    systemUpdate(systemItem, document.getElementById(systemItem).checked ? 1 : 0);
-                    break;
-                default:
-                    break;
-            }
-        }
+      // 正常input更新
+      function update(systemItem) {
+        systemUpdate(systemItem, $('#' + systemItem).val());
+      }
 
-        // 使用通知渠道 发送测试消息
-        @can('admin.test.notify')
-        function sendTestNotification(channel) {
-            $.post('{{route('admin.test.notify')}}', {_token: '{{csrf_token()}}', channel: channel}, function(ret) {
-                if (ret.status === 'success') {
-                    swal.fire({title: ret.message, icon: 'success', timer: 1500, showConfirmButton: false});
-                } else {
-                    swal.fire({title: ret.message, icon: 'error'});
-                }
-            });
+      // 需要检查限制的更新
+      function updateFromInput(systemItem, lowerBound = false, upperBound = false) {
+        let value = parseInt($('#' + systemItem).val());
+        if (lowerBound !== false && value < lowerBound) {
+          swal.fire({title: '不能小于' + lowerBound, icon: 'warning', timer: 1500, showConfirmButton: false});
+        } else if (upperBound !== false && value > upperBound) {
+          swal.fire({title: '不能大于' + upperBound, icon: 'warning', timer: 1500, showConfirmButton: false});
+        } else {
+          systemUpdate(systemItem, value);
         }
-        @endcan
+      }
 
-        // 生成网站安全码
-        function makeWebsiteSecurityCode() {
-            $.get('{{route('createStr')}}', function(ret) {
-                $('#website_security_code').val(ret);
+      // 其他项更新选择
+      function updateFromOther(inputType, systemItem) {
+        let input = $('#' + systemItem);
+        switch (inputType) {
+          case 'select':
+            input.on('changed.bs.select', function() {
+              systemUpdate(systemItem, $(this).val());
             });
+            break;
+          case 'multiSelect':
+            input.on('changed.bs.select', function() {
+              systemUpdate(systemItem, $(this).val().join(','));
+            });
+            break;
+          case 'switch':
+            systemUpdate(systemItem, document.getElementById(systemItem).checked ? 1 : 0);
+            break;
+          default:
+            break;
         }
+      }
+
+      // 使用通知渠道 发送测试消息
+      @can('admin.test.notify')
+      function sendTestNotification(channel) {
+        $.post('{{route('admin.test.notify')}}', {_token: '{{csrf_token()}}', channel: channel}, function(ret) {
+          if (ret.status === 'success') {
+            swal.fire({title: ret.message, icon: 'success', timer: 1500, showConfirmButton: false});
+          } else {
+            swal.fire({title: ret.message, icon: 'error'});
+          }
+        });
+      }
+      @endcan
+
+      // 生成网站安全码
+      function makeWebsiteSecurityCode() {
+        $.get('{{route('createStr')}}', function(ret) {
+          $('#website_security_code').val(ret);
+        });
+      }
 
-        @can('admin.test.epay')
-        function epayInfo() {
-            $.get('{{route('admin.test.epay')}}', function(ret) {
-                if (ret.status === 'success') {
-                    swal.fire({
-                        title: '易支付信息(仅供参考)',
-                        html: '商户状态: ' + ret.data['active'] + ' | 账号余额: ' + ret.data['money'] + ' | 结算账号:' + ret.data['account'] +
-                            '<br\><br\>渠道手续费:【支付宝 - ' + (100 - ret.data['alirate']) + '% | 微信 - ' + (100 - ret.data['wxrate']) +
-                            '% | QQ钱包 - ' + (100 - ret.data['qqrate']) + '%】<br\><br\> 请按照支付平台的介绍为准,本信息纯粹为Api获取信息',
-                        icon: 'info',
-                    });
-                } else {
-                    swal.fire({title: ret.message, icon: 'error'});
-                }
+      @can('admin.test.epay')
+      function epayInfo() {
+        $.get('{{route('admin.test.epay')}}', function(ret) {
+          if (ret.status === 'success') {
+            swal.fire({
+              title: '易支付信息(仅供参考)',
+              html: '商户状态: ' + ret.data['active'] + ' | 账号余额: ' + ret.data['money'] + ' | 结算账号:' + ret.data['account'] +
+                  '<br\><br\>渠道手续费:【支付宝 - ' + (100 - ret.data['alirate']) + '% | 微信 - ' + (100 - ret.data['wxrate']) +
+                  '% | QQ钱包 - ' + (100 - ret.data['qqrate']) + '%】<br\><br\> 请按照支付平台的介绍为准,本信息纯粹为Api获取信息',
+              icon: 'info',
             });
-        }
+          } else {
+            swal.fire({title: ret.message, icon: 'error'});
+          }
+        });
+      }
         @endcan
     </script>
 @endsection

+ 6 - 2
resources/views/admin/coupon/create.blade.php

@@ -64,8 +64,10 @@
                         <label class="col-md-2 col-form-label" for="value">优惠额度</label>
                         <div class="col-md-10">
                             <div class="input-group">
+                                <div class="input-group-prepend" id="amount">
+                                    <span class="input-group-text">{{array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')]}}</span>
+                                </div>
                                 <input type="number" class="form-control col-md-3" min="1" max="99" name="value" id="value" value="{{old('value')}}" required/>
-                                <span class="input-group-text" id="amount">元</span>
                                 <span class="input-group-text discount" style="display: none;">%</span>
                             </div>
                             <span class="text-help discount" style="display: none;"> 范围为 1% ~ 99% </span>
@@ -93,8 +95,10 @@
                             <label class="col-md-2 col-form-label" for="minimum">满减条件</label>
                             <div class="col-md-10">
                                 <div class="input-group">
+                                    <div class="input-group-prepend">
+                                        <span class="input-group-text">{{array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')]}}</span>
+                                    </div>
                                     <input type="number" class="form-control col-md-3" name="minimum" id="minimum" value="{{old('minimum')}}" step="0.01"/>
-                                    <span class="input-group-text">元</span>
                                 </div>
                                 <span class="text-help"> 当支付金额超过N值时,才能使用本优惠劵;不设置/0,即为无限制 </span>
                             </div>

+ 1 - 1
resources/views/admin/coupon/index.blade.php

@@ -73,7 +73,7 @@
                             <td> {{$coupon->priority}} </td>
                             <td> {{$coupon->type === 3 ? '一次性' : ($coupon->usable_times ?? '无限制')}} </td>
                             <td>
-                                {{($coupon->type === 2 ?'减 ':'抵 ').$coupon->value.($coupon->type === 2 ?' %':' 元')}}
+                                {{($coupon->type === 2 ?'减 ':'抵 ').($coupon->type === 2 ? $coupon->value.' %': \App\Components\Helpers::getPriceTag($coupon->value))}}
                             </td>
                             <td> {{$coupon->start_time}} ~ {{$coupon->end_time}} </td>
                             <td>

+ 3 - 3
resources/views/admin/coupon/show.blade.php

@@ -56,13 +56,13 @@
                         <p class="form-control text-fit">
                             @switch ($coupon->type)
                                 @case(1)
-                                    抵用 <code>{{$coupon->value}}</code>
+                                    抵用 <code>{{ \App\Components\Helpers::getPriceTag($coupon->value) }}</code>
                                     @break
                                 @case(2)
                                     减 <code>{{$coupon->value}}</code> %
                                     @break
                                 @case(3)
-                                    充值 <code>{{$coupon->value}}</code>
+                                    充值 <code>{{ \App\Components\Helpers::getPriceTag($coupon->value) }}</code>
                                     @break
                                 @default
                                     未知卡券
@@ -92,7 +92,7 @@
                         <div class="form-group row">
                             <label class="col-md-2 col-form-label" for="minimum">满减条件</label>
                             <div class="col-md-10">
-                                <p class="form-control text-fit">当支付金额超过<strong> {{$coupon->limit['minimum']}}</strong> 时,才能使用本优惠劵</p>
+                                <p class="form-control text-fit">当支付金额超过<strong> {{ \App\Components\Helpers::getPriceTag($coupon->limit['minimum']) }}</strong> 时,才能使用本优惠劵</p>
                             </div>
                         </div>
                     @endisset

+ 1 - 1
resources/views/admin/layouts.blade.php

@@ -38,7 +38,7 @@
                     </li>
                     <li class="nav-item dropdown">
                         <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown" role="button">
-                            <span class="icon wb-flag"></span>
+                            <span class="icon wb-globe"></span>
                             <span class="icon wb-chevron-down-mini"></span>
                         </a>
                         <div class="dropdown-menu" role="menu">

+ 1 - 1
resources/views/admin/logs/callback.blade.php

@@ -54,7 +54,7 @@
                                     {{$log->out_trade_no}}
                                 @endcan
                             </td>
-                            <td> {{$log->amount}}</td>
+                            <td> {{$log->amount_tag}}</td>
                             <td> {!! $log->trade_status_label !!} </td>
                             <td> {{$log->created_at}} </td>
                         </tr>

+ 2 - 2
resources/views/admin/logs/order.blade.php

@@ -108,8 +108,8 @@
                             <td> {{$order->sn}}</td>
                             <td> {{$order->goods->name  ?? trans('user.recharge_credit')}} </td>
                             <td> {{$order->coupon ? $order->coupon->name . ' - ' . $order->coupon->sn : ''}} </td>
-                            <td> ¥{{$order->origin_amount}} </td>
-                            <td> ¥{{$order->amount}} </td>
+                            <td> {{$order->origin_amount_tag}} </td>
+                            <td> {{$order->amount_tag}} </td>
                             <td>
                                 {{$order->pay_way_label}}
                             </td>

+ 1 - 1
resources/views/admin/shop/index.blade.php

@@ -75,7 +75,7 @@
                                 @endif
                             </td>
                             <td> {{$goods->traffic_label}} </td>
-                            <td> {{$goods->price}}</td>
+                            <td> {{$goods->price_tag}}</td>
                             <td> {{$goods->sort}} </td>
                             <td><code>{{$goods->use_count}} / {{$goods->total_count}}</code></td>
                             <td>

+ 6 - 2
resources/views/admin/shop/info.blade.php

@@ -52,8 +52,10 @@
                             <div class="form-group row">
                                 <label class="col-md-2 col-form-label" for="price">售价</label>
                                 <div class="col-md-4 input-group">
+                                    <div class="input-group-prepend">
+                                        <span class="input-group-text">{{array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')]}}</span>
+                                    </div>
                                     <input type="number" class="form-control" name="price" id="price" step="0.01" required/>
-                                    <span class="input-group-text">元</span>
                                 </div>
                             </div>
                             <div class="form-group row">
@@ -79,8 +81,10 @@
                             <div class="form-group row package-renew">
                                 <label class="col-md-2 col-form-label" for="renew">流量重置价格</label>
                                 <div class="col-md-4 input-group">
+                                    <div class="input-group-prepend">
+                                        <span class="input-group-text">{{array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')]}}</span>
+                                    </div>
                                     <input type="number" class="form-control" name="renew" id="renew" step="0.01" value="0"/>
-                                    <span class="input-group-text">元</span>
                                 </div>
                             </div>
                             <div class="form-group row package-renew">

+ 2 - 2
resources/views/components/payment/detail.blade.php

@@ -35,10 +35,10 @@
                         <div class="panel-body">
                             <ul class="list-group">
                                 <li class="list-group-item">
-                                    订单原价:¥{{ $order->origin_amount }}
+                                    订单原价:{{ $order->origin_amount_tag }}
                                 </li>
                                 <li class="list-group-item">
-                                    实际支付金额:¥{{ $order->origin_amount }}
+                                    实际支付金额:{{ $order->origin_amount_tag }}
                                 </li>
                                 <li class="list-group-item">
                                     {{ trans('user.payment_method') }}:

+ 2 - 2
resources/views/components/system/select.blade.php

@@ -5,8 +5,8 @@
         <label class="col-md-3 col-form-label" for="{{$code}}">{{$title}}</label>
         <div class="col-md-9">
             <select id="{{$code}}" data-plugin="selectpicker" data-style="btn-outline btn-primary" onchange="updateFromOther('select','{{$code}}')" @if ($multiple) multiple @endif>
-                @foreach ($list as $item => $value)
-                    <option value="{{$value}}">{{$item}}</option>
+                @foreach ($list as $key => $value)
+                    <option value="{{$value}}">{{$key}}</option>
                 @endforeach
             </select>
             @isset($help)

+ 2 - 2
resources/views/components/ticket/reply.blade.php

@@ -35,10 +35,10 @@
                         <div class="panel-body">
                             <ul class="list-group">
                                 <li class="list-group-item">
-                                    订单原价:¥{{ $order->origin_amount }}
+                                    订单原价:{{ $order->origin_amount_tag }}
                                 </li>
                                 <li class="list-group-item">
-                                    实际支付金额:¥{{ $order->origin_amount }}
+                                    实际支付金额:{{ $order->origin_amount_tag }}
                                 </li>
                                 <li class="list-group-item">
                                     {{ trans('user.payment_method') }}:

+ 118 - 114
resources/views/user/buy.blade.php

@@ -26,7 +26,7 @@
                                 <br>
                                 {{trans('user.account.speed_limit')}}<strong> {{ $goods->speed_limit ? $goods->speed_limit.' Mbps' : trans('user.service.unlimited') }} </strong>
                             </td>
-                            <td class="text-middle"> ¥{{$goods->price}} </td>
+                            <td class="text-middle"> {{$goods->price_tag}} </td>
                             <td class="text-middle"> x 1</td>
                         </tr>
                         </tbody>
@@ -47,10 +47,10 @@
                         <div class="col-lg-3 offset-lg-6 text-right">
                             <p id="discount"></p>
                             <p>{{trans('user.shop.subtotal')}}
-                                <span>¥{{$goods->price}}</span>
+                                <span>{{$goods->price_tag}}</span>
                             </p>
                             <p class="page-invoice-amount">{{trans('user.shop.total')}}
-                                <span class="grand-total">¥{{$goods->price}}</span>
+                                <span class="grand-total">{{$goods->price_tag}}</span>
                             </p>
                         </div>
                     @endif
@@ -71,122 +71,126 @@
 @endsection
 @section('javascript')
     <script>
-        // 校验优惠券是否可用
-        function redeemCoupon() {
-            const coupon_sn = $('#coupon_sn').val();
-            const goods_price = '{{$goods->price}}';
-            $.ajax({
-                method: 'POST',
-                url: '{{route('redeemCoupon', $goods)}}',
-                dataType: 'json',
-                data: {_token: '{{csrf_token()}}', coupon_sn: coupon_sn},
-                success: function(ret) {
-                    $('.input-group-prepend').remove();
-                    if (ret.status === 'success') {
-                        $('#coupon_sn').parent().prepend(
-                            '<div class="input-group-prepend"><span class="input-group-text bg-green-700"><i class="icon wb-check white" aria-hidden="true"></i></span></div>');
-                        // 根据类型计算折扣后的总金额
-                        let total_price = 0;
-                        let coupon_text = document.getElementById('discount');
-                        if (ret.data.type === 2) {
-                            total_price = goods_price * (1 - ret.data.value / 100);
-                            coupon_text.innerHTML = '【{{trans('user.coupon.attribute')}}】:' + ret.data.name + ' - '
-                                + (100 - ret.data.value) + '%<br> {{trans('user.coupon.discount')}} - ¥' + total_price.toFixed(2);
-                            total_price = goods_price - total_price;
-                        } else {
-                            total_price = goods_price - ret.data.value;
-                            total_price = total_price > 0 ? total_price : 0;
-                            if (ret.data.type === 1) {
-                                coupon_text.innerHTML = '【{{trans('user.coupon.voucher')}}】:' + ret.data.name + ' -¥' + ret.data.value;
-                            }
-                        }
+      // 校验优惠券是否可用
+      function redeemCoupon() {
+        const coupon_sn = $('#coupon_sn').val();
+        let tag = '{{$goods->price_tag}}'.match(/(.*?[^0-9])(\d+\.?.*)/);
+        const goods_price = tag[2];
+        const sign = tag[1];
+        $.ajax({
+          method: 'POST',
+          url: '{{route('redeemCoupon', $goods)}}',
+          dataType: 'json',
+          data: {_token: '{{csrf_token()}}', coupon_sn: coupon_sn},
+          success: function(ret) {
+            $('.input-group-prepend').remove();
+            if (ret.status === 'success') {
+              $('#coupon_sn').parent().prepend(
+                  '<div class="input-group-prepend"><span class="input-group-text bg-green-700"><i class="icon wb-check white" aria-hidden="true"></i></span></div>');
+              // 根据类型计算折扣后的总金额
+              let total_price = 0;
+              let coupon_text = document.getElementById('discount');
+              if (ret.data.type === 2) {
+                const discount = goods_price * (ret.data.value / 100);
 
-                        // 四舍五入,保留2位小数
-                        $('.grand-total').text('¥' + total_price.toFixed(2));
-                        swal.fire({
-                            title: ret.message,
-                            icon: 'success',
-                            timer: 1300,
-                            showConfirmButton: false,
-                        });
-                    } else {
-                        $('.grand-total').text('¥' + goods_price);
-                        $('#coupon_sn').parent().prepend(
-                            '<div class="input-group-prepend"><span class="input-group-text bg-red-700"><i class="icon wb-close white" aria-hidden="true"></i></span></div>');
-                        swal.fire({
-                            title: ret.title,
-                            text: ret.message,
-                            icon: 'error',
-                        });
-                    }
-                },
-            });
-        }
+                coupon_text.innerHTML = '【{{trans('user.coupon.attribute')}}】:' + ret.data.name + '_'
+                    + (100 - ret.data.value) + '%<br> {{trans('user.coupon.discount')}}: - ' + sign + discount.toFixed(2);
+                total_price = goods_price - discount;
+              } else {
+                console.log(ret.data.value);
+                total_price = goods_price - ret.data.value.match(/(.*?[^0-9])(\d+\.?.*)/)[2];
+                total_price = total_price > 0 ? total_price : 0;
+                if (ret.data.type === 1) {
+                  coupon_text.innerHTML = '【{{trans('user.coupon.voucher')}}】:' + ret.data.name + ' -' + ret.data.value;
+                }
+              }
 
-        // 检查预支付
-        function pay(method, pay_type) {
-            // 存在套餐 和 购买类型为套餐时 出现提示
-            if ('{{$activePlan}}' === '1' && '{{$goods->type}}' === '2') {
-                swal.fire({
-                    title: '{{trans('user.shop.conflict')}}',
-                    html: '{!! trans('user.shop.conflict_tips') !!}',
-                    icon: 'info',
-                    showCancelButton: true,
-                    cancelButtonText: '{{trans('common.back')}}',
-                    confirmButtonText: '{{trans('common.continues')}}',
-                }).then((result) => {
-                    if (result.value) {
-                        contiousPay(method, pay_type);
-                    }
-                });
+              // 四舍五入,保留2位小数
+              $('.grand-total').text(sign + total_price.toFixed(2));
+              swal.fire({
+                title: ret.message,
+                icon: 'success',
+                timer: 1300,
+                showConfirmButton: false,
+              });
             } else {
-                contiousPay(method, pay_type);
+              $('.grand-total').text(sign + goods_price);
+              $('#coupon_sn').parent().prepend(
+                  '<div class="input-group-prepend"><span class="input-group-text bg-red-700"><i class="icon wb-close white" aria-hidden="true"></i></span></div>');
+              swal.fire({
+                title: ret.title,
+                text: ret.message,
+                icon: 'error',
+              });
             }
-        }
+          },
+        });
+      }
 
-        function contiousPay(method, pay_type) {
-            const goods_id = '{{$goods->id}}';
-            const coupon_sn = $('#coupon_sn').val();
-            $.ajax({
-                method: 'POST',
-                url: '{{route('purchase')}}',
-                dataType: 'json',
-                data: {
-                    _token: '{{csrf_token()}}',
-                    goods_id: goods_id,
-                    coupon_sn: coupon_sn,
-                    method: method,
-                    pay_type: pay_type,
-                },
-                success: function(ret) {
-                    if (ret.status === 'success') {
-                        swal.fire({
-                            title: ret.message,
-                            icon: 'success',
-                            timer: 1300,
-                            showConfirmButton: false,
-                        });
-                        if (method === 'credit') {
-                            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.href = '{{route('invoice')}}');
-                        }
-                        if (ret.data) {
-                            window.location.href = '{{route('orderDetail', '')}}/' + ret.data;
-                        } else if (ret.url) {
-                            window.location.href = ret.url;
-                        }
-                    } else if (ret.status === 'info') {
-                        swal.fire({title: ret.title, text: ret.message, icon: 'question'});
-                    } else {
-                        swal.fire({
-                            title: ret.message,
-                            icon: 'error',
-                        });
-                    }
-                },
-                error: function() {
-                    swal.fire('{{trans('user.unknown').trans('common.error')}}', '{{trans('user.shop.call4help')}}', 'error');
-                },
-            });
+      // 检查预支付
+      function pay(method, pay_type) {
+        // 存在套餐 和 购买类型为套餐时 出现提示
+        if ('{{$activePlan}}' === '1' && '{{$goods->type}}' === '2') {
+          swal.fire({
+            title: '{{trans('user.shop.conflict')}}',
+            html: '{!! trans('user.shop.conflict_tips') !!}',
+            icon: 'info',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('common.back')}}',
+            confirmButtonText: '{{trans('common.continues')}}',
+          }).then((result) => {
+            if (result.value) {
+              contiousPay(method, pay_type);
+            }
+          });
+        } else {
+          contiousPay(method, pay_type);
         }
+      }
+
+      function contiousPay(method, pay_type) {
+        const goods_id = '{{$goods->id}}';
+        const coupon_sn = $('#coupon_sn').val();
+        $.ajax({
+          method: 'POST',
+          url: '{{route('purchase')}}',
+          dataType: 'json',
+          data: {
+            _token: '{{csrf_token()}}',
+            goods_id: goods_id,
+            coupon_sn: coupon_sn,
+            method: method,
+            pay_type: pay_type,
+          },
+          success: function(ret) {
+            if (ret.status === 'success') {
+              swal.fire({
+                title: ret.message,
+                icon: 'success',
+                timer: 1300,
+                showConfirmButton: false,
+              });
+              if (method === 'credit') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.href = '{{route('invoice')}}');
+              }
+              if (ret.data) {
+                window.location.href = '{{route('orderDetail', '')}}/' + ret.data;
+              } else if (ret.url) {
+                window.location.href = ret.url;
+              }
+            } else if (ret.status === 'info') {
+              swal.fire({title: ret.title, text: ret.message, icon: 'question'});
+            } else {
+              swal.fire({
+                title: ret.message,
+                icon: 'error',
+              });
+            }
+          },
+          error: function() {
+            swal.fire('{{trans('user.unknown').trans('common.error')}}', '{{trans('user.shop.call4help')}}', 'error');
+          },
+        });
+      }
     </script>
 @endsection

+ 3 - 3
resources/views/user/components/payment/default.blade.php

@@ -14,10 +14,10 @@
                 <div class="row">
                     <div class="col-md-6">
                         <ul class="list-group list-group-dividered">
-                            <li class="list-group-item">{{trans('user.shop.service').''.$name}}</li>
-                            <li class="list-group-item">{{trans('user.shop.price').':¥'.$payment->amount}}</li>
+                            <li class="list-group-item">{{trans('user.shop.service').': '.$name}}</li>
+                            <li class="list-group-item">{{trans('user.shop.price').': '.$payment->amount_tag}}</li>
                             @if($days !== 0)
-                                <li class="list-group-item">{{trans('common.available_date').''.$days.trans_choice('validation.attributes.day', 1)}}</li>
+                                <li class="list-group-item">{{trans('common.available_date').': '.$days.trans_choice('validation.attributes.day', 1)}}</li>
                             @endif
                             <li class="list-group-item"> {!! trans('user.payment.close_tips', ['minutes' => config('tasks.close.orders')]) !!}</li>
                         </ul>

+ 3 - 3
resources/views/user/components/payment/manual.blade.php

@@ -158,10 +158,10 @@
                     </div>
                     <div class="mx-auto w-md-p50 w-lg-p25">
                         <ul class="list-group list-group-dividered">
-                            <li class="list-group-item">{{trans('user.shop.service').''.$name}}</li>
-                            <li class="list-group-item">{{trans('user.shop.price').':¥'.$payment->amount}}</li>
+                            <li class="list-group-item">{{trans('user.shop.service').': '.$name}}</li>
+                            <li class="list-group-item">{{trans('user.shop.price').': '.$payment->amount_tag}}</li>
                             @if($days !== 0)
-                                <li class="list-group-item">{{trans('common.available_date').''.$days.trans_choice('validation.attributes.day', 1)}}</li>
+                                <li class="list-group-item">{{trans('common.available_date').': '.$days.trans_choice('validation.attributes.day', 1)}}</li>
                             @endif
                         </ul>
                     </div>

+ 2 - 2
resources/views/user/invoiceDetail.blade.php

@@ -60,10 +60,10 @@
                                     {{trans('user.recharge_credit')}}
                                 @endif
                             </td>
-                            <td><strong>¥</strong> {{$order->origin_amount}} </td>
+                            <td> {{$order->origin_amount_tag}} </td>
                             <td> 1</td>
                             <td>{{$order->coupon->name ?? trans('common.none')}}</td>
-                            <td> ¥{{$order->amount}} </td>
+                            <td> {{$order->amount_tag}} </td>
                             <td> {!! $order->status_label !!} </td>
                         </tr>
                         </tbody>

+ 1 - 1
resources/views/user/invoices.blade.php

@@ -35,7 +35,7 @@
                             <td><a href="/invoice/{{$order->sn}}" target="_blank">{{$order->sn}}</a></td>
                             <td>{{$order->goods->name ?? trans('user.recharge_credit')}}</td>
                             <td>{{$order->pay_way === 1 ? trans('user.shop.pay_credit') : trans('user.shop.pay_online')}}</td>
-                            <td>¥{{$order->amount}}</td>
+                            <td>{{$order->amount_tag}}</td>
                             <td>{{$order->created_at}}</td>
                             <td>{{empty($order->goods) || $order->goods_id === null || $order->status === 3 ? '' : $order->expired_at}}</td>
                             <td>{!! $order->status_label !!}</td>

+ 14 - 2
resources/views/user/layouts.blade.php

@@ -45,8 +45,7 @@
                     <li class="nav-item dropdown">
                         <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown" data-animation="scale-up"
                            aria-expanded="false" role="button">
-                            <span class="icon wb-flag"></span>
-                            <span class="icon wb-chevron-down-mini"></span>
+                            <span class="icon font-size-16 wb-globe"></span>
                         </a>
                         <div class="dropdown-menu" role="menu">
                             @foreach (config('common.language') as $key => $value)
@@ -55,6 +54,19 @@
                                 </a>
                             @endforeach
                         </div>
+                    <li class="nav-item dropdown">
+                        <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown" data-animation="scale-up"
+                           aria-expanded="false" role="button">
+                            <span class="icon wb-payment"></span>
+                        </a>
+                        <div class="dropdown-menu" role="menu">
+                            @foreach (config('common.currency') as $country_code => $currency)
+                                <a class="dropdown-item" href="{{route('currency', ['code' => $currency['code']])}}" role="menuitem">
+                                    <i class="fi fi-{{$country_code}}"></i> <span style="padding: inherit;">{{$currency['symbol'].' '.$currency['name']}}</span>
+                                </a>
+                            @endforeach
+                        </div>
+                    </li>
                     </li>
                     <li class="nav-item dropdown">
                         <a href="#" aria-expanded="false" class="nav-link navbar-avatar" data-animation="scale-up"

+ 5 - 5
resources/views/user/referral.blade.php

@@ -88,8 +88,8 @@
                                 <tr>
                                     <td> {{$loop->iteration}} </td>
                                     <td> {{empty($referralLog->invitee) ? '【'.trans('common.deleted_item', ['attribute' => trans('common.account')]).'】' : str_replace(mb_substr($referralLog->invitee->username, 3, 4), "****", $referralLog->invitee->username)}} </td>
-                                    <td> ¥{{$referralLog->amount}} </td>
-                                    <td> ¥{{$referralLog->commission}} </td>
+                                    <td> {{$referralLog->amount_tag}} </td>
+                                    <td> {{$referralLog->commission_tag}} </td>
                                     <td> {{$referralLog->created_at}} </td>
                                     <td>{!! $referralLog->status_label !!}</td>
                                 </tr>
@@ -104,7 +104,7 @@
                             </div>
                             <div class="col-md-6 col-sm-6">
                                 <nav class="Page navigation float-right">
-                                    {{$referralLogList->appends(Arr::except(Request::query(), 'user_page'))->links()}}
+                                    {{$referralLogList->appends(Arr::except(Request::query(), 'log_page'))->links()}}
                                 </nav>
                             </div>
                         </div>
@@ -129,7 +129,7 @@
                                 <tr>
                                     <td> {{$loop->iteration}} </td>
                                     <td> {{$referralApply->created_at}} </td>
-                                    <td> ¥{{$referralApply->amount}} </td>
+                                    <td> {{$referralApply->amount_tag}} </td>
                                     <td>
                                         {!! $referralApply->status_label !!}
                                     </td>
@@ -140,7 +140,7 @@
                     </div>
                     <div class="card-footer card-footer-transparent">
                         <nav class="Page navigation float-right">
-                            {{$referralApplyList->appends(Arr::except(Request::query(), 'user_page'))->links()}}
+                            {{$referralApplyList->appends(Arr::except(Request::query(), 'apply_page'))->links()}}
                         </nav>
                     </div>
                 </div>

+ 110 - 110
resources/views/user/services.blade.php

@@ -13,7 +13,7 @@
                         </button>
                         <span class="font-weight-400">{{trans('user.account.credit')}}</span>
                         <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{Auth::getUser()->credit}}</span>
+                            <span class="font-size-40 font-weight-100">{{Auth::getUser()->credit_tag}}</span>
                             <br/>
                             <button class="btn btn-danger float-right mr-15" data-toggle="modal" data-target="#charge_modal">{{trans('user.recharge')}}</button>
                         </div>
@@ -27,7 +27,7 @@
                             </button>
                             <span class="font-weight-400">{{trans('user.reset_data.')}}</span>
                             <div class="content-text text-center mb-0">
-                                <span class="font-size-20 font-weight-100">{{trans('user.reset_data.required')}} <code>¥{{$renewTraffic}}</code></span>
+                                <span class="font-size-20 font-weight-100">{{trans('user.reset_data.required')}} <code>{{$renewTraffic}}</code></span>
                                 <br/>
                                 <button class="btn btn-danger mt-10" onclick="resetTraffic()">{{trans('common.reset')}}</button>
                             </div>
@@ -61,8 +61,7 @@
                                         <div class="pricing-header text-white" style="background-color: {{$goods->color}}">
                                             <div class="pricing-title font-size-20">{{$goods->name}}</div>
                                             <div class="pricing-price text-white @if($goods->type === 1) text-center @endif">
-                                                <span class="pricing-currency">¥</span>
-                                                <span class="pricing-amount">{{$goods->price}}</span>
+                                                <span class="pricing-amount">{{$goods->price_tag}}</span>
                                                 @if($goods->type === 2)
                                                     <span class="pricing-period">/ {{$goods->days.trans_choice('validation.attributes.day', 1)}}</span>
                                                 @endif
@@ -123,7 +122,8 @@
                             <div class="form-group row charge_credit">
                                 <label for="amount" class="offset-md-1 col-md-2 col-form-label">{{trans('user.shop.change_amount')}}</label>
                                 <div class="col-md-8">
-                                    <input type="text" name="amount" id="amount" data-plugin="ionRangeSlider" data-min=1 data-max=300 data-from=40 data-prefix="¥"/>
+                                    <input type="text" name="amount" id="amount" data-plugin="ionRangeSlider" data-min=1 data-max=300 data-from=40 data-prefix="{{array_column
+                                    (config('common.currency'), 'symbol', 'code')[session('currency') ?? sysConfig('standard_currency')]}}"/>
                                 </div>
                             </div>
                         @endif
@@ -150,122 +150,122 @@
     <script src="assets/global/vendor/ionrangeslider/ion.rangeSlider.min.js"></script>
     <script src="assets/global/js/Plugin/ionrangeslider.js"></script>
     <script>
-        function itemControl(value) {
-            if (value === 1) {
-                $('.charge_credit').show();
-                $('#change_btn').hide();
-                $('#charge_coupon_code').hide();
-            } else {
-                $('.charge_credit').hide();
-                $('#charge_coupon_code').show();
-                $('#change_btn').show();
-            }
+      function itemControl(value) {
+        if (value === 1) {
+          $('.charge_credit').show();
+          $('#change_btn').hide();
+          $('#charge_coupon_code').hide();
+        } else {
+          $('.charge_credit').hide();
+          $('#charge_coupon_code').show();
+          $('#change_btn').show();
         }
+      }
 
-        $(document).ready(function() {
-            let which_selected = 2;
-            @if(sysConfig('is_onlinePay') || sysConfig('alipay_qrcode') || sysConfig('wechat_qrcode'))
-                which_selected = 1;
-            @endif
+      $(document).ready(function() {
+        let which_selected = 2;
+          @if(sysConfig('is_onlinePay') || sysConfig('alipay_qrcode') || sysConfig('wechat_qrcode'))
+              which_selected = 1;
+          @endif
 
-            itemControl(which_selected);
-            $('charge_type').val(which_selected);
-        });
+          itemControl(which_selected);
+        $('charge_type').val(which_selected);
+      });
 
-        // 切换充值方式
-        $('#charge_type').change(function() {
-            itemControl(parseInt($(this).val()));
-        });
+      // 切换充值方式
+      $('#charge_type').change(function() {
+        itemControl(parseInt($(this).val()));
+      });
 
-        // 重置流量
-        function resetTraffic() {
-            swal.fire({
-                title: '{{trans('user.reset_data.')}}',
-                text: '{{trans('user.reset_data.cost_tips', ['amount' => $renewTraffic])}}',
-                icon: 'question',
-                showCancelButton: true,
-                cancelButtonText: '{{trans('common.close')}}',
-                confirmButtonText: '{{trans('common.confirm')}}',
-            }).then((result) => {
-                if (result.value) {
-                    $.post('{{route('resetTraffic')}}', {_token: '{{csrf_token()}}'}, function(ret) {
-                        if (ret.status === 'success') {
-                            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                        } else {
-                            swal.fire({
-                                title: ret.message,
-                                text: ret.data,
-                                icon: 'error',
-                            }).then(() => window.location.reload());
-                        }
-                    });
-                }
+      // 重置流量
+      function resetTraffic() {
+        swal.fire({
+          title: '{{trans('user.reset_data.')}}',
+          text: '{{trans('user.reset_data.cost_tips', ['amount' => $renewTraffic])}}',
+          icon: 'question',
+          showCancelButton: true,
+          cancelButtonText: '{{trans('common.close')}}',
+          confirmButtonText: '{{trans('common.confirm')}}',
+        }).then((result) => {
+          if (result.value) {
+            $.post('{{route('resetTraffic')}}', {_token: '{{csrf_token()}}'}, function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({
+                  title: ret.message,
+                  text: ret.data,
+                  icon: 'error',
+                }).then(() => window.location.reload());
+              }
             });
-        }
+          }
+        });
+      }
 
-        // 充值
-        function pay(method, pay_type) {
-            const paymentType = parseInt($('#charge_type').val() ?? 2);
-            const charge_coupon = $('#charge_coupon').val().trim();
-            const amount = parseInt($('#amount').val());
-            if (paymentType === 1) {
-                if (amount <= 0) {
-                    swal.fire({title: '{{trans('common.error')}}', text: '{{trans('user.payment.error')}}', icon: 'warning', timer: 1000, showConfirmButton: false});
-                    return false;
-                }
+      // 充值
+      function pay(method, pay_type) {
+        const paymentType = parseInt($('#charge_type').val() ?? 2);
+        const charge_coupon = $('#charge_coupon').val().trim();
+        const amount = parseInt($('#amount').val());
+        if (paymentType === 1) {
+          if (amount <= 0) {
+            swal.fire({title: '{{trans('common.error')}}', text: '{{trans('user.payment.error')}}', icon: 'warning', timer: 1000, showConfirmButton: false});
+            return false;
+          }
 
-                $.ajax({
-                    method: 'POST',
-                    url: '{{route('purchase')}}',
-                    data: {_token: '{{csrf_token()}}', amount: amount, method: method, pay_type: pay_type},
-                    dataType: 'json',
-                    beforeSend: function() {
-                        $('#charge_msg').show().html('{{trans('user.payment.creating')}}');
-                    },
-                    success: function(ret) {
-                        $('#charge_msg').show().html(ret.message);
-                        if (ret.status === 'fail') {
-                            return false;
-                        } else {
-                            if (ret.data) {
-                                window.location.href = '{{route('orderDetail' , '')}}/' + ret.data;
-                            } else if (ret.url) {
-                                window.location.href = ret.url;
-                            }
-                        }
-                    },
-                    error: function() {
-                        $('#charge_msg').show().html("{{trans('user.error_response')}}");
-                    },
-                });
-            } else if (paymentType === 2) {
-                if (charge_coupon === '') {
-                    $('#charge_msg').show().html("{{trans('validation.required', ['attribute' => trans('user.coupon.attribute')])}}");
-                    $('#charge_coupon').focus();
-                    return false;
+          $.ajax({
+            method: 'POST',
+            url: '{{route('purchase')}}',
+            data: {_token: '{{csrf_token()}}', amount: amount, method: method, pay_type: pay_type},
+            dataType: 'json',
+            beforeSend: function() {
+              $('#charge_msg').show().html('{{trans('user.payment.creating')}}');
+            },
+            success: function(ret) {
+              $('#charge_msg').show().html(ret.message);
+              if (ret.status === 'fail') {
+                return false;
+              } else {
+                if (ret.data) {
+                  window.location.href = '{{route('orderDetail' , '')}}/' + ret.data;
+                } else if (ret.url) {
+                  window.location.href = ret.url;
                 }
+              }
+            },
+            error: function() {
+              $('#charge_msg').show().html("{{trans('user.error_response')}}");
+            },
+          });
+        } else if (paymentType === 2) {
+          if (charge_coupon === '') {
+            $('#charge_msg').show().html("{{trans('validation.required', ['attribute' => trans('user.coupon.attribute')])}}");
+            $('#charge_coupon').focus();
+            return false;
+          }
 
-                $.ajax({
-                    method: 'POST',
-                    url: '{{route('recharge')}}',
-                    data: {_token: '{{csrf_token()}}', coupon_sn: charge_coupon},
-                    beforeSend: function() {
-                        $('#charge_msg').show().html("{{trans('user.recharging')}}");
-                    },
-                    success: function(ret) {
-                        if (ret.status === 'fail') {
-                            $('#charge_msg').show().html(ret.message);
-                            return false;
-                        }
+          $.ajax({
+            method: 'POST',
+            url: '{{route('recharge')}}',
+            data: {_token: '{{csrf_token()}}', coupon_sn: charge_coupon},
+            beforeSend: function() {
+              $('#charge_msg').show().html("{{trans('user.recharging')}}");
+            },
+            success: function(ret) {
+              if (ret.status === 'fail') {
+                $('#charge_msg').show().html(ret.message);
+                return false;
+              }
 
-                        $('#charge_modal').modal('hide');
-                        window.location.reload();
-                    },
-                    error: function() {
-                        $('#charge_msg').show().html("{{trans('user.error_response')}}");
-                    },
-                });
-            }
+              $('#charge_modal').modal('hide');
+              window.location.reload();
+            },
+            error: function() {
+              $('#charge_msg').show().html("{{trans('user.error_response')}}");
+            },
+          });
         }
+      }
     </script>
 @endsection

+ 1 - 0
routes/user.php

@@ -22,6 +22,7 @@ Route::match(['get', 'post'], 'profile', 'UserController@profile')->name('profil
 Route::post('switchToAdmin', 'UserController@switchToAdmin')->name('switch'); // 转换成管理员的身份
 Route::post('charge', 'UserController@charge')->name('recharge'); // 卡券余额充值
 Route::get('knowledge', 'UserController@knowledge')->name('knowledge'); // 帮助中心
+Route::get('currency/{code}', 'UserController@switchCurrency')->name('currency'); // 语言切换
 
 Route::namespace('User')->group(function () {
     Route::get('referral', 'AffiliateController@referral')->name('commission'); // 推广返利