uniapp+java/springboot实现微信小程序APIV3支付功能

news/2024/5/10 7:45:53/文章来源:https://blog.csdn.net/lianzhang861/article/details/128930921

微信小程序的支付跟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;

在库里是这么配置的:

这样完整的支付流程就完成了;

因为距离开发这玩意儿挺久了,有一些细节可能没想起来,有问题及时留言联系 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_255458.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义input[type=file]上传按钮样式的四种方案,你知道几种?

目录前言方案1 opacity: 0;实现方案2 display:none样式元素选择 &#xff1a;label样式元素选择&#xff1a;其他元素::file-selector-button兼容性用法&#x1f9e8;&#x1f9e8;&#x1f9e8; 大家好&#xff0c;我是搞前端的半夏 &#x1f9d1;&#xff0c;一个热爱写文的前…

二、Linux文件 - Open函数讲解实战

目录 1.Open函数讲解 2.open函数实战 2.1 man 1 ls 查询Shell命令 2.2 man 2 open 查看系统调用函数 2.3项目实战 1.Open函数讲解 高频使用的Linux系统调用&#xff1a;open write read close Linux自带的工具&#xff1a;man手册&#xff1a; man 1是普通的shell…

【树和二叉树】数据结构二叉树和树的概念认识

前言&#xff1a;在之前&#xff0c;我们已经把栈和队列的相关概念以及实现的方法进行了学习&#xff0c;今天我们将认识一个新的知识“树”&#xff01;&#xff01;&#xff01; 目录1.树概念及结构1.1树的概念1.2树的结构1.3树的相关概念1.4 树的表示1.5 树在实际中的运用&a…

全国青少年编程等级考试scratch二级真题2022年9月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手1.数列&#xff1a;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;6&#xff0c;…

程序的编译与链接(C语言为例) #代码写好后到运行期间要经过怎样的过程呢?# 粗略版 #

编译与链接前言程序的环境程序的编译与链接写在最后前言 每当我们运行一段代码时&#xff0c;编译器都会自动的帮我们编译代码并将代码转换为一个二进制可执行文件&#xff08;.exe&#xff09;&#xff0c; 有了这个可执行文件&#xff0c;便可以执行我们写的程序了。那么编译…

基于风光储能和需求响应的微电网日前经济调度(Python代码实现)【1】

目录 1 概述 2 知识点及数学模型 3 算例实现 3.1算例介绍 3.2风光参与的模型求解 3.3 风光和储能参与的模型求解 3.5 风光储能和需求响应都参与模型求解 3.6 结果分析对比 4 Python代码及算例数据 1 概述 近年来&#xff0c;微电网、清洁能源等已成为全球关注的热点…

数智化转型赋能企业,端看低代码如何发展

随着大数据、云计算、区块链等新兴技术的广泛运用&#xff0c; 现代企业在生产经营中产生的大量数据与信息逐渐成为发展的核心资产。与传统生产要素一起共同创建新的经济范式&#xff0c; 标志着我国经济发展正式迈入数字经济时代。 数字经济的快速崛起促使产业升级不断加速&am…

ASEMI整流模块MDQ75-16封装,MDQ75-16大小

编辑-Z ASEMI整流模块MDQ75-16参数&#xff1a; 型号&#xff1a;MDQ75-16 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;1600V 最大RMS电桥输入电压&#xff08;VRMS&#xff09;&#xff1a;1700V 最大平均正向整流输出电流&#xff08;IF&#xff09;…

Java IO流 序列化流 ObjectOutputStream ObjectInputStream

Java IO流 打印流 PrintStream PrintWriter Java IO流 序列化流 ObjectOutputStream ObjectInputStream Java IO流 缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter Java IO流 字符流 目录拷贝 Java IO流 字符流 FileWriter Java IO流 字符流 …

Sentinel服务熔断和流控

简介Sentinel随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件&#xff0c;主要以流量为切入点&#xff0c;从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。源…

Java基础学习笔记(二十)—— 多线程(2)

