(spring boot微信jsapi支付) 话不多说,撸起袖子就是干,下面上源码
pom.xml
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
## 配置文件 resources/wx.properties
#未知是否再支付的时候必传
pay.appSecret=你自己的appSecret
pay.appID=你自己的appID
#必传
pay.mchID=你自己的mchID
#必传,但不参与签名
pay.key=自己的key
#随机数 必传
pay.nonceStr=
#必传,支付成功后的通知地址,通知url必须为外网可访问的url,不能携带参数。
pay.notifyUrl=http://***********/pay/hasOrder
#必传,终端IP支持IPV4和IPV6两种格式的IP地址。用户的客户端IP,这里测试就写死
pay.spbillCreateIp= 127.0.0.1
#支付方式
pay.tradeType=JSAPI
#设备信息,非必须
pay.deviceInfo=WEB
#JSAPI方式必传,只是这里用测试测试就写死
pay.openid=oPgg30mSXJTvd372-pgnyb1M9aEI
#HTTP(S) 连接超时时间,单位毫秒
pay.httpConnectTimeoutMs=180000
#HTTP(S) 读数据超时时间,单位毫秒
pay.httpReadTimeoutMs=180000
#证书路径 非必传
pay.certPath=E:/******
## 配置类
import com.github.wxpay.sdk.WXPayConfig;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
@Data
@Component
@PropertySource(value = "classpath:wx.properties")
@ConfigurationProperties(prefix = "pay")
public class Pay implements WXPayConfig{
private String appSecret;
// 公众账号ID
private String appID;
// 商户号
private String mchID;
// 设备号
private String deviceInfo;
// 商户的key【API密匙】
private String key;
// api请求地址
//private String url;
// 服务器异步通知页面路径
private String notifyUrl;
private String spbillCreateIp;
private String tradeType;
private String openid;
/**
* HTTP(S) 连接超时时间,单位毫秒
*/
private int httpConnectTimeoutMs;
/**
* HTTP(S) 读数据超时时间,单位毫秒
*/
private int httpReadTimeoutMs;
/**
* API证书绝对路径
*/
private String certPath;
private String weChatRedirectUrl;
@Override
public InputStream getCertStream() {
File certFile = new File(certPath);
InputStream inputStream = null;
try {
inputStream = new FileInputStream(certFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return inputStream;
}
@Override
public String getKey() {
// if (useSandbox) {
// return sandboxKey;
// }
return key;
}
}
## service(WechatPayService)
public interface WechatPayService {
ResResult goWeChatPay(String money,String des, HttpServletRequest request) throws Exception;
String reply(HttpServletRequest request, HttpServletResponse response) throws IOException;
}
## 上impl(WechatPayServiceImpl)之前还是先上一个获取IP的工具类和返回工具类( ResResult)
### ResResult
@Data
public class ResResult {
private int code=200;
private Object data;
private String description="请求成功!";
public ResResult() {
}
public ResResult ok(){
return this;
}
public ResResult data(Object data){
this.data=data;
return this;
}
public ResResult err(int code,Object data,String des){
this.code=code;
this.data=data;
this.description=des;
return this;
}
public ResResult err(int code,Object data){
this.code=code;
this.data=data;
this.description="请求失败!";
return this;
}
public ResResult err(Object data,String des){
this.code=500;
this.data=data;
this.description=des;
return this;
}
static class Nei{
private static ResResult resResult=new ResResult();
}
}
### GetRealyIp
import javax.servlet.http.HttpServletRequest;
public class GetRealyIp {
public static String getIPAddress(HttpServletRequest request) {
String ip = null;
//X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
//有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
//还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
## impl(WechatPayServiceImpl)
@Service
public class WechatPayServiceImpl implements WechatPayService {
@Autowired
private Pay pay;
//这个是你你们本地的订单service,你们可以删除
@Autowired
private Orderservice orderservice;
//这个是你你们本地的订单service,你们可以删除
@Autowired
private CoreUserService userService;
public ResResult goWeChatPay(String money,String des, HttpServletRequest request) throws Exception {
//new一个微信支付,实际开发中最好使用单例模式
WXPay wxPay = new WXPay(pay);
//订单实体,和你们的不同,你们自己换,我使用的是jpa,id是uaa自增长,我这样写的目的是
//在下边能使用订单的id做一些事
Orders order=new Orders();
order.setCoreUser(user);
order=orderservice.save(order);
String outTradeNo= order.getId()
.replaceAll("-","0")
.substring(5);
LocalDateTime time_start= LocalDateTime.now();
String substring = time_start
.toString()
.substring(0, time_start.toString().lastIndexOf("."));
substring=substring
.replaceAll("-","")
.replaceAll("T","")
.replaceAll(":","")
.substring(0,14);
Map<String, String> params = new HashMap<String, String>();
params.put("notify_url", pay.getNotifyUrl());
params.put("device_info",pay.getDeviceInfo());
params.put("total_fee", String.valueOf(money));
params.put("trade_type",pay.getTradeType());
params.put("body", des);//商品描述
params.put("openid", user.getOpenId());
params.put("out_trade_no", outTradeNo);
params.put("spbill_create_ip", GetRealyIp.getIPAddress(request));
params.put("time_start",String.valueOf(Long.parseLong(substring)));
//加上下边这个东西好像报错,报一个啥啥一分钟,啥啥其他不能低于5分钟,忘记了,你们自行研究,
//这里我是没搞好的,你们谁搞好了帮忙通知一哈我(微信:gdc1997i),谢谢
// long expire = Long.parseLong(substring)+240;
// params.put("time_expire", String.valueOf(expire));
//这下边也是我的订单实体的,你们自己换成你们自己的
order.setWxOutTradeNo(outTradeNo);
order.setOrderName(des);
orderservice.save(order);
System.out.println("out_trade_no:"+ outTradeNo);
System.out.println("time_start:"+Long.parseLong(substring));
Map<String, String> resultMap = null;
try {
resultMap = wxPay.unifiedOrder(params);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("--------------------------------------------------------------------------------");
System.out.println("return_code:"+resultMap.get("result_code"));
System.out.println("result_code:"+resultMap.get("result_code"));
System.out.println("appid:"+resultMap.get("appid"));
System.out.println("mch_id:"+resultMap.get("mch_id"));
System.out.println("device_info:"+resultMap.get("device_info"));
System.out.println("nonce_str:"+resultMap.get("nonce_str"));
System.out.println("sign:"+resultMap.get("sign"));
System.out.println("err_code:"+resultMap.get("err_code"));
System.out.println("err_code_des:"+resultMap.get("err_code_des"));
System.out.println("--------------------------------------------------------------------------------");
if ("FAIL".equals(resultMap.get("return_code"))) {
//这里,我是如果预下单不成功,就删除刚才新建的order
orderservice.delete(order);
System.out.println("FAIL");
return new ResResult().err(500,resultMap);
}else if ("SUCCESS".equals(resultMap.get("result_code"))) {
System.out.println("trade_type:"+resultMap.get("trade_type"));
System.out.println("prepay_id:"+resultMap.get("prepay_id"));
System.out.println("code_url:"+resultMap.get("code_url"));
Map<String,String> map=new HashMap<>();
map.put("appId",resultMap.get("appid"));
//微信那边要求时间戳只能是十位,我这样干估计有问题,但现在还没遇到,哈哈哈!
String time = String.valueOf(System.currentTimeMillis()).substring(0,10);
System.out.println("timeStamp:"+time);
map.put("timeStamp",new Date().toString());
map.put("nonceStr",WXPayUtil.generateNonceStr());
map.put("package","prepay_id="+resultMap.get("prepay_id"));
//加密方式和之前预下单的一样,预下单没看见传这个是因为它封装好了的,
//可以从下单的那个方法跟着代码就能看见
map.put("signType","MD5");
String sing=WXPayUtil.generateSignature(map,pay.getKey());
map.put("paySign",sing);
//这里返回给前端
return new ResResult().ok().data(map);
}
return null;
}
//这个支付成功是返回通知
@Override
public String reply(HttpServletRequest request, HttpServletResponse response) throws IOException {
String res = "";
InputStream inputStream = request.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 xml = stringBuffer.toString();
//System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息");
InputStream is = null;
try {
is = request.getInputStream();
//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
String RETURN_CODE = "return_code";
Map<String, String> notifyMap = WXPayUtil.xmlToMap(xml);//将微信发的xml转map
if(notifyMap.get("return_code").equals("SUCCESS")){
if(notifyMap.get("result_code").equals("SUCCESS")){
//商户订单号
String ordersSn = notifyMap.get("out_trade_no");
//实际支付的订单金额:单位 分
String amountpaid = notifyMap.get("total_fee");
//将分转换成元-实际支付金额:元
BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);
//如果有需要可以获取
String openid = notifyMap.get("openid");
String trade_type = notifyMap.get("trade_type");
/*以下是自己的业务处理------仅做参考
* 更新order对应字段/已支付金额/状态码
*/
}
}
//告诉微信服务器收到信息了,不要在调用回调action了
//========这里很重要回复微信服务器信息用流发送一个xml即可
res= "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
response.getWriter().write(res);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
}
## controller(WeichatPayController )
@RestController
@RequestMapping("pay")
public class WeichatPayController {
@Autowired
private WechatPayService wechatPayService;
@RequestMapping("/wechatpay")
public ResResult wxUnifiedorder(@RequestParam("money") String money,@RequestParam("des") String des, HttpServletRequest request) throws Exception {
return wechatPayService.goWeChatPay(money,des, request) ;
}
@RequestMapping("hasOrder")
public String reply(HttpServletRequest request,HttpServletResponse response) throws Exception {
response.getWriter().write( wechatPayService.reply(request, response));
return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
}
}
## 前端(我这里是没有前后端分离,引擎使用的是thymeleaf)
### 终于到最后一步了html,新建一个pay.html
==pay.html的位置不必和我的一样,但方式一定一样,内容我就不全写了,把重要的部分干出来==
==immediateRecharge()这个函数就是你点击需要支付产品的时候出发的函数,它就会调起微信的h5支付页面==
//这个必须要加载你的html那
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js" type="text/javascript"></script>
<script type="text/javascript">
function addMoney(money) {
$("#money").text(money);
}
var appId,timeStamp,nonceStr,package,signType,paySign;
function immediateRecharge(){
//money=9990不能是小数必须是整数,它那边还要化成元,基本单位是分,这个只是题外的话,不管他了
var url="/pay/wechatpay?money=9990&des=佐良科技-会员"
$.get(url,function(result) {
if (result.data.code==200){
appId = result.data.appId;
timeStamp = result.data.timeStamp;
nonceStr = result.data.nonceStr;
package = result.data.package;
signType = result.data.signType;
paySign = result.data.paySign;
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady',
onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady',
onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady',
onBridgeReady);
}
} else {
onBridgeReady();
}
}else {
alert("服务器错误")
}
});
}
function onBridgeReady(){
WeixinJSBridge.invoke( 'getBrandWCPayRequest', {
"appId":appId, //公众号名称,由商户传入
"timeStamp":timeStamp, //时间戳,自1970年以来的秒数
"nonceStr":nonceStr, //随机串
"package":package,
"signType":signType, //微信签名方式:
"paySign":paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
console.log('支付成功');
//支付成功后跳转的页面
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
console.log('支付取消');
}else if(res.err_msg == "get_brand_wcpay_request:fail"){
console.log('支付失败');
WeixinJSBridge.call('closeWindow');
}
//使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
});
}
</script>
## spring boot微信支付到此结束!