微信小程序支付前需要统一下单,下单成功,返回json对象。获取json中值拿去支付
微信小程序我是用nui框架,发送一个下单请求:
//下单请求
uni.request({
url: "....",//自己后端链接
data: {
//传一些值,比如金额,商品信息,用户id 啥的
},
success: (res) => {
uni.hideLoading();
let resdata = res.data;
//下单失败
if (resdata.rn_code == "FAIL") {
uni.showToast({
title: "订单异常"
})
return;
}
//下单
if (resdata.rn_code == "SUCCESS") {
uni.showToast({
title: "提交成功"
});
let timeSp = resdata.timeStamp;
let nonceSr = resdata.nonceStr;
let pacKe = resdata.package;
let paySn = resdata.paySign;
this.payFun(timeSp, nonceSr, pacKe,paySn)
}
},
fail: (e) => {
uni.showToast({
title: "网络异常!"
});
}
})
//支付函数
payFun: function(tSp, neStr, pae, paySign) {
// console.log(paySign)
uni.requestPayment({
provider: 'wxpay',
timeStamp: tSp,
nonceStr: neStr,
package: pae,
signType: 'MD5',
paySign: paySign,
success: function(res) {
console.log('success:' + JSON.stringify(res));
let rsMsg = res.errMsg;
if (rsMsg == "requestPayment:ok") {
// this.paySuccess();
uni.showToast({
title: "支付成功",
duration: 2000
});
}
uni.navigateBack();
uni.navigateTo({
url: "../user/my_order?current=0"
})
},
fail: function(err) {
// console.log('fail:' + JSON.stringify(err));
let rsMsg = err.errMsg;
// if(rsMsg=="requestPayment:fail cancel"){
uni.showToast({
title: "未成功支付!",
duration: 2000
});
}
});
},
java两个函数1.统一下单,2.支付成功回调函数:
/**
* 微信小程序-订单-生成
*/
@RequestMapping(value = "/WX_order_generate")
public String WX_order_generate(String openId, HttpServletRequest request)throws Exception {
JSONObject jObject;
String payMy="";
//用户id openId
//获取真实IP
String spbill_IP=IpUtil.getIpAddr(request);
//获取随机订单号
String or_id=WxConfig.getRandomString(32);
//统一下单。。返回JsonObj
jObject = WxConfig.WxXiaDan(or_id,openId,"商品标题",payMy,spbill_IP);
String rnCode= (String) jObject.get("rn_code");//获取返回码
if(rnCode.equals("FAIL")){//失败
return jObject.toString();
}
if(rnCode.equals("SUCCESS")) {//成功
//返回json中数据、传过来的值,保存在自己表里
//int totalInt= userServices.WX_add_order();
//if (totalInt==0){
// jObject.put("rn_code", "FAIL");
//}
}
return jObject.toString();
}
/**
* 微信小程序支付成功回调函数
* @param request
* @param response
* @throws Exception
*/
@RequestMapping(value = "/WX/callback")
public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while((line = br.readLine()) != null){
sb.append(line);
}
br.close();
//sb为微信返回的xml
String notityXml = sb.toString();
String resXml = "";
System.out.println("接收到的报文:" + notityXml);
Map map = PayUtil.doXMLParse(notityXml);
String returnCode = (String) map.get("return_code");
if("SUCCESS".equals(returnCode)){
//验证签名是否正确
Map validParams = PayUtil.paraFilter(map); //回调验签时需要去除sign和空值参数
String validStr = PayUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String sign = PayUtil.sign(validStr, WxConfig.Mch_key, "utf-8").toUpperCase();//拼装生成服务器端验证的签名
// 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了
//根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等
if(sign.equals(map.get("sign"))){
/**此处添加自己的业务逻辑代码start**/
// System.out.println("订单支付的金额:" + map.get("cash_fee"));
// System.out.println("订单支付的用户openid:" + map.get("openid"));
// System.out.println("订单支付状态:" + map.get("result_code"));
// System.out.println("订单编号:" + map.get("nonce_str"));
String orderId = (String) map.get("nonce_str");
String wxOpenId = (String) map.get("openid");
// 需要支付的单位是分 , total_fee需要支付的金额 cash_fee实际支付金额,扣除红包后金额
String total_fee = (String) map.get("total_fee");
int cashFee = Integer.valueOf(total_fee);
String checkFee = String.valueOf(cashFee / 100);
// System.out.println("订单验证的金额:" + checkFee);
/**可以保存修改支付状态信息**/
/**此处添加自己的业务逻辑代码end**/
//通知微信服务器已经支付成功
resXml = "" + " "
+ " " + " ";
} else {
System.out.println("微信支付回调失败!签名不一致");
}
}else{
resXml = "" + " "
+ " " + " ";
}
System.out.println(resXml);
System.out.println("微信支付回调数据结束");
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
微信小程序支付的处理类(核心):
package com.WxPay;
import com.alibaba.fastjson.JSONObject;
import com.toolUtils.DateUtils;
import com.toolUtils.MD5Util;
import com.toolUtils.PayUtil;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class WxConfig {
private static String AppID = "";// 小程序ID
private static String Secret = "";// 小程序 Secret
private static String Mch_id = "";// 商户号
public static String Mch_key = "";// 商户号 key
//支付成功后的自己服务器回调url
private static String notify_url = "https://??????.com/WX/callback";
//签名方式,固定值
private static String SIGNTYPE = "MD5";
//交易类型,小程序支付的固定值为JSAPI
private static String TRADETYPE = "JSAPI";
//微信统一下单接口地址
private static String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public static JSONObject WxXiaDan(String or_id,String openId,String gsTitle,String totalFee ,String spbill_IP ) throws Exception {
String body =gsTitle;// 商品描述
// String notify_url = ""; // 支付成功的回调地址 可访问 不带参数
String nonce_str = or_id;//随机字符串
String out_trade_no = or_id;// 商户订单号
// String total_fee = "1";//总金额 单位为分
String total_fee = totalFee;//总金额 单位为分
// System.out.println("支付的金额:"+total_fee);
// int timestamp = Math.round(new Date().getTime() / 1000); // 当前时间
//获取客户端的ip地址
String spbill_create_ip = spbill_IP;
// IpUtil.getIpAddr(request);
//组装参数,用户生成统一下单接口的签名
Map packageParams = new HashMap<>();
packageParams.put("appid", AppID);
packageParams.put("mch_id", Mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("out_trade_no", out_trade_no);//商户订单号
packageParams.put("total_fee", total_fee);//支付金额,这边需要转成字符串类型,否则后面的签名会失败
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);//支付成功后的回调地址
packageParams.put("trade_type", TRADETYPE);//支付方式
packageParams.put("openid", openId);
String preStr = PayUtil.createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
String mysign = PayUtil.sign(preStr, Mch_key, "utf-8").toUpperCase();
//拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
String xml = "" + "" + AppID + " "
+ ""
+ "" + Mch_id + " "
+ "" + nonce_str + " "
+ "" + notify_url + " "
+ "" + openId + " "
+ "" + out_trade_no + " "
+ "" + spbill_create_ip + " "
+ "" +total_fee + " "
+ "" + TRADETYPE + " "
+ "" + mysign + " "
+ " ";
System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);
//调用统一下单接口,并接受返回的结果
String result = PayUtil.httpRequest(pay_url, "POST", xml);
System.out.println("调试模式_统一下单接口 返回XML数据:" + result);
// 将解析结果存储在HashMap中
Map map = PayUtil.doXMLParse(result);
String return_code = (String) map.get("return_code");//返回状态码
JSONObject jObject = new JSONObject();//返回给小程序端需要的参数
jObject.put("rn_code", return_code);
if(return_code.equals("SUCCESS")){
String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
jObject.put("nonceStr", nonce_str);
jObject.put("package", "prepay_id=" + prepay_id);
Long timeStamp = System.currentTimeMillis() / 1000;
jObject.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
//拼接签名需要的参数
String stringSignTemp = "appId=" + AppID + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp;
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String paySign = PayUtil.sign(stringSignTemp, Mch_key, "utf-8").toUpperCase();
jObject.put("paySign", paySign);
}
jObject.put("appid", AppID);
return jObject;
}
public static String loginRequest(String getCode){
String urlSr="https://api.weixin.qq.com/sns/jscode2session?appid="+AppID+"&secret="+Secret+"&js_code="+getCode+"&grant_type=authorization_code";
String result = PayUtil.httpRequest(urlSr, "POST", null);
System.out.println(result);
return result;
}
public static String getAccess_token() {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + AppID + "&secret=" + Secret;
String accessToken = null;
try {
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet
.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
accessToken = new String(jsonBytes, "UTF-8");
System.out.println("获取accessToken值:" + accessToken);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
JSONObject result = JSONObject.parseObject(accessToken);
Map map = JSONObject.toJavaObject(result, Map.class);
System.out.println(map.get("access_token"));
return map.get("access_token");
}
/*
* (随机数+当前时间)再MD5加密,长度在32位以内
* */
public static String getRandomMD5() throws ParseException {
String random = DateUtils.getRandom() + DateUtils.getNewTimeyMd();
String md5DateName = MD5Util.EncryptionMD5(random);
return md5DateName;
}
/*
* 获取随机字符串,传入字符串长度
* */
public static String getRandomString(int length) {
//产生随机数
Random random = new Random();
StringBuffer sb = new StringBuffer();
//循环length次
for (int i = 0; i < length; i++) {
//产生0-2个随机数,既与a-z,A-Z,0-9三种可能
int number = random.nextInt(3);
long result = 0;
switch (number) {
//如果number产生的是数字0;
case 0:
//产生A-Z的ASCII码
result = Math.round(Math.random() * 25 + 65);
//将ASCII码转换成字符
sb.append(String.valueOf((char) result));
break;
case 1:
//产生a-z的ASCII码
result = Math.round(Math.random() * 25 + 97);
sb.append(String.valueOf((char) result));
break;
case 2:
//产生0-9的数字
sb.append(String.valueOf(new Random().nextInt(10)));
break;
}
}
return sb.toString();
}
//商户订单号
public static String getWxPayOrderID() {
Calendar calendar = Calendar.getInstance();
//年份
int year = calendar.get(Calendar.YEAR);
//月份
int mouth = calendar.get(Calendar.MONTH) + 1;
//日期
int day = calendar.get(Calendar.DAY_OF_MONTH);
//小时
int hour = calendar.get(Calendar.HOUR);
//分
int minute = calendar.get(Calendar.MINUTE);
//秒
int second = calendar.get(Calendar.SECOND);
//毫秒
int mSecond = calendar.get(Calendar.MILLISECOND);
String mouthStr = String.valueOf(mouth);
String dayStr = String.valueOf(day);
String hourStr = String.valueOf(hour);
String minuteStr = String.valueOf(minute);
String secondStr = String.valueOf(second);
String mSecondStr = String.valueOf(mSecond);
if (mouth < 10) {
mouthStr = 0 + String.valueOf(mouth);
}
if (day < 10) {
dayStr = 0 + String.valueOf(mouth);
}
if (hour < 10) {
hourStr = 0 + String.valueOf(hour);
}
if (minute < 10) {
minuteStr = 0 + String.valueOf(minute);
}
if (second < 10) {
secondStr =0 + String.valueOf(second);
}
if (mSecond < 10) {
mSecondStr = 00 + String.valueOf(mSecond);
} else if (mSecond >= 10 && mSecond < 100) {
mSecondStr =0 + String.valueOf(second);
}
String currentDate = year + mouthStr + dayStr + hourStr + minuteStr + secondStr + mSecondStr;
return currentDate;
}
/**
* IpUtils工具类方法
* 获取真实的ip地址
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
}