后台下单购买插件

购买插件

wip

wip

购买插件微信支付

wip

优化插件购买

wip

修复插件购买

添加插件接口

返回json

wip

优化插件详情页购买ui交互

添加弹出层插件

wip

wip

wip

插件市场设置 token 优化
This commit is contained in:
Edward Yang 2022-12-06 10:09:37 +08:00
parent ec30668fa4
commit 9f67ec43f0
15 changed files with 263 additions and 22 deletions

View File

@ -32,6 +32,7 @@ class MarketingController
$plugins = MarketingService::getInstance()->getList($filters);
$data = [
'plugins' => $plugins,
'domain' => str_replace(['http://', 'https://'], '', config('app.url')),
'types' => PluginRepo::getTypes(),
];
@ -52,8 +53,12 @@ class MarketingController
$pluginCode = $request->code;
$plugin = MarketingService::getInstance()->getPlugin($pluginCode);
$data = [
'domain' => str_replace(['http://', 'https://'], '', config('app.url')),
'plugin' => $plugin,
];
if ($request->expectsJson()) {
return $data;
}
return view('admin::pages.marketing.show', $data);
} catch (\Exception $e) {
return redirect(admin_route('marketing.index'))->withErrors(['error' => $e->getMessage()]);
@ -61,6 +66,22 @@ class MarketingController
}
/**
* 下单购买插件
*/
public function buy(Request $request)
{
try {
$postData = $request->getContent();
$pluginCode = $request->code;
$result = MarketingService::getInstance()->buy($pluginCode, $postData);
return json_success('获取成功', $result);
} catch (\Exception $e) {
return json_fail($e->getMessage());
}
}
/**
* 下载插件安装包到本地
*/

View File

@ -143,6 +143,7 @@ Route::prefix($adminName)
// 插件市场
Route::middleware('can:marketing_index')->get('marketing', [Controllers\MarketingController::class, 'index'])->name('marketing.index');
Route::middleware('can:marketing_show')->get('marketing/{code}', [Controllers\MarketingController::class, 'show'])->name('marketing.show');
Route::middleware('can:marketing_buy')->post('marketing/{code}/buy', [Controllers\MarketingController::class, 'buy'])->name('marketing.buy');
Route::middleware('can:marketing_download')->post('marketing/{code}/download', [Controllers\MarketingController::class, 'download'])->name('marketing.download');

View File

