没看过官方文档先去简单看一下官方文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4
wx支付前端官方文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#4
1.先设置支付目录:登录微信支付商户平台(pay.weixin.qq.com)-->产品中心-->开发配置(我这里都是用的是公司设置好的)
2.设置授权域名,因为我这是测试所以通过小米球的方式注册了一个域名填写(免费) http://ngrok.ciqiuwl.cn/
--------------------------------------- 前面准备工作大概就是这样,下面开始实战 ---------------------------------------
3.引入相关依赖
<dependencies>
<!-- 微信支付需要的jar包(主要是用里面的工具类) -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<!-- web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 导入配置文件处理器,配置文件进行绑定就会有提示 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 热启动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- json转换工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<!-- thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
4.编写配置类(填写自己的)
#小程序ID
pay.appid=xx
#appId对应的接口密码
pay.secret=xx
#域名
pay.DomainNameUrl=xxx
#商户Id
pay.mch_id=xxx
#商户平台设置的密钥key
pay.key=xxx
package com.borui.util;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* 将配置文件中配置得每一个属性得值,映射到这个组件中
*@ConfigurationProperties:告诉springboot将本类中得所有属性和配置文件中相关得配置进行绑定
*prefix = "person" 将配置文件中那个下面得所有属性一一映射
*/
@Component
@PropertySource(value="classpath:pay.properties")
@ConfigurationProperties(prefix = "pay")
//@Configuration //@Configuration指明当前类是一个配置类,代替之前的spring配置文件
public class Pay {
private String appid;
private String secret;
private String DomainNameUrl;
private String mch_id;
private String key;
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getDomainNameUrl() {
return DomainNameUrl;
}
public void setDomainNameUrl(String domainNameUrl) {
DomainNameUrl = domainNameUrl;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
5. https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1(统一下单官方文档)先看文档得知我们必须传的参数有哪些
6.得知openid是必须传的,所以在用户访问公众号的时候得到openid,而我是测试所以通过用户手动授权获取openid
获取openid官方文档、先大概了解下 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
7.用户点击授权获取到openid
<div th:if="${session.openid==null}" style="margin-left:200px;margin-top:50px">
<a
style="width: 500px;height: 60px;font-size: 22px;line-height: 45px"
class="btn btn-primary"
<!-- 微信提供的地址访问就可以(我是采用前端访问) -->
href="http://open.weixin.qq.com/connect/oauth2/authorize?
appid=填写自己的appid&
<!-- 微信将code返回到这个controller路径(前面是我的域名) -->
redirect_uri=http://test.ngrok2.xiaomiqiu.cn/wx/getOpenid&
<!--返回类型,请填写code-->
response_type=code
<!-- 应用授权作用域,snsapi_base 不弹出授权页面,直接跳转,只能获取用户openid
snsapi_userinfo (弹出授权页面,可通过openid也能获取其信息 )
-->
&scope=snsapi_userinfo
&state=STATE#wechat_redirect">
点击授权
</a>
</div>
@RequestMapping("/wx/getOpenid")
public ModelAndView getCode() throws Exception {
//获取code
String code = request.getParameter("code");
//wx提供的地址
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?
appid="+pay.getAppid()+
"&secret="+pay.getSecret()+
"&code="+code+
"&grant_type=authorization_code";
//发送请求
String resp = wxMethods.requestWithCert(url,"POST",6000, 6000, null);
//将数据转换成json格式
JSONObject json = JSONObject.parseObject(resp);
//获取openid
String openid = (String)json.get("openid");
//放到session
request.getSession().setAttribute("openid", openid);
//跳转页面
return new ModelAndView("pay");
}
<!-- pay页面 -->
<script type="text/javascript" src="/bootstrap/js/jquery.min.js"></script>
<!-- 微信官方提供的js -->
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js">
</script>
<div th:if="${session.openid!=null}" style="margin-left:200px;margin-top:50px">
<button type="button" id="btn"
th:url="@{/wx/pay}"
style="width: 500px;height: 60px;font-size: 22px;line-height: 60px"
class="btn btn-success">支付
</button>
</div>
<script type="text/javascript">
$("#btn").click(function(){
var url = $(this).attr("url");
$.ajax({
type:'POST',
url:url,
success:function(res){
wx.config({
debug: false,
appId: res.appId,
timestamp: res.timeStamp,
nonceStr: res.nonceStr,
signature: res.signType,
jsApiList: ['chooseWXPay']
});
//成功执行的方法
wx.ready(function(){
wx.chooseWXPay({
timestamp: res.timeStamp,
nonceStr: res.nonceStr,
package: res.package,
signType: res.signType,
paySign: res.paySign,
success: function (res) {
// location.href="/wx/success";
}
});
});
wx.error(function(res){
alert("支付异常,请联系管理员!")
});
}
})
})
</script>
@RequestMapping("/wx/pay")
@ResponseBody
public Map<String, String> pay() throws Exception {
String openid = (String)request.getSession().getAttribute("openid");
Map<String, String> map = new HashMap<String, String>();
map.put("appid", pay.getAppid());
map.put("body", "JSAPI测试");
map.put("mch_id",pay.getMch_id());
map.put("nonce_str",WXPayUtil.generateNonceStr());
map.put("notify_url",pay.getDomainNameUrl()+"/paySuccess");
map.put("openid",openid);
map.put("out_trade_no", UU.uu());
map.put("spbill_create_ip", UU.getIp(request));
map.put("total_fee", "1");
map.put("trade_type", "JSAPI");
String currentTimeMillis = String.valueOf(System.currentTimeMillis()/1000);
//一次加密并返回xml格式(sign字段需要加密)
//加密规则
//https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
String xml =WXPayUtil.generateSignedXml(map, pay.getKey());
String url1 = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String xm = wxMethods.requestWithCert(url1, "POST", 6000, 6000, xml);
Map<String, String> payMap = new HashMap<String, String>();
@SuppressWarnings("unused")
String prepay_id = "";//预支付id
Map<String, String> map2 = WXPayUtil.xmlToMap(xm);
prepay_id = map2.get("prepay_id");
payMap.put("appId", pay.getAppid());
payMap.put("timeStamp", currentTimeMillis);
payMap.put("nonceStr", WXPayUtil.generateNonceStr());
payMap.put("signType", "MD5");
payMap.put("package", "prepay_id=" + prepay_id);
//二次加密 前端需要paySign这个字段加密
//加密规则
//https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
String paySign = WXPayUtil.generateSignature(payMap, pay.getKey());
payMap.put("paySign", paySign);
return payMap;
}
当用户支付成功后微信会以流的方式通知商家是否支付成功,商家需要进行签名验证后通知微信,否则微信会一直回调此方法
// 支付成功后微信回调此方法
@RequestMapping("/paySuccess")
@ResponseBody
public String paySuccess(HttpServletRequest request, HttpServletResponse response) {
String notifyXml = "";
BufferedReader reader;
String line = null;
String resXml = "";
try {
reader = request.getReader();
while ((line = reader.readLine()) != null) {
notifyXml += line;
}
request.getReader().close();
if (StringUtils.isEmpty(notifyXml)) {
throw new NullPointerException("xml为空");
}
System.out.println(notifyXml);
Map<String, String> xmlToMap = WXPayUtil.xmlToMap(notifyXml);
if (!WXPayUtil.isSignatureValid(xmlToMap, "C5FDE9DE2D29789A81D1BC0F16292048")
|| !"SUCCESS".equals(xmlToMap.get("result_code"))
|| !"SUCCESS".equals(xmlToMap.get("return_code"))) {
System.out.println("today" + "验证签名失败或返回错误结果码");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[FAIL]]></return_msg>" + "</xml> ";
} else {
System.out.println("支付成功");
resXml = "<xml>\n" +
"<return_code><![CDATA[SUCCESS]]></return_code>\n" +
"<return_msg><![CDATA[OK]]></return_msg>\n" +
"</xml>";; // 通知微信
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return resXml;
}
8.上面需要用的工具类
package com.wisdom.cc.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
public class UU {
public static String uu() {
int hashCodeV = UUID.randomUUID().toString().hashCode();
if(hashCodeV < 0) {
hashCodeV = -hashCodeV;
}
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
Date date=new Date();
String format = sdf.format(date);
StringBuilder str=new StringBuilder();
str.append(hashCodeV);
str.append(format);
String strr = str.toString();
return strr;
}
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
ip = request.getRemoteAddr();
}
if(ip.indexOf(",")!=-1){
String[] ips = ip.split(",");
ip = ips[0].trim();
}
return ip;
}
}
package com.wisdom.cc.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class WxMethods {
public static String requestWithCert(String url,String method,int connectTimeoutMs, int readTimeoutMs,String reqBody)throws Exception {
URL httpUrl = new URL(url);
//打开连接 得到 HttpURLConnection对象
HttpURLConnection httpURLConnection = (HttpURLConnection)httpUrl.openConnection();
//setDoOutput()默认是false,需要手动设置为true,然后就可以调用getOutputStream()方法从服务器端获得字节输出流
httpURLConnection.setDoOutput(true);
//设置请求方式
httpURLConnection.setRequestMethod(method);
//设置连接超时时间
httpURLConnection.setConnectTimeout(60000);
//读取数据的超时时间
httpURLConnection.setReadTimeout(60000);
//进行连接
httpURLConnection.connect();
String UTF8 = "UTF-8";
OutputStream outputStream = null;
if(reqBody!=null) {
outputStream = httpURLConnection.getOutputStream();
outputStream.write(reqBody.getBytes(UTF8));
}
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, UTF8));
StringBuffer stringBuffer = new StringBuffer();
String line = null;
while ((line = bufferedReader.readLine()) != null)
stringBuffer.append(line);
String resp = stringBuffer.toString();
bufferedReader.close();
inputStream.close();
if(outputStream!=null) {
outputStream.close();
}
return resp;
}
}
支付一定要在真机测试支付,金额单位是分也就是你输入1实际支付一分钱
我这个前端调起支付方法是v3版本 wx.chooseWXPay v2版本WeixinJSBridge
两张区别可参考 两种js方法发起微信支付
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=odt8ufxh6m4k
See you next time