微信小程序的支付跟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依赖
<dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version>
</dependency>
<dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-apache-httpclient</artifactId><version>0.4.8</version>
</dependency>
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;*/@Autowiredprivate EnergyMapper energyMapper;/*** 获取微信预支付订单号* @return*/public Map<String, String> initWechatPay(Map<String, Object> wxOrder) throws IOException, SignatureException {Map<String, String> param = new HashMap<>();Map<String, Object> 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 为商户的idparams.put("supplierId",wxOrder.get("supplierId"));// 应用IDjsonObject.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);//微信支付规定访问接口需要设置请求头 AuthorizationString 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");//给前端拼接paySignString 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<String,Object> 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<String,Object> 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 私钥对象* <p>* 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名*/public PrivateKey getPrivateKey(Map<String,Object> 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("<" + name + ">");if(!list.isEmpty()) {sb.append(getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();}/*支付通知和退款通知给服务器的回调 请求头验签*/public boolean signVerify(String serialNumber, String message, String signature ,String supplierId) {Map<String, Object> 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));// 从证书管理器中获取verifierVerifier 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<String, Object> 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<String, String> 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<String, String> 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<String, String> 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<NameValuePair> paramList = new ArrayList<NameValuePair>();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); //指定传输参数为jsonhttpPost.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); //指定传输参数为jsonhttpPost.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)@ResponseBodypublic Object initWechatPay(@RequestParam Map<String,Object> 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<String,Object>> orderList=energyService.getUserOrderList(params);if(isNotNull(orderList)){Map<String,Object> order=orderList.get(0);//订单金额actualAmount=new BigDecimal(order.get("money").toString()).setScale(2, BigDecimal.ROUND_HALF_UP);//商户idsupplierId=order.get("supplier_id").toString();}out_trade_no = orderId;}else{ret.setCode("-1");ret.setMsg("未获取订单信息");ret.setSuccess(false);return ret;}//初始化微信统一下单接口Map<String, Object> 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<String,String> 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)@ResponseBodypublic 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<String, String> result = new HashMap<String, String>();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<String,Object> params=new HashMap<>();params.put("id",orderId);if(!StringUtils.isEmpty(orderId)) {List<Map<String,Object>> orderList=energyService.getUserOrderList(params);if(isNotNull(orderList)){Map<String,Object> 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;
在库里是这么配置的:
这样完整的支付流程就完成了;
因为距离开发这玩意儿挺久了,有一些细节可能没想起来,有问题及时留言联系