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

微信小程序的支付跟H5的支付和APP支付流程不一样,本次只描述下小程序支付流程。

一.账号准备

1.微信小程序账号

文档:小程序申请

小程序支付需要先认证,如果你有已认证的公众号,也可以通过公众号免费注册认证小程序。

一般300元,我是认证的政府的免费。

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

然后登录小程序,设置APPSecret,记录好自己的AppID和 APPSecret。

2.商户账号申请

申请地址:https://pay.weixin.qq.com/index.php/core/info

准备好要求的各种资质,申请好账号,然后登录,点击账户中心-》API安全

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

申请证书和AIPv3秘钥,v2现在有淘汰趋势,不需要申请v2,直接v3走起。 

ps:我到处网上查询支付文档时候,很多写的是v2的调用方式,还有v3的,还不标明是用哪种方法调用的,导致我蒙蔽了好久。我这次只用v3,不涉及v2

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

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

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

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

 也可以看看文档:小程序支付接入前准备

里面说了有直连模式和服务商模式,并建议多商户的那种应用用服务商模式。但是我不想那么麻烦,虽然我的也是多商户,但是我选择配置多个商户信息,还是直连模式

3.小程序绑定商户号

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

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

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

 

二.开发

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

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

 

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

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

 2.引入maven依赖

    com.github.wxpay
    wxpay-sdk
    0.0.3


    com.github.wechatpay-apiv3
    wechatpay-apache-httpclient
    0.4.8

3.商户配置表

首先因为我是多商户模式的,所以我把商户信息都放在一张表里了,设计表是这样的:

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

秘钥和证书路径都在表里了,这样做不够安全,严谨的小伙伴可以自行加密

单商户的也可以用这个表,只写一条数据就行了

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;

在库里是这么配置的:

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

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

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

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/ee72e5ca7e.html