微信支付,微信web登录功能优化

xsign
nineven 3 years ago
parent dd1fb29ba0
commit c765658fb5
  1. 24
      fir_client/src/components/user/FirUserOrders.vue
  2. 41
      fir_client/src/components/user/FirUserProfileInfo.vue
  3. 29
      fir_client/src/restful/index.js
  4. 6
      fir_ser/api/urls.py
  5. 31
      fir_ser/api/views/login_wx.py
  6. 28
      fir_ser/api/views/order.py
  7. 6
      fir_ser/common/base/magic.py
  8. 64
      fir_ser/common/libs/mp/wechat.py

@ -247,7 +247,7 @@
<script>
import {my_order} from "@/restful";
import {my_order, order_sync} from "@/restful";
import {format_choices, getUserInfoFun} from '@/utils'
import VueQr from 'vue-qr';
@ -297,17 +297,37 @@ export default {
methods: 'PUT', data: {order_number: order.order_number, act: 'cancel'},
})
},
loop_get_order_info(order_number, c_count = 1) {
order_sync(data => {
c_count += 1;
if (c_count > 30) {
return;
}
if (data.code === 1000) {
this.$message.success(data.msg);
this.get_data_from_tabname();
this.wx_pay = false;
} else if (data.code === 1001) {
return this.loop_get_order_info(order_number, c_count)
}
}, {
"methods": "POST",
data: {"order_number": order_number}
})
},
goto_pay(order) {
this.current_order_info = order;
my_order(res => {
if (res.code === 1000) {
let data = res.data;
if (data && data.url) {
if (data && data.type === 'WX') {
this.pay_code_url = data.url;
this.wx_pay = true;
this.$message.success("请用微信扫描支付");
this.loop_get_order_info(order.order_number)
} else if (data && data.type === 'ALI') {
let pay_url = data.url;
if (pay_url && pay_url.length > 10) {

@ -11,6 +11,22 @@
微信授权登录用户信息
</span>
<el-tag style="margin-bottom: 10px">授权用户将可以使用微信扫码直接登录</el-tag>
<el-button plain style="float: right;margin-top: 10px" @click="wx_web_login_fun"> 点击扫码同步微信用户信息</el-button>
<el-dialog
:visible.sync="show_web_visible"
append-to-body
center
title="手机微信扫码更新个人信息"
width="360px">
<vue-qr ref="qr"
:correctLevel="qrinfo.correctLevel"
:logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:margin="qrinfo.margin"
:size="300" :text="wx_web_login_url"
class="code-wrap">
</vue-qr>
</el-dialog>
<el-table
:data="wx_user_list"
border
@ -286,11 +302,15 @@
</template>
<script>
import {changeInfoFun, getAuthcTokenFun, userinfos, wxBindFun, wxLoginFun, wxutils} from '@/restful'
import {changeInfoFun, getAuthcTokenFun, userinfos, wxBindFun, wxLoginFun, wxutils, wxWebScanFun} from '@/restful'
import {deepCopy, geetest} from "@/utils";
import VueQr from 'vue-qr';
export default {
name: "FirUserProfileInfo",
components: {
VueQr
},
data() {
return {
userinfo: {
@ -311,10 +331,29 @@ export default {
wx_visible: false,
loop_flag: false,
show_wx_visible: false,
show_web_visible: false,
qrinfo: {
logoScale: 0.3,
logoCornerRadius: 12,
correctLevel: 3,
margin: 20
},
wx_web_login_url: '',
wx_user_list: [],
pagination: {"currentPage": 1, "total": 0, "pagesize": 999},
}
}, methods: {
wx_web_login_fun() {
wxWebScanFun(data => {
if (data.code === 1000) {
this.show_web_visible = true;
this.wx_web_login_url = data.data;
} else {
this.$message.error("信息获取失败")
}
}, {methods: 'GET'})
},
delete_wx_u(wx_user_info) {
this.$prompt(`此操作将导致微信用户 “${wx_user_info.nickname}” 无法通过扫码登录, 是否继续删除?`, '警告', {
confirmButtonText: '确定',

@ -254,6 +254,20 @@ export function wxBindFun(callBack, params, load = true) {
);
}
/**微信web扫码授权 */
export function wxWebScanFun(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/mp.web.login',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**获取验证token */
export function getAuthTokenFun(callBack, params, load = true) {
@ -560,6 +574,21 @@ export function my_order(callBack, params, load = true) {
);
}
/**用户订单支付同步 */
export function order_sync(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/orders.sync',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**实名认证 */
export function user_certification(callBack, params, load = true) {
getData(

@ -21,9 +21,9 @@ from api.views.domain import DomainCnameView, DomainInfoView
from api.views.download import ShortDownloadView, InstallView, DownloadView
from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationView, ChangeAuthorizationView, \
UserApiTokenView, CertificationView, ChangeInfoView
from api.views.login_wx import WeChatLoginView, WeChatLoginCheckView, WeChatBindView
from api.views.login_wx import WeChatLoginView, WeChatLoginCheckView, WeChatBindView, WeChatWebLoginView
from api.views.logout import LogoutView
from api.views.order import PriceView, OrderView, PaySuccess
from api.views.order import PriceView, OrderView, PaySuccess, OrderSyncView
from api.views.report import ReportView
from api.views.storage import StorageView, CleanStorageView
from api.views.thirdlogin import ValidWxChatToken, ThirdWxAccount
@ -55,11 +55,13 @@ urlpatterns = [
re_path("^qrcode$", AppsQrcodeShowView.as_view()),
re_path("^package_prices$", PriceView.as_view()),
re_path("^orders$", OrderView.as_view()),
re_path("^orders.sync$", OrderSyncView.as_view()),
re_path("^certification$", CertificationView.as_view()),
re_path(r"^pay_success/(?P<name>\w+)$", PaySuccess.as_view()),
re_path("^cname_domain$", DomainCnameView.as_view()),
re_path("^domain_info$", DomainInfoView.as_view()),
re_path("^mp.weixin$", ValidWxChatToken.as_view()),
re_path("^mp.web.login$", WeChatWebLoginView.as_view(), name="mp.web.login"),
re_path("^third.wx.login$", WeChatLoginView.as_view()),
re_path("^third.wx.bind$", WeChatBindView.as_view()),
re_path("^third.wx.sync$", WeChatLoginCheckView.as_view()),

@ -3,7 +3,7 @@ import logging
from rest_framework.response import Response
from rest_framework.views import APIView
from api.models import UserInfo
from api.models import UserInfo, ThirdWeChatUserInfo
from api.utils.response import BaseResponse
from api.utils.serializer import UserInfoSerializer
from api.utils.utils import set_user_token
@ -12,7 +12,7 @@ from common.base.magic import get_pending_result
from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config
from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle
from common.libs.mp.wechat import make_wx_login_qrcode, show_qrcode_url
from common.libs.mp.wechat import make_wx_login_qrcode, show_qrcode_url, WxWebLogin
from common.utils.caches import set_wx_ticket_login_info_cache, get_wx_ticket_login_info_cache
logger = logging.getLogger(__name__)
@ -91,3 +91,30 @@ class WeChatLoginCheckView(APIView):
else:
ret.code = 1006
return Response(ret.dict)
class WeChatWebLoginView(APIView):
def get(self, request):
logger.error(request.query_params)
wx_login_obj = WxWebLogin()
ret = BaseResponse()
code = request.query_params.get('code')
if code:
wx_login_obj.get_wx_token(code)
wx_user_info = wx_login_obj.get_user_info()
logger.info(f'{wx_user_info}')
wx_user_info = {
'openid': wx_user_info.get('openid'),
'nickname': wx_user_info.get('nickname'),
'sex': wx_user_info.get('sex'),
'subscribe_time': wx_user_info.get('subscribe_time', 0),
'head_img_url': wx_user_info.get('headimgurl', ''),
'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}",
'subscribe': wx_user_info.get('subscribe', 0),
}
logger.info(f'{wx_user_info}')
ThirdWeChatUserInfo.objects.filter(openid=wx_user_info.get('openid')).update(**wx_user_info)
return Response('更新成功')
ret.data = wx_login_obj.make_auth_uri()
return Response(ret.dict)

@ -14,6 +14,7 @@ from api.utils.modelutils import PageNumber
from api.utils.response import BaseResponse
from api.utils.serializer import PriceSerializer, OrdersSerializer
from common.base.baseutils import get_order_num, get_choices_dict
from common.base.magic import get_pending_result
from common.core.auth import ExpiringTokenAuthentication
from common.libs.pay.util import get_pay_obj_form_name, get_enable_pay_choices, get_payment_type
from common.utils.caches import update_order_status
@ -50,7 +51,6 @@ class OrderView(APIView):
pay_id = request.data.get("pay_id", None)
order_number = request.data.get("order_number", None)
if (price_id and pay_id) or order_number:
price_obj = Price.objects.filter(name=price_id).first()
order_obj = Order.objects.filter(user_id=request.user, order_number=order_number).first()
if order_obj and order_obj.status in [1, 2] and order_obj.payment_name:
pay_obj = get_pay_obj_form_name(order_obj.payment_name)
@ -60,6 +60,8 @@ class OrderView(APIView):
res.data = pay_url
logger.info(f"{request.user} 下单成功 {res.dict}")
return Response(res.dict)
price_obj = Price.objects.filter(name=price_id).first()
if price_obj:
try:
order_number = get_order_num()
@ -117,6 +119,30 @@ class OrderView(APIView):
return Response(res.dict)
def get_order_obj(user_obj, order_number):
return Order.objects.filter(user_id=user_obj, order_number=order_number).first()
def except_result(result, *args, **kwargs):
if result and result.status in [0, 4, 5, 6]:
return True
class OrderSyncView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def post(self, request):
res = BaseResponse()
order_number = request.data.get("order_number", None)
if order_number:
status, result = get_pending_result(get_order_obj, except_result, order_number=order_number,
locker_key=order_number, user_obj=request.user)
if not status and result:
res.code = 1001
res.msg = result.get_status_display()
return Response(res.dict)
class PriceView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]

@ -65,7 +65,7 @@ def call_function_try_attempts(try_attempts=3, sleep_time=2, failed_callback=Non
return decorator
def get_pending_result(func, expect_func, timeout=10, sleep_time=3, *args, **kwargs):
def get_pending_result(func, expect_func, timeout=30, sleep_time=3, *args, **kwargs):
try:
with cache.lock("%s_%s" % ('get_pending_result', kwargs.get('locker_key')), timeout=timeout + sleep_time):
count = 1
@ -76,8 +76,8 @@ def get_pending_result(func, expect_func, timeout=10, sleep_time=3, *args, **kwa
return True, result
time.sleep(sleep_time)
if timeout < count * sleep_time:
return False, 'timeout'
return False, result
count += 1
except Exception as e:
logger.warning(f'get pending result exception: {e}')
return False, 'timeout'
return False, result

@ -5,14 +5,17 @@
# date: 2021/9/6
import json
import logging
import urllib
from hashlib import sha1
import requests
from django.urls import reverse
from common.base.baseutils import get_format_time
from common.cache.storage import WxTokenCache
from common.core.sysconfig import Config
from common.libs.mp.utils import WxMsgCryptBase
from common.libs.storage.localApi import LocalStorage
logger = logging.getLogger(__name__)
@ -401,3 +404,64 @@ class WxTemplateMsg(object):
},
}
return self.send_msg(to_user, msg_id, content_data)
class WxWebLogin(object):
"""
通过网页授权获取用户授权信息公众号已经无法获取用户的昵称头像等隐私信息
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
"""
def __init__(self):
self.openid = None
self.access_token = None
auth_info = Config.THIRDLOGINCONF.get('auth')
self.app_id = auth_info.get('app_id')
self.app_secret = auth_info.get('app_secret')
def make_auth_uri(self):
"""
第一步 用户通过微信客户端打开该URI
:return:
"""
# url = 'https://app.hehelucky.cn/api/v1/fir/server/wxweb' # 该url是前端页面,用户微信跳转,后端页面也行
local_storage = LocalStorage(**Config.IOS_PMFILE_DOWNLOAD_DOMAIN)
url = f'{local_storage.get_base_url()}{reverse("mp.web.login")}'
encode_url = urllib.parse.quote(url, safe='/', encoding=None, errors=None)
code_url = f'https://open.weixin.qq.com/connect/oauth2/authorize?appid={self.app_id}&redirect_uri={encode_url}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'
return code_url
def get_wx_token(self, code):
"""
第二步 获取授权之后微信会携带code并且自动跳转,然后通过code获取授权token自测该token只能获取该用户信息无法获取其他用户信息
:param code:
:return:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
"""
t_url = f'https://api.weixin.qq.com/sns/oauth2/access_token?appid={self.app_id}&secret={self.app_secret}&code={code}&grant_type=authorization_code'
req = requests.get(t_url)
if req.status_code == 200:
logger.info(f"get access token {req.status_code} {req.text}")
auth_info = req.json()
self.access_token = auth_info.get('access_token')
self.openid = auth_info.get('openid')
return auth_info
return {}
def get_user_info(self):
"""
第三步 通过token获取用户信息
:return:
"""
t_url = f'https://api.weixin.qq.com/sns/userinfo?access_token={self.access_token}&openid={self.openid}&lang=zh_CN'
req = requests.get(t_url)
if req.status_code == 200:
req.encoding = 'utf-8'
return req.json()
return {}

Loading…
Cancel
Save