@ -35,7 +35,14 @@ class MarketingService
return new self;
}
public function getList($filters = [])
/**
* 获取可插件市场插件列表
*
* @param array $filters
* @return mixed
*/
public function getList(array $filters = []): mixed
{
$url = config('beike.api_url') . '/api/plugins';
if (!empty($filters)) {
@ -44,9 +51,16 @@ class MarketingService
return $this->httpClient->get($url)->json();
}
public function getPlugin($pluginCode)
/**
* 获取插件市场单个插件信息
*
* @param $pluginCode
* @return mixed
*/
public function getPlugin($pluginCode): mixed
{
$url = config('beike.api_url') . '/api/plugins/' . $pluginCode;
$url = config('beike.api_url') . "/api/plugins/{$pluginCode}";
$plugin = $this->httpClient->get($url)->json();
if (empty($plugin)) {
throw new NotFoundHttpException('该插件不存在或已下架');
@ -54,6 +68,35 @@ class MarketingService
return $plugin;
}
/**
* 购买插件市场单个插件
*
* @throws \Exception
*/
public function buy($pluginCode, $postData)
{
$url = config('beike.api_url') . "/api/plugins/{$pluginCode}/buy";
$content = $this->httpClient->withBody($postData, 'application/json')
->post($url)
->json();
$status = $content['status'] ?? '';
if ($status == 'success') {
return $content['data'];
} else {
throw new \Exception($content['message'] ?? '');
}
}
/**
* 下载插件到网站
*
* @param $pluginCode
* @throws \Exception
*/
public function download($pluginCode)
{
$datetime = date('Y-m-d');
@ -68,5 +111,4 @@ class MarketingService
$zipFile = Zip::open($pluginZip);
$zipFile->extract(base_path('plugins'));
}
}

BIN
public/image/alipay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
public/image/wechat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

4
public/vendor/qrcode/qrcode.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -324,4 +324,8 @@ body.page-seller-product {
table.table thead th, .fw-bold, h1,h2,h3, h4, h5, h6, b, strong, .card .card-header {
font-family: 'Poppins-Medium', sans-serif;
}
.swal2-confirm:focus {
box-shadow: none !important;
}

View File

@ -30,4 +30,17 @@ body.page-marketing {
}
body.page-marketing-info {
.radio-group {
> .el-radio {
height: auto;
padding: 8px 15px 8px 10px;
.el-radio__label {
overflow: hidden;
> img {
max-height: 26px;
}
}
}
}
}

View File

@ -60,7 +60,8 @@
class="img-fluid"></a></div>
<div class="plugin-name fw-bold mb-2">@{{ plugin.name }}</div>
<div class="d-flex align-items-center justify-content-between">
<span class="text-success">{{ __('admin/marketing.text_free') }}</span>
<span class="text-success" v-if="plugin.price == 0">{{ __('admin/marketing.text_free') }}</span>
<span class="text-success" v-else>@{{ plugin.price_format }}</span>
<span class="text-secondary">{{ __('admin/marketing.download_count') }}@{{ plugin.downloaded }}</span>
</div>
</div>
@ -84,12 +85,12 @@
width="500px">
<el-input
type="textarea"
:rows="4"
:rows="3"
placeholder="{{ __('admin/marketing.set_token') }}"
v-model="setTokenDialog.token">
</el-input>
<div class="d-flex justify-content-between align-items-center mt-5">
<a href="https://beikeshop.com/account/websites" class="link-primary" target="_blank">{{ __('admin/marketing.get_token') }}</a>
<div class="mt-3 text-secondary fs-6">{{ __('admin/marketing.get_token_text') }} <a href="{{ config('beike.api_url') }}/account/websites?domain={{ $domain }}" class="link-primary" target="_blank">{{ __('admin/marketing.get_token') }}</a></div>
<div class="d-flex justify-content-end align-items-center mt-4">
<span slot="footer" class="dialog-footer">
<el-button @click="setTokenDialog.show = false">{{ __('common.cancel') }}</el-button>
<el-button type="primary" @click="submitToken">{{ __('common.confirm') }}</el-button>

View File

@ -4,13 +4,20 @@
@section('body-class', 'page-marketing-info')
@push('header')
<script src="{{ asset('vendor/qrcode/qrcode.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/sweetalert2/sweetalert2.min.css') }}">
<script src="{{ asset('vendor/sweetalert2/sweetalert2.min.js') }}"></script>
@endpush
@section('content')
@php
$data = $plugin['data'];
@endphp
<div class="card mb-4" id="app">
<div class="card-header"><h6 class="card-title">{{ __('admin/marketing.marketing_show') }}</h6></div>
<div class="card-body">
<div class="d-flex mb-5">
<div class="d-flex">
<div class="border wp-400 hp-400 d-flex justify-content-between align-items-center"><img src="{{ $data['icon_big'] }}" class="img-fluid"></div>
<div class="ms-5 mt-2">
<h3 class="card-title mb-4">{{ $data['name'] }}</h3>
@ -24,7 +31,7 @@
<div class="mb-2 fw-bold">{{ __('admin/marketing.text_compatibility') }}</div>
<div>{{ $data['version_name_format'] }}</div>
</div>
<div class="mb-5">
<div class="mb-4">
<div class="mb-2 fw-bold">{{ __('admin/marketing.text_author') }}</div>
<div class="d-flex">
<div class="border wh-60 d-flex justify-content-between align-items-center"><img src="{{ $data['developer']['avatar'] }}" class="img-fluid"></div>
@ -35,17 +42,28 @@
</div>
</div>
<div>
<button class="btn btn-primary btn-lg" @click="downloadPlugin"><i class="bi bi-cloud-arrow-down-fill"></i> {{ __('admin/marketing.download_plugin') }}</button>
<div class="mt-3 d-none download-help"><a href="{{ admin_route('plugins.index') }}" class=""><i class="bi bi-cursor-fill"></i> <span></span></a></div>
<div class="mb-4">
@if ($data['downloadable'])
<button class="btn btn-primary btn-lg" @click="downloadPlugin"><i class="bi bi-cloud-arrow-down-fill"></i> {{ __('admin/marketing.download_plugin') }}</button>
<div class="mt-3 d-none download-help"><a href="{{ admin_route('plugins.index') }}" class=""><i class="bi bi-cursor-fill"></i> <span></span></a></div>
@else
<div class="mb-2 fw-bold">{{ __('admin/marketing.select_pay') }}</div>
<div class="mb-4">
<el-radio-group v-model="payCode" size="small" class="radio-group">
<el-radio class="rounded-0 me-1" label="wechatpay" border><img src="{{ asset('image/wechat.png') }}" class="img-fluid"></el-radio>
<el-radio class="rounded-0" label="alipay" border><img src="{{ asset('image/alipay.png') }}" class="img-fluid"></el-radio>
</el-radio-group>
</div>
<button class="btn btn-primary btn-lg" @click="marketingBuy">{{ __('admin/marketing.btn_buy') }} ({{ $data['price_format'] }})</button>
@endif
</div>
</div>
</div>
</div>
<div>
<h5 class="text-center">{{ __('admin/marketing.download_description') }}</h5>
<div>{{ $data['description'] }}</div>
</div>
<div class="code-pop" style="display: none;">
<div class="text-center py-3 fs-5">{{ __('admin/marketing.text_pay') }}<span class="fs-3 text-danger fw-bold">@{{ wechatpay_price }}</span></div>
<div class="d-flex justify-content-center align-items-center" id="code-info"></div>
</div>
<el-dialog
@ -55,12 +73,12 @@
width="500px">
<el-input
type="textarea"
:rows="4"
:rows="3"
placeholder="{{ __('admin/marketing.set_token') }}"
v-model="setTokenDialog.token">
</el-input>
<div class="d-flex justify-content-between align-items-center mt-5">
<a href="https://beikeshop.com/account/websites" class="link-primary" target="_blank">{{ __('admin/marketing.get_token') }}</a>
<div class="mt-3 text-secondary fs-6">{{ __('admin/marketing.get_token_text') }} <a href="{{ config('beike.api_url') }}/account/websites?domain={{ $domain }}" class="link-primary" target="_blank">{{ __('admin/marketing.get_token') }}</a></div>
<div class="d-flex justify-content-end align-items-center mt-4">
<span slot="footer" class="dialog-footer">
<el-button @click="setTokenDialog.show = false">{{ __('common.cancel') }}</el-button>
<el-button type="primary" @click="submitToken">{{ __('common.confirm') }}</el-button>
@ -68,6 +86,15 @@
</div>
</el-dialog>
</div>
@if ($data['description'])
<div class="card h-min-200">
<div class="card-header"><h6 class="card-title">{{ __('admin/marketing.download_description') }}</h6></div>
<div class="card-body">
{{ $data['description'] }}
</div>
</div>
@endif
@endsection
@push('footer')
@ -76,6 +103,9 @@
el: '#app',
data: {
payCode: 'wechatpay',
wechatpay_price: '',
radio3: '1',
setTokenDialog: {
show: false,
token: @json(system_setting('base.developer_token') ?? ''),
@ -93,6 +123,94 @@
})
},
marketingBuy() {
if (!this.setTokenDialog.token) {
return this.setTokenDialog.show = true;
}
$http.post('{{ admin_route('marketing.buy', ['code' => $data['code']]) }}', {
payment_code: this.payCode, return_url: '{{ admin_route('marketing.show', ['code' => $data['code']]) }}'}).then((res) => {
if (res.status == "fail") {
layer.msg(res.message, () => {})
return;
}
if (res.data.payment_code == 'wechatpay') {
this.wechatpay_price = res.data.price_format
this.getQrcode(res.data.pay_url);
}
if (res.data.payment_code == 'alipay') {
window.open(res.data.pay_url, '_blank');
Swal.fire({
title: '{{ __('admin/marketing.ali_pay_success') }}',
text: '{{ __('admin/marketing.ali_pay_text') }}',
icon: 'question',
confirmButtonColor: '#fd560f',
confirmButtonText: '{{ __('common.confirm') }}',
willClose: function () {
window.location.reload();
},
})
}
})
},
getQrcode(url) {
const self = this;
new QRCode('code-info', {
text: url,
width: 270,
height: 270,
correctLevel : QRCode.CorrectLevel.M
});
setTimeout(() => {
Swal.fire({
title: '{{ __('admin/marketing.wxpay') }}',
width: 400,
height: 470,
heightAuto: false,
html: $('.code-pop').html(),
showConfirmButton: false,
didOpen: function () {
// 微信支付二维码 轮询监控支付状态
self.chekOrderStatus();
self.timer = window.setInterval(() => {
setTimeout(self.chekOrderStatus(), 0);
}, 1000)
},
didClose: function () {
$('#code-info').html('');
},
didDestroy: function () {
window.clearInterval(self.timer)
},
})
}, 100)
},
chekOrderStatus() {
$http.get('{{ admin_route('marketing.show', ['code' => $data['code']]) }}', null, {hload: true}).then((res) => {
console.log(res.plugin.data.downloadable)
if (res.plugin.data.downloadable) {
window.clearInterval(this.timer)
Swal.fire({
title: '{{ __('admin/marketing.pay_success_title') }}',
text: '{{ __('admin/marketing.pay_success_text') }}',
icon: 'success',
focusConfirm: false,
confirmButtonColor: '#75bc4d',
confirmButtonText: '{{ __('common.confirm') }}',
didClose: function () {
window.location.reload();
},
})
}
})
},
submitToken() {
if (!this.setTokenDialog.token) {
return;
@ -103,6 +221,10 @@
layer.msg(res.message);
})
}
},
destroyed() {
window.clearInterval(this.timer)
}
})
</script>

