微信登录和微信支付
在上一章我们初步的完成了前端的编写,接下来我们来操作微信的登录和微信的支付
微信开放平台(针对开发者和公司):
对应的微信官方文档:
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
登录用,对应的AppID和AppSecret需要申请才可操作,需要在管理中心创建申请对应的网站,一般是必须要上线的网站:
https://open.weixin.qq.com/
后面我给出了对应的信息(用来测试用),这样就不用你自己申请了
准备工作:
网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统
在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号
并拥有一个已审核通过的网站应用(或者其他应用,这里就以网站即网页为例子,后面的都说明网站)
并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程
注册帐号和申请应用都是免费的,必须要有一个线上的网站,才能审核通过(过程还是挺麻烦的)
就可以使用微信的登录了,但是如果想使用微信支付的功能,就必须认证开发者资质(认证一次300块人民币)
名词解释:
OAuth2.0协议
玩抖音,发视频,抖音需要访问你相册的授权,话筒的授权,地理位置的授权等等
一句话,我不想帐号密码给第三方应用,但我还想用他们的功能,而他们的功能需要我的部分数据来协助
ok,咱玩令牌,令牌与密码的作用都可以进入系统,但是有三点差异:
1:令牌是短期的,到期会自动失效,用户自己无法修改,密码一般长期有效,用户不修改,就不会发生变化
2:令牌可以被数据所有者撤销,会立即失效,以上例而言,屋主可以随时取消快递员的令牌,密码一般不允许被他人撤销
3:令牌有权限范围,比如只能进小区的二号门,对于网络服务来说,只读令牌就比读写令牌更安全,密码一般是完整权限
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全
OAuth的四种授权模式:
1:授权码模式(功能最完整,流程最严密的授权模式)
说白了,授权码模式,不再client和user之间商量授权,而是client想要被授权
所以client去找了一个和事佬大妈,大妈将client和user叫到了一起(认证服务器)
给大妈个面子,这事就这么定了,就是这样的一个过程,全程中认证服务器会发布一个认证码贯穿始终
下面的可以百度了解即可
2:密码模式
一般通过账号密码就可以访问,他们可以通过你的账号密码进行访问(保存在他们的数据库里)
3:简化模式
授权码模式的减低版,没有code授权码
4:客户端模式
最不安全的模式,基本上不需要什么操作就可以访问,他们保存了你的令牌(你给的),保存在数据库里
安全性:授权码模式 > 简化模式 > 密码模式 > 客户端模式
那么具体的信任程度,一般是:客户端模式>密码模式>简化模式>授权码模式
因为对应的第三方必须信任要高,才会放心的给出最简便,但最不安全的模式,但最好还是使用授权码模式
因为在不安全的情况下,黑客也更加容易得到你的信息
由于授权码模式总体来说是最好的,所以我们操作授权码模式
AppID:应用ID,唯一标识(身份证号)
AppSecret :应用的密钥(密码)
code:授权的临时凭证(例如:临时身份证)
access_token :接口调用凭证(例如:真正的身份证,虎符,令牌)
登录授权时序图 :
上面的二维码一般保存对应的地址,你可以进行测试,在百度上搜索"草料二维码生成器",进行官方网站
输入"中华人民共和国"这个内容,点击生成二维码,用微信扫一扫,就会出现该内容
当然多次生成一样的,对应的码基本都相同
当然你也可以输入网站,如http://www.baidu.com,那么会自动的进行跳转,那么为什么不会直接显示内容,而是跳转呢
主要是观察是否有"😕/“,其中单独的”//“,若前面没有值,那么默认”//"后面是一个网站,否则就是内容
而"😕/"代表整体是一个网站了(第一个开始,分割线)
具体可以自己进行测试
接下来我们继续说明一下对应的模式
其中授权码模式,上面的图片中,就是一个授权码模式
code就是授权码,token就是令牌,AppID和AppSecret 是网站的对应唯一值,这样该网站可以通过令牌得到用户的信息了
而简化模式,就是没有code这个授权码
而密码模式,一般输入的密码是对应的第三方的,第三方直接通过你的密码进行授权并操作
而客户端模式,自己通过自己的信息得到令牌,然后将令牌给第三方
第三方可以说是我们要访问的客户端,或者网站等等,通过上面的描述,可以知道
的确客户端模式是最不安全的,因为他能直接操作最终的令牌
开发步骤:
vue项目安装
微信官方提供的生成二维码的js,有对应的组件,包含了对应的js
npm install vue-wxlogin
如果不是vue的项目,可以直接引用官方提供的js文件,来生成二维码
http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
页面引入:
在对应的前端找到Header.vue(上一章的前端项目)
这里只会给出部分代码,需要自己去进行对比修改 ,由于是部分,对应的代码可能不是全的
< script>
import wxlogin from 'vue-wxlogin' ;
export default {
name : "Header" ,
components : {
wxlogin
} ,
< el-dialog
style = " width : 800px; margin : 0px auto; "
title = " "
:visible.sync = " dialogFormVisible" >
< div id = " loginForm" >
< el-form>
< el-form-item>
< h1 style = " font-size : 30px; color : #00B38A" > 拉勾 h1>
el-form-item>
< el-form-item>
< el-input v-model = " phone" placeholder = " 请输入常用手机号..." > el-input>
el-form-item>
< el-form-item>
< el-input v-model = " password" placeholder = " 请输入密码..." > el-input>
el-form-item>
el-form>
< el-button
style = " width : 100%; margin : 0px auto; background-color : #00B38A; font-size : 20px"
type = " primary"
@click = " login" > 确 定 el-button>
< p> p>
< img
@click = " goToLoginWX"
src = " http://www.lgstatic.com/lg-passport-fed/static/pc/modules/common/img/icon-wechat@2x_68c86d1.png"
alt = " "
/>
div>
< wxlogin id = " wxLoginForm" style = " display : none"
:appid = " appid" :scope = " scope" :redirect_uri = " redirect_uri" > wxlogin>
el-dialog>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
data ( ) {
return {
isLogin : false ,
userDTO : null ,
isHasNewMessage : false ,
dialogFormVisible : false ,
phone : "" ,
password : "" ,
appid : "wxd99431bbff8305a0" ,
scope : "snsapi_login" ,
redirect_uri : "http://www.pinzhi365.com/wxlogin" ,
} ;
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
goToLoginWX ( ) {
document. getElementById ( "loginForm" ) . style. display= "none"
document. getElementById ( "wxLoginForm" ) . style. display= "block"
} ,
接下来点击对应的微信图标,那么就会出现二维码
假设:如果对应的网站Scope权限没有开通或者scope的值不正确,那么会提示对应错误,这是该网站的问题
具体可以百度,这里并没有问题
当然对应的appid的值不正确或者redirect_uri的值不正确,也会出现对应的提示错误,他们都会去验证的
所以申请时也一般只能是上线的网站或者其他应用,否则申请基本会失败,你可以自己试验一下(故意修改参数值)
总体来说,若二维码生成失败,那么就是上面三个参数的问题,只要都正确,基本才会生成出二维码
修改hosts文件(若有自己的远程服务器可以不用这样操作):
文件位置:C:\Windows\System32\drivers\etc\hosts
127.0 .0.1 www.pinzhi365.com
回调默认指定的是80端口,别忘记将对应的tomcat的端口修改成80再次启动一个项目(web层项目的那个服务器)
可能80端口被占用,且杀死不了,主要是因为对应的服务是系统的服务,所以我们需要以管理员的方式进入命令行窗口(cmd)
执行net stop http,选择y,等待关闭,注意需要管理员方式,否则可能操作不了
这时80端口没有占用了(对应关闭的服务并不是特别需要)
但可能重启还是会继续占用,则继续执行sc config http start= disabled,使得重启不占用
来到后端的web层项目:
引入依赖:
< dependency>
< groupId> javax.servlet groupId>
< artifactId> servlet-api artifactId>
< version> 2.4 version>
< scope> provided scope> dependency>
< dependency>
< groupId> org.apache.httpcomponents groupId>
< artifactId> httpclient artifactId>
< version> 4.5.12 version>
dependency>
在lagou包下,创建wx包,并在里面创建WxLoginController类:
package com. lagou. wx ;
import org. springframework. web. bind. annotation. GetMapping ;
import org. springframework. web. bind. annotation. RestController ;
import javax. servlet. http. HttpServletRequest ;
@RestController
public class WxLoginController {
@GetMapping ( "wxlogin" )
public Object wxlogin ( HttpServletRequest request) {
String code = request. getParameter ( "code" ) ;
System . out. println ( "【临时凭证】code=" + code) ;
return null ;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
记得观察对应扫描包时,是否包括这个包,否则相当于没有写(不会跳转)
< dubbo: annotation package = " com.lagou.controller,com.lagou.wx" />
这时我们进行扫描二维码(点击登录的框框中的微信图形,用手机扫描一下二维码,点击允许
在后端观看是否得到code的数据,若得到,则操作成功
注意:对应的不同的浏览器可能会有对应的问题(如谷歌,若出现问题,可以换一个浏览器)
特别是新的版本,现在一般会有,具体的问题在后面说明
至此我们最后来操作code这个临时数据(临时授权码或者临时凭证):
在程序上进行发送请求(这里是java):
在对应的web层项目上的java资源文件下,创建包commons,并在里面创建HttpClientUtil类:
package commons ;
import org. apache. http. client. methods. CloseableHttpResponse ;
import org. apache. http. client. methods. HttpGet ;
import org. apache. http. client. utils. URIBuilder ;
import org. apache. http. impl. client. CloseableHttpClient ;
import org. apache. http. impl. client. HttpClients ;
import org. apache. http. util. EntityUtils ;
import java. net. URI;
import java. util. Map ;
public class HttpClientUtil {
public static String doGet ( String url) {
String s = doGet ( url, null ) ;
return s;
}
public static String doGet ( String url, Map < String , String > param) {
CloseableHttpClient aDefault = HttpClients . createDefault ( ) ;
String s = null ;
CloseableHttpResponse response = null ;
try {
URIBuilder uriBuilder = new URIBuilder ( url) ;
if ( param!= null ) {
for ( String key: param. keySet ( ) ) {
uriBuilder. addParameter ( key, param. get ( key) ) ;
}
}
URI uri = uriBuilder. build ( ) ;
HttpGet httpGet = new HttpGet ( uri) ;
response = aDefault. execute ( httpGet) ;
int statusCode = response. getStatusLine ( ) . getStatusCode ( ) ;
System . out. println ( response) ;
System . out. println ( "响应的状态:" + response. getStatusLine ( ) ) ;
System . out. println ( "响应的状态:" + statusCode) ;
if ( statusCode== 200 ) {
System . out. println ( response. getEntity ( ) ) ;
System . out. println ( "---" ) ;
s = EntityUtils . toString ( response. getEntity ( ) , "UTF-8" ) ;
System . out. println ( s) ;
System . out. println ( "---" ) ;
System . out. println ( 1 ) ;
}
} catch ( Exception e) {
e. printStackTrace ( ) ;
} finally {
try {
if ( response != null ) {
response. close ( ) ;
}
aDefault. close ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
return s;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
对应后端代码:
@GetMapping ( "wxlogin" )
public Object wxlogin ( HttpServletRequest request) {
String code = request. getParameter ( "code" ) ;
System . out. println ( "【临时凭证】code=" + code) ;
String getTokenByCode_url =
"https: / / api. weixin. qq. com/ sns/ oauth2/ access_token?
appid= wxd99431bbff8305a0& secret= 60f 78681d 063590 a469f1b297feff3c4& code= "+
code+ "&grant_type=authorization_code" ;
String tokenString = HttpClientUtil . doGet ( getTokenByCode_url) ;
System . out. println ( tokenString) ;
return null ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
扫描二维码允许后,就得到对应的json字符串
对应格式化之后是如下:
{
"access_token" : 不能写(文章的检测不能写)
"expires_in" : 7200 ,
"refresh_token" : "59_4V4PgKjvJ3JAGgRAH46VwqelRNNNgQ2-6FU48jtR98DgTWFWnVuFCMWgN0v5bI1DZ-VCL7a7IVP36dVddV-jkFlZVpLXPj3IW_Zys5P5Z6k" ,
"openid" : "od4PTw7JYdV2XYGkJPPqF4nZvlfA" ,
"scope" : "snsapi_login" ,
"unionid" : "oEg8VuHZxBTgpzcrTi3rAEvwsU88"
}
查看对应的微信官方文档中,是否是正确的格式:
对比发现,的确是是正确的,至此得到对应的token令牌成功(字符串有令牌参数及其值,简称为token字符串),即操作成功
接下来我们通过token字符串来获得微信用户的信息
在这之前我们需要先创建一个类:
在java资源文件夹下,创建entity包,并在里面创建Token类:
package entity ;
public class Token {
private String access_token;
private String expires_in;
private String refresh_token;
private String openid;
private String scope;
private String unionid;
public Token ( ) {
}
public Token ( String access_token, String expires_in, String refresh_token, String openid,
String scope, String unionid) {
this . access_token = access_token;
this . expires_in = expires_in;
this . refresh_token = refresh_token;
this . openid = openid;
this . scope = scope;
this . unionid = unionid;
}
@Override
public String toString ( ) {
return "Token{" +
"xxx'" + access_token + '\'' +
", expires_in='" + expires_in + '\'' +
", refresh_token='" + refresh_token + '\'' +
", openid='" + openid + '\'' +
", scope='" + scope + '\'' +
", unionid='" + unionid + '\'' +
'}' ;
}
public String getAccess_token ( ) {
return access_token;
}
public void setAccess_token ( String access_token) {
this . access_token = access_token;
}
public String getExpires_in ( ) {
return expires_in;
}
public void setExpires_in ( String expires_in) {
this . expires_in = expires_in;
}
public String getRefresh_token ( ) {
return refresh_token;
}
public void setRefresh_token ( String refresh_token) {
this . refresh_token = refresh_token;
}
public String getOpenid ( ) {
return openid;
}
public void setOpenid ( String openid) {
this . openid = openid;
}
public String getScope ( ) {
return scope;
}
public void setScope ( String scope) {
this . scope = scope;
}
public String getUnionid ( ) {
return unionid;
}
public void setUnionid ( String unionid) {
this . unionid = unionid;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
创建User类:
package entity ;
public class User {
private String openid;
private String nickname;
private String sex;
private String province;
private String city;
private String country;
private String headimgurl;
private String privilege;
private String unionid;
public User ( ) {
}
public User ( String openid, String nickname, String sex, String province, String city, String
country, String headimgurl, String privilege, String unionid) {
this . openid = openid;
this . nickname = nickname;
this . sex = sex;
this . province = province;
this . city = city;
this . country = country;
this . headimgurl = headimgurl;
this . privilege = privilege;
this . unionid = unionid;
}
@Override
public String toString ( ) {
return "User{" +
"openid='" + openid + '\'' +
", nickname='" + nickname + '\'' +
", sex='" + sex + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
", country='" + country + '\'' +
", headimgurl='" + headimgurl + '\'' +
", privilege='" + privilege + '\'' +
", unionid='" + unionid + '\'' +
'}' ;
}
public String getOpenid ( ) {
return openid;
}
public void setOpenid ( String openid) {
this . openid = openid;
}
public String getNickname ( ) {
return nickname;
}
public void setNickname ( String nickname) {
this . nickname = nickname;
}
public String getSex ( ) {
return sex;
}
public void setSex ( String sex) {
this . sex = sex;
}
public String getProvince ( ) {
return province;
}
public void setProvince ( String province) {
this . province = province;
}
public String getCity ( ) {
return city;
}
public void setCity ( String city) {
this . city = city;
}
public String getCountry ( ) {
return country;
}
public void setCountry ( String country) {
this . country = country;
}
public String getHeadimgurl ( ) {
return headimgurl;
}
public void setHeadimgurl ( String headimgurl) {
this . headimgurl = headimgurl;
}
public String getPrivilege ( ) {
return privilege;
}
public void setPrivilege ( String privilege) {
this . privilege = privilege;
}
public String getUnionid ( ) {
return unionid;
}
public void setUnionid ( String unionid) {
this . unionid = unionid;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
接下来继续修改对应的后端:
@GetMapping ( "wxlogin" )
public Object wxlogin ( HttpServletRequest request) {
String code = request. getParameter ( "code" ) ;
System . out. println ( "【临时凭证】code=" + code) ;
String getTokenByCode_url =
"https: / / api. weixin. qq. com/ sns/ oauth2/ access_token?
appid= wxd99431bbff8305a0& secret= 60f 78681d 063590 a469f1b297feff3c4& code= "+
code+ "&grant_type=authorization_code" ;
String tokenString = HttpClientUtil . doGet ( getTokenByCode_url) ;
System . out. println ( tokenString) ;
Token token = JSON. parseObject ( tokenString, Token . class ) ;
String getUserByToken =
"https://api.weixin.qq.com/sns/userinfo?access_token=" + token. getAccess_token ( ) +
"&openid=" + token. getOpenid ( ) ;
System . out. println ( "----------------------" ) ;
String UserString = HttpClientUtil . doGet ( getUserByToken) ;
System . out. println ( "UserString = " + UserString ) ;
User user = JSON. parseObject ( UserString , User . class ) ;
System . out. println ( "微信的用户昵称 = " + user. getNickname ( ) ) ;
System . out. println ( "微信的用户头像 = " + user. getHeadimgurl ( ) ) ;
return null ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
至此微信用户的信息就得到了,比如说"nickname":“king”,其中king就是我微信的名称
当然一些值是0或者是""的,则是我们微信并没有设置的
接下来我们修改对应的注册逻辑:
dao层:
Integer register ( @Param ( "phone" ) String phone, @Param ( "password" ) String
Password , @Param ( "nickname" ) String nickname, @Param ( "headimg" ) String headimg) ;
service层:
Integer register ( String phone, String Password , String nickname, String headimg) ;
@Override
public Integer register ( String phone, String Password , String nickname, String headimg) {
Integer register = userDao. register ( phone, Password , nickname, headimg) ;
return register;
}
web层:
Intege
对应的UserController类的ogin方法:
@GetMapping ( "login" )
public UserDTO login ( String phone, String password, String nickname, String headimg) {
UserDTO userDTO = new UserDTO < > ( ) ;
System . out. println ( phone) ;
System . out. println ( password) ;
System . out. println ( nickname) ;
System . out. println ( headimg) ;
User login = null ;
Integer integer = userService. checkPhone ( phone) ;
if ( integer == 0 ) {
userService. register ( phone, password, nickname, headimg) ;
userDTO. setMessage ( "手机号尚未注册,系统已帮您自动注册,请牢记密码!" ) ;
login = userService. login ( phone, password) ;
} else {
login = userService. login ( phone, password) ;
if ( login != null ) {
userDTO. setState ( 200 ) ;
userDTO. setMessage ( "登录成功" ) ;
} else {
userDTO. setState ( 300 ) ;
userDTO. setMessage ( "登录失败" ) ;
}
}
userDTO. setContent ( login) ;
System . out. println ( login) ;
return userDTO;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
改变后,对应的xml配置文件也进行改变:
< insert id = " register" parameterType = " string" >
insert into user
(name,portrait,phone,password,create_time,update_time)
values
(#{nickname},#{headimg},#{phone},#{password},sysdate(),sysdate())
insert>
至此我们运行对应的测试类TestUser里的register方法:
@Test
public void register ( ) {
Integer integer = userDao. register ( "11544" , "123123" , "1" , "2" ) ;
System . out. println ( integer) ;
}
若数据库有对应的数据,则修改成功
对应的部分前端代码(Header.vue组件里面的代码):
return this . axios. get ( "http://localhost:8002/user/login" , {
params : {
phone : this . phone,
password : this . password,
nickname : "" ,
headimg : "" ,
}
} ) . then ( res => {
至此测试登录,随便写一个没有的,登录后,会自动注册,并登录,然后去数据库看看是否有对应数据,若有,则操作成功
我们在对应的wxlogin方法里添加如下代码:
System . out. println ( "微信的unionid参数值:" + user. getUnionid ( ) ) ;
return "user/login?phone=" + user. getUnionid ( ) + "&password="
+ user. getUnionid ( ) + "&nickname=" + user. getNickname ( ) + "&headimg=" + user. getHeadimgurl ( ) ;
至此启动对应的项目,并在前端扫描二维码进行登录,若返回了对应的数据,且数据库里也有对应数据,则登录成功
且你会发现,对应的url那里前端进行了访问,但后端的对应的请求并没有显示出来
也就是不依靠浏览器的显示了,安全性的确提高
但是对应的使用return会到其他的方法里面去,实际上我们操作该方法后,需要跳转到原来的前端页面
至此对应的wxlogin方法要进行改变,但对应的数据并不会得到,所以我们需要修改WxLoginController类:
package com. lagou. wx ;
import com. alibaba. dubbo. config. annotation. Reference ;
import com. alibaba. fastjson. JSON;
import com. lagou. entity. UserDTO ;
import com. lagou. service. UserService ;
import commons. HttpClientUtil ;
import entity. Token ;
import entity. User ;
import org. springframework. web. bind. annotation. GetMapping ;
import org. springframework. web. bind. annotation. RestController ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. io. IOException ;
@RestController
public class WxLoginController {
@Reference
private UserService userService;
private UserDTO userDTO = null ;
@GetMapping ( "wxlogin" )
public UserDTO wxlogin ( HttpServletRequest request, HttpServletResponse response) throws
IOException {
String code = request. getParameter ( "code" ) ;
System . out. println ( "【临时凭证】code=" + code) ;
String getTokenByCode_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wxd99431bbff8305a0&secret=60f78681d063590a469f1b297feff3c4&code=" + code + "&grant_type=authorization_code" ;
String tokenString = HttpClientUtil . doGet ( getTokenByCode_url) ;
System . out. println ( tokenString) ;
Token token = JSON. parseObject ( tokenString, Token . class ) ;
String getUserByToken = "https://api.weixin.qq.com/sns/userinfo?access_token=" +
token. getAccess_token ( ) + "&openid=" + token. getOpenid ( ) ;
System . out. println ( "----------------------" ) ;
String UserString = HttpClientUtil . doGet ( getUserByToken) ;
System . out. println ( "UserString = " + UserString ) ;
User user = JSON. parseObject ( UserString , User . class ) ;
System . out. println ( "微信的用户昵称 = " + user. getNickname ( ) ) ;
System . out. println ( "微信的用户头像 = " + user. getHeadimgurl ( ) ) ;
System . out. println ( "微信的unionid参数值:" + user. getUnionid ( ) ) ;
userDTO = new UserDTO < > ( ) ;
com. lagou. entity. User login = null ;
Integer integer = userService. checkPhone ( user. getUnionid ( ) ) ;
if ( integer == 0 ) {
userService. register ( user. getUnionid ( ) , user. getUnionid ( ) , user. getNickname ( ) ,
user. getHeadimgurl ( ) ) ;
userDTO. setMessage ( "手机号尚未注册,系统已帮您自动注册,请牢记密码!" ) ;
login = userService. login ( user. getUnionid ( ) , user. getUnionid ( ) ) ;
} else {
login = userService. login ( user. getUnionid ( ) , user. getUnionid ( ) ) ;
if ( login != null ) {
userDTO. setState ( 200 ) ;
userDTO. setMessage ( "登录成功" ) ;
} else {
userDTO. setState ( 300 ) ;
userDTO. setMessage ( "登录失败" ) ;
}
}
userDTO. setContent ( login) ;
response. sendRedirect ( "http://localhost:8080/" ) ;
return null ;
}
@GetMapping ( "checkWXStatus" )
public UserDTO checkWXStatus ( ) {
return userDTO;
}
@GetMapping ( "logout" )
public Object logout ( ) {
userDTO = null ;
return null ;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
对应的前端Header.vue:
created ( ) {
console. log ( JSON . parse ( localStorage. getItem ( "user" ) ) ) ;
this . userDTO = JSON . parse ( localStorage. getItem ( "user" ) ) ;
console. log ( this . userDTO)
if ( this . userDTO != null ) {
this . isLogin = true
} else {
return this . axios. get ( "http://localhost:80/checkWXStatus" ) . then ( res => {
console. log ( 22 )
console. log ( res)
this . userDTO = res. data
if ( this . userDTO!= "" ) {
this . phone = this . userDTO. content. phone
this . password = this . userDTO. content. password
this . login ( )
}
} ) . catch ( err => {
this . $message. error ( "登录失败" )
} )
}
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
logout ( ) {
localStorage. setItem ( "user" , null )
this . isLogin = false
return this . axios. get ( "http://localhost:80/logout" ) . then ( res => {
this . $router. push ( "/" ) ;
window. location. reload ( )
alert ( "已登出" )
} ) . catch ( err => {
this . $message. error ( "登出失败" )
} )
alert ( 1 )
}
我们可以发现,对应的端口都是80(虽然也可以是其他端口),但最好相同,因为他们都是得到和设置值
需要在一个服务器里面操作(可以说,一个端口代表一个服务器的运行)
至此我们扫描二维码进行操作,若有对应的信息(登录那里),则微信登录完成
在前面应该说过,微信的扫描二维码并允许后,可能会出现浏览器的问题(一般是谷歌)
解决二维码在谷歌浏览器的bug :
通过官方js:http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
可以知道,对应的二维码是放在iframe标签下的,但是一般新版本的谷歌可能并没有设置跨域问题
旧版的谷歌却可以(但不一定)也就是如下:
为什么不能让他跨域呢,因为是安全问题,iframe可以内嵌其他网页,而对应的网页我们却无法控制
也就是说,若该网页是恶意的网站,那么对应用户来说是非常危险的,所以一般会让他不能跨域
sandbox包含的属性及作用:
加上 sandbox="allow-scripts allow-top-navigation allow-same-origin"属性
即可解决官方js:http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
由于基本无法修改微信服务器上的js文件,所以我们将js代码放在本地并进行修改(复制上面官方的js):
created ( ) {
! ( function ( a, b, c ) {
function d ( a ) {
var c = "default" ;
a. self_redirect === ! 0
? ( c = "true" )
: a. self_redirect === ! 1 && ( c = "false" ) ;
var d = b. createElement ( "iframe" ) ,
e =
"https://open.weixin.qq.com/connect/qrconnect?appid=" +
a. appid +
"&scope=" +
a. scope +
"&redirect_uri=" +
a. redirect_uri +
"&state=" +
a. state +
"&login_type=jssdk&self_redirect=" +
c +
"&styletype=" +
( a. styletype || "" ) +
"&sizetype=" +
( a. sizetype || "" ) +
"&bgcolor=" +
( a. bgcolor || "" ) +
"&rst=" +
( a. rst || "" ) ;
( e += a. style ? "&style=" + a. style : "" ) ,
( e += a. href ? "&href=" + a. href : "" ) ,
( d. src = e) ,
( d. frameBorder = "0" ) ,
( d. allowTransparency = "true" ) ,
( d. sandbox = "allow-scripts allow-top-navigation allow-same-origin" ) ,
( d. scrolling = "no" ) ,
( d. width = "300px" ) ,
( d. height = "400px" ) ;
var f = b. getElementById ( a. id) ;
( f. innerHTML = "" ) , f. appendChild ( d) ;
}
a. WxLogin = d;
} ) ( window, document) ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
我们直接将该代码放到对应的created()的钩子函数里面,使得操作我们自己的js,而不使用官方的
将对应的引入的注释:
组件也不同声明了:
components : {
} ,
对应的html也注释掉:
实际上对应的组件操作也是封装了二维码js的执行,我们可以直接自己定义来保存二维码:
< div id = " wxLoginForm" > div>
接下来我们找到对应的方法,因为组件操作了对应二维码的生成,和一些属性
但我们自己定义的,需要自己进行操作,这里当然是默认没有的,因为什么都没有操作,看如下代码:
goToLoginWX ( ) {
document. getElementById ( "loginForm" ) . style. display= "none"
this . $nextTick ( function ( ) {
this . createCode ( ) ;
} ) ;
} ,
createCode ( ) {
var obj = new WxLogin ( {
id : "wxLoginForm" ,
appid : "wxd99431bbff8305a0" ,
scope : "snsapi_login" ,
redirect_uri : "http://www.pinzhi365.com/wxlogin" ,
} )
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
至此,浏览器的问题就解决了,我们发现,对应的js实际上就是直接设置iframe的属性
可以得知,浏览器可能会给该标签默认设置其他不跨域的属性,所以需要我们手动设置,使得不操作默认
若我们需要修改二维码的样式,可以添加如下代码:
.impowerBox .qrcode { width : 200px; }
.impowerBox .title { display : none; }
.impowerBox .info { width : 200px; }
.status_icon { display : none} cs
.impowerBox .status { text-align : center; }
但是实际上要操作该样式,在对应的参数下面基本不能这样的写,因为{}不能对json格式不对,如:
href : "
. impowerBox . qrcode { width : 200px; }
. impowerBox . title { display : none; }
. impowerBox . info { width : 200px; }
. status_icon { display : none} cs
. impowerBox . status { text- align: center; }
"
那么如何使得可以操作对应的样式呢,我们可以在对应的WxLogin对象中(方法里的操作)
直接传递href值,由于该值会被请求的地址服务器解析(二维码的显示的请求)
使得进行对应的操作显示,而该解析我们需要对应的加密,这是规定的,因为对应地址服务的解析时,会解密进行操作
我们用站长工具对样式代码进行base64加密:http://tool.chinaz.com/Tools/Base64.aspx
LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDIwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30=
这时,对应的js代码是:
var obj = new WxLogin ( {
id : "wxLoginForm" ,
appid : "wxd99431bbff8305a0" ,
scope : "snsapi_login" ,
redirect_uri : "http://www.pinzhi365.com/wxlogin" ,
href : "data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDIwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30="
} )
这时我们再次看看二维码,发现二维码变小的,至此样式也操作成功
我们操作完了微信的登录,接下来我们操作微信的支付:
创建二维码:
安装 qrcodejs2 (注意:安装的是qrcodejs2,不要安装qrcode —> 会报错)
npm install qrcodejs2 --save
在Course.vue组件的页面中引入:
< el-dialog :visible.sync = " dialogFormVisible" style = " width : 800px; margin : 0px auto; " >
< h1 style = " font-size : 30px; color : #00B38A" > 微信扫一扫支付 h1>
< div id = " qrcode" style = " width : 210px; margin : 20px auto; " > div>
el-dialog>
data ( ) {
return {
comment : null ,
activeName : "intro" ,
course : null ,
totalLessons : 0 ,
commentList : null ,
isLogin : false ,
isBuy : false ,
user : null ,
myCourseList : [ ] ,
dialogFormVisible : false
} ;
< script>
import Header from "./Header/Header" ;
import Footer from "./Footer/index" ;
import QRCode from 'qrcodejs2' ;
export default {
name : "Course" ,
components : {
Header,
Footer,
QRCode
} ,
这里有个问题,前面的操作问题
if ( this . user != null ) {
this . isLogin = true
if ( this . course!= null ) {
this . getMyCourseList ( )
}
this . getComment ( ) ;
}
buy ( courseid ) {
this . dialogFormVisible = true ;
this . $nextTick ( function ( ) {
this . createCode ( )
} ) ;
} ,
createCode ( ) {
document. getElementById ( "qrcode" ) . innerHTML = "" ;
let qrcode = new QRCode ( "qrcode" , {
width : 200 ,
height : 200 ,
text : "我爱你中国"
} ) ;
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
至此,点击立即购买,发现,对应的二维码出来了,用微信扫描,就会出现吗设置的信息"我爱你中国",至此二维码显示操作完毕
准备工作:
名词介绍:
如果获得这些信息,需要注册认证公众号,费用300元/次
获取认证的流程 :
注册公众号(类型:服务号) ,或者微信开发平台操作的网站(如企业),这里就操作公众号了
根据营业执照类型选择以下主体注册:个体工商户 | 企业/公司 | 政府 | 媒体 | 其他类型
认证公众号:
公众号认证后才基本可以去申请微信支付:300元/次
提交材料申请微信支付 :
登录公众平台,左侧菜单【微信支付】,开始填写资料等待审核,审核时间1~5工作日这里需要提交的资料有营业执照
开户成功,登录商户平台进行验证:
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证
在线签署协议:
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效
查看自己的公众号的参数 :
测试的数据,一般不能操作,下面只是用来看的,让你知道需要哪些数据
即当你使用下面的信息时,一个返回的数据中会说明签名错误,因为没有对应数据(如appid,企业一般是corpid,对应于appid)
他们一般是微信公众号和企业微信,都是微信,因为使用微信支付总得是微信吧
所以到这里,后面的可以进行了解,当你有对应企业或者微信公众号的这些数据时(自己进行申请)
可以回到这里再次查看进行操作:
public class PayConfig {
public static String appid = "wx8397f8696b538317" ;
public static String partner = "1473426802" ;
public static String partnerKey = "8A627A4578ACE384017C997F12D68B23" ;
public static String notifyurl = "http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify" ;
}
支付流程:
工具介绍:
SDK(软开开发工具包,也有对应的其他介绍,如API列表):
https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=11_1
如果不是maven项目,一般需要下载对应的jar包,若是maven项目,则引入依赖即可
对应的依赖:
< dependency>
< groupId> com.github.wxpay groupId>
< artifactId> wxpay-sdk artifactId>
< version> 0.0.3 version>
dependency>
主要使用sdk中的三个功能:
获取随机字符串(生成订单编号):
WXPayUtil . generateNonceStr ( ) ;
将map转换成xml字符串(自动添加签名,对应给微信支付的信息一般需要是xml信息):
WXPayUtil . generateSignedXml ( map, partnerKey) ;
将xml字符串转换整map:
WXPayUtil . xmlToMap ( result) ;
JFinal 框架:
JFinal 是基于Java 语言的极速 web 开发框架
其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展
基本可以取代HttpClient
对应的依赖:
< dependency>
< groupId> com.jfinal groupId>
< artifactId> jfinal artifactId>
< version> 3.5 version>
dependency>
构建二维码 :
外面需要将对应的支付连接放在二维码里面
在Course.vue组件里面:
createCode ( ) {
document. getElementById ( "qrcode" ) . innerHTML = "" ;
this . axios. get ( "http://localhost:80/createCode" , {
params : {
courseid : this . course. id,
coursename : this . course. courseName,
price : this . course. discounts
}
} ) . then ( res => {
console. log ( res)
let qrcode = new QRCode ( "qrcode" , {
width : 200 ,
height : 200 ,
text : res
} ) ;
} ) . catch ( err => {
this . $message. error ( "生成二维码失败" )
} )
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
在web层项目下的commons包下创建实体类PayConfig:
package commons ;
public class PayConfig {
public static String appid = "wx8397f8696b538317" ;
public static String partner = "1473426802" ;
public static String partnerKey = "8A627A4578ACE384017C997F12D68B23" ;
public static String notifyurl = "http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify" ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
该类先放在这里,后面有可能会再次进行操作(如修改数值,虽然并不会)
在对应的web层项目中的wx包下,创建WxPayController类:
package com. lagou. wx ;
import com. github. wxpay. sdk. WXPayConfig ;
import com. github. wxpay. sdk. WXPayUtil ;
import com. jfinal. kit. HttpKit ;
import commons. PayConfig ;
import org. springframework. web. bind. annotation. GetMapping ;
import org. springframework. web. bind. annotation. RestController ;
import java. util. HashMap ;
import java. util. Map ;
@RestController
public class WxPayController {
@GetMapping ( "createCode" )
public Object createCode ( String courseid, String coursename, String price) throws Exception {
Map < String , String > map = new HashMap < > ( ) ;
map. put ( "appid" , PayConfig . appid) ;
map. put ( "mch_id" , PayConfig . partner) ;
map. put ( "nonce_str" , WXPayUtil . generateNonceStr ( ) ) ;
map. put ( "body" , coursename) ;
map. put ( "out_trade_no" , WXPayUtil . generateNonceStr ( ) ) ;
map. put ( "total_fee" , price) ;
map. put ( "spbill_create_ip" , "127.0.0.1" ) ;
map. put ( "notify_url" , PayConfig . notifyurl) ;
map. put ( "trade_type" , "NATIVE" ) ;
System . out. println ( "商户信息:" + map) ;
String xml = WXPayUtil . generateSignedXml ( map, PayConfig . partnerKey) ;
System . out. println ( "商户的xml信息:" + xml) ;
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder" ;
String post = HttpKit . post ( url, xml) ;
System . out. println ( post) ;
Map < String , String > map1 = WXPayUtil . xmlToMap ( post) ;
System . out. println ( map1) ;
return map1;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
对应的Course.vue:
let qrcode = new QRCode ( "qrcode" , {
width: 200 ,
height: 200 ,
text: res. data. code_url
} ) ;
这时打开你的手机,扫描对应的二维码,我的出现如下:
100分也就是1.00元
至此若出现如上的操作,那么说明链接正确,即操作完成
但是我们会发现有对应的乱码,对应解决方法:
coursename = new String ( coursename. getBytes ( "ISO-8859-1" ) , "UTF-8" ) ;
与前面的操作(好像是上一章的操作)一样,操作完成后,那么应该会出现如下(我这里是):
至此中文乱码解决
检查支付状态:
在这之前,我们需要修改一下对应后端的代码:
Map < String , String > map1 = WXPayUtil . xmlToMap ( post) ;
map1. put ( "orderId" , map. get ( "out_trade_no" ) ) ;
对应的前端(Course.vue):
createCode ( ) {
document. getElementById ( "qrcode" ) . innerHTML = "" ;
this . axios. get ( "http://localhost:80/createCode" , {
params : {
courseid : this . course. id,
coursename : this . course. courseName,
price : this . course. discounts
}
} ) . then ( res => {
let qrcode = new QRCode ( "qrcode" , {
width : 200 ,
height : 200 ,
text : res. data. code_url
} ) ;
this . axios. get ( "http://localhost:80/checkOrderStatus" , {
params : {
orderId : res. data. orderId
}
} ) . then ( res => {
} ) . catch ( err => {
this . $message. error ( "查询订单状态失败" )
} )
} ) . catch ( err => {
this . $message. error ( "生成二维码失败" )
} )
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
对应的后端:
@GetMapping ( "checkOrderStatus" )
public Object createCode ( String orderId) throws Exception {
Map < String , String > map = new HashMap < > ( ) ;
map. put ( "appid" , PayConfig . appid) ;
map. put ( "mch_id" , PayConfig . partner) ;
map. put ( "out_trade_no " , orderId) ;
map. put ( "nonce_str" , WXPayUtil . generateNonceStr ( ) ) ;
String xml = WXPayUtil . generateSignedXml ( map, PayConfig . partnerKey) ;
String url = "https://api.mch.weixin.qq.com/pay/orderquery" ;
String post = HttpKit . post ( url, xml) ;
Map < String , String > map1 = WXPayUtil . xmlToMap ( post) ;
return map1;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
至此,我们点击二维码,不需要扫描,执行对应的方法
在对应的map集合中可以获得对应的状态(一般是订单未支付,一般保存在trade_state_desc字段)
对于状态的判断,我们一般需要在后端进行如下的编写,前面分析过
我们的支付成功与否,对应修改对应的编号的状态,所以我们需要循环获取:
String url = "https://api.mch.weixin.qq.com/pay/orderquery" ;
long l = System . currentTimeMillis ( ) ;
while ( true ) {
String post = HttpKit . post ( url, xml) ;
Map < String , String > map1 = WXPayUtil . xmlToMap ( post) ;
if ( map1. get ( "trade_state" ) . equalsIgnoreCase ( "SUCCESS" ) ) {
return map1;
}
if ( System . currentTimeMillis ( ) - l> 30 * 1000 ) {
return map1;
}
Thread . sleep ( 3000 ) ;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
上面如果在30秒内支付成功,那对应前端界面会显示出来你已经支付
但是如果30秒内没有支付或者没有支付成功,前端界面会显示未支付
一般的我们会重新生成二维码,或者对应过期时间设置为二维码的过期时间
防止在前端返回未支付的情况下,可以支付,操作对应数据的不合理,甚至会购买不上
对应的前端:
< el-dialog :visible.sync = " dialogFormVisible" style = " width : 800px; margin : 0px auto; " >
< h1 style = " font-size : 30px; color : #00B38A" > 微信扫一扫支付 h1>
< div id = " qrcode" style = " width : 210px; margin : 20px auto; " > div>
< h2 id = " statusText" > h2>
el-dialog>
this . axios. get ( "http://localhost:80/checkOrderStatus" , {
params : {
orderId : res. data. orderId
}
} ) . then ( res => {
console. log ( res)
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
}
} ) . catch ( err => {
this . $message. error ( "查询订单状态失败" )
} )
至此可以扫描二维码,进行支付,发现,出现对应的样式,显示支付成功
当你再次扫描时一般会提示已经操作(因为一个已经支付的二维码一般是会销毁的,可能有些情况下会继续支付)
至此,微信支付完成
接下来操作关闭二维码:
< el-dialog :visible.sync = " dialogFormVisible" style = " width : 800px; margin : 0px auto; " >
< h1 style = " font-size : 30px; color : #00B38A" > 微信扫一扫支付 h1>
< div id = " qrcode" style = " width : 210px; margin : 20px auto; " > div>
< h2 id = " statusText" > h2>
< p id = " closeTest" > p>
el-dialog>
data ( ) {
return {
comment : null ,
activeName : "intro" ,
course : null ,
totalLessons : 0 ,
commentList : null ,
isLogin : false ,
isBuy : false ,
user : null ,
myCourseList : [ ] ,
dialogFormVisible : false ,
time : null ,
} ;
} ,
console. log ( res)
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
this . saveOrder ( ) ;
let s = 3 ;
this . closeQRForm ( s)
}
修改web层项目的对应的OrderController类的saveOrder方法:
@GetMapping ( "saveOrder/{userid}/{courseid}/{acid}/{stype}" )
public String saveOrder ( String orderNo, @PathVariable ( "userid" ) String userid,
@PathVariable ( "courseid" ) String courseid,
@PathVariable ( "acid" ) String acid, @PathVariable ( "stype" ) String stype) {
orderService. saveOrder ( orderNo, userid, courseid, acid, stype) ;
return orderNo;
}
给data加上对应的订单属性(这里就不全部写出来了):
orderNo : "" ,
let qrcode = new QRCode ( "qrcode" , {
width : 200 ,
height : 200 ,
text : res. data. code_url
} ) ;
this . orderNo = res. data. orderId;
this . axios. get ( "http://localhost:80/checkOrderStatus" , {
params : {
orderId : this . orderNo
}
saveOrder ( ) {
this . axios. get ( "http://localhost:8002/order/saveOrder/" + this . user. content. id+
"/" + this . course. id+ "/" + this . course. id+ "/1" , {
params : {
orderNo : this . orderNo
}
} ) . then ( res => {
console. log ( res)
} ) . catch ( err => {
this . $message. error ( "保存订单失败" )
} )
} ,
closeQRForm ( s ) {
let that = this ;
that. time = setInterval ( function ( ) {
document. getElementById ( "closeTest" ) . innerHTML = "(" + s-- + ")秒后关闭本窗口"
if ( s== 0 ) {
clearInterval ( that. time)
that. dialogFormVisible = false
that. isBuy = true ;
}
} , 1000 )
} ,
但是我们发现,对应的数据不对,因为订单中的status并不是20,使得还是试看,而不是播放
若还是播放,那么应该是设置了购买,或者已经购买
已经购买的话,一般保存订单会失败,即后端返回异常信息,使得前端报错
所以我们需要进行修改:
saveOrder ( ) {
this . axios. get ( "http://localhost:8002/order/saveOrder/" + this . user. content. id+
"/" + this . course. id+ "/" + this . course. id+ "/1" , {
params : {
orderNo : this . orderNo
}
} ) . then ( res => {
this . axios. get ( "http://localhost:8002/order/updateOrder/" + this . orderNo+ "/20" ) . then ( res => {
console. log ( res)
} ) . catch ( err => {
this . $message. error ( "设置订单状态失败" )
} )
} ) . catch ( err => {
this . $message. error ( "保存订单失败" )
} )
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
实际上对应的保存订单在查询支付状态里面,假如查询不了怎么办,那么订单也就不会生成,所以说
对应的订单应该在状态之前,而不会受任何影响,且是创建的订单
this . orderNo = res. data. orderId;
this . saveOrder ( ) ;
this . axios. get ( "http://localhost:80/checkOrderStatus" , {
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
}
let s = 3 ;
this . closeQRForm ( s)
那么对应的保存订单中的设置20,就要进行位置变化了:
console. log ( res)
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
this . axios. get ( "http://localhost:8002/order/updateOrder/" + this . orderNo+ "/20" ) . then ( res => {
console. log ( res)
} ) . catch ( err => {
this . $message. error ( "设置订单状态失败" )
} )
}
我们可以将对应的更新状态封装成一个函数:
updateOrder ( ss ) {
this . axios. get ( "http://localhost:8002/order/updateOrder/" + this . orderNo+ "/" + ss) . then ( res => {
console. log ( res)
} ) . catch ( err => {
this . $message. error ( "设置订单状态失败" )
} )
那么原来的可以这样写:
console. log ( res)
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
this . updateOrder ( 20 )
< i style= 'color: #00 B38A' class = 'el- icon- success'> < / i> 未支付"
}
至此可以进行测试,每次的购买后,去已购那里看看发现的确在那里了,且对应都是播放
但是这里有一个问题,如果是未支付的情况下,可能是有相同的保存,那么会报错
通过修改后端代码可以解决问题(在文章最后面)
若不修改后端代码,如何解决:
在查询支付状态里面可以解决此问题(位置没改变之前,就是这样,只是回来了而已)
因为和状态的设置是一起的(且是支付成功后的操作)
所以为了解决此问题,外面还是将对应的保存订单放回到对应的判断里面:
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
this . saveOrder ( ) ;
< i style= 'color: #00 B38A' class = 'el- icon- success'> < / i> 未支付"
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
saveOrder ( ) {
this . axios. get ( "http://localhost:8002/order/saveOrder/"
+ this . user. content. id+ "/" + this . course. id+ "/" + this . course. id+ "/1" , {
params: {
orderNo: this . orderNo
}
} ) . then ( res = > {
this . updateOrder ( 20 )
} ) . catch ( err = > {
this . $message. error ( "保存订单失败" )
} )
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
注意:这只是测试,实际情况下,外面需要修改对应的后端代码使得进行判断,而不是修改前端
因为该保存的订单也基本固定了20的状态,或者也可以将对应的方法加上参数,如:
this . saveOrder ( 20 ) ;
saveOrder ( ss ) {
this . axios. get ( "http://localhost:8002/order/saveOrder/"
+ this . user. content. id+ "/" + this . course. id+ "/" + this . course. id+ "/1" , {
params : {
orderNo : this . orderNo
}
} ) . then ( res => {
this . updateOrder ( ss)
} ) . catch ( err => {
this . $message. error ( "保存订单失败" )
} )
} ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
至此微信的操作完成
后端代码(修改后,就可以将保存方法放在查询状态外面,状态修改方法放在查询状态里面,他们都是单独的):
添加部分对应的OrderDap.xml:
< select id = " getOrder" resultMap = " UserCourseOrderMap" >
select * from user_course_order where user_id = #{user_id}
and course_id = #{course_id}
and source_type = #{source_type}
select>
对应的接口:
UserCourseOrder getOrder ( @Param ( "user_id" ) String user_id,
@Param ( "course_id" ) String course_id,
@Param ( "source_type" ) String source_type) ;
对应的service层的OrderServiceImpl实现类的saveOrder方法:
@Override
public void saveOrder ( String orderNo, String user_id,
String course_id, String activity_course_id, String source_type) {
UserCourseOrder order = orderDao. getOrder ( user_id, course_id, source_type) ;
System . out. println ( order) ;
if ( order == null ) {
orderDao. saveOrder ( orderNo, user_id, course_id, activity_course_id, source_type) ;
}
}
通过后端代码的编写,使得对应操作完成
这时候的前端可以是:
this . saveOrder ( ) ;
this . axios. get ( "http://localhost:80/checkOrderStatus" , {
params : {
orderId : this . orderNo
}
} ) . then ( res => {
console. log ( res)
if ( res. data. trade_state== "SUCCESS" ) {
document. getElementById ( "statusText" ) . innerHTML=
" 支付成功"
this . updateOrder ( 20 )
至此,微信的登录和微信的支付都操作完毕
但是要注意:除了在课程里购买,其他的地方的购买代码基本是类似的,即他们都是重复的代码
至此,并没有给出操作,可以自己进行尝试,还有些细节问题,可以自己进行查看,比如buy()方法,好像固定是7的参数
注意:当你的localhost被设置其他域名,虽然前端会执行,但还是默认本机,并不会执行对应ip
但是一些情况下,并不会显示,如富文本编辑器和对应的检查(有元素,网络,等等的)
说到富文本编辑器,接下来说明一下,如何在vue里操作吧:
很简单的步骤:
npm install mavon-editor --save
< template>
< div>
< mavon-editor
@save = " saveDoc"
@change = " updateDoc"
ref = " editor"
v-model = " doc" >
mavon-editor>
< button @click = " s" > sss button>
div>
template>
< script>
import { mavonEditor} from "mavon-editor" ;
import "mavon-editor/dist/css/index.css" ;
export default {
name : "jj" ,
components : { mavonEditor} ,
data ( ) {
return {
doc : "1"
} ;
} ,
methods : {
s ( ) {
alert ( 1 )
} ,
updateDoc ( markdown, html ) {
console. log ( "markdown内容: " + markdown) ;
console. log ( "html内容:" + markdown) ;
} ,
saveDoc ( markdown, html ) {
console. log ( "markdown内容:" + markdown) ;
console. log ( "html内容:" + html) ;
}
}
}
script>
< style>
style>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
至此操作完成,简单吧