多线程1 线程池1.1 线程状态1.2 线程池基本原理1.3 创建线程池1.4 任务拒绝策略2 共享变量的问题与解决2.1 存在的问题2.2 Volatile解决2.3 synchronized解决3 原子性3.1 原子性的实现&#xff08;synchronized&#xff09;3.2 AtomicInteger3.3 悲观锁和乐观锁4 并发工具类4.1…

算法 | 动态规划 | 系列题目讲解(思路记录part.1)

文章目录字符串分割三角矩阵路径总数最小路径和字符串分割 问题描述 给定一个字符串和一个词典dict&#xff0c;确定s是否可以根据词典中的词分成 一个或多个单词。 比如&#xff0c;给定 s “leetcode” dict [“leet”, “code”] 返回true&#xff0c;因为"leetcode…

吾剑未尝不利,国内Azure平替,科大讯飞人工智能免费AI语音合成(TTS)服务Python3.10接入

微软Azure平台的语音合成(TTS)技术确实神乎其技&#xff0c;这一点在之前的一篇&#xff1a;含辞未吐,声若幽兰,史上最强免费人工智能AI语音合成TTS服务微软Azure(Python3.10接入)&#xff0c;已经做过详细介绍&#xff0c;然则Azure平台需要信用卡验证&#xff0c;有一定门槛&…

基于Apache Maven构建多模块项目

title: 基于Apache Maven构建多模块项目 date: 2022-04-10 00:00:00 tags: Apache Maven多模块 categories:Maven 介绍 多模块项目由管理一组子模块的聚合器 POM 来构建。在大多数情况下聚合器位于项目的根目录中&#xff0c;并且必须是 pom 类型的项目。子模块是常规的 Mave…

CNN网络:ResNet(四)

ResNet论文[https://arxiv.org/pdf/1512.03385.pdf]。RestNet网络结构ResNet在2015年被提出&#xff0c;在ImageNet比赛classification任务上获得第一名&#xff0c;因为它“简单与实用”并存&#xff0c;之后很多方法都建立在ResNet50或者ResNet101的基础上完成的&#xff0c;…

手机逻辑系统(2)---逻 辑 音 频 电 路

第二节 逻 辑 音 频 电 路 逻辑音频电路在手机电路中占有重要的地位,它是手机系统的心脏。 逻辑音频电路包含无线通信呼叫处理、音频处理、数字语音处理、射频逻辑接口电路、各种射频功能控制、电源管理和用户接口模组等。 任何一部手机的逻辑音频电路部分都包含以上的一些功能…

1.2.4存储结构-磁盘管理:磁盘优化分布存储、磁盘优化分布存储例题

1.2.4存储结构-磁盘管理&#xff1a;磁盘优化分布存储、磁盘优化分布存储例题磁盘优化分布存储磁盘优化分布存储 假设某磁盘的每个磁道划分成11个物理块&#xff0c;每块存放1个逻辑记录。逻辑记录R0&#xff0c;R1&#xff0c;…&#xff0c;R9&#xff0c;R10存放在同一个磁…

将U盘制作为启动盘

将U盘制作为启动盘 制作之前需要先保证U盘中没有重要的文件&#xff0c;因为制作时会将已有文件删除 1 安装制作软件【wePE】 ①官网选择对应PE版本下载安装 官网下载地址:http://www.wepe.com.cn IT天空的U盘装机助理&#xff1a;https://www.itiankong.net/thread-357573-1…

Ubuntu18 安装python3.7及多版本切换

1.安装3.7添加源sudo add-apt-repository ppa:deadsnakes/ppa检查更新sudo apt-get update 安装python3.7sudo apt-get install python3.72.使用 update-alternatives 来为整个系统更改Python 版本查看python替代版本信息~$ update-alternatives --display python但是结果为upd…

数字化发展趋势:打破企业边界,实现产业互联

据中欧商业在线发布的《2022未来管理人才白皮书》显示&#xff0c;在参加调研的1000家企业中&#xff0c;有77%的企业已经在公司业务中运用了数字化技术&#xff1b;更有7%的企业表示将在更深层面推进数字化转型工作。 当企业在业务层面完成数字化转型&#xff0c;下一步会走向…