uniapp+java/springboot实现微信小程序APIV3支付功能
微信小程序的支付跟H5的支付和APP支付流程不一样,本次只描述下小程序支付流程。
一.账号准备
1.微信小程序账号
文档:小程序申请
小程序支付需要先认证,如果你有已认证的公众号,也可以通过公众号免费注册认证小程序。
一般300元,我是认证的政府的免费。

然后登录小程序,设置APPSecret,记录好自己的AppID和 APPSecret。
2.商户账号申请
申请地址:https://pay.weixin.qq.com/index.php/core/info
准备好要求的各种资质,申请好账号,然后登录,点击账户中心-》API安全

申请证书和AIPv3秘钥,v2现在有淘汰趋势,不需要申请v2,直接v3走起。
ps:我到处网上查询支付文档时候,很多写的是v2的调用方式,还有v3的,还不标明是用哪种方法调用的,导致我蒙蔽了好久。我这次只用v3,不涉及v2

记录好秘钥和下载好证书;

也可以看看文档:小程序支付接入前准备
里面说了有直连模式和服务商模式,并建议多商户的那种应用用服务商模式。但是我不想那么麻烦,虽然我的也是多商户,但是我选择配置多个商户信息,还是直连模式
3.小程序绑定商户号

如果是多商户模式,需要小程序依次绑定多个商户号

二.开发
1.后台调用支付文档:JSAPI下单

文档里写着调用支付需要的一堆参数;把一堆参数拼好请求接口,返回一个prepay_id,这个id是小程序调用支付时要用到的。

2.引入maven依赖
com.github.wxpay
wxpay-sdk
0.0.3
com.github.wechatpay-apiv3
wechatpay-apache-httpclient
0.4.8
3.商户配置表
首先因为我是多商户模式的,所以我把商户信息都放在一张表里了,设计表是这样的:

