From d1c5514682cb51ed35516d22c44f88dfc0eded81 Mon Sep 17 00:00:00 2001 From: wuhui_zzw <1760308791@qq.com> Date: Wed, 16 Aug 2023 14:04:20 +0800 Subject: [PATCH] update cache --- .../Admin/View/DesignBuilders/Bestseller.php | 45 +++ plugins/Bestseller/Bootstrap.php | 43 +++ plugins/Bestseller/Lang/en/common.php | 15 + plugins/Bestseller/Lang/zh_cn/common.php | 15 + .../Bestseller/Repositories/ProductRepo.php | 36 ++ plugins/Bestseller/Static/image/icon.png | Bin 0 -> 4207 bytes plugins/Bestseller/Static/image/logo.png | Bin 0 -> 43295 bytes .../admin/design_module_bestseller.blade.php | 54 +++ .../shop/design_module_bestseller.blade.php | 67 ++++ plugins/Bestseller/config.json | 18 + plugins/FlatShipping/Bootstrap.php | 65 ++++ plugins/FlatShipping/Lang/en/common.php | 17 + plugins/FlatShipping/Lang/zh_cn/common.php | 17 + .../FlatShipping/Static}/css/demo.css | 0 .../FlatShipping/Static}/image/logo.png | Bin .../FlatShipping/Static}/js/demo.js | 0 plugins/FlatShipping/columns.php | 29 ++ plugins/FlatShipping/config.json | 18 + plugins/LatestProducts/Bootstrap.php | 149 +++++++++ .../Controllers/MenusController.php | 48 +++ plugins/LatestProducts/Lang/en/header.php | 14 + plugins/LatestProducts/Lang/zh_cn/header.php | 14 + plugins/LatestProducts/Routes/admin.php | 15 + plugins/LatestProducts/Routes/shop.php | 15 + .../LatestProducts/Static}/css/demo.css | 0 plugins/LatestProducts/Static/image/logo.png | Bin 0 -> 7979 bytes .../LatestProducts/Static}/js/demo.js | 0 .../admin/product/edit_extra_field.blade.php | 1 + .../Views/shop/header_icon.blade.php | 3 + .../Views/shop/latest_products.blade.php | 21 ++ .../Views/shop/product_button.blade.php | 3 + plugins/LatestProducts/config.json | 12 + plugins/Openai/Bootstrap.php | 48 +++ .../Openai/Controllers/OpenaiController.php | 90 +++++ plugins/Openai/Lang/en/common.php | 27 ++ plugins/Openai/Lang/zh_cn/common.php | 27 ++ plugins/Openai/Lang/zh_hk/common.php | 27 ++ plugins/Openai/Libraries/OpenAI/Base.php | 161 +++++++++ plugins/Openai/Libraries/OpenAI/Chat.php | 66 ++++ .../Openai/Libraries/OpenAI/Completion.php | 33 ++ .../2023_02_27_173221_add_openai_logs.php | 38 +++ plugins/Openai/Models/OpenaiLog.php | 32 ++ plugins/Openai/Routes/admin.php | 18 + plugins/Openai/Services/OpenAIService.php | 120 +++++++ plugins/Openai/Static/image/logo.png | Bin 0 -> 22900 bytes plugins/Openai/Views/admin/openai.blade.php | 307 ++++++++++++++++++ plugins/Openai/columns.php | 31 ++ plugins/Openai/config.json | 18 + .../Paypal/Controllers/PaypalController.php | 118 +++++++ plugins/Paypal/Lang/en/setting.php | 15 + plugins/Paypal/Lang/zh_cn/setting.php | 15 + plugins/Paypal/Routes/admin.php | 10 + plugins/Paypal/Routes/shop.php | 18 + plugins/Paypal/Static/image/logo.png | Bin 0 -> 9004 bytes .../Paypal/Views/checkout/payment.blade.php | 73 +++++ plugins/Paypal/columns.php | 56 ++++ plugins/Paypal/config.json | 12 + plugins/Social/Bootstrap.php | 48 +++ .../Controllers/AdminSocialController.php | 29 ++ .../Controllers/ShopSocialController.php | 72 ++++ plugins/Social/Lang/en/providers.php | 36 ++ plugins/Social/Lang/en/setting.php | 60 ++++ plugins/Social/Lang/zh_cn/providers.php | 36 ++ plugins/Social/Lang/zh_cn/setting.php | 49 +++ ...2_10_13_100354_create_customer_socials.php | 37 +++ plugins/Social/Models/CustomerSocial.php | 30 ++ plugins/Social/Repositories/CustomerRepo.php | 120 +++++++ plugins/Social/Routes/admin.php | 15 + plugins/Social/Routes/shop.php | 16 + .../Social/Static}/image/alipay.png | Bin .../Social/Static}/image/azure.png | Bin .../Social/Static}/image/dingtalk.png | Bin .../Social/Static}/image/douban.png | Bin .../Social/Static}/image/douyin.png | Bin .../Social/Static}/image/facebook.png | Bin .../Social/Static}/image/feishu.png | Bin .../Social/Static}/image/figma.png | Bin .../Social/Static}/image/github.png | Bin .../Social/Static}/image/google.png | Bin .../Social/Static}/image/line.png | Bin .../Social/Static}/image/linkedin.png | Bin .../Social/Static}/image/logo.png | Bin .../Social/Static}/image/qq.png | Bin .../Social/Static}/image/wechat.png | Bin .../Social/Static}/image/weibo.png | Bin .../Social/Views/admin/config_form.blade.php | 187 +++++++++++ plugins/Social/Views/shop/callback.blade.php | 9 + .../Social/Views/shop/social_button.blade.php | 4 + plugins/Social/config.json | 12 + .../Stripe/Controllers/StripeController.php | 54 +++ plugins/Stripe/Lang/en/common.php | 31 ++ plugins/Stripe/Lang/zh_cn/common.php | 31 ++ plugins/Stripe/Routes/shop.php | 15 + .../Stripe/Services/StripePaymentService.php | 74 +++++ .../Stripe/Static}/css/demo.css | 2 +- .../Stripe/Static}/image/logo.png | Bin .../Stripe/Static}/image/pay-1.png | Bin .../Stripe/Static}/image/pay-2.png | Bin .../Stripe/Static}/image/pay-3.png | Bin .../Stripe/Static}/image/pay-4.png | Bin .../Stripe/Static}/image/pay-5.png | Bin .../Stripe/Static}/image/pay-image.png | Bin .../Stripe/Static}/js/demo.js | 0 .../Stripe/Views/checkout/payment.blade.php | 170 ++++++++++ plugins/Stripe/columns.php | 40 +++ plugins/Stripe/config.json | 12 + public/plugin/.gitignore | 2 + public/plugin/alipay/image/logo.png | Bin 15404 -> 0 bytes public/plugin/paypal/image/logo.png | Bin 11470 -> 0 bytes 109 files changed, 3284 insertions(+), 1 deletion(-) create mode 100644 plugins/Bestseller/Admin/View/DesignBuilders/Bestseller.php create mode 100644 plugins/Bestseller/Bootstrap.php create mode 100644 plugins/Bestseller/Lang/en/common.php create mode 100644 plugins/Bestseller/Lang/zh_cn/common.php create mode 100644 plugins/Bestseller/Repositories/ProductRepo.php create mode 100644 plugins/Bestseller/Static/image/icon.png create mode 100644 plugins/Bestseller/Static/image/logo.png create mode 100644 plugins/Bestseller/Views/admin/design_module_bestseller.blade.php create mode 100644 plugins/Bestseller/Views/shop/design_module_bestseller.blade.php create mode 100644 plugins/Bestseller/config.json create mode 100644 plugins/FlatShipping/Bootstrap.php create mode 100644 plugins/FlatShipping/Lang/en/common.php create mode 100644 plugins/FlatShipping/Lang/zh_cn/common.php rename {public/plugin/alipay => plugins/FlatShipping/Static}/css/demo.css (100%) rename {public/plugin/flat_shipping => plugins/FlatShipping/Static}/image/logo.png (100%) rename {public/plugin/alipay => plugins/FlatShipping/Static}/js/demo.js (100%) create mode 100644 plugins/FlatShipping/columns.php create mode 100644 plugins/FlatShipping/config.json create mode 100644 plugins/LatestProducts/Bootstrap.php create mode 100644 plugins/LatestProducts/Controllers/MenusController.php create mode 100644 plugins/LatestProducts/Lang/en/header.php create mode 100644 plugins/LatestProducts/Lang/zh_cn/header.php create mode 100644 plugins/LatestProducts/Routes/admin.php create mode 100644 plugins/LatestProducts/Routes/shop.php rename {public/plugin/flat_shipping => plugins/LatestProducts/Static}/css/demo.css (100%) create mode 100644 plugins/LatestProducts/Static/image/logo.png rename {public/plugin/flat_shipping => plugins/LatestProducts/Static}/js/demo.js (100%) create mode 100644 plugins/LatestProducts/Views/admin/product/edit_extra_field.blade.php create mode 100644 plugins/LatestProducts/Views/shop/header_icon.blade.php create mode 100644 plugins/LatestProducts/Views/shop/latest_products.blade.php create mode 100644 plugins/LatestProducts/Views/shop/product_button.blade.php create mode 100644 plugins/LatestProducts/config.json create mode 100644 plugins/Openai/Bootstrap.php create mode 100644 plugins/Openai/Controllers/OpenaiController.php create mode 100644 plugins/Openai/Lang/en/common.php create mode 100644 plugins/Openai/Lang/zh_cn/common.php create mode 100644 plugins/Openai/Lang/zh_hk/common.php create mode 100644 plugins/Openai/Libraries/OpenAI/Base.php create mode 100644 plugins/Openai/Libraries/OpenAI/Chat.php create mode 100644 plugins/Openai/Libraries/OpenAI/Completion.php create mode 100644 plugins/Openai/Migrations/2023_02_27_173221_add_openai_logs.php create mode 100644 plugins/Openai/Models/OpenaiLog.php create mode 100644 plugins/Openai/Routes/admin.php create mode 100644 plugins/Openai/Services/OpenAIService.php create mode 100644 plugins/Openai/Static/image/logo.png create mode 100644 plugins/Openai/Views/admin/openai.blade.php create mode 100644 plugins/Openai/columns.php create mode 100644 plugins/Openai/config.json create mode 100644 plugins/Paypal/Controllers/PaypalController.php create mode 100644 plugins/Paypal/Lang/en/setting.php create mode 100644 plugins/Paypal/Lang/zh_cn/setting.php create mode 100644 plugins/Paypal/Routes/admin.php create mode 100644 plugins/Paypal/Routes/shop.php create mode 100644 plugins/Paypal/Static/image/logo.png create mode 100644 plugins/Paypal/Views/checkout/payment.blade.php create mode 100644 plugins/Paypal/columns.php create mode 100644 plugins/Paypal/config.json create mode 100644 plugins/Social/Bootstrap.php create mode 100644 plugins/Social/Controllers/AdminSocialController.php create mode 100644 plugins/Social/Controllers/ShopSocialController.php create mode 100644 plugins/Social/Lang/en/providers.php create mode 100644 plugins/Social/Lang/en/setting.php create mode 100644 plugins/Social/Lang/zh_cn/providers.php create mode 100644 plugins/Social/Lang/zh_cn/setting.php create mode 100644 plugins/Social/Migrations/2022_10_13_100354_create_customer_socials.php create mode 100644 plugins/Social/Models/CustomerSocial.php create mode 100644 plugins/Social/Repositories/CustomerRepo.php create mode 100644 plugins/Social/Routes/admin.php create mode 100644 plugins/Social/Routes/shop.php rename {public/plugin/social => plugins/Social/Static}/image/alipay.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/azure.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/dingtalk.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/douban.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/douyin.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/facebook.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/feishu.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/figma.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/github.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/google.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/line.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/linkedin.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/logo.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/qq.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/wechat.png (100%) rename {public/plugin/social => plugins/Social/Static}/image/weibo.png (100%) create mode 100644 plugins/Social/Views/admin/config_form.blade.php create mode 100644 plugins/Social/Views/shop/callback.blade.php create mode 100644 plugins/Social/Views/shop/social_button.blade.php create mode 100644 plugins/Social/config.json create mode 100644 plugins/Stripe/Controllers/StripeController.php create mode 100644 plugins/Stripe/Lang/en/common.php create mode 100644 plugins/Stripe/Lang/zh_cn/common.php create mode 100644 plugins/Stripe/Routes/shop.php create mode 100644 plugins/Stripe/Services/StripePaymentService.php rename {public/plugin/stripe => plugins/Stripe/Static}/css/demo.css (86%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/logo.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/pay-1.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/pay-2.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/pay-3.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/pay-4.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/pay-5.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/image/pay-image.png (100%) rename {public/plugin/stripe => plugins/Stripe/Static}/js/demo.js (100%) create mode 100644 plugins/Stripe/Views/checkout/payment.blade.php create mode 100644 plugins/Stripe/columns.php create mode 100644 plugins/Stripe/config.json create mode 100644 public/plugin/.gitignore delete mode 100644 public/plugin/alipay/image/logo.png delete mode 100644 public/plugin/paypal/image/logo.png diff --git a/plugins/Bestseller/Admin/View/DesignBuilders/Bestseller.php b/plugins/Bestseller/Admin/View/DesignBuilders/Bestseller.php new file mode 100644 index 00000000..fd236855 --- /dev/null +++ b/plugins/Bestseller/Admin/View/DesignBuilders/Bestseller.php @@ -0,0 +1,45 @@ + + * @created 2022-07-08 17:09:15 + * @modified 2022-07-08 17:09:15 + */ + +namespace Plugin\Bestseller\Admin\View\DesignBuilders; + +use Illuminate\Contracts\View\View; +use Illuminate\View\Component; + +class Bestseller extends Component +{ + /** + * Create a new component instance. + * + * @return void + */ + public function __construct() + { + } + + /** + * Get the view / contents that represent the component. + * + * @return View + */ + public function render(): View + { + $data['register'] = [ + 'code' => 'bestseller', + 'sort' => 0, + 'name' => trans('Bestseller::common.module_name'), + 'icon' => plugin_asset('Bestseller', 'image/icon.png'), + 'view_path' => 'Bestseller::shop/design_module_bestseller', + ]; + + return view('Bestseller::admin/design_module_bestseller', $data); + } +} diff --git a/plugins/Bestseller/Bootstrap.php b/plugins/Bestseller/Bootstrap.php new file mode 100644 index 00000000..4dee1b78 --- /dev/null +++ b/plugins/Bestseller/Bootstrap.php @@ -0,0 +1,43 @@ + + * @created 2022-07-20 15:35:59 + * @modified 2022-07-20 15:35:59 + */ + +namespace Plugin\Bestseller; + +use Plugin\Bestseller\Repositories\ProductRepo; + +class Bootstrap +{ + public function boot() + { + /** + * Add module for admin design. + */ + add_hook_filter('admin.design.index.data', function ($data) { + $data['editors'][] = 'editor-bestseller'; + + return $data; + }); + + /** + * Get module content for home page and preview. + */ + add_hook_filter('service.design.module.content', function ($data) { + $module = $data['module_code'] ?? ''; + + if ($module == 'bestseller') { + $data['title'] = $data['title'][locale()] ?? ''; + $data['products'] = ProductRepo::getBestSellerProducts($data['limit']); + } + + return $data; + }); + } +} diff --git a/plugins/Bestseller/Lang/en/common.php b/plugins/Bestseller/Lang/en/common.php new file mode 100644 index 00000000..bcc7a8e9 --- /dev/null +++ b/plugins/Bestseller/Lang/en/common.php @@ -0,0 +1,15 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'module_name' => 'Bestseller', + 'limit' => 'Limit', +]; diff --git a/plugins/Bestseller/Lang/zh_cn/common.php b/plugins/Bestseller/Lang/zh_cn/common.php new file mode 100644 index 00000000..e962884f --- /dev/null +++ b/plugins/Bestseller/Lang/zh_cn/common.php @@ -0,0 +1,15 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'module_name' => '热卖模块', + 'limit' => '数量限制', +]; diff --git a/plugins/Bestseller/Repositories/ProductRepo.php b/plugins/Bestseller/Repositories/ProductRepo.php new file mode 100644 index 00000000..525ca388 --- /dev/null +++ b/plugins/Bestseller/Repositories/ProductRepo.php @@ -0,0 +1,36 @@ + + * @created 2023-03-08 11:56:17 + * @modified 2023-03-08 11:56:17 + */ + +namespace Plugin\Bestseller\Repositories; + +use Beike\Shop\Http\Resources\ProductSimple; + +class ProductRepo +{ + /** + * Get best seller + * + * @param $limit + * @return array + */ + public static function getBestSellerProducts($limit): array + { + $products = \Beike\Repositories\ProductRepo::getBuilder([ + 'active' => 1, + 'sort' => 'products.sales', + 'order' => 'desc', + ]) + ->whereHas('masterSku') + ->limit($limit)->get(); + + return ProductSimple::collection($products)->jsonSerialize(); + } +} diff --git a/plugins/Bestseller/Static/image/icon.png b/plugins/Bestseller/Static/image/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..00aafdab223c96311d4eb6af465594b858fd6eb5 GIT binary patch literal 4207 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 FV1nb~41NFr literal 0 HcmV?d00001 diff --git a/plugins/Bestseller/Static/image/logo.png b/plugins/Bestseller/Static/image/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cd2f2986dd8480ef9224d2d66615fcb4e93bd298 GIT binary patch literal 43295 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 literal 0 HcmV?d00001 diff --git a/plugins/Bestseller/Views/admin/design_module_bestseller.blade.php b/plugins/Bestseller/Views/admin/design_module_bestseller.blade.php new file mode 100644 index 00000000..f0bfb050 --- /dev/null +++ b/plugins/Bestseller/Views/admin/design_module_bestseller.blade.php @@ -0,0 +1,54 @@ + + + + +{{-- 定义模块的配置项 --}} +@push('footer-script') + +@endpush + diff --git a/plugins/Bestseller/Views/shop/design_module_bestseller.blade.php b/plugins/Bestseller/Views/shop/design_module_bestseller.blade.php new file mode 100644 index 00000000..9bba7f74 --- /dev/null +++ b/plugins/Bestseller/Views/shop/design_module_bestseller.blade.php @@ -0,0 +1,67 @@ +
+ @include('design._partial._module_tool') +
+
+
{{ $content['title'] }}
+ @if ($content['products']) +
+
+ @foreach ($content['products'] as $product) +
+ @include('shared.product') +
+ @endforeach +
+
+
+
+
+ @elseif (!$content['products'] and $design) +
+ @for ($s = 0; $s < 4; $s++) +
+
+
+
请配置商品
+
+ 66.66 + 99.99 +
+
+
+ @endfor +
+ @endif +
+
+ + +
diff --git a/plugins/Bestseller/config.json b/plugins/Bestseller/config.json new file mode 100644 index 00000000..f4ae6961 --- /dev/null +++ b/plugins/Bestseller/config.json @@ -0,0 +1,18 @@ +{ + "code": "bestseller", + "name": { + "zh_cn": "热卖商品模块", + "en": "Hot Items Module" + }, + "description": { + "zh_cn": "首页装修热卖商品模块", + "en": "Home Decoration Hot Products Module" + }, + "type": "feature", + "version": "v1.0.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/plugins/FlatShipping/Bootstrap.php b/plugins/FlatShipping/Bootstrap.php new file mode 100644 index 00000000..21c5371b --- /dev/null +++ b/plugins/FlatShipping/Bootstrap.php @@ -0,0 +1,65 @@ + + * @created 2022-07-20 15:35:59 + * @modified 2022-07-20 15:35:59 + */ + +namespace Plugin\FlatShipping; + +use Beike\Admin\Http\Resources\PluginResource; +use Beike\Plugin\Plugin; +use Beike\Shop\Services\CheckoutService; + +class Bootstrap +{ + /** + * 获取固定运费方式 + * + * @param CheckoutService $checkout + * @param Plugin $plugin + * @return array + * @throws \Exception + */ + public function getQuotes(CheckoutService $checkout, Plugin $plugin): array + { + $code = $plugin->code; + $pluginResource = (new PluginResource($plugin))->jsonSerialize(); + $quotes[] = [ + 'type' => 'shipping', + 'code' => "{$code}.0", + 'name' => $pluginResource['name'], + 'description' => $pluginResource['description'], + 'icon' => $pluginResource['icon'], + 'cost' => $this->getShippingFee($checkout), + ]; + + return $quotes; + } + + /** + * 计算固定运费 + * + * @param CheckoutService $checkout + * @return float|int + */ + public function getShippingFee(CheckoutService $checkout): float|int + { + $totalService = $checkout->totalService; + $amount = $totalService->amount; + $shippingType = plugin_setting('flat_shipping.type', 'fixed'); + $shippingValue = plugin_setting('flat_shipping.value', 0); + if ($shippingType == 'fixed') { + return $shippingValue; + } elseif ($shippingType == 'percent') { + return $amount * $shippingValue / 100; + } + + return 0; + + } +} diff --git a/plugins/FlatShipping/Lang/en/common.php b/plugins/FlatShipping/Lang/en/common.php new file mode 100644 index 00000000..7461686f --- /dev/null +++ b/plugins/FlatShipping/Lang/en/common.php @@ -0,0 +1,17 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'way' => 'Way', + 'flat_shipping' => 'Flat Shipping', + 'percentage' => 'Percentage', + 'shipping_value' => 'Shipping Value', +]; diff --git a/plugins/FlatShipping/Lang/zh_cn/common.php b/plugins/FlatShipping/Lang/zh_cn/common.php new file mode 100644 index 00000000..a7d395a0 --- /dev/null +++ b/plugins/FlatShipping/Lang/zh_cn/common.php @@ -0,0 +1,17 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'way' => '方式', + 'flat_shipping' => '固定运费', + 'percentage' => '百分比', + 'shipping_value' => '运费值', +]; diff --git a/public/plugin/alipay/css/demo.css b/plugins/FlatShipping/Static/css/demo.css similarity index 100% rename from public/plugin/alipay/css/demo.css rename to plugins/FlatShipping/Static/css/demo.css diff --git a/public/plugin/flat_shipping/image/logo.png b/plugins/FlatShipping/Static/image/logo.png similarity index 100% rename from public/plugin/flat_shipping/image/logo.png rename to plugins/FlatShipping/Static/image/logo.png diff --git a/public/plugin/alipay/js/demo.js b/plugins/FlatShipping/Static/js/demo.js similarity index 100% rename from public/plugin/alipay/js/demo.js rename to plugins/FlatShipping/Static/js/demo.js diff --git a/plugins/FlatShipping/columns.php b/plugins/FlatShipping/columns.php new file mode 100644 index 00000000..ed2e24f7 --- /dev/null +++ b/plugins/FlatShipping/columns.php @@ -0,0 +1,29 @@ + + * @created 2022-06-29 21:16:23 + * @modified 2022-06-29 21:16:23 + */ + +return [ + [ + 'name' => 'type', + 'label_key' => 'common.flat_shipping', + 'type' => 'select', + 'options' => [ + ['value' => 'fixed', 'label_key' => 'common.flat_shipping'], + ['value' => 'percent', 'label_key' => 'common.percentage'], + ], + 'required' => true, + ], + [ + 'name' => 'value', + 'label_key' => 'common.shipping_value', + 'type' => 'string', + 'required' => true, + ], +]; diff --git a/plugins/FlatShipping/config.json b/plugins/FlatShipping/config.json new file mode 100644 index 00000000..3cd9300c --- /dev/null +++ b/plugins/FlatShipping/config.json @@ -0,0 +1,18 @@ +{ + "code": "flat_shipping", + "name": { + "zh_cn": "固定运费", + "en": "Flat Rate Shipping" + }, + "description": { + "zh_cn": "按订单总额收取固定运费", + "en": "Fixed shipping fee by order total" + }, + "type": "shipping", + "version": "v1.0.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/plugins/LatestProducts/Bootstrap.php b/plugins/LatestProducts/Bootstrap.php new file mode 100644 index 00000000..bbfa640d --- /dev/null +++ b/plugins/LatestProducts/Bootstrap.php @@ -0,0 +1,149 @@ + + * @created 2022-07-20 15:35:59 + * @modified 2022-07-20 15:35:59 + */ + +namespace Plugin\LatestProducts; + +class Bootstrap +{ + /** + * 去除注释后可以观察网站头部以及产品详情页页面变化 + */ + public function boot() + { + $this->addLatestProducts(); + + // $this->modifyHeader(); + // $this->modifyProductDetail(); + + // $this->modifyAdminProductEdit(); + // $this->modifySetting(); + // $this->handlePaidOrder(); + } + + /** + * 在前台网页头部添加二级菜单链接 + */ + private function addLatestProducts() + { + add_hook_filter('menu.content', function ($data) { + $data[] = [ + 'name' => trans('LatestProducts::header.latest_products'), + 'link' => shop_route('latest_products'), + ]; + + return $data; + }, 0); + } + + /** + * 修改前台全局 header 模板演示 + */ + private function modifyHeader() + { + add_hook_blade('header.top.currency', function ($callback, $output, $data) { + return '货币前' . $output; + }); + + add_hook_blade('header.top.language', function ($callback, $output, $data) { + return $output . '语言后'; + }); + + add_hook_blade('header.top.telephone', function ($callback, $output, $data) { + return '电话前' . $output; + }); + + add_hook_blade('header.menu.logo', function ($callback, $output, $data) { + return $output . 'Logo后'; + }); + + add_hook_blade('header.menu.icon', function ($callback, $output, $data) { + $view = view('LatestProducts::shop.header_icon')->render(); + + return $output . $view; + }); + } + + /** + * 修改产品详情页演示 + * 1. 通过数据 hook 修改产品详情页产品名称 + * 2. 通过模板 hook 在产品详情页名称上面添加 Hot 标签 + * 3. 通过模板 hook 在产品详情页品牌下面添加信息 + * 4. 通过模板 hook 在产品详情页立即购买后添加按钮 + */ + private function modifyProductDetail() + { + // 通过数据 hook 修改产品详情页产品名称 + add_hook_filter('product.show.data', function ($product) { + $product['product']['name'] = '[疯狂热销]' . $product['product']['name']; + + return $product; + }); + + // 通过模板 hook 在产品详情页名称上面添加 Hot 标签 + add_hook_blade('product.detail.name', function ($callback, $output, $data) { + $badge = 'Hot'; + + return $badge . $output; + }); + + // 通过模板 hook 在产品详情页品牌下面添加信息 + add_hook_blade('product.detail.brand', function ($callback, $output, $data) { + return $output . '
Brand 2:品牌 2
'; + }); + + // 通过模板 hook 在产品详情页立即购买后添加按钮 + add_hook_blade('product.detail.buy.after', function ($callback, $output, $data) { + $view = ''; + + return $output . $view; + }); + } + + /** + * 后台产品编辑页添加自定义字段演示 + */ + private function modifyAdminProductEdit() + { + add_hook_blade('admin.product.edit.extra', function ($callback, $output, $data) { + $view = view('LatestProducts::admin.product.edit_extra_field', $data)->render(); + + return $output . $view; + }, 1); + } + + /** + * 系统设置添加新 tab + */ + private function modifySetting() + { + add_hook_blade('admin.setting.nav.after', function ($callback, $output, $data) { + return view('LatestProducts::admin.setting.nav')->render(); + }); + + add_hook_blade('admin.setting.after', function ($callback, $output, $data) { + return view('LatestProducts::admin.setting.tab')->render(); + }); + } + + /** + * 修改订单状态机流程演示 + */ + private function handlePaidOrder() + { + add_hook_filter('service.state_machine.machines', function ($data) { + $data['machines']['unpaid']['paid'][] = function () { + // 这里写订单由 unpaid 变为 paid 执行的逻辑 + }; + + return $data; + }, 0); + } +} diff --git a/plugins/LatestProducts/Controllers/MenusController.php b/plugins/LatestProducts/Controllers/MenusController.php new file mode 100644 index 00000000..44fd5e19 --- /dev/null +++ b/plugins/LatestProducts/Controllers/MenusController.php @@ -0,0 +1,48 @@ + + * @created 2022-07-21 10:00:25 + * @modified 2022-07-21 10:00:25 + */ + +namespace Plugin\LatestProducts\Controllers; + +use Beike\Repositories\ProductRepo; +use Beike\Shop\Http\Controllers\Controller; +use Beike\Shop\Http\Resources\ProductSimple; + +class MenusController extends Controller +{ + public function getRoutes(): array + { + return [ + 'method' => __METHOD__, + 'route_list' => [], + ]; + } + + public function latestProducts() + { + $products = ProductRepo::getBuilder( + [ + // 'active' => 1, + 'sort' => 'created_at', + 'order' => 'desc', + ]) + ->whereHas('masterSku') + ->with('inCurrentWishlist') + ->orderByDesc('created_at') + ->paginate(perPage()); + + $data = [ + 'products' => $products, + 'items' => ProductSimple::collection($products)->jsonSerialize(), + ]; + + return view('LatestProducts::shop.latest_products', $data); + } +} diff --git a/plugins/LatestProducts/Lang/en/header.php b/plugins/LatestProducts/Lang/en/header.php new file mode 100644 index 00000000..6d49946c --- /dev/null +++ b/plugins/LatestProducts/Lang/en/header.php @@ -0,0 +1,14 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'latest_products' => 'Latest Products', +]; diff --git a/plugins/LatestProducts/Lang/zh_cn/header.php b/plugins/LatestProducts/Lang/zh_cn/header.php new file mode 100644 index 00000000..5b82feb6 --- /dev/null +++ b/plugins/LatestProducts/Lang/zh_cn/header.php @@ -0,0 +1,14 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'latest_products' => '最新商品', +]; diff --git a/plugins/LatestProducts/Routes/admin.php b/plugins/LatestProducts/Routes/admin.php new file mode 100644 index 00000000..07f85855 --- /dev/null +++ b/plugins/LatestProducts/Routes/admin.php @@ -0,0 +1,15 @@ + + * @created 2022-08-04 16:17:53 + * @modified 2022-08-04 16:17:53 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\LatestProducts\Controllers\MenusController; + +Route::get('/routes', [MenusController::class, 'getRoutes'])->name('routes'); diff --git a/plugins/LatestProducts/Routes/shop.php b/plugins/LatestProducts/Routes/shop.php new file mode 100644 index 00000000..9bf997cb --- /dev/null +++ b/plugins/LatestProducts/Routes/shop.php @@ -0,0 +1,15 @@ + + * @created 2022-08-04 16:17:44 + * @modified 2022-08-04 16:17:44 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\LatestProducts\Controllers\MenusController; + +Route::get('/latest_products', [MenusController::class, 'latestProducts'])->name('latest_products'); diff --git a/public/plugin/flat_shipping/css/demo.css b/plugins/LatestProducts/Static/css/demo.css similarity index 100% rename from public/plugin/flat_shipping/css/demo.css rename to plugins/LatestProducts/Static/css/demo.css diff --git a/plugins/LatestProducts/Static/image/logo.png b/plugins/LatestProducts/Static/image/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..37ff0126945a4869a79c8207f8ad5e09531efa56 GIT binary patch literal 7979 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 H!?6DYXhSI* literal 0 HcmV?d00001 diff --git a/public/plugin/flat_shipping/js/demo.js b/plugins/LatestProducts/Static/js/demo.js similarity index 100% rename from public/plugin/flat_shipping/js/demo.js rename to plugins/LatestProducts/Static/js/demo.js diff --git a/plugins/LatestProducts/Views/admin/product/edit_extra_field.blade.php b/plugins/LatestProducts/Views/admin/product/edit_extra_field.blade.php new file mode 100644 index 00000000..f0bad4da --- /dev/null +++ b/plugins/LatestProducts/Views/admin/product/edit_extra_field.blade.php @@ -0,0 +1 @@ + diff --git a/plugins/LatestProducts/Views/shop/header_icon.blade.php b/plugins/LatestProducts/Views/shop/header_icon.blade.php new file mode 100644 index 00000000..fa2210f4 --- /dev/null +++ b/plugins/LatestProducts/Views/shop/header_icon.blade.php @@ -0,0 +1,3 @@ + diff --git a/plugins/LatestProducts/Views/shop/latest_products.blade.php b/plugins/LatestProducts/Views/shop/latest_products.blade.php new file mode 100644 index 00000000..7cec3144 --- /dev/null +++ b/plugins/LatestProducts/Views/shop/latest_products.blade.php @@ -0,0 +1,21 @@ +@extends('layout.master') +@section('content') + +
+
+ @foreach ($items as $product) +
@include('shared.product')
+ @endforeach +
+ {{ $products->links('shared/pagination/bootstrap-4') }} +
+@endsection diff --git a/plugins/LatestProducts/Views/shop/product_button.blade.php b/plugins/LatestProducts/Views/shop/product_button.blade.php new file mode 100644 index 00000000..f95f97e2 --- /dev/null +++ b/plugins/LatestProducts/Views/shop/product_button.blade.php @@ -0,0 +1,3 @@ + diff --git a/plugins/LatestProducts/config.json b/plugins/LatestProducts/config.json new file mode 100644 index 00000000..1561cf5b --- /dev/null +++ b/plugins/LatestProducts/config.json @@ -0,0 +1,12 @@ +{ + "code": "latest_products", + "name": "最新商品列表", + "description": "首页菜单添加最新商品列表功能", + "type": "feature", + "version": "v1.0.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/plugins/Openai/Bootstrap.php b/plugins/Openai/Bootstrap.php new file mode 100644 index 00000000..1e503d20 --- /dev/null +++ b/plugins/Openai/Bootstrap.php @@ -0,0 +1,48 @@ + + * @created 2023-02-27 13:57:38 + * @modified 2023-02-27 13:57:38 + */ + +namespace Plugin\Openai; + +class Bootstrap +{ + public function boot() + { + add_hook_filter('admin.sidebar.setting.prefix', function ($data) { + $data[] = 'openai'; + + return $data; + }); + + add_hook_filter('admin.sidebar.setting_routes', function ($data) { + $data[] = [ + 'route' => 'openai.index', + 'prefixes' => ['openai'], + 'title' => 'ChatGPT', + ]; + + return $data; + }); + + add_hook_filter('role.permissions.plugin', function ($data) { + $data[] = [ + 'title' => 'OpenAI', + 'permissions' => [ + [ + 'code' => 'openai_index', + 'name' => 'ChatGPT', + ], + ], + ]; + + return $data; + }); + } +} diff --git a/plugins/Openai/Controllers/OpenaiController.php b/plugins/Openai/Controllers/OpenaiController.php new file mode 100644 index 00000000..60ffc3b7 --- /dev/null +++ b/plugins/Openai/Controllers/OpenaiController.php @@ -0,0 +1,90 @@ + + * @created 2023-02-27 16:13:08 + * @modified 2023-02-27 16:13:08 + */ + +namespace Plugin\Openai\Controllers; + +use Beike\Admin\Http\Controllers\Controller; +use Illuminate\Http\Request; +use Plugin\Openai\Services\OpenAIService; + +class OpenaiController extends Controller +{ + /** + * OpenAI home page. + * + * @return mixed + */ + public function index() + { + $plugin = plugin('openai'); + + $error = ''; + $baseUrl = config('beike.api_url') . '/api/openai'; + $apiType = plugin_setting('openai.api_type'); + if ($apiType == 'own') { + $apiKey = plugin_setting('openai.api_key'); + if (empty($apiKey)) { + $error = trans('Openai::common.empty_api_key'); + } + $baseUrl = config('app.url') . '/admin/openai'; + } + + $data = [ + 'name' => $plugin->getLocaleName(), + 'description' => $plugin->getLocaleDescription(), + 'type' => $apiType, + 'base' => $baseUrl, + 'error' => $error, + ]; + + return view('Openai::admin.openai', $data); + } + + /** + * Send chat completions with OpenAI API + * + * @param Request $request + * @return array|mixed + * @throws \Throwable + */ + public function completions(Request $request) + { + try { + $result = (new OpenAIService())->requestAI($request->all()); + } catch (\Exception $e) { + $result = [ + 'error' => $e->getMessage(), + ]; + } + + return $result; + } + + /** + * Get histories + * + * @param Request $request + * @return array|mixed + */ + public function histories(Request $request) + { + try { + $perPage = $request->get('per_page', 10); + $result = (new OpenAIService())->getOpenaiLogs($perPage); + } catch (\Exception $e) { + $result = [ + 'error' => $e->getMessage(), + ]; + } + + return $result; + } +} diff --git a/plugins/Openai/Lang/en/common.php b/plugins/Openai/Lang/en/common.php new file mode 100644 index 00000000..efdcdf17 --- /dev/null +++ b/plugins/Openai/Lang/en/common.php @@ -0,0 +1,27 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + // Text + 'title' => 'OpenAI intelligent chat assistant', + 'sub_title' => 'Based on OpenAI GPT3.0 integrated development If you have any questions, please consult qq group 639108380', + 'no_question' => 'Please enter the search content in the box below', + 'enter_question' => 'Please enter a question', + 'loading' => 'loading...', + 'no_more' => 'no more', + 'qa_q' => 'ask', + 'qa_a' => 'answer', + 'number_free' => 'The remaining free times of the day', + 'api_type' => 'API Method', + 'own' => 'Own Key', + 'beikeshop' => 'BeikeShop', + 'empty_api_key' => 'API Key is empty, please go to the plugin settings - OpenAI - Edit and fill in the API Key first.', +]; diff --git a/plugins/Openai/Lang/zh_cn/common.php b/plugins/Openai/Lang/zh_cn/common.php new file mode 100644 index 00000000..7531ee85 --- /dev/null +++ b/plugins/Openai/Lang/zh_cn/common.php @@ -0,0 +1,27 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + // Text + 'title' => 'OpenAI 智能聊天助手', + 'sub_title' => '基于OpenAI GPT3.0 集成开发 如有疑问详询qq群639108380', + 'no_question' => '请在下面输入框搜索内容', + 'enter_question' => '请输入问题', + 'loading' => '加载中...', + 'no_more' => '没有更多了', + 'qa_q' => '问', + 'qa_a' => '答', + 'number_free' => '当日剩余免费次数', + 'api_type' => 'API 方式', + 'own' => '自有Key', + 'beikeshop' => 'BeikeShop平台', + 'empty_api_key' => 'API Key 为空, 请先到插件设置 - OpenAI - 编辑 填写API Key', +]; diff --git a/plugins/Openai/Lang/zh_hk/common.php b/plugins/Openai/Lang/zh_hk/common.php new file mode 100644 index 00000000..cee4b66b --- /dev/null +++ b/plugins/Openai/Lang/zh_hk/common.php @@ -0,0 +1,27 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + // Text + 'title' => 'OpenAI 智能聊天助手', + 'sub_title' => '基於OpenAI GPT3.0 集成開發 如有疑問詳詢qq群639108380', + 'no_question' => '請在下面輸入框搜索內容', + 'enter_question' => '請輸入問題', + 'loading' => '加載中...', + 'no_more' => '没有更多了', + 'qa_q' => '問', + 'qa_a' => '答', + 'number_free' => '當日剩餘免費次數', + 'api_type' => 'API 方式', + 'own' => '自有 Key', + 'beikeshop' => 'BeikeShop 平台', + 'empty_api_key' => 'API Key 為空,請先到插件設置 - OpenAI - 編輯 填寫 API Key', +]; diff --git a/plugins/Openai/Libraries/OpenAI/Base.php b/plugins/Openai/Libraries/OpenAI/Base.php new file mode 100644 index 00000000..43a8cbe6 --- /dev/null +++ b/plugins/Openai/Libraries/OpenAI/Base.php @@ -0,0 +1,161 @@ + + * @created 2023-02-22 20:31:42 + * @modified 2023-02-22 20:31:42 + */ + +namespace Plugin\Openai\Libraries\OpenAI; + +use Exception; + +class Base +{ + /** + * @var string|bool|mixed + */ + private string $apiKey = ''; + + /** + * @var int + */ + protected int $maxTokens = 1000; + + /** + * @var float + */ + protected float $temperature = 0.5; + + /** + * @var int + */ + protected int $number = 1; + + /** + * @var string + */ + protected string $prompt; + + /** + * OpenAI constructor. + * @param string|null $apiKey + */ + public function __construct(?string $apiKey = '') + { + if ($apiKey) { + $this->apiKey = $apiKey; + } + if (empty($this->apiKey)) { + $this->apiKey = env('OPENAI_API_KEY'); + } + } + + /** + * Get OpenAI instance. + * + * @param string|null $apiKey + * @return Base + */ + public static function getInstance(?string $apiKey = ''): static + { + return new self($apiKey); + } + + /** + * 设置 max_tokens的值 + * 一般来说,max_tokens值越大,模型的表现就越好, + * 但是需要考虑到计算资源的限制,max_tokens值不宜过大。 + * 一般来说,max_tokens值可以设置在比较合理的范围内,比如500到1000之间。 + * + * @param int $maxTokens + * @return $this + */ + public function setMaxTokens(int $maxTokens): static + { + $this->maxTokens = $maxTokens; + + return $this; + } + + /** + * 设置temperature参数值, 参数的范围是0.0到2.0之间。 + * 在较低的温度下,模型会生成更加安全的文本, + * 而在较高的温度下,模型会生成更加创新的文本。 + * + * @param float $temperature + * @return $this + */ + public function setTemperature(float $temperature): static + { + $this->temperature = $temperature; + + return $this; + } + + /** + * 设置 n 参数值 + * 指定了返回结果的数量。n参数越大,返回的结果数量也越多。 + * + * @param int $number + * @return $this + */ + public function setNumber(int $number): static + { + $this->number = $number; + + return $this; + } + + /** + * 设置 prompt 参数值 + * 用于指定一段文本,用于提供给模型参考,以便于生成更加相关的文本 + * + * @param string $prompt + * @return $this + * @throws Exception + */ + public function setPrompt(string $prompt): static + { + $this->prompt = trim($prompt); + if (empty($this->prompt)) { + throw new Exception('prompt 不能为空!'); + } + + return $this; + } + + /** + * 发送请求到 OpenAI + * + * @param $url + * @param $data + * @return mixed + * @throws Exception + */ + protected function request($url, $data): mixed + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $this->apiKey, + ]); + + $response = curl_exec($ch); + if ($response === false) { + throw new \Exception(curl_error($ch)); + } + curl_close($ch); + + return json_decode($response, true); + } +} diff --git a/plugins/Openai/Libraries/OpenAI/Chat.php b/plugins/Openai/Libraries/OpenAI/Chat.php new file mode 100644 index 00000000..336af1f2 --- /dev/null +++ b/plugins/Openai/Libraries/OpenAI/Chat.php @@ -0,0 +1,66 @@ + + * @created 2023-03-02 14:30:10 + * @modified 2023-03-02 14:30:10 + */ + +namespace Plugin\Openai\Libraries\OpenAI; + +class Chat extends Base +{ + /** + * @var array 聊天上下文 + */ + private array $messages; + + /** + * @param string|null $apiKey + * @return static + */ + public static function getInstance(?string $apiKey = ''): static + { + return new self($apiKey); + } + + /** + * https://platform.openai.com/docs/guides/chat/introduction + * + * @param $messages + * @param $prompt + * @return Chat + */ + public function setMessages($messages, $prompt): self + { + $messages[] = ['role' => 'user', 'content' => $prompt]; + $this->messages = $messages; + + return $this; + } + + /** + * 发送请求到 OpenAI + * + * @return mixed + * @throws \Exception + */ + public function create(): mixed + { + $model = 'gpt-3.5-turbo'; + $url = 'https://api.openai.com/v1/chat/completions'; + $data = [ + 'messages' => $this->messages, + 'max_tokens' => $this->maxTokens, + 'temperature' => $this->temperature, + 'n' => $this->number, + 'stop' => '\n', + 'model' => $model, + ]; + + return $this->request($url, $data); + } +} diff --git a/plugins/Openai/Libraries/OpenAI/Completion.php b/plugins/Openai/Libraries/OpenAI/Completion.php new file mode 100644 index 00000000..a30f822a --- /dev/null +++ b/plugins/Openai/Libraries/OpenAI/Completion.php @@ -0,0 +1,33 @@ + + * @created 2023-03-02 14:37:15 + * @modified 2023-03-02 14:37:15 + */ + +namespace Plugin\Openai\Libraries\OpenAI; + +class Completion extends Base +{ + /** + * @throws \Exception + */ + public function create() + { + $model = 'text-davinci-003'; + $url = 'https://api.openai.com/v1/completions'; + $data = [ + 'prompt' => $this->prompt, + 'max_tokens' => $this->maxTokens, + 'temperature' => $this->temperature, + 'n' => $this->number, + 'stop' => '\n', + 'model' => $model, + ]; + $this->request($url, $data); + } +} diff --git a/plugins/Openai/Migrations/2023_02_27_173221_add_openai_logs.php b/plugins/Openai/Migrations/2023_02_27_173221_add_openai_logs.php new file mode 100644 index 00000000..f668eebe --- /dev/null +++ b/plugins/Openai/Migrations/2023_02_27_173221_add_openai_logs.php @@ -0,0 +1,38 @@ +id(); + $table->integer('user_id')->index('user_id'); + $table->text('question'); + $table->text('answer'); + $table->string('request_ip'); + $table->text('user_agent'); + $table->timestamps(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('openai_logs'); + } +}; diff --git a/plugins/Openai/Models/OpenaiLog.php b/plugins/Openai/Models/OpenaiLog.php new file mode 100644 index 00000000..e6b8114f --- /dev/null +++ b/plugins/Openai/Models/OpenaiLog.php @@ -0,0 +1,32 @@ + + * @created 2023-03-13 16:48:17 + * @modified 2023-03-13 16:48:17 + */ + +namespace Plugin\Openai\Models; + +use Illuminate\Database\Eloquent\Model; + +class OpenaiLog extends Model +{ + public $timestamps = true; + + protected $table = 'openai_logs'; + + protected $fillable = [ + 'user_id', 'question', 'answer', 'request_ip', 'user_agent', + ]; + + protected $appends = ['created_format']; + + public function getCreatedFormatAttribute() + { + return $this->created_at->format('Y-m-d H:i:s'); + } +} diff --git a/plugins/Openai/Routes/admin.php b/plugins/Openai/Routes/admin.php new file mode 100644 index 00000000..184a3ed2 --- /dev/null +++ b/plugins/Openai/Routes/admin.php @@ -0,0 +1,18 @@ + + * @created 2022-08-04 16:17:53 + * @modified 2022-08-04 16:17:53 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\Openai\Controllers\OpenaiController; + +Route::middleware('can:openai_index')->get('/openai', [OpenaiController::class, 'index'])->name('openai.index'); + +Route::middleware('can:openai_index')->get('/openai/histories', [OpenaiController::class, 'histories'])->name('openai.histories'); +Route::middleware('can:openai_index')->post('/openai/completions', [OpenaiController::class, 'completions'])->name('openai.completions'); diff --git a/plugins/Openai/Services/OpenAIService.php b/plugins/Openai/Services/OpenAIService.php new file mode 100644 index 00000000..8eff9252 --- /dev/null +++ b/plugins/Openai/Services/OpenAIService.php @@ -0,0 +1,120 @@ + + * @created 2023-03-13 16:42:52 + * @modified 2023-03-13 16:42:52 + */ + +namespace Plugin\Openai\Services; + +use Plugin\Openai\Libraries\OpenAI\Chat; +use Plugin\Openai\Models\OpenaiLog; + +class OpenAIService +{ + /** + * 发起 OpenAI 请求 + * + * @param $data + * @return mixed + * @throws \Throwable + */ + public function requestAI($data) + { + $prompt = $data['prompt'] ?? ''; + $apiKey = plugin_setting('openai.api_key'); + + $openAI = Chat::getInstance($apiKey); + $messages = $this->getChatMessages(); + $response = $openAI->setMessages($messages, $prompt)->create(); + + $result['prompt'] = $prompt; + + $error = trim($response['error']['message'] ?? ''); + if ($error) { + $result['error'] = $error; + } else { + $content = trim($response['choices'][0]['message']['content'] ?? ''); + + $response['choices'][0]['text'] = $content; + + $result['response'] = $response; + $newLog = $this->createOpenaiLog($prompt, $content); + $result['created_format'] = $newLog->created_format; + } + + return $result; + } + + /** + * @param $question + * @param $answer + * @return OpenaiLog + * @throws \Throwable + */ + private function createOpenaiLog($question, $answer): OpenaiLog + { + $user = current_user(); + $newOpenaiLog = new OpenaiLog([ + 'user_id' => $user->id ?? 0, + 'question' => trim($question), + 'answer' => trim($answer), + 'request_ip' => request()->getClientIp(), + 'user_agent' => request()->userAgent(), + ]); + $newOpenaiLog->saveOrFail(); + + return $newOpenaiLog; + } + + /** + * 获取聊天记录 + * + * @param int $perPage + * @return mixed + */ + public function getOpenaiLogs(int $perPage = 10) + { + $user = current_user(); + + return OpenaiLog::query() + ->select(['user_id', 'question', 'answer', 'created_at']) + ->where('user_id', $user->id) + ->orderByDesc('created_at') + ->paginate($perPage); + } + + /** + * https://platform.openai.com/docs/guides/chat/introduction + * + * messages=[ + * {"role": "system", "content": "You are a helpful assistant."}, + * {"role": "user", "content": "Who won the world series in 2020?"}, + * {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}, + * {"role": "user", "content": "Where was it played?"} + * ] + * + * @return array + */ + public function getChatMessages() + { + $logs = OpenaiLog::query() + ->select(['user_id', 'question', 'answer', 'created_at']) + ->limit(5) + ->get(); + + $messages[] = [ + 'role' => 'system', 'content' => 'You are a helpful assistant.', + ]; + foreach ($logs as $log) { + $messages[] = ['role' => 'user', 'content' => $log->question]; + $messages[] = ['role' => 'assistant', 'content' => $log->answer]; + } + + return $messages; + } +} diff --git a/plugins/Openai/Static/image/logo.png b/plugins/Openai/Static/image/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d510068d149b328ee76c5fe423b62a2342218b12 GIT binary patch literal 22900 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 + + +@endpush + +@section('content') +
+
+
+
{{ __('Openai::common.no_question') }}
+
+
+ + +
+
+
+
+ @if ($type != 'own') +
{{ __('Openai::common.number_free') }}: + {{ __('Openai::common.loading') }} +
+ @endif + @if ($error) +
+ + {{ $error }} +
+ @endif +
{{ $description }}
+
+
+ + + +@endsection diff --git a/plugins/Openai/columns.php b/plugins/Openai/columns.php new file mode 100644 index 00000000..6f076006 --- /dev/null +++ b/plugins/Openai/columns.php @@ -0,0 +1,31 @@ + + * @created 2023-03-13 16:08:41 + * @modified 2023-03-13 16:08:41 + */ + +return [ + [ + 'name' => 'api_type', + 'label_key' => 'common.api_type', + 'type' => 'select', + 'options' => [ + ['value' => 'own', 'label_key' => 'common.own'], + ['value' => 'beikeshop', 'label_key' => 'common.beikeshop'], + ], + 'required' => true, + 'description' => '如果选择 BeikeShop 平台, 则 API Key 可以留空', + ], + [ + 'name' => 'api_key', + 'label' => 'API Key', + 'type' => 'string', + 'required' => false, + 'description' => '
获取 API Key', + ], +]; diff --git a/plugins/Openai/config.json b/plugins/Openai/config.json new file mode 100644 index 00000000..fe658e11 --- /dev/null +++ b/plugins/Openai/config.json @@ -0,0 +1,18 @@ +{ + "code": "openai", + "name": { + "zh_cn": "ChatGPT 智能聊天助手", + "en": "Integration with OpenAI ChatGPT API" + }, + "description": { + "zh_cn": "ChatGPT 智能聊天助手, 基于 OpenAI API(gpt-3.5-turbo) 开发, 如有疑问详询QQ群 639108380", + "en": "Integration with OpenAI ChatGPT API" + }, + "type": "feature", + "version": "v1.1.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/plugins/Paypal/Controllers/PaypalController.php b/plugins/Paypal/Controllers/PaypalController.php new file mode 100644 index 00000000..2798948b --- /dev/null +++ b/plugins/Paypal/Controllers/PaypalController.php @@ -0,0 +1,118 @@ + + * @created 2022-08-10 18:57:56 + * @modified 2022-08-10 18:57:56 + * + * https://www.zongscan.com/demo333/1311.html + * https://clickysoft.com/how-to-integrate-paypal-payment-gateway-in-laravel/ + * https://www.positronx.io/how-to-integrate-paypal-payment-gateway-in-laravel/ + */ + +namespace Plugin\Paypal\Controllers; + +use Beike\Repositories\OrderPaymentRepo; +use Beike\Repositories\OrderRepo; +use Beike\Services\StateMachineService; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Srmklive\PayPal\Services\PayPal; + +class PaypalController +{ + private PayPal $paypalClient; + + private function initPaypal() + { + $paypalSetting = plugin_setting('paypal'); + $config = [ + 'mode' => $paypalSetting['sandbox_mode'] ? 'sandbox' : 'live', + 'sandbox' => [ + 'client_id' => $paypalSetting['sandbox_client_id'], + 'client_secret' => $paypalSetting['sandbox_secret'], + ], + 'live' => [ + 'client_id' => $paypalSetting['live_client_id'], + 'client_secret' => $paypalSetting['live_secret'], + ], + 'payment_action' => 'Sale', + 'currency' => 'USD', + 'notify_url' => '', + 'locale' => 'en_US', + 'validate_ssl' => false, + ]; + config(['paypal' => null]); + $this->paypalClient = new PayPal($config); + $token = $this->paypalClient->getAccessToken(); + $this->paypalClient->setAccessToken($token); + } + + /** + * 创建 paypal 订单 + * @param Request $request + * @return JsonResponse + * @throws \Throwable + */ + public function create(Request $request): JsonResponse + { + $this->initPaypal(); + $data = \json_decode($request->getContent(), true); + $orderNumber = $data['orderNumber']; + $customer = current_customer(); + $order = OrderRepo::getOrderByNumber($orderNumber, $customer); + $total = round($order->total, 2); + + $paypalOrder = $this->paypalClient->createOrder([ + 'intent' => 'CAPTURE', + 'purchase_units' => [ + [ + 'amount' => [ + 'currency_code' => $order->currency_code, + 'value' => $total, + ], + 'description' => 'test', + ], + ], + ]); + + return response()->json($paypalOrder); + } + + /** + * 客户同意后扣款回调 + * @param Request $request + * @return JsonResponse + * @throws \Throwable + */ + public function capture(Request $request): JsonResponse + { + $this->initPaypal(); + $data = \json_decode($request->getContent(), true); + $orderNumber = $data['orderNumber']; + $customer = current_customer(); + $order = OrderRepo::getOrderByNumber($orderNumber, $customer); + + OrderPaymentRepo::createOrUpdatePayment($order->id, ['request' => $data]); + $paypalOrderId = $data['paypalOrderId']; + $result = $this->paypalClient->capturePaymentOrder($paypalOrderId); + OrderPaymentRepo::createOrUpdatePayment($order->id, ['response' => $result]); + + try { + DB::beginTransaction(); + if ($result['status'] === 'COMPLETED') { + StateMachineService::getInstance($order)->changeStatus(StateMachineService::PAID); + DB::commit(); + } + } catch (\Exception $e) { + DB::rollBack(); + dd($e); + } + + return response()->json($result); + } +} diff --git a/plugins/Paypal/Lang/en/setting.php b/plugins/Paypal/Lang/en/setting.php new file mode 100644 index 00000000..e6b4211a --- /dev/null +++ b/plugins/Paypal/Lang/en/setting.php @@ -0,0 +1,15 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + 'sandbox_mode' => 'Sandbox Mode', + 'enabled' => 'Enabled', +]; diff --git a/plugins/Paypal/Lang/zh_cn/setting.php b/plugins/Paypal/Lang/zh_cn/setting.php new file mode 100644 index 00000000..098ac7db --- /dev/null +++ b/plugins/Paypal/Lang/zh_cn/setting.php @@ -0,0 +1,15 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + 'sandbox_mode' => '沙盒模式', + 'enabled' => '开启', +]; diff --git a/plugins/Paypal/Routes/admin.php b/plugins/Paypal/Routes/admin.php new file mode 100644 index 00000000..bff13b1f --- /dev/null +++ b/plugins/Paypal/Routes/admin.php @@ -0,0 +1,10 @@ + + * @created 2022-08-12 10:33:01 + * @modified 2022-08-12 10:33:01 + */ diff --git a/plugins/Paypal/Routes/shop.php b/plugins/Paypal/Routes/shop.php new file mode 100644 index 00000000..d40b5028 --- /dev/null +++ b/plugins/Paypal/Routes/shop.php @@ -0,0 +1,18 @@ + + * @created 2022-08-12 10:33:13 + * @modified 2022-08-12 10:33:13 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\Paypal\Controllers\PaypalController; + +Route::group(['prefix' => 'paypal'], function () { + Route::post('/create', [PaypalController::class, 'create']); + Route::post('/capture', [PaypalController::class, 'capture']); +}); diff --git a/plugins/Paypal/Static/image/logo.png b/plugins/Paypal/Static/image/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5f64640b194d28800154a5d612569adc8b48d46b GIT binary patch literal 9004 zcmeHt`B#$L7q?|)T`Moww87MGTAA0>W@qtUr(2d*ma|Zq^FYoD3Yj;>kz3|GXKLnv zIiVt&iJJ2aDhi3210o^_3IZ>8y??{I-e2wy&wAE6>paid`|Quz`?L2x3HPlnkNtAy z7YPZ8W4He@dnh5XkNxj+_>g#HfIvwUzmB~4%Q09&LR$9UXP-n)-YE$Q74mJfKORNo z(dQ%E@*hWPZ!Z7+kIBowfBo}N+?7Kru_r4IBPT0Dy-Eq7>h45UpGKSy4ixXCLGY=j zm%8{=PE|hgZG2YkRBiqRu+P#X`^b?4zr8j2?d_>+`TSm$%%h%#!2~SV7`CiOVJxS~ z{CM60L+7p6BN$k6V}a*)Z^qfPMS0w&& zMVzNDGy;4HmY1EB0+$5?w*37GTI$WzhXeo^0l|Y{9O(IyU`iqqE!3qB*qi&!j?=~@?spln zwQ`K2xkp$?r0MbGhzXXev512_Y1l$6*;f>2!gxP30W*5#aXd$!$vJ5r^_>ed`c1;Z zswjNyvlJJ4fUZZ|K}wI%wl7JXIu=7V$I|3@9BboiR0xw%FL9Nli;CH z%UXT%KLG&#h8%NFZ5w(*;;4C1c;DW9)N7cAXW=XjAaUhH4EciP;M(@#R=3V#fYe3D zb*X<6Oc-xh`QnA=SszNEM*AhgE{cc*W zhQ4I}Srp#6FQJ^I8dURLYI@ z7-l1l40dXi&tAqY>i8u)V9!0PH{QZF#(IV{MT`?Vw+g0fuTtNDE&OI?Tb9X+_wpaM z_euh;PE)T+0zPlOLAqD=1h_aJW@~*jUTP1tMO)s7j2NkqMRmw>t(9RGWY)#Fmf;Fu ze(3^)8~@5oGf5B4@i!wCvthuwFJ)-gNmjG6F7IR@>e5mvQ-@Q%O zCk$_n);{>jqAtJ`&l+Y3v+4jVu2;A(L8u~ZfYTfeFbQKxx1Rh7So$P-DdGT+GZgkdkrx3ymmq4 z_{vv9m1l(yj8hd`;>k~8HvP^Q0&+bIrp1dG(3{C^8_f80>(D}&bGXeVuYhqpQugQe z&&I+CGrR#@jXD8=UtW=|oY}1xj%}o81ho711Iu*bZ$uZWKRwO5mn;Gz4-s`w07Z z?p5oi5XGw+R9lf-@9Gjfl%0oEPlFjHQm01b*91SmCl;+X=@_E(u6RDGpP!;vcpQMn zE9OSEMthztIC&K&0->HoNO5O!;_QJ2RJ_tXW?khfpA9`=0Hw_U;?GXg9)u8zr7YIL z%xDl!f$?GWf>?tYZV|)pt4a3n>z)FK!^@`2VnF&$$k=We@Fz0GALobuSa0ps2G8dQ zK35wxs8a4KAssImqi}_(pZ2btmbNW7Gi1C2p&eaEN0E!iSGOYp=H5*BkFn%=iT?DOrlA{V=lXOj_kd3vf9RxTJmO^%s#ynDxkd)MMQ~%&q}e>B(UuV>u`X-V4~sgv{|gC+?1c^2)Go zo$QL@qx!__C2qVIU})N{rraj8wpBOPlb9?Z#_u{Wy(Bk9^L>?v{0eU3$J*Z5y(Pib zx3WLY+g5k!kBf5K85_zgw&jxtdHt4mcAu|h2Qi9vqnn+=E-Ti81=$gpn-#N|^@Xv= zpIQY%Bv9A^0AeeJH=D8>5iB=vO4}jqLkrmcu!;9b;7&ZpdivUliX0#&AbQBOjHDnI zZ4y;k(K}ll-GpfB%ZYPOclI#e>Ato!i#G$t7)|~78_h}FMYp?%&XArr$Ae(wpNby@ z8(Zspwb`^-vca~m^(JVj7F^K4;MUC{`=uJ&`quKC5DOQ_$b*sGapYs#8d&Z9oN#7- zs^Ae8LC!N_{F6P=!}x_uzlnQDkHcNOsc|L<9Gj7Gulz_jmtTUdS5F%l@vnAfR02H$ zyJXwJ7j+jZ^AAQw_=opgRdP5%JjYtLxcdTn!nsKdKa_!&lVdi>*F5$`!Q`e|g?G_N z-kHGf4MDH<+&Y4`>b!&Tv6TZ-)P75qjA|dw++z1$%Me-l9yE(FUz=%?#(id%{j-2M zos)5zpMJ<374W;K46ja z*s=LCl?I&Irqy#$5yO1|SaR)m?|}>?P2OVe#mFYAa{KFq#qp$=OKRDay~u1HnY?}o z<90}jMp=~QBC|-}8|AgpSSQyPUXh5rkO=%M)BdnlyQbq7bK55P5k_J&HO|#j(Sxqc zd_OUeU{cBLH8DGCf!E|(xWJ>6dXG*Hxd?;Y@QPfNkh%U>QTVsw#i}^|eWm#mpFg06 zYf!#oadvZft1O(F`Jngxwx1iNCHyb&p4$NJs5|&ROM;OIP~VR}nGgEPE)h8zN2#ii z1yp4Dpt2yi+EJM)$W;~z*wL!S{NB*YT3~1-zS}-o$n0v_Y-?roGK@8;I-<5cqbBcb z^X)Gj@v0qF20yU{@dDX!339@xDjlEfsRU{ink^yvF70`;K<0Tk4QN zA-#u{r_~0NnpZ*r%OX&87GwQGm#VR_XN!VF+NORofG;P8*_IF8t}-UM{OgIs}I)v*8~Mt7{jzkmFB=Q))4wAYS+ z1orn!PwGdb8Wdr7y}}OKVWl|9i#cE;@Dx>&uMGw;>&kUt!*AAGEL*7PmR9mCwLLKH zT1UCEJz|h!yqCyx6K+dk3!?6RY{Ovh(dk*sd2>~{q{N$8G7EJnNn6`cn~JePBZkMa z^fakP+UmZyEqr&^2)=*#*yuLh+nq=@VZ1D>bgy467nEEtCx8{r%&I;){2j9Q6rdax zDeyHjbLn`|%Wok??WHW8FD7wvZt!}wpf6TF2Ow;s8?24WBJJD7!3}VU?cr}Oqud%zyhr3g+nnczA zUUVqQ_gZU3=LtVdLuhiU6oKSm|9ErZ|?r%cJt#o<(8=~j00sx4unc@!ehaWV7OiIAm^3~vh^vH<{4iae< zpC#AB_3DN2XST(5i_{1E5AWHclW}VZnPluX^AU zJ{|$H#-YARc2yOmp1zs`P$`E6kkf5W+QraqZovs=)5)-?OW2(iov=NDx}|v=+j|}{ zUly0^AsjyD9%GkMu7%veLX!8I{XwEUo}zS8re)OLH~|5F9y^+kL*Pl^M+lc{>Gsw| zMq%W`Y`~g*b=lnVNOf*2@m*>3ew-w)`rK`8moQhfWo-#ulO4<#04VKGd(k+N`%oc- zl~EVJL%86M+U*%y!awgUE^Yqi-P@!?MlKL7s+{Zzl9N?Ezz7R(1%sU5u)TxcO(vU) zq8odKI@z74rGc%u6ALpQVt&SLZ`fs3!){R5ocr%~E6Tj9hVk#FMtpth?opag=pfBa zJnP4C?4A$t4#Zs?B|UOJqF$n~;ca)kKKD2zc(&e|+-N)6P_M(UrzGt3-!!$c1qD0r zG6Lhx+LqRN-b{K*!~IpUK`_Mpmj6+ufv1F5#a7rG@?9&?QK7y^qoU9-3+0VFGYxvw zXi?oFQTXG+kbaH7;^o&-?LkAM(q}OATHV!6Bt~e*vVP-Bz>rTrB)Ja>SRBVCa+#{< zO=r#AzH)8G_TjJ_p?45kN>HL_*injln(DBBZEe8JCnQU#MU|zT*)3SC=9;%HFjR#s z&uz5qgRfB^h&vl8+as`_vKH_`7y{ECyKpZ_MQe9q6P4W#K@d_RNabPc?cqCrl)m$= zHkYx;gZ+W`Y&QzYf-vG>DxlD(^*Q?iOM)?<5h%U-Tl@`o?_3jzEmrzTSa`U0%Fc|d zn17`n&(n`mq=9z6Wvap>22#QN{OfB9yee^6$VpnG4T+XAb3)%>7lm)Q=h5@`MM`3A z7Z^!}aF>>F7q=HzsQHDbcLQ0=8QrOZAF-M1x>HI3W)8R#QNUOJoq?L&2!IHl*T1do z8XBBx(rRDs~=n{KC;0lWK=Z;W=N@IZzoYNMo(kXSg$F^W;0!>S#@Uzs(f7rsH7J=xN4I3z4w zVW!F7i~h;+a&us?>vjiY{sc3XQUOc3E^jD?6K}jOzP-mD@rVaD9*n@l(Dd`QX*R%wKZ-&9*A|>9OS^@9oM> zONB$K73v>@dIg~ZI??EiI9={<*!A+y1p&owqP*}5Z&BsW;YqE2L!YpD+`PZMR#Nj$ z;#Q8cR6HE9CuVu79hcRjP)$r>7UG3q`A%9>jX~};Jt{I$gSN0iBobnNUCBQ;N*blX z8r{UK?;-oW^2ttrbl(&)F2x}LV_luR=0AhXpNVlxLuf$%dkfcYB=(le$@3pG| zcg(XFPSK4Sot;RTJ-C1t#oO{I{nPWtr6W<>)o$W^ACRU2F)$19cpX7+7umr^J=g7Z zBpahbvuBFL0p=L}-Nc;YT=U0wC+U*{(&y0+HA08*1mAI)Hre_=mW}H-n2p`OD*b-F zHXEk|zY8xOe7ONS!(7!^LnV1N4K?&}ixpuvb_xdb2;_!saScjaw{asYU9)V?7jcL* zotk7O?Qj>`Yzi9 z;?@5{=(l;_m-YmY8yJvTMg?fwFE%>o^18p6)N47pMIR*mAan;8o?w24+~Q**#dWx4 z0aOrp4`RBvnNjBtfu7VSyKYX^wYFARs6{C{ys#_jW3y(;GxQJMvpxjpvw5o=IbWxd z!-G|AqpSE=#=pdnN5_KKhP`@c-%F|58FFF_bsaYL15Z&`RY)qe|#|MEfUdD zw#%&Npdppmjj?Opz>J)#V_9N+?gihGEIKoy*V0}XZHDjaYSZ!Y)Cwm`BjM*q71lDh z-WZU?PR^(>X(zCoJa;+A1@NyZ&ysDtGSFJ*0-w7N^vf=r?@ei_3fBfyDdBngrFZK^ z8`(oW)7A+y$cN9%CSu6XAM-h>Ij)Ei_)<>YFR;s+jx&C7ixtu;;tG+w2IVjNU2(xi zT#p$Nr8rHwQi4%D$GR|zuc393&ZNyTrKWJ<$sW%v%NtlAlrNyu)gfyn9fB0ptVKtl z4N2PJ*{IfFvB`+3S>&lp_7=T^np3j{fe-5iJ0!!&pPxiD`3%P*Z$%#v`;9iHL$7JEcih2ww!etCMjW`ONf!Hv}zvg2aau;cPsLXn9%X4_Yf@Ok_>DiGZ?c-h77 zPEXj^N%j0=EF{7H$<3S)#_UW+bt%qE?S|eFFrh@Yok4M-oQ7PIJ=D)HNqed^29+}N zpIIt1P(PJp0{N(1iFbFLBRop`{RwXFTJQpbz0qhno**YiXV6E>Z_9Op zGR_O8bzt1tE=Nr9_E%hFIxyA^*LB_?hWrIkzqTQplHTpsR@hp&UT5FSagtMqf z2xSyYgt$+&4(Fc*jrC9%V07}%s$%}>UJ#4`DW7detmwE9l00 z8FFWOC1c1YY2A0-o*JI?%F+vEFIixSG?{m-j?sB<(M=jnI(4r7!?sjdwKiyGy(Ob6 zS=imq+dk+xJ+BQnIUmNI0zO8rntv8-$cm*fT4qNAm z)3Yt(ok%B<7hZYK6gE$Ca%4ZjS87#>=Db2*MSOvWO3M&|KK;0f15imp#Ow9|Q$&=+ zcE32MSDw(;RLC?w%j}mnFc2W<^8jrT#hvun-!{WKV2VexpMs!xlS}y>a^ugf7QbHA zx-`-MUdjrCu`Z|u?Dh{B>WYmp7JX_Z-0(^} zLN{ls-Q+uy*gdHX&2;a z&nea!MVY(DVbM30_E_Oo-XkqiM@5}=y?_4}B4REg8SI%?4dQf9iw9MQ@D$3;@^=45 zMwYB-;5?#Rd|LN-$N@BoIdUr?HDUy4*|x$C8o#c4H+M+PLn*!1cUzRNtBEVEHNcwu z9*wSDKE&dvg?jt-Ili0N?PYW#734E;-v!Ut4e->Mm@O6;3`8|kd2f2H z^JB_!!Oe(2kv@U3K_$#n0RiLDV|9soqsAxGY&s%DN^z%sxckOF#m7L&#W}OQmwTIeOK> za(Nk5>w_#415kuhv^HBDsl%h23>IUUW@1D?cjxC9s;Fo|UGcIgFHvOE@l=MUFFSr_ zLRGkfSwr!^oW$1q%k(Hz&cVFw;+<;AxR4)V38Vf9!6%+uO>3h_zOZxSP7(LpxkgfBQDXamZ+b)=G0U@s=Bu#U&vn$`a`@&CKI@c;k1#ty +
+ + +@if($payment_setting['sandbox_mode']) + +@else + +@endif + + diff --git a/plugins/Paypal/columns.php b/plugins/Paypal/columns.php new file mode 100644 index 00000000..266a4f36 --- /dev/null +++ b/plugins/Paypal/columns.php @@ -0,0 +1,56 @@ + + * @created 2022-06-29 21:16:23 + * @modified 2022-06-29 21:16:23 + */ + +return [ + [ + 'name' => 'sandbox_client_id', + 'label' => 'Sandbox Client ID', + 'type' => 'string', + 'required' => true, + 'rules' => 'required|size:80', + 'description' => '沙盒模式 Client ID', + ], + [ + 'name' => 'sandbox_secret', + 'label' => 'Sandbox Secret', + 'type' => 'string', + 'required' => true, + 'rules' => 'required|size:80', + 'description' => '沙盒模式 Secret', + ], + [ + 'name' => 'live_client_id', + 'label' => 'Live Client ID', + 'type' => 'string', + 'required' => true, + 'rules' => 'required|size:80', + 'description' => '正式环境 Client ID', + ], + [ + 'name' => 'live_secret', + 'label' => 'Live Secret', + 'type' => 'string', + 'required' => true, + 'rules' => 'required|size:80', + 'description' => '正式环境 Secret', + ], + [ + 'name' => 'sandbox_mode', + 'label_key' => 'setting.sandbox_mode', + 'type' => 'select', + 'options' => [ + ['value' => '1', 'label_key' => 'setting.enabled'], + ['value' => '0', 'label' => '关闭'], + ], + 'required' => true, + 'description' => '', + ], +]; diff --git a/plugins/Paypal/config.json b/plugins/Paypal/config.json new file mode 100644 index 00000000..252c7b0e --- /dev/null +++ b/plugins/Paypal/config.json @@ -0,0 +1,12 @@ +{ + "code": "paypal", + "name": "PayPal", + "description": "PayPal 支付 PayPal Developer", + "type": "payment", + "version": "v1.0.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/plugins/Social/Bootstrap.php b/plugins/Social/Bootstrap.php new file mode 100644 index 00000000..fa572c7a --- /dev/null +++ b/plugins/Social/Bootstrap.php @@ -0,0 +1,48 @@ + + * @created 2022-10-12 17:33:29 + * @modified 2022-10-12 17:33:29 + */ + +namespace Plugin\Social; + +class Bootstrap +{ + public function boot() + { + $this->addSocialData(); + + add_hook_blade('admin.plugin.form', function ($callback, $output, $data) { + $code = $data['plugin']->code; + if ($code == 'social') { + return view('Social::admin.config_form', $data)->render(); + } + + return $output; + }, 1); + } + + /** + * 增加第三方登录方式 + */ + private function addSocialData() + { + add_hook_filter('login.social.buttons', function ($buttons) { + $providers = plugin_setting('social.setting'); + if (empty($providers)) { + return $buttons; + } + + foreach ($providers as $provider) { + $buttons[] = view('Social::shop/social_button', ['provider' => $provider])->render(); + } + + return $buttons; + }); + } +} diff --git a/plugins/Social/Controllers/AdminSocialController.php b/plugins/Social/Controllers/AdminSocialController.php new file mode 100644 index 00000000..dd8c4e56 --- /dev/null +++ b/plugins/Social/Controllers/AdminSocialController.php @@ -0,0 +1,29 @@ + + * @created 2022-09-30 18:46:38 + * @modified 2022-09-30 18:46:38 + */ + +namespace Plugin\Social\Controllers; + +use Beike\Admin\Http\Controllers\Controller; +use Beike\Repositories\SettingRepo; +use Illuminate\Http\Request; + +class AdminSocialController extends Controller +{ + /** + * @throws \Throwable + */ + public function saveSetting(Request $request): array + { + SettingRepo::storeValue('setting', $request->all(), 'social', 'plugin'); + + return json_success('保存成功'); + } +} diff --git a/plugins/Social/Controllers/ShopSocialController.php b/plugins/Social/Controllers/ShopSocialController.php new file mode 100644 index 00000000..e2e012dd --- /dev/null +++ b/plugins/Social/Controllers/ShopSocialController.php @@ -0,0 +1,72 @@ + + * @created 2022-09-30 18:46:38 + * @modified 2022-09-30 18:46:38 + */ + +namespace Plugin\Social\Controllers; + +use Beike\Admin\Http\Controllers\Controller; +use Beike\Models\Customer; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Config; +use Laravel\Socialite\Facades\Socialite; +use Plugin\Social\Repositories\CustomerRepo; + +class ShopSocialController extends Controller +{ + public function initSocial() + { + $providerSettings = plugin_setting('social.setting', []); + foreach ($providerSettings as $providerSetting) { + $provider = $providerSetting['provider']; + if (empty($provider)) { + continue; + } + $config = [ + 'client_id' => $providerSetting['key'], + 'client_secret' => $providerSetting['secret'], + 'redirect' => shop_route('social.callback', $provider), + ]; + Config::set("services.{$provider}", $config); + } + } + + /** + * @param $provider + * @return mixed + */ + public function redirect($provider) + { + try { + $this->initSocial(); + + return Socialite::driver($provider)->redirect(); + } catch (\Exception $e) { + exit($e->getMessage()); + } + } + + /** + * @param $provider + * @return mixed + */ + public function callback($provider) + { + try { + $this->initSocial(); + $userData = Socialite::driver($provider)->user(); + $customer = CustomerRepo::createCustomer($provider, $userData); + Auth::guard(Customer::AUTH_GUARD)->login($customer); + + return view('Social::shop/callback'); + } catch (\Exception $e) { + exit($e->getMessage()); + } + } +} diff --git a/plugins/Social/Lang/en/providers.php b/plugins/Social/Lang/en/providers.php new file mode 100644 index 00000000..18c9d669 --- /dev/null +++ b/plugins/Social/Lang/en/providers.php @@ -0,0 +1,36 @@ + + * @created 2022-10-13 11:30:33 + * @modified 2022-10-13 11:30:33 + */ + +return [ + 'alipay' => 'Alipay', + 'azure' => 'Azure', + 'dingtalk' => 'Dingtalk', + 'douyin' => 'Douyin', + 'douban' => 'Douban', + 'facebook' => 'Facebook', + 'feishu' => 'Feishu', + 'figma' => 'Figma', + 'github' => 'Github', + 'gitee' => 'Gitee', + 'google' => 'Google', + 'line' => 'Line', + 'linkedin' => 'Linkedin', + 'open-wework' => 'Open-wework', + 'outlook' => 'Outlook', + 'qcloud' => 'Qcloud', + 'qq' => 'QQ', + 'taobao' => 'Taobao', + 'tapd' => 'Tapd', + 'twitter' => 'Twitter', + 'wechat' => 'Wechat', + 'wework' => 'Wework', + 'weibo' => 'Weibo', +]; diff --git a/plugins/Social/Lang/en/setting.php b/plugins/Social/Lang/en/setting.php new file mode 100644 index 00000000..7d38c294 --- /dev/null +++ b/plugins/Social/Lang/en/setting.php @@ -0,0 +1,60 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + // Text + 'text_module' => 'Modules', + 'text_success' => 'Success: You have modified module OpenCart OmniAuth!', + 'text_copyright' => 'OpenCart.cn OmniAuth © %s', + 'text_omni_explain' => 'This plugin supports Facebook, Twitter, Google etc.', + 'text_omni_explain_2' => 'To use a third-party login, you need to apply to the corresponding platform, and fill in the obtained ID and key to the corresponding input box.', + 'text_wechat_title' => 'WeChat scan code login application address', + 'text_wechat_info' => 'WeChat open platform', + 'text_qq_title' => 'QQ login application address', + 'text_qq_info' => 'QQ interconnection', + 'text_weibo_title' => 'Weibo login application address', + 'text_weibo_info' => 'Weibo open platform', + 'text_facebook_title' => 'Facebook login application address', + 'text_google_title' => 'Google login application address', + 'text_twitter_title' => 'Twitter login application address', + 'text_help_msg' => 'help information', + + 'copy_success' => 'Copy Success', + + // Entry + 'entry_status' => 'Status', + 'entry_bind' => 'Force Bind', + 'entry_debug' => 'Debug Mode', + + 'entry_provider' => 'Provider', + 'entry_key' => 'Client ID', + 'entry_secret' => 'Client Secret', + 'entry_scope' => 'Flags (optional)', + 'entry_callback' => 'Callback URL', + 'entry_sort_order' => 'Sort Order', + + // Button + 'button_add_row' => 'Add Provider', + + // Error + 'error_permission' => 'Warning: You do not have permission to modify module OpenCart OmniAuth!', + + // Providers + 'wechat' => 'WeChat', + 'wechatofficial' => 'WeChatOfficial', + 'qq' => 'QQ', + 'weibo' => 'Weibo', + 'facebook' => 'Facebook', + 'google' => 'Google', + 'twitter' => 'Twitter', + + 'instagram' => 'Instagram', +]; diff --git a/plugins/Social/Lang/zh_cn/providers.php b/plugins/Social/Lang/zh_cn/providers.php new file mode 100644 index 00000000..a81983f4 --- /dev/null +++ b/plugins/Social/Lang/zh_cn/providers.php @@ -0,0 +1,36 @@ + + * @created 2022-10-13 11:30:33 + * @modified 2022-10-13 11:30:33 + */ + +return [ + 'alipay' => '支付宝', + 'azure' => 'Azure', + 'dingtalk' => '钉钉', + 'douyin' => '抖音', + 'douban' => '豆瓣', + 'facebook' => 'Facebook', + 'feishu' => '飞书', + 'figma' => 'Figma', + 'github' => 'GitHub', + 'gitee' => 'Gitee', + 'google' => 'Google', + 'line' => 'Line', + 'linkedin' => 'Linkedin', + 'open-wework' => 'open-wework', + 'outlook' => 'Outlook', + 'qcloud' => '腾讯云', + 'qq' => 'QQ', + 'taobao' => '淘宝', + 'tapd' => 'Tapd', + 'twitter' => 'Twitter', + 'wechat' => '微信', + 'wework' => '企业微信', + 'weibo' => '微博', +]; diff --git a/plugins/Social/Lang/zh_cn/setting.php b/plugins/Social/Lang/zh_cn/setting.php new file mode 100644 index 00000000..99c0be3d --- /dev/null +++ b/plugins/Social/Lang/zh_cn/setting.php @@ -0,0 +1,49 @@ + + * @created 2022-08-11 15:26:18 + * @modified 2022-08-11 15:26:18 + */ + +return [ + // Text + 'text_module' => '模块', + 'text_success' => '成功: 您成功修改第三方登录配置!', + 'text_copyright' => 'OpenCart.cn 获取帮助 © %s', + 'text_omni_explain' => '本模块支持微Facebook, Twitter, Google等第三方登录', + 'text_omni_explain_2' => '要使用第三方登录, 需要到对应平台申请开通, 并把获取到的 ID 和密钥填写到上面对应的输入框', + 'text_wechat_title' => '微信扫码登录申请地址', + 'text_wechat_info' => '微信开放平台', + 'text_qq_title' => 'QQ登录申请地址', + 'text_qq_info' => 'QQ互联', + 'text_weibo_title' => '微博登录申请地址', + 'text_weibo_info' => '微博开放平台', + 'text_facebook_title' => 'Facebook登录申请地址', + 'text_google_title' => 'Google登录申请地址', + 'text_twitter_title' => 'Twitter登录申请地址', + 'text_help_msg' => '帮助信息', + + 'copy_success' => '复制成功', + + // Entry + 'entry_status' => '状态', + 'entry_bind' => '强制绑定', + 'entry_debug' => '调试模式', + + 'entry_provider' => '类型', + 'entry_key' => 'Client ID', + 'entry_secret' => 'Client Secret', + 'entry_scope' => 'Flags (可选项)', + 'entry_callback' => '回调地址', + 'entry_sort_order' => '排序', + + // Button + 'button_add_row' => '添加类型', + + // Error + 'error_permission' => '警告: 您没有权限修改此配置!', +]; diff --git a/plugins/Social/Migrations/2022_10_13_100354_create_customer_socials.php b/plugins/Social/Migrations/2022_10_13_100354_create_customer_socials.php new file mode 100644 index 00000000..796a242c --- /dev/null +++ b/plugins/Social/Migrations/2022_10_13_100354_create_customer_socials.php @@ -0,0 +1,37 @@ +id(); + $table->integer('customer_id'); + $table->string('provider'); + $table->string('user_id'); + $table->string('union_id'); + $table->text('access_token'); + $table->text('extra'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('customer_socials'); + } +}; diff --git a/plugins/Social/Models/CustomerSocial.php b/plugins/Social/Models/CustomerSocial.php new file mode 100644 index 00000000..20419eca --- /dev/null +++ b/plugins/Social/Models/CustomerSocial.php @@ -0,0 +1,30 @@ + + * @created 2022-10-13 10:35:44 + * @modified 2022-10-13 10:35:44 + */ + +namespace Plugin\Social\Models; + +use Beike\Models\Customer; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; + +class CustomerSocial extends Model +{ + public $table = 'customer_socials'; + + public $fillable = [ + 'customer_id', 'provider', 'user_id', 'union_id', 'access_token', 'extra', + ]; + + public function customer(): BelongsTo + { + return $this->belongsTo(Customer::class); + } +} diff --git a/plugins/Social/Repositories/CustomerRepo.php b/plugins/Social/Repositories/CustomerRepo.php new file mode 100644 index 00000000..2b7dc358 --- /dev/null +++ b/plugins/Social/Repositories/CustomerRepo.php @@ -0,0 +1,120 @@ + + * @created 2022-10-13 09:57:13 + * @modified 2022-10-13 09:57:13 + */ + +namespace Plugin\Social\Repositories; + +use Beike\Models\Customer; +use Beike\Shop\Services\AccountService; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Str; +use Laravel\Socialite\Two\User; +use Plugin\Social\Models\CustomerSocial; + +class CustomerRepo +{ + /** + * 允许的第三方服务 + */ + private const PROVIDERS = [ + 'facebook', + 'twitter', + 'google', + ]; + + public static function allProviders(): array + { + $items = []; + foreach (self::PROVIDERS as $provider) { + $items[] = [ + 'code' => $provider, + 'label' => trans("Social::providers.{$provider}"), + ]; + } + + return $items; + } + + /** + * 创建客户, 关联第三方用户数据 + * + * @param $provider + * @param User $userData + * @return Customer + */ + public static function createCustomer($provider, User $userData): Customer + { + $social = self::getCustomerByProvider($provider, $userData->getId()); + $customer = $social->customer ?? null; + if ($customer) { + return $customer; + } + + $email = $userData->getEmail(); + if (empty($email)) { + $email = strtolower(Str::random(8)) . "@{$provider}.com"; + } + $customer = Customer::query()->where('email', $email)->first(); + if (empty($customer)) { + $customerData = [ + 'from' => $provider, + 'email' => $email, + 'name' => $userData->getName(), + 'avatar' => $userData->getAvatar(), + ]; + $customer = AccountService::register($customerData); + } + + self::createSocial($customer, $provider, $userData); + + return $customer; + } + + /** + * @param $customer + * @param $provider + * @param User $userData + * @return Model|Builder + */ + public static function createSocial($customer, $provider, User $userData): Model|Builder + { + $social = self::getCustomerByProvider($provider, $userData->getId()); + if ($social) { + return $social; + } + + $socialData = [ + 'customer_id' => $customer->id, + 'provider' => $provider, + 'user_id' => $userData->getId(), + 'union_id' => '', + 'access_token' => $userData->token, + 'extra' => json_encode($userData->getRaw()), + ]; + + return CustomerSocial::query()->create($socialData); + } + + /** + * 通过 provider 和 user_id 获取已存在 social + * @param $provider + * @param $userId + * @return Model|Builder|null + */ + private static function getCustomerByProvider($provider, $userId): Model|Builder|null + { + return CustomerSocial::query() + ->with(['customer']) + ->where('provider', $provider) + ->where('user_id', $userId) + ->first(); + } +} diff --git a/plugins/Social/Routes/admin.php b/plugins/Social/Routes/admin.php new file mode 100644 index 00000000..0bf948a1 --- /dev/null +++ b/plugins/Social/Routes/admin.php @@ -0,0 +1,15 @@ + + * @created 2022-08-04 16:17:53 + * @modified 2022-08-04 16:17:53 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\Social\Controllers\AdminSocialController; + +Route::post('/social/setting', [AdminSocialController::class, 'saveSetting'])->name('plugin.social.setting'); diff --git a/plugins/Social/Routes/shop.php b/plugins/Social/Routes/shop.php new file mode 100644 index 00000000..50f26314 --- /dev/null +++ b/plugins/Social/Routes/shop.php @@ -0,0 +1,16 @@ + + * @created 2022-09-30 18:52:54 + * @modified 2022-09-30 18:52:54 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\Social\Controllers\ShopSocialController; + +Route::get('/social/redirect/{provider}', [ShopSocialController::class, 'redirect'])->name('social.redirect'); +Route::get('/social/callbacks/{provider}', [ShopSocialController::class, 'callback'])->name('social.callback'); diff --git a/public/plugin/social/image/alipay.png b/plugins/Social/Static/image/alipay.png similarity index 100% rename from public/plugin/social/image/alipay.png rename to plugins/Social/Static/image/alipay.png diff --git a/public/plugin/social/image/azure.png b/plugins/Social/Static/image/azure.png similarity index 100% rename from public/plugin/social/image/azure.png rename to plugins/Social/Static/image/azure.png diff --git a/public/plugin/social/image/dingtalk.png b/plugins/Social/Static/image/dingtalk.png similarity index 100% rename from public/plugin/social/image/dingtalk.png rename to plugins/Social/Static/image/dingtalk.png diff --git a/public/plugin/social/image/douban.png b/plugins/Social/Static/image/douban.png similarity index 100% rename from public/plugin/social/image/douban.png rename to plugins/Social/Static/image/douban.png diff --git a/public/plugin/social/image/douyin.png b/plugins/Social/Static/image/douyin.png similarity index 100% rename from public/plugin/social/image/douyin.png rename to plugins/Social/Static/image/douyin.png diff --git a/public/plugin/social/image/facebook.png b/plugins/Social/Static/image/facebook.png similarity index 100% rename from public/plugin/social/image/facebook.png rename to plugins/Social/Static/image/facebook.png diff --git a/public/plugin/social/image/feishu.png b/plugins/Social/Static/image/feishu.png similarity index 100% rename from public/plugin/social/image/feishu.png rename to plugins/Social/Static/image/feishu.png diff --git a/public/plugin/social/image/figma.png b/plugins/Social/Static/image/figma.png similarity index 100% rename from public/plugin/social/image/figma.png rename to plugins/Social/Static/image/figma.png diff --git a/public/plugin/social/image/github.png b/plugins/Social/Static/image/github.png similarity index 100% rename from public/plugin/social/image/github.png rename to plugins/Social/Static/image/github.png diff --git a/public/plugin/social/image/google.png b/plugins/Social/Static/image/google.png similarity index 100% rename from public/plugin/social/image/google.png rename to plugins/Social/Static/image/google.png diff --git a/public/plugin/social/image/line.png b/plugins/Social/Static/image/line.png similarity index 100% rename from public/plugin/social/image/line.png rename to plugins/Social/Static/image/line.png diff --git a/public/plugin/social/image/linkedin.png b/plugins/Social/Static/image/linkedin.png similarity index 100% rename from public/plugin/social/image/linkedin.png rename to plugins/Social/Static/image/linkedin.png diff --git a/public/plugin/social/image/logo.png b/plugins/Social/Static/image/logo.png similarity index 100% rename from public/plugin/social/image/logo.png rename to plugins/Social/Static/image/logo.png diff --git a/public/plugin/social/image/qq.png b/plugins/Social/Static/image/qq.png similarity index 100% rename from public/plugin/social/image/qq.png rename to plugins/Social/Static/image/qq.png diff --git a/public/plugin/social/image/wechat.png b/plugins/Social/Static/image/wechat.png similarity index 100% rename from public/plugin/social/image/wechat.png rename to plugins/Social/Static/image/wechat.png diff --git a/public/plugin/social/image/weibo.png b/plugins/Social/Static/image/weibo.png similarity index 100% rename from public/plugin/social/image/weibo.png rename to plugins/Social/Static/image/weibo.png diff --git a/plugins/Social/Views/admin/config_form.blade.php b/plugins/Social/Views/admin/config_form.blade.php new file mode 100644 index 00000000..1108797f --- /dev/null +++ b/plugins/Social/Views/admin/config_form.blade.php @@ -0,0 +1,187 @@ +@section('page-title-right') + +@endsection + +
+ {{--
+
{{ $plugin->name }}
+ +
--}} + {{-- --}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ __('Social::setting.entry_provider') }}{{ __('Social::setting.entry_status') }}{{ __('Social::setting.entry_key') }}{{ __('Social::setting.entry_secret') }}{{ __('Social::setting.entry_callback') }}{{ __('Social::setting.entry_sort_order') }}
+ + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+ + + + + +
+
+
{{ __('common.no_data') }}
+ +
+
+
+
+ +
{{ __('Social::setting.text_help_msg') }}
+
    +
  1. {{ __('Social::setting.text_omni_explain') }}
  2. +
  3. {{ __('Social::setting.text_omni_explain_2') }}
  4. +
  5. {{ __('Social::setting.text_facebook_title') }} + Facebook +
  6. +
  7. {{ __('Social::setting.text_twitter_title') }} + Twitter +
  8. +
  9. {{ __('Social::setting.text_google_title') }} + Google +
  10. + ....... +
+ + + + + diff --git a/plugins/Social/Views/shop/callback.blade.php b/plugins/Social/Views/shop/callback.blade.php new file mode 100644 index 00000000..ba9b029d --- /dev/null +++ b/plugins/Social/Views/shop/callback.blade.php @@ -0,0 +1,9 @@ + diff --git a/plugins/Social/Views/shop/social_button.blade.php b/plugins/Social/Views/shop/social_button.blade.php new file mode 100644 index 00000000..dcd250d1 --- /dev/null +++ b/plugins/Social/Views/shop/social_button.blade.php @@ -0,0 +1,4 @@ + diff --git a/plugins/Social/config.json b/plugins/Social/config.json new file mode 100644 index 00000000..f3907ed0 --- /dev/null +++ b/plugins/Social/config.json @@ -0,0 +1,12 @@ +{ + "code": "social", + "name": "Social", + "description": "第三方登录(包括Facebook、Twitter、Google)", + "type": "social", + "version": "v1.0.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/plugins/Stripe/Controllers/StripeController.php b/plugins/Stripe/Controllers/StripeController.php new file mode 100644 index 00000000..fe865bcd --- /dev/null +++ b/plugins/Stripe/Controllers/StripeController.php @@ -0,0 +1,54 @@ + + * @created 2022-08-08 15:58:36 + * @modified 2022-08-08 15:58:36 + */ + +namespace Plugin\Stripe\Controllers; + +use Beike\Repositories\OrderPaymentRepo; +use Beike\Repositories\OrderRepo; +use Beike\Services\StateMachineService; +use Beike\Shop\Http\Controllers\Controller; +use Illuminate\Http\Request; +use Plugin\Stripe\Services\StripePaymentService; + +class StripeController extends Controller +{ + /** + * 订单支付扣款 + * + * @param Request $request + * @return array + * @throws \Throwable + */ + public function capture(Request $request) + { + try { + $number = request('order_number'); + $customer = current_customer(); + $order = OrderRepo::getOrderByNumber($number, $customer); + $creditCardData = $request->all(); + + OrderPaymentRepo::createOrUpdatePayment($order->id, ['request' => $creditCardData]); + $result = (new StripePaymentService($order))->capture($creditCardData); + OrderPaymentRepo::createOrUpdatePayment($order->id, ['response' => $result]); + + if ($result) { + StateMachineService::getInstance($order)->setShipment()->changeStatus(StateMachineService::PAID); + + return json_success(trans('Stripe::common.capture_success')); + } + + return json_success(trans('Stripe::common.capture_fail')); + + } catch (\Exception $e) { + return json_fail($e->getMessage()); + } + } +} diff --git a/plugins/Stripe/Lang/en/common.php b/plugins/Stripe/Lang/en/common.php new file mode 100644 index 00000000..d369e986 --- /dev/null +++ b/plugins/Stripe/Lang/en/common.php @@ -0,0 +1,31 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'publishable_key' => 'Publishable Key', + + 'title_info' => 'Card information', + 'cardnum' => 'Cardnum', + 'expiration_date' => 'Expiration Date', + 'year' => 'Year', + 'month' => 'Month', + 'cvv' => 'Cvv', + 'remenber' => 'Keep this card in mind for future use', + 'btn_submit' => 'Submit', + + 'error_cardnum' => 'Please enter the card number', + 'error_cvv' => 'Please enter the security code', + 'error_year' => 'Please select the year', + 'error_month' => 'Please select a month', + + 'capture_success' => 'Capture Successfully', + 'capture_fail' => 'Capture Failed', +]; diff --git a/plugins/Stripe/Lang/zh_cn/common.php b/plugins/Stripe/Lang/zh_cn/common.php new file mode 100644 index 00000000..d1fd6e14 --- /dev/null +++ b/plugins/Stripe/Lang/zh_cn/common.php @@ -0,0 +1,31 @@ + + * @created 2022-07-28 16:19:06 + * @modified 2022-07-28 16:19:06 + */ + +return [ + 'publishable_key' => '公钥', + + 'title_info' => '卡信息', + 'cardnum' => '卡号', + 'expiration_date' => '截止日期', + 'year' => '选择年', + 'month' => '选择月', + 'cvv' => '安全码', + 'remenber' => '记住这张卡以便将来使用', + 'btn_submit' => '提交支付', + + 'error_cardnum' => '请输入卡号', + 'error_cvv' => '请输入安全码', + 'error_year' => '请选择年', + 'error_month' => '请选择月', + + 'capture_success' => '支付成功', + 'capture_fail' => '支付失败', +]; diff --git a/plugins/Stripe/Routes/shop.php b/plugins/Stripe/Routes/shop.php new file mode 100644 index 00000000..fdf208ca --- /dev/null +++ b/plugins/Stripe/Routes/shop.php @@ -0,0 +1,15 @@ + + * @created 2022-08-04 16:17:44 + * @modified 2022-08-04 16:17:44 + */ + +use Illuminate\Support\Facades\Route; +use Plugin\Stripe\Controllers\StripeController; + +Route::post('/stripe/capture', [StripeController::class, 'capture'])->name('stripe_capture'); diff --git a/plugins/Stripe/Services/StripePaymentService.php b/plugins/Stripe/Services/StripePaymentService.php new file mode 100644 index 00000000..f097009f --- /dev/null +++ b/plugins/Stripe/Services/StripePaymentService.php @@ -0,0 +1,74 @@ + + * @created 2022-08-08 16:09:21 + * @modified 2022-08-08 16:09:21 + */ + +namespace Plugin\Stripe\Services; + +use Beike\Shop\Services\PaymentService; +use Stripe\Exception\ApiErrorException; +use Stripe\Stripe; + +class StripePaymentService extends PaymentService +{ + // 零位十进制货币 https://stripe.com/docs/currencies#special-cases + public const ZERO_DECIMAL = [ + 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', + 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF', + ]; + + /** + * @throws ApiErrorException + * @throws \Exception + */ + public function capture($creditCardData): bool + { + $apiKey = plugin_setting('stripe.secret_key'); + Stripe::setApiKey($apiKey); + $tokenId = $creditCardData['token'] ?? ''; + if (empty($tokenId)) { + throw new \Exception('Invalid token'); + } + $currency = $this->order->currency_code; + + if (! in_array($currency, self::ZERO_DECIMAL)) { + $total = round($this->order->total, 2) * 100; + } else { + $total = floor($this->order->total); + } + + $stripeChargeParameters = [ + 'amount' => $total, + 'currency' => $currency, + 'metadata' => [ + 'orderId' => $this->order->id, + ], + 'source' => $tokenId, + // 'customer' => $this->createCustomer(), + ]; + + $charge = \Stripe\Charge::create($stripeChargeParameters); + + return $charge['paid'] && $charge['captured']; + } + + /** + * 创建 stripe customer + * @return mixed + * @throws ApiErrorException + */ + private function createCustomer(): mixed + { + $customer = \Stripe\Customer::create([ + 'email' => $this->order->email, + ]); + + return $customer['id']; + } +} diff --git a/public/plugin/stripe/css/demo.css b/plugins/Stripe/Static/css/demo.css similarity index 86% rename from public/plugin/stripe/css/demo.css rename to plugins/Stripe/Static/css/demo.css index 30fdbc42..04cd16f6 100644 --- a/public/plugin/stripe/css/demo.css +++ b/plugins/Stripe/Static/css/demo.css @@ -4,5 +4,5 @@ */ #bk-stripe-app .form-wrap { - max-width: 400px; + /* max-width: 500px; */ } \ No newline at end of file diff --git a/public/plugin/stripe/image/logo.png b/plugins/Stripe/Static/image/logo.png similarity index 100% rename from public/plugin/stripe/image/logo.png rename to plugins/Stripe/Static/image/logo.png diff --git a/public/plugin/stripe/image/pay-1.png b/plugins/Stripe/Static/image/pay-1.png similarity index 100% rename from public/plugin/stripe/image/pay-1.png rename to plugins/Stripe/Static/image/pay-1.png diff --git a/public/plugin/stripe/image/pay-2.png b/plugins/Stripe/Static/image/pay-2.png similarity index 100% rename from public/plugin/stripe/image/pay-2.png rename to plugins/Stripe/Static/image/pay-2.png diff --git a/public/plugin/stripe/image/pay-3.png b/plugins/Stripe/Static/image/pay-3.png similarity index 100% rename from public/plugin/stripe/image/pay-3.png rename to plugins/Stripe/Static/image/pay-3.png diff --git a/public/plugin/stripe/image/pay-4.png b/plugins/Stripe/Static/image/pay-4.png similarity index 100% rename from public/plugin/stripe/image/pay-4.png rename to plugins/Stripe/Static/image/pay-4.png diff --git a/public/plugin/stripe/image/pay-5.png b/plugins/Stripe/Static/image/pay-5.png similarity index 100% rename from public/plugin/stripe/image/pay-5.png rename to plugins/Stripe/Static/image/pay-5.png diff --git a/public/plugin/stripe/image/pay-image.png b/plugins/Stripe/Static/image/pay-image.png similarity index 100% rename from public/plugin/stripe/image/pay-image.png rename to plugins/Stripe/Static/image/pay-image.png diff --git a/public/plugin/stripe/js/demo.js b/plugins/Stripe/Static/js/demo.js similarity index 100% rename from public/plugin/stripe/js/demo.js rename to plugins/Stripe/Static/js/demo.js diff --git a/plugins/Stripe/Views/checkout/payment.blade.php b/plugins/Stripe/Views/checkout/payment.blade.php new file mode 100644 index 00000000..9a405686 --- /dev/null +++ b/plugins/Stripe/Views/checkout/payment.blade.php @@ -0,0 +1,170 @@ + + + + +
+
+
+
{{ __('Stripe::common.title_info') }}
+
+
+ +
+
+
+
Cardholder Name
+
+ +
+
+
@{{ errors.cardholderName }}
+ +
+
Credit Card Number
+
+
+
+
@{{ errors.cardNumber }}
+ +
+
Expiration Date
+
+
+
+
@{{ errors.cardExpiry }}
+
+
CVV
+
+
+
@{{ errors.cardCvc }}
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/plugins/Stripe/columns.php b/plugins/Stripe/columns.php new file mode 100644 index 00000000..ec130ee0 --- /dev/null +++ b/plugins/Stripe/columns.php @@ -0,0 +1,40 @@ + + * @created 2022-06-29 21:16:23 + * @modified 2022-06-29 21:16:23 + */ + +return [ + [ + 'name' => 'publishable_key', + 'label_key' => 'common.publishable_key', + 'type' => 'string', + 'required' => true, + 'rules' => 'required|min:32', + 'description' => '公钥(Publishable key)', + ], + [ + 'name' => 'secret_key', + 'label' => '密钥', + 'type' => 'string', + 'required' => true, + 'rules' => 'required|min:32', + 'description' => '密钥(Secret key)', + ], + [ + 'name' => 'test_mode', + 'label' => '测试模式', + 'type' => 'select', + 'options' => [ + ['value' => '1', 'label' => '开启'], + ['value' => '0', 'label' => '关闭'], + ], + 'required' => true, + 'description' => '如开启测试模式请填写测试公钥和密钥, 关闭测试模式则填写正式公钥和密钥', + ], +]; diff --git a/plugins/Stripe/config.json b/plugins/Stripe/config.json new file mode 100644 index 00000000..c87af7c3 --- /dev/null +++ b/plugins/Stripe/config.json @@ -0,0 +1,12 @@ +{ + "code": "stripe", + "name": "Stripe 支付", + "description": "Stripe 支付 Stripe", + "type": "payment", + "version": "v1.0.0", + "icon": "/image/logo.png", + "author": { + "name": "成都光大网络科技有限公司", + "email": "yangjin@guangda.work" + } +} diff --git a/public/plugin/.gitignore b/public/plugin/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/public/plugin/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/public/plugin/alipay/image/logo.png b/public/plugin/alipay/image/logo.png deleted file mode 100644 index 4c21b8dc282861ba1b32e001ffb4c35f7340bdcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15404 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 F@qa+c-8%pP