mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-27 21:10:24 +08:00
!42 v1.2.1阶段性合并
* 计划任务执行路径,周期 * !39 修复课程分类未过滤 * 优化第三方登录,修复注册密码加密问题 * !33 开放登录阶段性合并 * !24 修复添加课时后进入编辑页面500错误
This commit is contained in:
parent
70682f1ebb
commit
78bbfd3577
@ -1,3 +1,7 @@
|
||||
### [v1.2.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.1)(2020-12-10)
|
||||
- 增加QQ,微信,微博第三方登录
|
||||
- 代码优化以及问题修复
|
||||
|
||||
### [v1.2.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.0)(2020-11-25)
|
||||
- 增加客户端api
|
||||
- 代码优化以及问题修复
|
||||
|
@ -6,9 +6,9 @@
|
||||
|
||||
酷瓜云课堂,依托腾讯云基础服务架构,采用C扩展框架Phalcon开发,GPL-2.0开源协议,致力开源网课系统,开源网校系统,开源在线教育系统。
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### 系统功能
|
||||
|
@ -298,4 +298,33 @@ class SettingController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/oauth", name="admin.setting.oauth")
|
||||
*/
|
||||
public function oauthAction()
|
||||
{
|
||||
$settingService = new SettingService();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
$section = $this->request->getPost('section', 'string');
|
||||
|
||||
$data = $this->request->getPost();
|
||||
|
||||
$settingService->updateSettings($section, $data);
|
||||
|
||||
return $this->jsonSuccess(['msg' => '更新配置成功']);
|
||||
|
||||
} else {
|
||||
|
||||
$qqAuth = $settingService->getQQAuthSettings();
|
||||
$weixinAuth = $settingService->getWeixinAuthSettings();
|
||||
$weiboAuth = $settingService->getWeiboAuthSettings();
|
||||
|
||||
$this->view->setVar('qq_auth', $qqAuth);
|
||||
$this->view->setVar('weixin_auth', $weixinAuth);
|
||||
$this->view->setVar('weibo_auth', $weiboAuth);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -744,6 +744,12 @@ class AuthNode extends Service
|
||||
'type' => 'menu',
|
||||
'route' => 'admin.setting.im',
|
||||
],
|
||||
[
|
||||
'id' => '5-1-12',
|
||||
'title' => '开放登录',
|
||||
'type' => 'menu',
|
||||
'route' => 'admin.setting.oauth',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -9,10 +9,39 @@ use App\Repos\Vip as VipRepo;
|
||||
class Setting extends Service
|
||||
{
|
||||
|
||||
public function getQQAuthSettings()
|
||||
{
|
||||
$oauth = $this->getSettings('oauth.qq');
|
||||
|
||||
$oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.qq_callback']);
|
||||
|
||||
return $oauth;
|
||||
}
|
||||
|
||||
public function getWeixinAuthSettings()
|
||||
{
|
||||
$oauth = $this->getSettings('oauth.weixin');
|
||||
|
||||
$oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.weixin_callback']);
|
||||
|
||||
return $oauth;
|
||||
}
|
||||
|
||||
public function getWeiboAuthSettings()
|
||||
{
|
||||
$oauth = $this->getSettings('oauth.weibo');
|
||||
|
||||
$oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_callback']);
|
||||
$oauth['refuse_uri'] = $oauth['refuse_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_refuse']);
|
||||
|
||||
return $oauth;
|
||||
}
|
||||
|
||||
public function getAlipaySettings()
|
||||
{
|
||||
$alipay = $this->getSettings('pay.alipay');
|
||||
|
||||
$alipay['return_url'] = $alipay['return_url'] ?: kg_full_url(['for' => 'home.alipay_callback']);
|
||||
$alipay['notify_url'] = $alipay['notify_url'] ?: kg_full_url(['for' => 'home.alipay_notify']);
|
||||
|
||||
return $alipay;
|
||||
@ -22,6 +51,7 @@ class Setting extends Service
|
||||
{
|
||||
$wxpay = $this->getSettings('pay.wxpay');
|
||||
|
||||
$wxpay['return_url'] = $wxpay['return_url'] ?: kg_full_url(['for' => 'home.wxpay_callback']);
|
||||
$wxpay['notify_url'] = $wxpay['notify_url'] ?: kg_full_url(['for' => 'home.wxpay_notify']);
|
||||
|
||||
return $wxpay;
|
||||
|
24
app/Http/Admin/Views/setting/oauth.volt
Normal file
24
app/Http/Admin/Views/setting/oauth.volt
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title kg-tab-title">
|
||||
<li class="layui-this">QQ登录</li>
|
||||
<li>微信登录</li>
|
||||
<li>新浪微博</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
{{ partial('setting/oauth_qq') }}
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
{{ partial('setting/oauth_weixin') }}
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
{{ partial('setting/oauth_weibo') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
35
app/Http/Admin/Views/setting/oauth_qq.volt
Normal file
35
app/Http/Admin/Views/setting/oauth_qq.volt
Normal file
@ -0,0 +1,35 @@
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.oauth'}) }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">开启登录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="enabled" value="1" title="是" {% if qq_auth.enabled == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="enabled" value="0" title="否" {% if qq_auth.enabled == "0" %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">App ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="client_id" value="{{ qq_auth.client_id }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">App Secret</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="client_secret" value="{{ qq_auth.client_secret }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Callback Url</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="redirect_uri" value="{{ qq_auth.redirect_uri }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
<input type="hidden" name="section" value="oauth.qq">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
41
app/Http/Admin/Views/setting/oauth_weibo.volt
Normal file
41
app/Http/Admin/Views/setting/oauth_weibo.volt
Normal file
@ -0,0 +1,41 @@
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.oauth'}) }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">开启登录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="enabled" value="1" title="是" {% if weibo_auth.enabled == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="enabled" value="0" title="否" {% if weibo_auth.enabled == "0" %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">App Key</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="client_id" value="{{ weibo_auth.client_id }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">App Secret</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="client_secret" value="{{ weibo_auth.client_secret }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Callback Url</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="redirect_uri" value="{{ weibo_auth.redirect_uri }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Refuse Url</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="refuse_uri" value="{{ weibo_auth.refuse_uri }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
<input type="hidden" name="section" value="oauth.weibo">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
35
app/Http/Admin/Views/setting/oauth_weixin.volt
Normal file
35
app/Http/Admin/Views/setting/oauth_weixin.volt
Normal file
@ -0,0 +1,35 @@
|
||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.setting.oauth'}) }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">开启登录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="enabled" value="1" title="是" {% if weixin_auth.enabled == "1" %}checked="checked"{% endif %}>
|
||||
<input type="radio" name="enabled" value="0" title="否" {% if weixin_auth.enabled == "0" %}checked="checked"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">App ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="client_id" value="{{ weixin_auth.client_id }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">App Secret</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="client_secret" value="{{ weixin_auth.client_secret }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Callback Url</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="redirect_uri" value="{{ weixin_auth.redirect_uri }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||
<input type="hidden" name="section" value="oauth.weixin">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -24,6 +24,12 @@
|
||||
<input class="layui-input" type="text" name="notify_url" value="{{ alipay.notify_url }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Return Url</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="return_url" value="{{ alipay.return_url }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
|
@ -30,6 +30,12 @@
|
||||
<input class="layui-input" type="text" name="notify_url" value="{{ wxpay.notify_url }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Return Url</label>
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="return_url" value="{{ wxpay.return_url }}" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-block">
|
||||
|
@ -22,5 +22,6 @@
|
||||
|
||||
{% block include_js %}{% endblock %}
|
||||
{% block inline_js %}{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
@ -25,26 +25,6 @@ class TradeController extends Controller
|
||||
return $this->jsonSuccess(['trade' => $trade]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/h5/pay", name="api.trade.h5_pay")
|
||||
*/
|
||||
public function h5PayAction()
|
||||
{
|
||||
$sn = $this->request->getQuery('sn', 'string');
|
||||
|
||||
$service = new TradeService();
|
||||
|
||||
$response = $service->h5Pay($sn);
|
||||
|
||||
if (!$response) {
|
||||
echo "H5支付跳转失败,请回退重试";
|
||||
}
|
||||
|
||||
$response->send();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/h5/create", name="api.trade.h5_create")
|
||||
*/
|
||||
@ -52,13 +32,9 @@ class TradeController extends Controller
|
||||
{
|
||||
$service = new TradeService();
|
||||
|
||||
$trade = $service->createH5Trade();
|
||||
$content = $service->createH5Trade();
|
||||
|
||||
$service = new TradeInfoService();
|
||||
|
||||
$trade = $service->handle($trade->sn);
|
||||
|
||||
return $this->jsonSuccess(['trade' => $trade]);
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,7 @@ namespace App\Http\Api\Services;
|
||||
use App\Models\Client as ClientModel;
|
||||
use App\Models\Trade as TradeModel;
|
||||
use App\Services\Logic\OrderTrait;
|
||||
use App\Services\Logic\Trade\TradeInfo;
|
||||
use App\Services\Logic\TradeTrait;
|
||||
use App\Services\Pay\Alipay;
|
||||
use App\Services\Pay\Wxpay;
|
||||
@ -17,23 +18,6 @@ class Trade extends Service
|
||||
use OrderTrait;
|
||||
use TradeTrait;
|
||||
|
||||
public function h5Pay($sn)
|
||||
{
|
||||
$trade = $this->checkTradeBySn($sn);
|
||||
|
||||
$response = null;
|
||||
|
||||
if ($trade->channel == TradeModel::CHANNEL_ALIPAY) {
|
||||
$alipay = new Alipay();
|
||||
$response = $alipay->wap($trade);
|
||||
} elseif ($trade->channel == TradeModel::CHANNEL_WXPAY) {
|
||||
$wxpay = new Wxpay();
|
||||
$response = $wxpay->wap($trade);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function createH5Trade()
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
@ -62,7 +46,24 @@ class Trade extends Service
|
||||
|
||||
$trade->create();
|
||||
|
||||
return $trade;
|
||||
$redirect = '';
|
||||
|
||||
if ($trade->channel == TradeModel::CHANNEL_ALIPAY) {
|
||||
$alipay = new Alipay();
|
||||
$response = $alipay->wap($trade);
|
||||
$redirect = $response ? $response->getTargetUrl() : '';
|
||||
} elseif ($trade->channel == TradeModel::CHANNEL_WXPAY) {
|
||||
$wxpay = new Wxpay();
|
||||
$response = $wxpay->wap($trade);
|
||||
$redirect = $response ? $response->getTargetUrl() : '';
|
||||
}
|
||||
|
||||
$payment = ['redirect' => $redirect];
|
||||
|
||||
return [
|
||||
'trade' => $this->handleTradeInfo($trade->sn),
|
||||
'payment' => $payment,
|
||||
];
|
||||
}
|
||||
|
||||
public function createMpTrade()
|
||||
@ -122,4 +123,11 @@ class Trade extends Service
|
||||
return $this->request->getHeader('X-Platform');
|
||||
}
|
||||
|
||||
protected function handleTradeInfo($sn)
|
||||
{
|
||||
$service = new TradeInfo();
|
||||
|
||||
return $service->handle($sn);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace App\Http\Home\Controllers;
|
||||
|
||||
use App\Http\Home\Services\Account as AccountService;
|
||||
use App\Services\Logic\Account\EmailUpdate as EmailUpdateService;
|
||||
use App\Services\Logic\Account\OAuthProvider as OAuthProviderService;
|
||||
use App\Services\Logic\Account\PasswordReset as PasswordResetService;
|
||||
use App\Services\Logic\Account\PasswordUpdate as PasswordUpdateService;
|
||||
use App\Services\Logic\Account\PhoneUpdate as PhoneUpdateService;
|
||||
@ -62,8 +63,13 @@ class AccountController extends Controller
|
||||
|
||||
$captcha = $service->getSettings('captcha');
|
||||
|
||||
$service = new OAuthProviderService();
|
||||
|
||||
$oauthProvider = $service->handle();
|
||||
|
||||
$returnUrl = $this->request->getHTTPReferer();
|
||||
|
||||
$this->view->setVar('oauth_provider', $oauthProvider);
|
||||
$this->view->setVar('return_url', $returnUrl);
|
||||
$this->view->setVar('captcha', $captcha);
|
||||
}
|
||||
|
138
app/Http/Home/Controllers/ConnectController.php
Normal file
138
app/Http/Home/Controllers/ConnectController.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Home\Controllers;
|
||||
|
||||
use App\Http\Home\Services\Connect as ConnectService;
|
||||
use App\Models\Connect as ConnectModel;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/oauth")
|
||||
*/
|
||||
class ConnectController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/qq", name="home.oauth.qq")
|
||||
*/
|
||||
public function qqAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$url = $service->getAuthorizeUrl(ConnectModel::PROVIDER_QQ);
|
||||
|
||||
return $this->response->redirect($url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/weixin", name="home.oauth.weixin")
|
||||
*/
|
||||
public function weixinAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$url = $service->getAuthorizeUrl(ConnectModel::PROVIDER_WEIXIN);
|
||||
|
||||
return $this->response->redirect($url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/weibo", name="home.oauth.weibo")
|
||||
*/
|
||||
public function weiboAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$url = $service->getAuthorizeUrl(ConnectModel::PROVIDER_WEIBO);
|
||||
|
||||
return $this->response->redirect($url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/qq/callback", name="home.oauth.qq_callback")
|
||||
*/
|
||||
public function qqCallbackAction()
|
||||
{
|
||||
$this->handleCallback(ConnectModel::PROVIDER_QQ);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/weixin/callback", name="home.oauth.weixin_callback")
|
||||
*/
|
||||
public function weixinCallbackAction()
|
||||
{
|
||||
$this->handleCallback(ConnectModel::PROVIDER_WEIXIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/weibo/callback", name="home.oauth.weibo_callback")
|
||||
*/
|
||||
public function weiboCallbackAction()
|
||||
{
|
||||
$this->handleCallback(ConnectModel::PROVIDER_WEIBO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/weibo/refuse", name="home.oauth.weibo_refuse")
|
||||
*/
|
||||
public function weiboRefuseAction()
|
||||
{
|
||||
return $this->response->redirect(['for' => 'home.account.login']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/bind/login", name="home.oauth.bind_login")
|
||||
*/
|
||||
public function bindLoginAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$service->bindLogin();
|
||||
|
||||
$location = $this->url->get(['for' => 'home.uc.account']);
|
||||
|
||||
return $this->jsonSuccess(['location' => $location]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/bind/register", name="home.oauth.bind_register")
|
||||
*/
|
||||
public function bindRegisterAction()
|
||||
{
|
||||
$service = new ConnectService();
|
||||
|
||||
$service->bindRegister();
|
||||
|
||||
$location = $this->url->get(['for' => 'home.uc.account']);
|
||||
|
||||
return $this->jsonSuccess(['location' => $location]);
|
||||
}
|
||||
|
||||
protected function handleCallback($provider)
|
||||
{
|
||||
$code = $this->request->getQuery('code');
|
||||
$state = $this->request->getQuery('state');
|
||||
|
||||
$service = new ConnectService();
|
||||
|
||||
$openUser = $service->getOpenUserInfo($code, $state, $provider);
|
||||
$connect = $service->getConnectRelation($openUser['id'], $openUser['provider']);
|
||||
|
||||
if ($connect && $connect->deleted == 0) {
|
||||
if ($this->authUser->id > 0) {
|
||||
$service->bindUser($openUser);
|
||||
return $this->response->redirect(['for' => 'home.uc.account']);
|
||||
} else {
|
||||
$service->authLogin($connect);
|
||||
return $this->response->redirect(['for' => 'home.index']);
|
||||
}
|
||||
}
|
||||
|
||||
$captcha = $service->getSettings('captcha');
|
||||
|
||||
$this->view->pick('connect/bind');
|
||||
$this->view->setVar('captcha', $captcha);
|
||||
$this->view->setVar('provider', $provider);
|
||||
$this->view->setVar('open_user', $openUser);
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ use App\Services\Logic\Order\OrderCancel as OrderCancelService;
|
||||
use App\Services\Logic\Order\OrderConfirm as OrderConfirmService;
|
||||
use App\Services\Logic\Order\OrderCreate as OrderCreateService;
|
||||
use App\Services\Logic\Order\OrderInfo as OrderInfoService;
|
||||
use App\Services\Logic\Order\PayProvider as PayProviderService;
|
||||
use Phalcon\Mvc\Dispatcher;
|
||||
use Phalcon\Mvc\View;
|
||||
|
||||
@ -82,6 +83,10 @@ class OrderController extends Controller
|
||||
{
|
||||
$sn = $this->request->getQuery('sn', 'string');
|
||||
|
||||
$service = new PayProviderService();
|
||||
|
||||
$payProvider = $service->handle();
|
||||
|
||||
$service = new OrderInfoService();
|
||||
|
||||
$order = $service->handle($sn);
|
||||
@ -90,6 +95,7 @@ class OrderController extends Controller
|
||||
$this->response->redirect(['for' => 'home.uc.orders']);
|
||||
}
|
||||
|
||||
$this->view->setVar('pay_provider', $payProvider);
|
||||
$this->view->setVar('order', $order);
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,22 @@ class PublicController extends \Phalcon\Mvc\Controller
|
||||
return $this->jsonSuccess(['token' => $token]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/alipay/callback", name="home.alipay_callback")
|
||||
*/
|
||||
public function alipayCallbackAction()
|
||||
{
|
||||
return $this->response->redirect('/h5/#/pages/me/index', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/wxpay/callback", name="home.wxpay_callback")
|
||||
*/
|
||||
public function wxpayCallbackAction()
|
||||
{
|
||||
return $this->response->redirect('/h5/#/pages/me/index', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/alipay/notify", name="home.alipay_notify")
|
||||
*/
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Http\Home\Controllers;
|
||||
|
||||
use App\Services\Logic\Account\OAuthProvider as OAuthProviderService;
|
||||
use App\Services\Logic\User\Console\AccountInfo as AccountInfoService;
|
||||
use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService;
|
||||
use App\Services\Logic\User\Console\ConnectList as ConnectListService;
|
||||
use App\Services\Logic\User\Console\ConsultList as ConsultListService;
|
||||
use App\Services\Logic\User\Console\CourseList as CourseListService;
|
||||
use App\Services\Logic\User\Console\FavoriteList as FavoriteListService;
|
||||
@ -59,13 +62,21 @@ class UserConsoleController extends Controller
|
||||
*/
|
||||
public function accountAction()
|
||||
{
|
||||
$type = $this->request->getQuery('type', 'string', 'info');
|
||||
|
||||
$service = new AccountInfoService();
|
||||
|
||||
$captcha = $service->getSettings('captcha');
|
||||
|
||||
$account = $service->handle();
|
||||
|
||||
$type = $this->request->getQuery('type', 'string', 'info');
|
||||
$service = new OAuthProviderService();
|
||||
|
||||
$oauthProvider = $service->handle();
|
||||
|
||||
$service = new ConnectListService();
|
||||
|
||||
$connects = $service->handle();
|
||||
|
||||
if ($type == 'info') {
|
||||
$this->view->pick('user/console/account_info');
|
||||
@ -77,6 +88,8 @@ class UserConsoleController extends Controller
|
||||
$this->view->pick('user/console/account_password');
|
||||
}
|
||||
|
||||
$this->view->setVar('oauth_provider', $oauthProvider);
|
||||
$this->view->setVar('connects', $connects);
|
||||
$this->view->setVar('captcha', $captcha);
|
||||
$this->view->setVar('account', $account);
|
||||
}
|
||||
@ -207,4 +220,23 @@ class UserConsoleController extends Controller
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/connect/{id:[0-9]+}/delete", name="home.uc.unconnect")
|
||||
*/
|
||||
public function deleteConnectAction($id)
|
||||
{
|
||||
$service = new ConnectDeleteService();
|
||||
|
||||
$service->handle($id);
|
||||
|
||||
$location = $this->url->get(['for' => 'home.uc.account']);
|
||||
|
||||
$content = [
|
||||
'location' => $location,
|
||||
'msg' => '解除登录绑定成功',
|
||||
];
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
}
|
||||
|
211
app/Http/Home/Services/Connect.php
Normal file
211
app/Http/Home/Services/Connect.php
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Home\Services;
|
||||
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use App\Models\User as UserModel;
|
||||
use App\Repos\Connect as ConnectRepo;
|
||||
use App\Repos\User as UserRepo;
|
||||
use App\Services\Auth\Home as AuthService;
|
||||
use App\Services\Logic\Account\Register as RegisterService;
|
||||
use App\Services\OAuth\QQ as QQAuth;
|
||||
use App\Services\OAuth\WeiBo as WeiBoAuth;
|
||||
use App\Services\OAuth\WeiXin as WeiXinAuth;
|
||||
use App\Validators\Account as AccountValidator;
|
||||
|
||||
class Connect extends Service
|
||||
{
|
||||
|
||||
public function bindLogin()
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$auth = $this->getConnectAuth($post['provider']);
|
||||
|
||||
$auth->checkState($post['state']);
|
||||
|
||||
$validator = new AccountValidator();
|
||||
|
||||
$user = $validator->checkUserLogin($post['account'], $post['password']);
|
||||
|
||||
$openUser = json_decode($post['open_user'], true);
|
||||
|
||||
$this->handleConnectRelation($user, $openUser);
|
||||
|
||||
$auth = $this->getAppAuth();
|
||||
|
||||
$auth->saveAuthInfo($user);
|
||||
}
|
||||
|
||||
public function bindRegister()
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$auth = $this->getConnectAuth($post['provider']);
|
||||
|
||||
$auth->checkState($post['state']);
|
||||
|
||||
$openUser = json_decode($post['open_user'], true);
|
||||
|
||||
$registerService = new RegisterService();
|
||||
|
||||
$account = $registerService->handle();
|
||||
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($account->id);
|
||||
|
||||
$this->handleConnectRelation($user, $openUser);
|
||||
|
||||
$auth = $this->getAppAuth();
|
||||
|
||||
$auth->saveAuthInfo($user);
|
||||
}
|
||||
|
||||
public function bindUser(array $openUser)
|
||||
{
|
||||
$user = $this->getLoginUser();
|
||||
|
||||
$this->handleConnectRelation($user, $openUser);
|
||||
}
|
||||
|
||||
public function authLogin(ConnectModel $connect)
|
||||
{
|
||||
$userRepo = new UserRepo();
|
||||
|
||||
$user = $userRepo->findById($connect->user_id);
|
||||
|
||||
$auth = $this->getAppAuth();
|
||||
|
||||
$auth->saveAuthInfo($user);
|
||||
}
|
||||
|
||||
public function getAuthorizeUrl($provider)
|
||||
{
|
||||
$auth = $this->getConnectAuth($provider);
|
||||
|
||||
return $auth->getAuthorizeUrl();
|
||||
}
|
||||
|
||||
public function getOpenUserInfo($code, $state, $provider)
|
||||
{
|
||||
$auth = $this->getConnectAuth($provider);
|
||||
|
||||
$auth->checkState($state);
|
||||
|
||||
$token = $auth->getAccessToken($code);
|
||||
|
||||
$openId = $auth->getOpenId($token);
|
||||
|
||||
return $auth->getUserInfo($token, $openId);
|
||||
}
|
||||
|
||||
public function getConnectRelation($openId, $provider)
|
||||
{
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
return $connectRepo->findByOpenId($openId, $provider);
|
||||
}
|
||||
|
||||
public function getConnectAuth($provider)
|
||||
{
|
||||
$auth = null;
|
||||
|
||||
switch ($provider) {
|
||||
case ConnectModel::PROVIDER_QQ:
|
||||
$auth = $this->getQQAuth();
|
||||
break;
|
||||
case ConnectModel::PROVIDER_WEIXIN:
|
||||
$auth = $this->getWeiXinAuth();
|
||||
break;
|
||||
case ConnectModel::PROVIDER_WEIBO:
|
||||
$auth = $this->getWeiBoAuth();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$auth) {
|
||||
throw new \Exception('Invalid OAuth Provider');
|
||||
}
|
||||
|
||||
return $auth;
|
||||
}
|
||||
|
||||
protected function getQQAuth()
|
||||
{
|
||||
$settings = $this->getSettings('oauth.qq');
|
||||
|
||||
return new QQAuth(
|
||||
$settings['client_id'],
|
||||
$settings['client_secret'],
|
||||
$settings['redirect_uri']
|
||||
);
|
||||
}
|
||||
|
||||
protected function getWeiXinAuth()
|
||||
{
|
||||
$settings = $this->getSettings('oauth.weixin');
|
||||
|
||||
return new WeiXinAuth(
|
||||
$settings['client_id'],
|
||||
$settings['client_secret'],
|
||||
$settings['redirect_uri']
|
||||
);
|
||||
}
|
||||
|
||||
protected function getWeiBoAuth()
|
||||
{
|
||||
$settings = $this->getSettings('oauth.weibo');
|
||||
|
||||
return new WeiBoAuth(
|
||||
$settings['client_id'],
|
||||
$settings['client_secret'],
|
||||
$settings['redirect_uri']
|
||||
);
|
||||
}
|
||||
|
||||
protected function getAppAuth()
|
||||
{
|
||||
/**
|
||||
* @var $auth AuthService
|
||||
*/
|
||||
$auth = $this->getDI()->get('auth');
|
||||
|
||||
return $auth;
|
||||
}
|
||||
|
||||
protected function handleConnectRelation(UserModel $user, array $openUser)
|
||||
{
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
$connect = $connectRepo->findByOpenId($openUser['id'], $openUser['provider']);
|
||||
|
||||
if ($connect) {
|
||||
|
||||
$connect->open_name = $openUser['name'];
|
||||
$connect->open_avatar = $openUser['avatar'];
|
||||
|
||||
if ($connect->user_id != $user->id) {
|
||||
$connect->user_id = $user->id;
|
||||
}
|
||||
|
||||
if ($connect->deleted == 1) {
|
||||
$connect->deleted = 0;
|
||||
}
|
||||
|
||||
$connect->update();
|
||||
|
||||
} else {
|
||||
|
||||
$connect = new ConnectModel();
|
||||
|
||||
$connect->user_id = $user->id;
|
||||
$connect->open_id = $openUser['id'];
|
||||
$connect->open_name = $openUser['name'];
|
||||
$connect->open_avatar = $openUser['avatar'];
|
||||
$connect->provider = $openUser['provider'];
|
||||
|
||||
$connect->create();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -27,6 +27,17 @@
|
||||
<span class="separator">·</span>
|
||||
<a class="forget-link" href="{{ url({'for':'home.account.forget_pwd'}) }}">忘记密码</a>
|
||||
</div>
|
||||
<div class="oauth">
|
||||
{% if oauth_provider.qq.enabled == 1 %}
|
||||
<a class="layui-icon layui-icon-login-qq login-qq" href="{{ url({'for':'home.oauth.qq'}) }}"></a>
|
||||
{% endif %}
|
||||
{% if oauth_provider.weixin.enabled == 1 %}
|
||||
<a class="layui-icon layui-icon-login-wechat login-wechat" href="{{ url({'for':'home.oauth.weixin'}) }}"></a>
|
||||
{% endif %}
|
||||
{% if oauth_provider.weibo.enabled == 1 %}
|
||||
<a class="layui-icon layui-icon-login-weibo login-weibo" href="{{ url({'for':'home.oauth.weibo'}) }}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
34
app/Http/Home/Views/connect/bind.volt
Normal file
34
app/Http/Home/Views/connect/bind.volt
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends 'templates/main.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="layui-breadcrumb breadcrumb">
|
||||
<a href="/">首页</a>
|
||||
<a><cite>登录绑定</cite></a>
|
||||
</div>
|
||||
|
||||
<div class="login-wrap wrap">
|
||||
<div class="layui-tab layui-tab-brief login-tab">
|
||||
<ul class="layui-tab-title login-tab-title">
|
||||
<li class="layui-this">绑定已有帐号</li>
|
||||
<li>注册并绑定帐号</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
{{ partial('connect/bind_login') }}
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
{{ partial('connect/bind_register') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block include_js %}
|
||||
|
||||
{{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }}
|
||||
{{ js_include('home/js/captcha.verify.js') }}
|
||||
|
||||
{% endblock %}
|
21
app/Http/Home/Views/connect/bind_login.volt
Normal file
21
app/Http/Home/Views/connect/bind_login.volt
Normal file
@ -0,0 +1,21 @@
|
||||
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.oauth.bind_login'}) }}">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button id="submit-btn" class="layui-btn layui-btn-fluid" lay-submit="true" lay-filter="go">登录并绑定已有帐号</button>
|
||||
<input type="hidden" name="provider" value="{{ provider }}">
|
||||
<input type="hidden" name="code" value="{{ request.get('code') }}">
|
||||
<input type="hidden" name="state" value="{{ request.get('state') }}">
|
||||
<input type="hidden" name="open_user" value='{{ open_user|json_encode }}'>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
32
app/Http/Home/Views/connect/bind_register.volt
Normal file
32
app/Http/Home/Views/connect/bind_register.volt
Normal file
@ -0,0 +1,32 @@
|
||||
<form class="layui-form account-form" method="POST" action="{{ url({'for':'home.oauth.bind_register'}) }}">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<input id="cv-account" class="layui-input" type="text" name="account" autocomplete="off" placeholder="手机 / 邮箱" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<input class="layui-input" type="password" name="password" autocomplete="off" placeholder="密码(字母数字特殊字符6-16位)" lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-inline verify-input-inline">
|
||||
<input class="layui-input" type="text" name="verify_code" placeholder="验证码" lay-verify="required">
|
||||
</div>
|
||||
<div class="layui-input-inline verify-btn-inline">
|
||||
<button id="cv-verify-emit" class="layui-btn layui-btn-primary layui-btn-disabled" type="button" disabled="disabled">获取验证码</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button id="cv-submit-btn" class="layui-btn layui-btn-fluid layui-btn-disabled" disabled="disabled" lay-submit="true" lay-filter="go">注册并绑定帐号</button>
|
||||
<input type="hidden" name="provider" value="{{ provider }}">
|
||||
<input type="hidden" name="code" value="{{ request.get('code') }}">
|
||||
<input type="hidden" name="state" value="{{ request.get('state') }}">
|
||||
<input type="hidden" name="open_user" value='{{ open_user|json_encode }}'>
|
||||
<input id="cv-app-id" type="hidden" value="{{ captcha.app_id }}">
|
||||
<input id="cv-ticket" type="hidden" name="ticket">
|
||||
<input id="cv-rand" type="hidden" name="rand">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -17,8 +17,12 @@
|
||||
支付金额:<span class="amount">{{ '¥%0.2f'|format(order.amount) }}</span>
|
||||
</div>
|
||||
<div class="channel">
|
||||
<a class="alipay btn-pay" href="javascript:" data-channel="alipay">{{ image('home/img/alipay.png') }}</a>
|
||||
<a class="wxpay btn-pay" href="javascript:" data-channel="wxpay">{{ image('home/img/wxpay.png') }}</a>
|
||||
{% if pay_provider.alipay.enabled == 1 %}
|
||||
<a class="alipay btn-pay" href="javascript:" data-channel="alipay">{{ image('home/img/alipay.png') }}</a>
|
||||
{% endif %}
|
||||
{% if pay_provider.wxpay.enabled == 1 %}
|
||||
<a class="wxpay btn-pay" href="javascript:" data-channel="wxpay">{{ image('home/img/wxpay.png') }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<span class="tips">友情提示:请在12小时内完成支付,有问题请联系客服</span>
|
||||
|
@ -2,6 +2,23 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{%- macro connect_provider(item) %}
|
||||
{% if item.provider == 1 %}
|
||||
<i class="layui-icon layui-icon-login-qq login-qq"></i>
|
||||
{% elseif item.provider == 2 %}
|
||||
<i class="layui-icon layui-icon-login-wechat login-wechat"></i>
|
||||
{% elseif item.provider == 3 %}
|
||||
<i class="layui-icon layui-icon-login-weibo login-weibo"></i>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro connect_user(item) %}
|
||||
{% if item.open_avatar %}
|
||||
<span class="open-avatar"><img src="{{ item.open_avatar }}"></span>
|
||||
{% endif %}
|
||||
<span class="open-name">{{ item.open_name }}</span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% set edit_pwd_url = url({'for':'home.uc.account'},{'type':'password'}) %}
|
||||
{% set edit_phone_url = url({'for':'home.uc.account'},{'type':'phone'}) %}
|
||||
{% set edit_email_url = url({'for':'home.uc.account'},{'type':'email'}) %}
|
||||
@ -34,7 +51,7 @@
|
||||
<div class="security-item">
|
||||
<span class="icon"><i class="layui-icon layui-icon-email"></i></span>
|
||||
<span class="title">邮箱绑定</span>
|
||||
{% if account.phone %}
|
||||
{% if account.email %}
|
||||
<span class="summary">已绑定邮箱:{{ account.email|anonymous }}</span>
|
||||
<span class="action"><a class="layui-btn layui-btn-sm btn-edit-email" href="{{ edit_email_url }}">修改</a></span>
|
||||
{% else %}
|
||||
@ -43,6 +60,45 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-nav">
|
||||
<span class="title">开放登录</span>
|
||||
</div>
|
||||
{% if connects %}
|
||||
<div class="connect-tips">已经绑定的第三方帐号</div>
|
||||
<div class="connect-list">
|
||||
<table class="layui-table">
|
||||
<tr>
|
||||
<td>序号</td>
|
||||
<td>提供方</td>
|
||||
<td>用户信息</td>
|
||||
<td>创建日期</td>
|
||||
<td width="15%">操作</td>
|
||||
</tr>
|
||||
{% for connect in connects %}
|
||||
{% set url = url({'for':'home.uc.unconnect','id':connect.id}) %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ connect_provider(connect) }}</td>
|
||||
<td>{{ connect_user(connect) }}</td>
|
||||
<td>{{ date('Y-m-d H:i',connect.create_time) }}</td>
|
||||
<td><a class="layui-btn layui-btn-danger layui-btn-sm kg-delete" href="javascript:" data-url="{{ url }}" data-tips="确定要解除绑定吗?">解除绑定</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="connect-tips">支持绑定的第三方帐号</div>
|
||||
<div class="oauth-list">
|
||||
{% if oauth_provider.qq.enabled == 1 %}
|
||||
<a class="layui-icon layui-icon-login-qq login-qq" href="{{ url({'for':'home.oauth.qq'}) }}"></a>
|
||||
{% endif %}
|
||||
{% if oauth_provider.qq.enabled == 1 %}
|
||||
<a class="layui-icon layui-icon-login-wechat login-wechat" href="{{ url({'for':'home.oauth.weixin'}) }}"></a>
|
||||
{% endif %}
|
||||
{% if oauth_provider.qq.enabled == 1 %}
|
||||
<a class="layui-icon layui-icon-login-weibo login-weibo" href="{{ url({'for':'home.oauth.weibo'}) }}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
{% set full_user_url = full_url({'for':'home.user.show','id':user.id}) %}
|
||||
{% set qrcode_url = url({'for':'home.qrcode'},{'text':full_user_url}) %}
|
||||
{% set user.area = user.area ? user.area : '火星' %}
|
||||
{% set user.about = user.about ? user.about : '这个家伙很懒,什么都没留下!' %}
|
||||
|
||||
<div class="breadcrumb">
|
||||
<span class="layui-breadcrumb">
|
||||
@ -32,9 +34,7 @@
|
||||
<p><span><i class="layui-icon layui-icon-location"></i></span><span>{{ user.area }}</span></p>
|
||||
<p><span><i class="layui-icon layui-icon-time"></i></span><span>{{ date('Y-m-d H:i',user.active_time) }}</span></p>
|
||||
</div>
|
||||
{% if user.about %}
|
||||
<div class="about">{{ user.about }}</div>
|
||||
{% endif %}
|
||||
<div class="about">{{ user.about }}</div>
|
||||
</div>
|
||||
|
||||
{% set show_tab_courses = user.course_count > 0 %}
|
||||
|
@ -11,7 +11,7 @@ class AppInfo
|
||||
|
||||
protected $link = 'https://gitee.com/koogua';
|
||||
|
||||
protected $version = '1.2.0';
|
||||
protected $version = '1.2.1';
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Library;
|
||||
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
|
||||
abstract class OAuth
|
||||
{
|
||||
|
||||
protected $appId;
|
||||
protected $appSecret;
|
||||
protected $redirectUri;
|
||||
protected $accessToken;
|
||||
protected $openId;
|
||||
|
||||
public function __construct($appId, $appSecret, $redirectUri)
|
||||
{
|
||||
$this->appId = $appId;
|
||||
$this->appSecret = $appSecret;
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
public function httpGet($uri, $params = [], $headers = [])
|
||||
{
|
||||
$client = new HttpClient();
|
||||
|
||||
$options = ['query' => $params, 'headers' => $headers];
|
||||
|
||||
$response = $client->get($uri, $options);
|
||||
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
public function httpPost($uri, $params = [], $headers = [])
|
||||
{
|
||||
$client = new HttpClient();
|
||||
|
||||
$options = ['query' => $params, 'headers' => $headers];
|
||||
|
||||
$response = $client->post($uri, $options);
|
||||
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
abstract public function getAuthorizeUrl();
|
||||
|
||||
abstract public function getAccessToken($code);
|
||||
|
||||
abstract public function getOpenId($accessToken);
|
||||
|
||||
abstract public function getUserInfo($accessToken, $openId);
|
||||
|
||||
}
|
104
app/Models/Connect.php
Normal file
104
app/Models/Connect.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||
|
||||
class Connect extends Model
|
||||
{
|
||||
|
||||
const PROVIDER_QQ = 1; // QQ
|
||||
const PROVIDER_WEIXIN = 2; // 微信
|
||||
const PROVIDER_WEIBO = 3; // 微博
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $user_id;
|
||||
|
||||
/**
|
||||
* 开放ID
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $open_id;
|
||||
|
||||
/**
|
||||
* 开放名称
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $open_name;
|
||||
|
||||
/**
|
||||
* 开放头像
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $open_avatar;
|
||||
|
||||
/**
|
||||
* 提供商
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $provider;
|
||||
|
||||
/**
|
||||
* 删除标识
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $deleted;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $create_time;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $update_time;
|
||||
|
||||
public function getSource(): string
|
||||
{
|
||||
return 'kg_connect';
|
||||
}
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->addBehavior(
|
||||
new SoftDelete([
|
||||
'field' => 'deleted',
|
||||
'value' => 1,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function beforeCreate()
|
||||
{
|
||||
$this->create_time = time();
|
||||
}
|
||||
|
||||
public function beforeUpdate()
|
||||
{
|
||||
$this->update_time = time();
|
||||
}
|
||||
|
||||
}
|
62
app/Repos/Connect.php
Normal file
62
app/Repos/Connect.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repos;
|
||||
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use Phalcon\Mvc\Model;
|
||||
use Phalcon\Mvc\Model\Resultset;
|
||||
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||
|
||||
class Connect extends Repository
|
||||
{
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @return ResultsetInterface|Resultset|ConnectModel[]
|
||||
*/
|
||||
public function findAll($where = [])
|
||||
{
|
||||
$query = ConnectModel::query();
|
||||
|
||||
$query->where('1 = 1');
|
||||
|
||||
if (isset($where['user_id'])) {
|
||||
$query->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]);
|
||||
}
|
||||
|
||||
if (isset($where['provider'])) {
|
||||
$query->andWhere('provider = :provider:', ['provider' => $where['provider']]);
|
||||
}
|
||||
|
||||
if (isset($where['deleted'])) {
|
||||
$query->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]);
|
||||
}
|
||||
|
||||
$query->orderBy('id DESC');
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return ConnectModel|Model|bool
|
||||
*/
|
||||
public function findById($id)
|
||||
{
|
||||
return ConnectModel::findFirst($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $openId
|
||||
* @param int $provider
|
||||
* @return ConnectModel|Model|bool
|
||||
*/
|
||||
public function findByOpenId($openId, $provider)
|
||||
{
|
||||
return ConnectModel::findFirst([
|
||||
'conditions' => 'open_id = ?1 and provider = ?2',
|
||||
'bind' => [1 => $openId, 2 => $provider],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
23
app/Services/Logic/Account/OAuthProvider.php
Normal file
23
app/Services/Logic/Account/OAuthProvider.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\Account;
|
||||
|
||||
use App\Services\Logic\Service;
|
||||
|
||||
class OAuthProvider extends Service
|
||||
{
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$weixin = $this->getSettings('oauth.weixin');
|
||||
$weibo = $this->getSettings('oauth.weibo');
|
||||
$qq = $this->getSettings('oauth.qq');
|
||||
|
||||
return [
|
||||
'weixin' => ['enabled' => $weixin['enabled']],
|
||||
'weibo' => ['enabled' => $weibo['enabled']],
|
||||
'qq' => ['enabled' => $qq['enabled']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services\Logic\Account;
|
||||
|
||||
use App\Library\Utils\Password as PasswordUtil;
|
||||
use App\Library\Validators\Common as CommonValidator;
|
||||
use App\Models\Account as AccountModel;
|
||||
use App\Models\ImUser as ImUserModel;
|
||||
@ -42,6 +43,10 @@ class Register extends Service
|
||||
|
||||
$data['password'] = $accountValidator->checkPassword($post['password']);
|
||||
|
||||
$data['salt'] = PasswordUtil::salt();
|
||||
|
||||
$data['password'] = PasswordUtil::hash($data['password'], $data['salt']);
|
||||
|
||||
try {
|
||||
|
||||
$this->db->begin();
|
||||
|
21
app/Services/Logic/Order/PayProvider.php
Normal file
21
app/Services/Logic/Order/PayProvider.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\Order;
|
||||
|
||||
use App\Services\Logic\Service;
|
||||
|
||||
class PayProvider extends Service
|
||||
{
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$alipay = $this->getSettings('pay.alipay');
|
||||
$wxpay = $this->getSettings('pay.wxpay');
|
||||
|
||||
return [
|
||||
'alipay' => ['enabled' => $alipay['enabled']],
|
||||
'wxpay' => ['enabled' => $wxpay['enabled']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
26
app/Services/Logic/User/Console/ConnectDelete.php
Normal file
26
app/Services/Logic/User/Console/ConnectDelete.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\User\Console;
|
||||
|
||||
use App\Services\Logic\Service;
|
||||
use App\Validators\Connect as ConnectValidator;
|
||||
|
||||
class ConnectDelete extends Service
|
||||
{
|
||||
|
||||
public function handle($id)
|
||||
{
|
||||
$user = $this->getLoginUser();
|
||||
|
||||
$validator = new ConnectValidator();
|
||||
|
||||
$connect = $validator->checkConnect($id);
|
||||
|
||||
$validator->checkOwner($user->id, $connect->user_id);
|
||||
|
||||
$connect->deleted = 1;
|
||||
|
||||
$connect->update();
|
||||
}
|
||||
|
||||
}
|
45
app/Services/Logic/User/Console/ConnectList.php
Normal file
45
app/Services/Logic/User/Console/ConnectList.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Logic\User\Console;
|
||||
|
||||
use App\Repos\Connect as ConnectRepo;
|
||||
use App\Services\Logic\Service;
|
||||
|
||||
class ConnectList extends Service
|
||||
{
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$user = $this->getLoginUser();
|
||||
|
||||
$params = [
|
||||
'user_id' => $user->id,
|
||||
'deleted' => 0,
|
||||
];
|
||||
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
$connects = $connectRepo->findAll($params);
|
||||
|
||||
if ($connects->count() == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = [];
|
||||
|
||||
foreach ($connects as $connect) {
|
||||
$items[] = [
|
||||
'id' => $connect->id,
|
||||
'open_id' => $connect->open_id,
|
||||
'open_name' => $connect->open_name,
|
||||
'open_avatar' => $connect->open_avatar,
|
||||
'provider' => $connect->provider,
|
||||
'create_time' => $connect->create_time,
|
||||
'update_time' => $connect->update_time,
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
}
|
107
app/Services/OAuth.php
Normal file
107
app/Services/OAuth.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
use Phalcon\Crypt;
|
||||
use Phalcon\Di;
|
||||
|
||||
abstract class OAuth extends Service
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $clientId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $clientSecret;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectUri;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $openId;
|
||||
|
||||
public function __construct($clientId, $clientSecret, $redirectUri)
|
||||
{
|
||||
$this->clientId = $clientId;
|
||||
$this->clientSecret = $clientSecret;
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
public function httpGet($uri, $params = [], $headers = [])
|
||||
{
|
||||
$client = new HttpClient();
|
||||
|
||||
$options = ['query' => $params, 'headers' => $headers];
|
||||
|
||||
$response = $client->get($uri, $options);
|
||||
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
public function httpPost($uri, $params = [], $headers = [])
|
||||
{
|
||||
$client = new HttpClient();
|
||||
|
||||
$options = ['query' => $params, 'headers' => $headers];
|
||||
|
||||
$response = $client->post($uri, $options);
|
||||
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
public function getState()
|
||||
{
|
||||
/**
|
||||
* @var $crypt Crypt
|
||||
*/
|
||||
$crypt = Di::getDefault()->get('crypt');
|
||||
|
||||
return $crypt->encryptBase64(rand(1000, 9999));
|
||||
}
|
||||
|
||||
public function checkState($state)
|
||||
{
|
||||
/**
|
||||
* 注意事项:
|
||||
* callback中的state参数并未做encode处理,参数中含有"+"
|
||||
* 获取参数的时候却自动做了decode处理,"+"变成了空格
|
||||
*/
|
||||
$state = str_replace(' ', '+', $state);
|
||||
|
||||
/**
|
||||
* @var $crypt Crypt
|
||||
*/
|
||||
$crypt = Di::getDefault()->get('crypt');
|
||||
|
||||
$value = $crypt->decryptBase64($state);
|
||||
|
||||
if ($value < 1000 || $value > 9999) {
|
||||
throw new \Exception('Invalid OAuth State Value');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract public function getAuthorizeUrl();
|
||||
|
||||
abstract public function getAccessToken($code);
|
||||
|
||||
abstract public function getOpenId($accessToken);
|
||||
|
||||
abstract public function getUserInfo($accessToken, $openId);
|
||||
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Library\OAuth;
|
||||
namespace App\Services\OAuth;
|
||||
|
||||
use App\Library\OAuth;
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use App\Services\OAuth;
|
||||
|
||||
class QQ extends OAuth
|
||||
{
|
||||
@ -15,12 +16,13 @@ class QQ extends OAuth
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$params = [
|
||||
'client_id' => $this->appId,
|
||||
'client_id' => $this->clientId,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'state' => $this->getState(),
|
||||
'response_type' => 'code',
|
||||
'scope' => '',
|
||||
'scope' => 'get_user_info',
|
||||
];
|
||||
|
||||
|
||||
return self::AUTHORIZE_URL . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
@ -28,86 +30,85 @@ class QQ extends OAuth
|
||||
{
|
||||
$params = [
|
||||
'code' => $code,
|
||||
'client_id' => $this->appId,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'grant_type' => 'authorization_code',
|
||||
'state' => 'ok',
|
||||
];
|
||||
|
||||
|
||||
$response = $this->httpPost(self::ACCESS_TOKEN_URL, $params);
|
||||
|
||||
|
||||
$this->accessToken = $this->parseAccessToken($response);
|
||||
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function getOpenId($accessToken)
|
||||
{
|
||||
$params = ['access_token' => $accessToken];
|
||||
|
||||
|
||||
$response = $this->httpGet(self::OPENID_URL, $params);
|
||||
|
||||
|
||||
$this->openId = $this->parseOpenId($response);
|
||||
|
||||
|
||||
return $this->openId;
|
||||
}
|
||||
|
||||
public function getUserInfo($accessToken, $openId)
|
||||
{
|
||||
$params = [
|
||||
'oauth_consumer_key' => $this->clientId,
|
||||
'access_token' => $accessToken,
|
||||
'openid' => $openId,
|
||||
'oauth_consumer_key' => $this->appId,
|
||||
];
|
||||
|
||||
|
||||
$response = $this->httpGet(self::USER_INFO_URL, $params);
|
||||
|
||||
$this->parseUserInfo($response);
|
||||
|
||||
return $this->parseUserInfo($response);
|
||||
}
|
||||
|
||||
protected function parseAccessToken($response)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
|
||||
parse_str($response, $result);
|
||||
|
||||
|
||||
if (!isset($result['access_token'])) {
|
||||
throw new \Exception("Fetch Access Token Failed:{$response}");
|
||||
}
|
||||
|
||||
|
||||
return $result['access_token'];
|
||||
}
|
||||
|
||||
protected function parseOpenId($response)
|
||||
{
|
||||
$result = $match = [];
|
||||
|
||||
$result = $matches = [];
|
||||
|
||||
if (!empty($response)) {
|
||||
preg_match('/callback\(\s+(.*?)\s+\)/i', $response, $match);
|
||||
$result = json_decode($match[1], true);
|
||||
preg_match('/callback\(\s+(.*?)\s+\)/i', $response, $matches);
|
||||
$result = json_decode($matches[1], true);
|
||||
}
|
||||
|
||||
|
||||
if (!isset($result['openid'])) {
|
||||
throw new \Exception("Fetch Openid Failed:{$response}");
|
||||
throw new \Exception("Fetch OpenId Failed:{$response}");
|
||||
}
|
||||
|
||||
|
||||
return $result['openid'];
|
||||
}
|
||||
|
||||
protected function parseUserInfo($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($data['ret'] != 0) {
|
||||
throw new \Exception("Fetch User Info Failed:{$data['msg']}");
|
||||
|
||||
if (isset($data['ret']) && $data['ret'] != 0) {
|
||||
throw new \Exception("Fetch User Info Failed:{$response}");
|
||||
}
|
||||
|
||||
$userInfo['type'] = 'QQ';
|
||||
|
||||
$userInfo['id'] = $this->openId;
|
||||
$userInfo['name'] = $data['nickname'];
|
||||
$userInfo['nick'] = $data['nickname'];
|
||||
$userInfo['head'] = $data['figureurl_2'];
|
||||
|
||||
$userInfo['avatar'] = $data['figureurl'];
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_QQ;
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
@ -1,24 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Library\OAuth;
|
||||
namespace App\Services\OAuth;
|
||||
|
||||
use App\Library\OAuth;
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use App\Services\OAuth;
|
||||
|
||||
class WeiBo extends OAuth
|
||||
{
|
||||
|
||||
const AUTHORIZE_URL = 'https://api.weibo.com/oauth2/authorize';
|
||||
const ACCESS_TOKEN_URL = 'https://api.weibo.com/oauth2/oauth2/access_token';
|
||||
const ACCESS_TOKEN_URL = 'https://api.weibo.com/oauth2/access_token';
|
||||
const USER_INFO_URL = 'https://api.weibo.com/2/users/show.json';
|
||||
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$params = [
|
||||
'client_id' => $this->appId,
|
||||
'client_id' => $this->clientId,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'state' => $this->getState(),
|
||||
'response_type' => 'code',
|
||||
];
|
||||
|
||||
|
||||
return self::AUTHORIZE_URL . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
@ -26,16 +28,16 @@ class WeiBo extends OAuth
|
||||
{
|
||||
$params = [
|
||||
'code' => $code,
|
||||
'client_id' => $this->appId,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'grant_type' => 'authorization_code',
|
||||
];
|
||||
|
||||
|
||||
$response = $this->httpPost(self::ACCESS_TOKEN_URL, $params);
|
||||
|
||||
|
||||
$this->accessToken = $this->parseAccessToken($response);
|
||||
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
@ -50,38 +52,38 @@ class WeiBo extends OAuth
|
||||
'access_token' => $accessToken,
|
||||
'uid' => $openId,
|
||||
];
|
||||
|
||||
|
||||
$response = $this->httpGet(self::USER_INFO_URL, $params);
|
||||
|
||||
|
||||
return $this->parseUserInfo($response);
|
||||
}
|
||||
|
||||
private function parseAccessToken($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (!isset($data['access_token']) || isset($data['uid'])) {
|
||||
|
||||
if (!isset($data['access_token']) || !isset($data['uid'])) {
|
||||
throw new \Exception("Fetch Access Token Failed:{$response}");
|
||||
}
|
||||
|
||||
|
||||
$this->openId = $data['uid'];
|
||||
|
||||
|
||||
return $data['access_token'];
|
||||
}
|
||||
|
||||
private function parseUserInfo($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($data['error_code'] != 0) {
|
||||
throw new \Exception("Fetch User Info Failed:{$data['error']}");
|
||||
|
||||
if (isset($data['error_code']) && $data['error_code'] != 0) {
|
||||
throw new \Exception("Fetch User Info Failed:{$response}");
|
||||
}
|
||||
|
||||
$userInfo['type'] = 'WEIBO';
|
||||
|
||||
$userInfo['id'] = $data['id'];
|
||||
$userInfo['name'] = $data['name'];
|
||||
$userInfo['nick'] = $data['screen_name'];
|
||||
$userInfo['head'] = $data['avatar_large'];
|
||||
|
||||
$userInfo['avatar'] = $data['profile_image_url'];
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_WEIBO;
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Library\OAuth;
|
||||
namespace App\Services\OAuth;
|
||||
|
||||
use App\Library\OAuth;
|
||||
use App\Models\Connect as ConnectModel;
|
||||
use App\Services\OAuth;
|
||||
|
||||
class WeiXin extends OAuth
|
||||
{
|
||||
@ -14,13 +15,13 @@ class WeiXin extends OAuth
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$params = [
|
||||
'appid' => $this->appId,
|
||||
'appid' => $this->clientId,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'state' => $this->getState(),
|
||||
'response_type' => 'code',
|
||||
'scope' => 'snsapi_login',
|
||||
'state' => 'dev',
|
||||
];
|
||||
|
||||
|
||||
return self::AUTHORIZE_URL . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
@ -28,15 +29,15 @@ class WeiXin extends OAuth
|
||||
{
|
||||
$params = [
|
||||
'code' => $code,
|
||||
'appid' => $this->appId,
|
||||
'secret' => $this->appSecret,
|
||||
'appid' => $this->clientId,
|
||||
'secret' => $this->clientSecret,
|
||||
'grant_type' => 'authorization_code',
|
||||
];
|
||||
|
||||
|
||||
$response = $this->httpPost(self::ACCESS_TOKEN_URL, $params);
|
||||
|
||||
|
||||
$this->accessToken = $this->parseAccessToken($response);
|
||||
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
@ -51,38 +52,38 @@ class WeiXin extends OAuth
|
||||
'access_token' => $accessToken,
|
||||
'openid' => $openId,
|
||||
];
|
||||
|
||||
|
||||
$response = $this->httpGet(self::USER_INFO_URL, $params);
|
||||
|
||||
|
||||
return $this->parseUserInfo($response);
|
||||
}
|
||||
|
||||
private function parseAccessToken($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
|
||||
if (isset($data['errcode']) && $data['errcode'] != 0) {
|
||||
throw new \Exception("Fetch Access Token Failed:{$data['errmsg']}");
|
||||
throw new \Exception("Fetch Access Token Failed:{$response}");
|
||||
}
|
||||
|
||||
|
||||
$this->openId = $data['openid'];
|
||||
|
||||
|
||||
return $data['access_token'];
|
||||
}
|
||||
|
||||
private function parseUserInfo($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
|
||||
|
||||
if (isset($data['errcode']) && $data['errcode'] != 0) {
|
||||
throw new \Exception("Fetch User Info Failed:{$data['errmsg']}");
|
||||
throw new \Exception("Fetch User Info Failed:{$response}");
|
||||
}
|
||||
|
||||
$userInfo['type'] = 'WEIXIN';
|
||||
$userInfo['name'] = $data['name'];
|
||||
$userInfo['nick'] = $data['screen_name'];
|
||||
$userInfo['head'] = $data['avatar_large'];
|
||||
|
||||
|
||||
$userInfo['id'] = $data['openid'];
|
||||
$userInfo['name'] = $data['nickname'];
|
||||
$userInfo['avatar'] = $data['headimgurl'];
|
||||
$userInfo['provider'] = ConnectModel::PROVIDER_WEIXIN;
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ class Alipay extends PayService
|
||||
'out_trade_no' => $trade->sn,
|
||||
'total_amount' => $trade->amount,
|
||||
'subject' => $trade->subject,
|
||||
'http_method' => 'GET',
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
@ -47,6 +47,7 @@ class AlipayGateway extends Service
|
||||
'alipay_root_cert' => config_path('alipay/alipayRootCert.crt'), // 支付宝根证书
|
||||
'app_cert_public_key' => config_path('alipay/appCertPublicKey.crt'), // 应用公钥证书
|
||||
'notify_url' => $this->settings['notify_url'],
|
||||
'return_url' => $this->settings['return_url'],
|
||||
'log' => [
|
||||
'file' => log_path('alipay.log'),
|
||||
'level' => $level,
|
||||
|
@ -6,6 +6,7 @@ use App\Models\Refund as RefundModel;
|
||||
use App\Models\Trade as TradeModel;
|
||||
use App\Repos\Trade as TradeRepo;
|
||||
use App\Services\Pay as PayService;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Yansongda\Pay\Gateways\Wechat;
|
||||
use Yansongda\Pay\Log;
|
||||
@ -90,7 +91,7 @@ class Wxpay extends PayService
|
||||
* wap支付
|
||||
*
|
||||
* @param TradeModel $trade
|
||||
* @return Response|bool
|
||||
* @return RedirectResponse|bool
|
||||
*/
|
||||
public function wap(TradeModel $trade)
|
||||
{
|
||||
@ -102,11 +103,6 @@ class Wxpay extends PayService
|
||||
'body' => $trade->subject,
|
||||
]);
|
||||
|
||||
/**
|
||||
* 微信H5支付会检查Referer,构造Referer头信息
|
||||
*/
|
||||
$result->headers->set('Referer', kg_site_url());
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
Log::error('Wxpay Wap Exception', [
|
||||
|
@ -21,6 +21,11 @@ class WxpayGateway extends Service
|
||||
$this->settings = array_merge($defaults, $options);
|
||||
}
|
||||
|
||||
public function setReturnUrl($returnUrl)
|
||||
{
|
||||
$this->settings['return_url'] = $returnUrl;
|
||||
}
|
||||
|
||||
public function setNotifyUrl($notifyUrl)
|
||||
{
|
||||
$this->settings['notify_url'] = $notifyUrl;
|
||||
@ -42,6 +47,7 @@ class WxpayGateway extends Service
|
||||
'mch_id' => $this->settings['mch_id'],
|
||||
'key' => $this->settings['key'],
|
||||
'notify_url' => $this->settings['notify_url'],
|
||||
'return_url' => $this->settings['return_url'],
|
||||
'cert_client' => config_path('wxpay/apiclient_cert.pem'),
|
||||
'cert_key' => config_path('wxpay/apiclient_key.pem'),
|
||||
'log' => [
|
||||
|
41
app/Validators/Connect.php
Normal file
41
app/Validators/Connect.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validators;
|
||||
|
||||
use App\Exceptions\BadRequest as BadRequestException;
|
||||
use App\Repos\Connect as ConnectRepo;
|
||||
|
||||
class Connect extends Validator
|
||||
{
|
||||
|
||||
public function checkConnect($id)
|
||||
{
|
||||
return $this->checkConnectById($id);
|
||||
}
|
||||
|
||||
public function checkConnectById($id)
|
||||
{
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
$connect = $connectRepo->findById($id);
|
||||
|
||||
if (!$connect) {
|
||||
throw new BadRequestException('connect.not_found');
|
||||
}
|
||||
|
||||
return $connect;
|
||||
}
|
||||
|
||||
public function checkConnectByOpenId($openId, $provider)
|
||||
{
|
||||
$connectRepo = new ConnectRepo();
|
||||
|
||||
$connect = $connectRepo->findByOpenId($openId, $provider);
|
||||
|
||||
if (!$connect) {
|
||||
throw new BadRequestException('connect.not_found');
|
||||
}
|
||||
|
||||
return $connect;
|
||||
}
|
||||
}
|
92
db/migrations/20201205091213_create_connect_table.php
Normal file
92
db/migrations/20201205091213_create_connect_table.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
class CreateConnectTable extends Phinx\Migration\AbstractMigration
|
||||
{
|
||||
public function change()
|
||||
{
|
||||
$this->table('kg_connect', [
|
||||
'id' => false,
|
||||
'primary_key' => ['id'],
|
||||
'engine' => 'InnoDB',
|
||||
'encoding' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
'comment' => '',
|
||||
'row_format' => 'DYNAMIC',
|
||||
])
|
||||
->addColumn('id', 'integer', [
|
||||
'null' => false,
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'identity' => 'enable',
|
||||
'comment' => '主键编号',
|
||||
])
|
||||
->addColumn('user_id', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'comment' => '用户编号',
|
||||
'after' => 'id',
|
||||
])
|
||||
->addColumn('open_id', 'string', [
|
||||
'null' => false,
|
||||
'default' => '',
|
||||
'limit' => 50,
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
'encoding' => 'utf8mb4',
|
||||
'comment' => '开放ID',
|
||||
'after' => 'user_id',
|
||||
])
|
||||
->addColumn('open_name', 'string', [
|
||||
'null' => false,
|
||||
'default' => '',
|
||||
'limit' => 30,
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
'encoding' => 'utf8mb4',
|
||||
'comment' => '开放名称',
|
||||
'after' => 'open_id',
|
||||
])
|
||||
->addColumn('open_avatar', 'string', [
|
||||
'null' => false,
|
||||
'default' => '',
|
||||
'limit' => 150,
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
'encoding' => 'utf8mb4',
|
||||
'comment' => '开放头像',
|
||||
'after' => 'open_name',
|
||||
])
|
||||
->addColumn('provider', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'comment' => '提供方',
|
||||
'after' => 'open_avatar',
|
||||
])
|
||||
->addColumn('deleted', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'comment' => '删除标识',
|
||||
'after' => 'provider',
|
||||
])
|
||||
->addColumn('create_time', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'comment' => '创建时间',
|
||||
'after' => 'deleted',
|
||||
])
|
||||
->addColumn('update_time', 'integer', [
|
||||
'null' => false,
|
||||
'default' => '0',
|
||||
'limit' => MysqlAdapter::INT_REGULAR,
|
||||
'comment' => '更新时间',
|
||||
'after' => 'create_time',
|
||||
])
|
||||
->addIndex(['open_id', 'provider'], [
|
||||
'name' => 'openid_provider',
|
||||
'unique' => false,
|
||||
])
|
||||
->create();
|
||||
}
|
||||
}
|
90
db/migrations/20201205112717_insert_oauth_setting_data.php
Normal file
90
db/migrations/20201205112717_insert_oauth_setting_data.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class InsertOauthSettingData extends AbstractMigration
|
||||
{
|
||||
|
||||
public function up()
|
||||
{
|
||||
$rows = [
|
||||
[
|
||||
'section' => 'oauth.qq',
|
||||
'item_key' => 'enabled',
|
||||
'item_value' => '0',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.qq',
|
||||
'item_key' => 'client_id',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.qq',
|
||||
'item_key' => 'client_secret',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.qq',
|
||||
'item_key' => 'redirect_uri',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weixin',
|
||||
'item_key' => 'enabled',
|
||||
'item_value' => '0',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weixin',
|
||||
'item_key' => 'client_id',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weixin',
|
||||
'item_key' => 'client_secret',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weixin',
|
||||
'item_key' => 'redirect_uri',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weibo',
|
||||
'item_key' => 'enabled',
|
||||
'item_value' => '0',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weibo',
|
||||
'item_key' => 'client_id',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weibo',
|
||||
'item_key' => 'client_secret',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weibo',
|
||||
'item_key' => 'redirect_uri',
|
||||
'item_value' => '',
|
||||
],
|
||||
[
|
||||
'section' => 'oauth.weibo',
|
||||
'item_key' => 'refuse_uri',
|
||||
'item_value' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$this->table('kg_setting')->insert($rows)->save();
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->execute("DELETE FROM kg_setting WHERE section = 'oauth.qq'");
|
||||
$this->execute("DELETE FROM kg_setting WHERE section = 'oauth.weixin'");
|
||||
$this->execute("DELETE FROM kg_setting WHERE section = 'oauth.weibo'");
|
||||
}
|
||||
|
||||
}
|
@ -1188,7 +1188,7 @@
|
||||
}
|
||||
|
||||
.login-wrap .link {
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
@ -1202,6 +1202,26 @@
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.login-wrap .oauth {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-wrap .oauth a {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.login-qq {
|
||||
color: dodgerblue;
|
||||
}
|
||||
|
||||
.login-wechat {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.login-weibo {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
position: relative;
|
||||
}
|
||||
@ -1537,9 +1557,12 @@
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.security-item-list {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.security-item {
|
||||
padding: 15px;
|
||||
line-height: 50px;
|
||||
line-height: 80px;
|
||||
border-bottom: 1px dashed #ccc;
|
||||
}
|
||||
|
||||
@ -1563,6 +1586,29 @@
|
||||
float: right;
|
||||
}
|
||||
|
||||
.connect-list {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.open-avatar img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.connect-tips {
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.oauth-list {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.oauth-list a {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.order-filter {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ $scheduler = new Scheduler();
|
||||
|
||||
$script = __DIR__ . '/console.php';
|
||||
|
||||
$bin = '/usr/bin/php';
|
||||
$bin = '/usr/local/bin/php';
|
||||
|
||||
$scheduler->php($script, $bin, ['--task' => 'deliver', '--action' => 'main'])
|
||||
->at('*/3 * * * *');
|
||||
|
Loading…
x
Reference in New Issue
Block a user