View File

@ -13,6 +13,7 @@ return [
'marketing_list' => 'Plugin Marketing',
'marketing_show' => 'Plugin Detail',
'set_token' => 'Set Token',
'get_token_text' => 'Log in to BeikeShop official website personal center - bind domain name, add current domain name',
'get_token' => 'Get Token',
'download_count' => 'download count',
'last_update' => 'last update',
@ -22,4 +23,12 @@ return [
'download_plugin' => 'download plugin',
'download_description' => 'Plugin description',
'text_free' => 'free',
'btn_buy' => 'Buy',
'text_pay' => 'Payment Amount',
'select_pay' => 'select payment method',
'wxpay' => 'WeChat scan code payment!',
'pay_success_title' => 'Payment successful!',
'pay_success_text' => 'The plug-in purchase is successful, click OK to refresh the page',
'ali_pay_success' => 'Payment completed? ',
'ali_pay_text' => 'Payment has been completed, please refresh the page',
];

View File

@ -13,7 +13,8 @@ return [
'marketing_list' => '插件市场',
'marketing_show' => '插件详情',
'set_token' => '设置 Token',
'get_token' => '获取 Token',
'get_token_text' => '登录 BeikeShop 官网个人中心-绑定域名,添加当前域名',
'get_token' => '点击获取 Token',
'download_count' => '下载次数',
'last_update' => '最后更新',
'text_version' => '版本',
@ -22,4 +23,12 @@ return [
'download_plugin' => '下载插件',
'download_description' => '插件描述',
'text_free' => '免费',
'btn_buy' => '购买',
'text_pay' => '支付金额',
'select_pay' => '选择支付方式',
'wxpay' => '微信扫码支付!',
'pay_success_title' => '支付成功!',
'pay_success_text' => '插件购买成功,点击确定刷新页面',
'ali_pay_success' => '已完成支付?',
'ali_pay_text' => '已完成支付,请刷新页面',
];

View File

@ -13,7 +13,8 @@ return [
'marketing_list' => '插件市場',
'marketing_show' => '插件詳情',
'set_token' => '設置 Token',
'get_token' => '獲取 Token',
'get_token_text' => '登錄 BeikeShop 官網個人中心-綁定域名,添加當前域名',
'get_token' => '點擊獲取 Token',
'download_count' => '下載次數',
'last_update' => '最後更新',
'text_version' => '版本',
@ -22,4 +23,12 @@ return [
'download_plugin' => '下載插件',
'download_description' => '插件描述',
'text_free' => '免費',
'btn_buy' => '購買',
'text_pay' => '支付金額',
'select_pay' => '選擇支付方式',
'wxpay' => '微信掃碼支付!',
'pay_success_title' => '支付成功!',
'pay_success_text' => '插件購買成功,點擊確定刷新頁面',
'ali_pay_success' => '已完成支付? ',
'ali_pay_text' => '已完成支付,請刷新頁面',
];