java 微信jsapi支付
抛开微信后台配置不说,只要认真看一下其他的博文就可以解决
微信就是血坑,翻车了好几次,加班通宵还几次,终于解决了
网上都没有完全的代码,demo还有偿,没办法自己研究
js:
$(function () {
zf()
})
function zf(){
$.ajax({
url: rootPath+'a/api/jssdk/wx-config',//调用方法1
type: 'GET',
dataType: 'json',
async:false,
data: {url: location.href.split('#')[0]},//回调地址,这地方测试用
complete:function (xhr,textStatus) {
var data = JSON.parse(xhr.responseText);
console.log(data);
if (data.code+""=="1") {
wx.config({
debug: false,
appId: data.appId,
timestamp:data.timestamp,
nonceStr:data.nonceStr,
signature: data.signature,
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
});
wx.ready(function() {
})
//点击支付按钮时支付
$("#zfs").click(function() {
//获取支付金额
var active = $(".active").attr("id");
console.log(active);
//金额为空时支付一分
if (active == undefined) {
active = "1";
}
//!!!!!!!!!重要!!!支付金额没有小数 是:当前金额*100
$.ajax({
type: "post",
async: false,
url: rootPath + 'a/api/zf',//方法二
data: {
jes: active,//支付金额
url:location.href.split('#')[0]
}, complete: function (xhr, textStatus) {
var datas = JSON.parse(xhr.responseText);
wx.chooseWXPay({
// "appId": datas.appids.toString(), //公众号名称,由商户传入
"timestamp": datas.timestampss, //时间戳,自1970年以来的秒数
"nonceStr": datas.nonceStrss, //随机串
"package": datas.packagess,
"signType": 'MD5', //微信签名方式:
"paySign": datas.paySignss,
complete:function (xhr,textStatus) {
if (xhr.errMsg == "chooseWXPay:ok") {
console.log(res);
window.location.href=rootPath+"a/personal.html";
var uid=$("#uid").val();
$.ajax({
type:"post",
async:false,
url:rootPath+'xxx.xxx.xxx',
data:{
xxx:xxx
}, complete:function (xhr,textStatus) {
var data = JSON.parse(xhr.responseText);
console.log(data);
var ss=data.results;
if (data.code + "" == "1") {
alert("充值成功!");
window.location.href=rootPath+"xxxxx.xxx";
}else {
alert("充值失败!");
}
}
})
}
}
});
}
});
})
}
}
})
}
方法1
@RequestMapping(value="/api/jssdk/wx-config")
@ResponseBody
public Map<String, Object> get_wx_config(@RequestParam(name = "url", required = false) String url ) throws ParseException {
//验证access_token是否在有效期(可以自己写存到数据库)
List<Map<String, Object>> list = loginServers.select_token();
Map<String, Object> sss=list.get(0);
String t1 = sss.get("newDate").toString();
String t2 = getBeforeByHourTime(1);
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String access_token="";
if(sdf.parse(t1).getTime()>sdf.parse(t2).getTime()){
access_token=sss.get("access_token").toString();
}else {
//access_token不在有效期
try {
access_token =weChatJSSDKController.getAccessToken();
} catch (Exception e) {
e.printStackTrace();
}
loginServers.insert_token(access_token);
}
//end 验证access_token是否在有效期
//定义返回配置JSON对象
JSONObject config = new JSONObject();
//获取微信js-sdk开发ticket
//注意,jsapi_ticket,有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket
//请自行保存到缓存
//本示例仅演示获取流程
//Ticket jsApiTicket = weixin.getJsApiTicket();
//获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
//签名算法
//签名生成规则如下:
//参与签名的字段包括noncestr(随机字符串),有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分)。
//对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。
//这里需要注意的是所有参数名均为小写字符。
//对string1作sha1加密,字段名和字段值都采用原始值,不进行URL转义。
//对应我们weixin4j的开发,则已经把签名算法写好了
String noncestr = UUID.randomUUID().toString().substring(0, 15);
Long timestamp = new Date().getTime();
String sjc=timestamp.toString().substring(0, 10);
//注:本链接仅作为演示,实际链接请以自己业务链接为主
//String urls = "http://www.huwangnong.com/index";
Map<String, String> map=new HashMap<>();
map=JsapiTicketUtil.JsapiTicket(access_token);
String string = "jsapi_ticket="+ map.get("ticket").toString() + "&noncestr=" + noncestr + "×tamp=" + sjc + "&url="+ url;
System.out.println(url);
String signature = SHA1.encode(string);
System.out.println(timestamp);
System.out.println(map.get("ticket").toString());
System.out.println(string);
System.out.println(noncestr);
//System.out.println(urls);
System.out.println(signature);
//返回wx.config参数
sss.clear();
sss.put("appId",appids);
sss.put("code", 1);
sss.put("timestamp", sjc);
sss.put("ticket", map.get("ticket").toString());
sss.put("nonceStr", noncestr);
sss.put("signature", signature);
sss.put("url", url);
return sss;
}
方法二:
@Value("${weixin4j.oauth.appids}")
private String appids;
@Value("${weixin4j.oauth.secrets}")
private String appsecret;
@Value("${weixin4j.oauth.mch_id}")
private String mch_id; //商户id
@Value("${weixin4j.oauth.MerchantKey}")
private String MerchantKey; //商户密钥
public Map<String, Object> zf1(String jes,String urls,
HttpServletRequest request, HttpServletResponse response){
//openid 用户的!我登陆时获取存到数据库及session中,便于获取
HttpSession session= request.getSession();
String openid=session.getAttribute("openid").toString();
Map<String, Object> returnMap=new HashMap<String, Object>();
String appId =appids;
String body = "xxx商品支付";
String merchantId = mch_id;
String tradeNo = String.valueOf(new Date().getTime());
String nonceStr1 = createNonceStr();
String notifyUrl = urls;
String openId = openid;
String totalFee = jes;//不可为小数,否则报错
TreeMap<String, String> map = new TreeMap<String, String>();
map.put("appid", appId);
map.put("mch_id", merchantId);
map.put("device_info", "WEB");
map.put("body", body);
map.put("trade_type", "JSAPI");
map.put("nonce_str", nonceStr1);
map.put("notify_url", notifyUrl);
map.put("out_trade_no", tradeNo);
map.put("total_fee", totalFee);
map.put("openid", openId);
String sign = createSign(map);
String xml = "<xml>" +
"<appid>" + appId + "</appid>" +
"<body>" + body +"</body>" +
"<device_info>WEB</device_info>" +
"<mch_id>" + merchantId + "</mch_id>" +
"<nonce_str>" + nonceStr1 + "</nonce_str>" +
"<notify_url>" + notifyUrl +"</notify_url>" +
"<openid>" + openId + "</openid>" +
"<out_trade_no>" + tradeNo + "</out_trade_no>" +
"<total_fee>" + totalFee + "</total_fee>" +
"<trade_type>JSAPI</trade_type>" +
"<sign>" + sign + "</sign>" +
"</xml>";
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String result = null;
result = HttpsUtil.httpsRequestToString(url, "POST", xml);
String reg = "<prepay_id><!\\[CDATA\\[(\\w+)\\]\\]></prepay_id>";
Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(result);
String prepayId = "";
while (matcher.find()) {
prepayId = matcher.group(1);
System.out.println("预支付ID:" + prepayId);
}
Date beijingDate = Calendar.getInstance(Locale.CHINA).getTime();
String datassss=String.valueOf(beijingDate.getTime() / 1000);
String nonceStrss =createNonceStr();
//appId, timeStamp, nonceStr, package, signType
//再一次签名,我看网上的有好多都没说,是后来报错了我搜索才发现要重新签名
String stringAs="appId="+appids+"&nonceStr="+nonceStrss+"&package=prepay_id="+prepayId+"&signType=MD5&timeStamp="+datassss+"&key="+MerchantKey;
System.out.println(stringAs);
String dd = null;
try {
dd = MD5(stringAs).toUpperCase();
} catch (Exception e) {
e.printStackTrace();
}
returnMap.put("paySignss",dd);
returnMap.put("appids",appids);
returnMap.put("timestampss", datassss);
returnMap.put("nonceStrss", nonceStrss);
returnMap.put("packagess", "prepay_id=" + prepayId);
returnMap.put("signTypess", "MD5");
return returnMap;
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成随机数
* <p>算法参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3</p>
* @return 随机数字符串
*/
public static String createNonceStr() {
SecureRandom random = new SecureRandom();
int randomNum = random.nextInt();
return Integer.toString(randomNum);
}
/**
* 生成签名,用于在微信支付前,获取预支付时候需要使用的参数sign
* <p>算法参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3</p>
* @param params 需要发送的所有数据设置为的Map
* @return 签名sign
*/
public String createSign(TreeMap<String, String> params) {
String signValue = "";
String stringSignTemp = "";
String stringA = "";
//获得stringA
Set<String> keys = params.keySet();
for (String key : keys) {
stringA += (key + "=" + params.get(key) + "&");
}
stringA = stringA.substring(0, stringA.length() - 1);
//获得stringSignTemp
stringSignTemp = stringA + "&key=" + MerchantKey;
//获得signValue
signValue = encryptByMD5(stringSignTemp).toUpperCase();
System.out.println("预支付签名:" + signValue);
return signValue;
}
/**
* MD5加密
* @param sourceStr
* @return
*/
public static String encryptByMD5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes("UTF-8"));
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
result = buf.toString();
} catch (NoSuchAlgorithmException e) {
System.out.println(e);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
补发工具类:
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.URL;
public class HttpsUtil {
/**
* 以https方式发送请求并将请求响应内容以String方式返回
*
* @param path 请求路径
* @param method 请求方法
* @param body 请求数据体
* @return 请求响应内容转换成字符串信息
*/
public static String httpsRequestToString(String path, String method, String body) {
if (path == null || method == null) {
return null;
}
String response = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
HttpsURLConnection conn = null;
try {
// 创建SSLConrext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new JEEWeiXinX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述对象中的到SSLSocketFactory
SSLSocketFactory ssf = sslContext.getSocketFactory();
System.out.println(path);
URL url = new URL(path);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
//设置请求方式(git|post)
conn.setRequestMethod(method);
//有数据提交时
if (null != body) {
OutputStream outputStream = conn.getOutputStream();
outputStream.write(body.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
inputStream = conn.getInputStream();
inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
response = buffer.toString();
} catch (Exception e) {
} finally {
if (conn != null) {
conn.disconnect();
}
try {
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
} catch (IOException execption) {
}
}
return response;
}
}
package com.ruoyi.system.util;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
class JEEWeiXinX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
知识无价 赞助有价,有疑问可致电邮箱 nies1996@qq.com
如果你觉得这篇内容对你挺有启发请点赞+关注
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!