秘钥和证书路径都在表里了,这样做不够安全,严谨的小伙伴可以自行加密
单商户的也可以用这个表,只写一条数据就行了
4.代码
写个工具类,也不算工具类,因为里面写了查询逻辑啥的:
package com.bomc.energy.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bomc.energy.mapper.EnergyMapper;
import com.bomc.energy.service.EnergyService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.wxpay.sdk.WXPayUtil;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import static org.springframework.util.FileCopyUtils.BUFFER_SIZE;
/**
* 微信支付工具类
*/
@Component
public class WxPayUtils {
/* @Value("${PrivateKeyPath}")
private String PrivateKeyPath;*/
@Value("${AppID}")
private String appId;
//appid我直接从application.properties中取了,其他证书秘钥啥的都是直接用mapper查库了
/*
@Value("${MerchantId}")
private String mchId;
@Value("${WechatNotifyUrl}")
private String WechatNotifyUrl;
@Value("${serialNo}")
private String serial_no;
@Value("${AppKeyV2}")
private String AppKeyV2;
@Value("${AppKeyV3}")
private String AppKeyV3;*/
@Autowired
private EnergyMapper energyMapper;
/**
* 获取微信预支付订单号
* @return
*/
public Map initWechatPay(Map wxOrder) throws IOException, SignatureException {
Map param = new HashMap();
Map params = new HashMap();
JSONObject jsonObject = new JSONObject();
JSONObject amountJsonObject = new JSONObject();
JSONObject payerJsonObject = new JSONObject();
amountJsonObject.put("total",wxOrder.get("total"));
payerJsonObject.put("openid",wxOrder.get("openId"));
try {
//因为是多商户,supplierId 为商户的id
params.put("supplierId",wxOrder.get("supplierId"));
// 应用ID
jsonObject.put("appid",appId);
// 商户号,直接根据商户id去库里查出对应的商户号,下面这种查询的均是这种逻辑
jsonObject.put("mchid",energyMapper.getEnergyMerchantSettingById(params).get("mch_id"));
// 商品描述
jsonObject.put("description","商品购买");
// 商户订单号
jsonObject.put("out_trade_no",wxOrder.get("orderId"));
// 通知地址,支付成功后的回调地址
jsonObject.put("notify_url",energyMapper.getEnergyMerchantSettingById(params).get("notify_url"));
// 订单金额信息
jsonObject.put("amount",amountJsonObject);
// 支付者信息
jsonObject.put("payer",payerJsonObject);
String body = jsonObject.toString();
System.out.println(body);
//微信支付规定访问接口需要设置请求头 Authorization
String authorization ="WECHATPAY2-SHA256-RSA2048 "+
signStr("POST", "/v3/pay/transactions/jsapi", body,params);
String result=HttpUtil.doPostJson("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",
body,authorization);
JSONObject jsonObject1 = JSON.parseObject(result);
String prepay_id = jsonObject1 == null ? "":(String) jsonObject1.get("prepay_id");
if (!prepay_id.equals("")){
//返回结果格式参照https://uniapp.dcloud.io/api/plugins/payment?id=requestpayment
//随机字符串
//时间戳,但是单位为s,不是毫秒
String timeStamp=String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr=WXPayUtil.generateNonceStr();
param.put("appId", appId);
param.put("timeStamp", timeStamp);
param.put("nonceStr", nonceStr);
param.put("package", "prepay_id=" + prepay_id);
//param.put("signType", "RSA");
//给前端拼接paySign
String message = appId + "\n"
+ timeStamp + "\n"
+ nonceStr + "\n"
+ "prepay_id=" + prepay_id + "\n";
String signature = sign(message.getBytes("utf-8"),params);
param.put("paySign", signature);
System.out.println(param);
return param;
}
} catch (Exception e) {
e.printStackTrace();
}
return param;
}
/*public String signStr2(String method, String url){
String nonceStr = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage2(method, url, timestamp, nonceStr);
String signature = null;
try {
signature = sign(message.getBytes("utf-8"));
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("签名后:[" + signature + "]");
return "mchid=\"" + mchId + "\","
+ "serial_no=\"" + serial_no + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "signature=\"" + signature + "\"";
}*/
public String buildMessage2(String method, String url, long timestamp, String nonceStr) {
String str = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n\n";
System.out.println("签名数据[" + str + "]");
return str;
}
public String signStr(String method, String url, String body,Map params){
String nonceStr = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
System.out.println("message:[" + message + "]");
String signature = null;
try {
signature = sign(message.getBytes("utf-8"),params);
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("signature=[" + signature + "]");
return "mchid=\"" + energyMapper.getEnergyMerchantSettingById(params).get("mch_id") + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + energyMapper.getEnergyMerchantSettingById(params).get("serial_no") + "\","
+ "signature=\"" + signature + "\"";
}
public String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
String str = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
return str;
}
public String sign(byte[] message,Map params) throws SignatureException {
Signature sign = null;
try {
sign = Signature.getInstance("SHA256withRSA");
//证书方式获取
PrivateKey privateKey = getPrivateKey(params);
sign.initSign(privateKey);
sign.update(message);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
*/
/*public static PrivateKey getPrivateKey() throws IOException {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(privateKey.getBytes("utf-8")));
return merchantPrivateKey;
}*/
/**
* 获取私钥。
*
* ///@param filename 私钥文件路径 (required)
* @return 私钥对象
* * 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名 */ public PrivateKey getPrivateKey(Map params) throws IOException { try { String PrivateKeyPath=energyMapper.getEnergyMerchantSettingById(params).get("private_key_path").toString(); String content = new String(Files.readAllBytes(Paths.get(PrivateKeyPath)), "utf-8"); String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate( new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("当前Java环境不支持RSA", e); } catch (InvalidKeySpecException e) { throw new RuntimeException("无效的密钥格式"); } } // xml解析 public static Map doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v = getChildrenText(children); } m.put(k, v); } //关闭流 in.close(); return m; } /** * 获取子结点的xml * * @param children * @return String */ public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) { Iterator it = children.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append(""); if(!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append(""); } } return sb.toString(); } /*支付通知和退款通知给服务器的回调 请求头验签*/ public boolean signVerify(String serialNumber, String message, String signature ,String supplierId) { Map params = new HashMap(); try { params.put("supplierId",supplierId); String mchId=energyMapper.getEnergyMerchantSettingById(params).get("mch_id").toString(); String AppKeyV3=energyMapper.getEnergyMerchantSettingById(params).get("app_key_v3").toString(); String PrivateKeyPath=energyMapper.getEnergyMerchantSettingById(params).get("private_key_path").toString(); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(PrivateKeyPath)); // 获取证书管理器实例 CertificatesManager certificatesManager = CertificatesManager.getInstance(); // 向证书管理器增加需要自动更新平台证书的商户信息 certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, new PrivateKeySigner(energyMapper.getEnergyMerchantSettingById(params).get("serial_no").toString(), merchantPrivateKey)), AppKeyV3.getBytes(StandardCharsets.UTF_8)); // 从证书管理器中获取verifier Verifier verifier = certificatesManager.getVerifier(mchId); return verifier.verify(serialNumber, message.getBytes(StandardCharsets.UTF_8), signature); } catch (HttpCodeException | NotFoundException | IOException | GeneralSecurityException e) { e.printStackTrace(); } return false; } /*支付通知和退款通知给服务器的回调 解密报文*/ public String decryptOrder(String body,String supplierId) { try { Map params = new HashMap(); params.put("supplierId",supplierId); String AppKeyV3=energyMapper.getEnergyMerchantSettingById(params).get("app_key_v3").toString(); AesUtil util = new AesUtil(AppKeyV3.getBytes(StandardCharsets.UTF_8)); ObjectMapper objectMapper = new ObjectMapper(); JsonNode node = objectMapper.readTree(body); JsonNode resource = node.get("resource"); String ciphertext = resource.get("ciphertext").textValue(); String associatedData = resource.get("associated_data").textValue(); String nonce = resource.get("nonce").textValue(); return util.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext); } catch (JsonProcessingException | UnsupportedEncodingException | GeneralSecurityException e) { e.printStackTrace(); } return null; } public static String read(Reader reader) throws IOException { StringWriter writer = new StringWriter(); try { write(reader, writer); return writer.getBuffer().toString(); } finally{ writer.close(); } } /** * write. * * @param reader Reader. * @param writer Writer. * @return count. * @throws IOException */ public static long write(Reader reader, Writer writer) throws IOException { return write(reader, writer, BUFFER_SIZE); } /** * write. * * @param reader Reader. * @param writer Writer. * @param bufferSize buffer size. * @return count. * @throws IOException */ public static long write(Reader reader, Writer writer, int bufferSize) throws IOException { int read; long total = 0; char[] buf = new char[BUFFER_SIZE]; while( ( read = reader.read(buf) ) != -1 ) { writer.write(buf, 0, read); total += read; } return total; }}
http请求工具类:
package com.bomc.energy.util;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpUtil {
public static String doGet(String url) { // 无参数get请求
return doGet(url, null);
}
public static String doGet(String url, Map param) { // 带参数get请求
CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个默认可关闭的Httpclient 对象
String resultMsg = ""; // 设置返回值
CloseableHttpResponse response = null; // 定义HttpResponse 对象
try {
URIBuilder builder = new URIBuilder(url); // 创建URI,可以设置host,设置参数等
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
HttpGet httpGet = new HttpGet(uri); // 创建http GET请求
// httpGet.setHeader(key,value); //设置请求的请求头
response = httpClient.execute(httpGet); // 执行请求
if (response.getStatusLine().getStatusCode() == 200) { // 判断返回状态为200则给返回值赋值
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 不要忘记关闭
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMsg;
}
public static String doGet(String url, Map param ,String authorization) { // 带参数get请求
CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个默认可关闭的Httpclient 对象
String resultMsg = ""; // 设置返回值
CloseableHttpResponse response = null; // 定义HttpResponse 对象
try {
URIBuilder builder = new URIBuilder(url); // 创建URI,可以设置host,设置参数等
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
HttpGet httpGet = new HttpGet(uri); // 创建http GET请求
//设置请求的请求头
httpGet.setHeader("Content-type", "application/json");
httpGet.setHeader("Accept", "application/json");
httpGet.addHeader("Authorization",authorization);
response = httpClient.execute(httpGet); // 执行请求
if (response.getStatusLine().getStatusCode() == 200) { // 判断返回状态为200则给返回值赋值
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 不要忘记关闭
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMsg;
}
public static String doPost(String url) { // 无参数post请求
return doPost(url, null);
}
public static String doPost(String url, Map param) {// 带参数post请求
CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个默认可关闭的Httpclient 对象
CloseableHttpResponse response = null;
String resultMsg = "";
try {
HttpPost httpPost = new HttpPost(url); // 创建Http Post请求
// httpPost.setHeader(key,value); //设置post请求的请求头
if (param != null) { // 创建参数列表
List paramList = new ArrayList();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);// 模拟表单
httpPost.setEntity(entity);
}
response = httpClient.execute(httpPost); // 执行http请求
if (response.getStatusLine().getStatusCode() == 200) {
resultMsg = EntityUtils.toString(response.getEntity(), "utf-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMsg;
}
public static String doPostJson(String url, String json) {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
HttpPost httpPost = new HttpPost(url);
// httpPost.setHeader(key,value); //设置post请求的请求头
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); //指定传输参数为json
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPostJson(String url, String json ,String authorization) {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-type", "application/json");
httpPost.setHeader("Accept", "application/json");
httpPost.addHeader("Authorization",authorization);
// httpPost.setHeader(key,value); //设置post请求的请求头
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); //指定传输参数为json
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println("返回结果:"+resultString);
}else{
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println("返回结果:"+resultString);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String postXmlRequest(String url, String xml) throws Exception {
HttpPost post = new HttpPost(url);
post.setHeader("Content-type", "text/xml");
post.setEntity(new StringEntity(xml));
post.setEntity(new StringEntity(xml, "UTF-8"));
CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = client.execute(post);
return response.getStatusLine().getStatusCode() == 200 ? EntityUtils.toString(response.getEntity(), "UTF-8") : null;
}
}
触发付款controller:获取订单号 openid啥的自行发挥,看一下怎么调wxPayUtils.initWechatPay 方法就行了。
/**微信公众号支付统一下单接口,订单已完成,用户点击付款的时候触发这个
*/
@RequestMapping(value = "initWechatPay", method = RequestMethod.POST)
@ResponseBody
public Object initWechatPay(@RequestParam Map params, HttpServletRequest req) {
RetBase ret = new RetBase();
String orderId=params.get("orderId").toString();
String openId="";
String token = req.getHeader("token");
String supplierId="";
try {
if(isNotNull(token)){
String userId = JWT.decode(token).getAudience().get(0);
if(isNotNull(userId)){
params.put("userId",userId);
EnergyUser user=loginService.getMiniUserById(params);
openId=user.getOpenId();
}
}else{
ret.setCode("-1");
ret.setMsg("用户未登录");
ret.setSuccess(false);
return ret;
}
//获取订单详情
BigDecimal actualAmount = null;
String out_trade_no = null;
if(!StringUtils.isEmpty(orderId)) {
List<Map> orderList=energyService.getUserOrderList(params);
if(isNotNull(orderList)){
Map order=orderList.get(0);
//订单金额
actualAmount=new BigDecimal(order.get("money").toString()).setScale(2, BigDecimal.ROUND_HALF_UP);
//商户id
supplierId=order.get("supplier_id").toString();
}
out_trade_no = orderId;
}else{
ret.setCode("-1");
ret.setMsg("未获取订单信息");
ret.setSuccess(false);
return ret;
}
//初始化微信统一下单接口
Map wxParam=new HashMap();
wxParam.put("total",actualAmount.multiply(new BigDecimal(100))
.intValue());
wxParam.put("openId",openId);
wxParam.put("orderId",orderId);
wxParam.put("supplierId",supplierId);
Map result=wxPayUtils.initWechatPay(wxParam);
if(isNotNull(result)){
ret.setData(result);
ret.setCode("0");
ret.setMsg("下单成功");
ret.setSuccess(true);
}else{
ret.setCode("-1");
ret.setMsg("下单失败");
ret.setSuccess(false);
}
} catch (Exception e) {
e.printStackTrace();
ret.setCode("-10");
ret.setMsg("程序错误");
ret.setSuccess(false);
}
return ret;
}
小程序调用起支付文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
而uniapp中 uni.requestPayment方法集成了小程序调用支付,
uniapp 前端调用支付:
//确认支付
confirm: async function() {
let self=this;
//alert(retUrl)
var data= {
orderId:this.orderId,
}
self.$request.TokenRequest({url:"energy/initWechatPay"}, data).then(res => {
if (res.success) {
var data = res.data;
console.log(res.data);
uni.requestPayment({
provider: 'wxpay',
timeStamp: data.timeStamp,
nonceStr: data.nonceStr,
package: data.package,
signType: 'RSA',
paySign: data.paySign,
success: function(res) {
console.log(res)
uni.showToast({
title: '支付成功',
duration: 1000
});
uni.redirectTo({
url: '/pages/money/paySuccess'
})
},
fail: function(err) {
console.log(err)
uni.showToast({
title: '支付失败'+err.errMsg,
icon:'none',
duration: 2500
});
console.log('fail:' + JSON.stringify(err));
}
});
} else {
console.log(res.body.status.errorDesc);
}
},error => {uni.showToast({
icon:'none',
title: error,
duration: 2000
}); }).catch(err=>{uni.showToast({
icon:'none',
title: err,
duration: 2000
}); })
/* uni.redirectTo({
url: '/pages/money/paySuccess'
}) */
},
这样调用支付就完成了;
支付完程序需要监听回调方法,从而改订单状态是否已付款;
controller方法:
/**
* 支付通知(回调)
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "payNotifyUrl/{supplierId}", method = RequestMethod.POST)
@ResponseBody
public JSONObject payNotifyUrl(@PathVariable String supplierId, HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
JSONObject jsonObject = new JSONObject();
try {
System.out.println("Wechatpay-Timestamp:" + request.getHeader("Wechatpay-Timestamp"));
System.out.println("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
System.out.println("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
System.out.println("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));
Map result = new HashMap();
result.put("code", "FAIL");
StringBuilder signStr = new StringBuilder();
signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
BufferedReader br = request.getReader();
String str = null;
StringBuilder builder = new StringBuilder();
while ((str = br.readLine()) != null) {
builder.append(str);
}
System.out.println(builder.toString());
signStr.append(builder.toString()).append("\n");
//进行验签,确保请求来自微信
if (!wxPayUtils.signVerify(request.getHeader("Wechatpay-Serial"),
signStr.toString(), request.getHeader("Wechatpay-Signature"),supplierId)) {
System.out.println("PayCallback==>>sign error");
result.put("message", "sign error");
jsonObject.put("code","FAIL");
jsonObject.put("message","sign error");
return jsonObject;
}
System.out.println("PayCallback==>>sign success");
//解密报文
String info = wxPayUtils.decryptOrder(builder.toString(),supplierId);
System.out.println(info);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = objectMapper.readTree(info);
//可以解密出很多参数,具体见[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_5.shtml)
String orderId = node.get("out_trade_no").toString().substring(1, node.get("out_trade_no").toString().length() - 1);
String bankType = node.get("bank_type").toString().substring(1, node.get("bank_type").toString().length() - 1);
Map params=new HashMap();
params.put("id",orderId);
if(!StringUtils.isEmpty(orderId)) {
List<Map> orderList=energyService.getUserOrderList(params);
if(isNotNull(orderList)){
Map order=orderList.get(0);
if(order.get("status").equals("1")){
params.put("status","2");
energyService.updateUserOrder(params);
}
}
}
result.put("code", "SUCCESS");
jsonObject.put("code","SUCCESS");
jsonObject.put("message","SUCCESS");
} catch (IOException e) {
e.printStackTrace();
jsonObject.put("code","FAIL");
jsonObject.put("message","支付失败");
return jsonObject;
} finally {
}
return jsonObject;
}
因为是多商户, 且回调url中不能有参数,所以只能把商户id写到url中,因为回调返回的信息需要用到该商户的v3秘钥解密才行,所以必须知道商户id;
在库里是这么配置的:

这样完整的支付流程就完成了;
因为距离开发这玩意儿挺久了,有一些细节可能没想起来,有问题及时留言联系
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/ee72e5ca7e.html
