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

这篇具有很好参考价值的文章主要介绍了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依赖

<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.商户配置表

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

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<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 为商户的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<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));
            // 从证书管理器中获取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<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);     //指定传输参数为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<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);
                    //商户id
                    supplierId=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)
    @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<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;

在库里是这么配置的:

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

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

因为距离开发这玩意儿挺久了,有一些细节可能没想起来,有问题及时留言联系 文章来源地址https://www.toymoban.com/news/detail-418241.html

到了这里,关于uniapp+java/springboot实现微信小程序APIV3支付功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于Java+SpringBoot+Vue+uniapp微信小程序外卖系统设计和实现

    博主介绍 : ✌ 全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专栏 推荐订阅 👇🏻 不然下次找不到哟 2022-2024年

    2024年02月13日
    浏览(63)
  • 基于Java+SpringBoot+Vue+uniapp微信小程序零食商城系统设计和实现

     博主介绍 : ✌ 全网粉丝20W+,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专栏 推荐订阅 👇🏻 不然下次找不到哟  java项

    2024年02月03日
    浏览(39)
  • 基于Java+SpringBoot+Vue+uniapp实现微信小程序的宠物用品商城小程序

    文末获取源码 开发语言:Java 框架:SpringBoot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏览器:谷歌浏览器 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序运行软件:微信开发者 随着当今网络的发

    2024年01月18日
    浏览(45)
  • java微信小程序支付-回调(Jsapi-APIv3)

            准备:  接入前准备-小程序支付 | 微信支付商户平台文档中心 准备好了就可以获得( 第二点里需要的参数 ):         参数1 商户号 merchantId:xxxxxx(全是数字)         参数2 商户APIV3密钥 apiV3key:xxxxxxx(32位字母数字大小写串,开发自己准备的)         参

    2024年02月08日
    浏览(37)
  • 微信支付apiV3异常:The corresponding provider for the merchant already exists

    异常信息 原因 这个错误是微信SDK抛出的,这是因为微信支付apiV3的RSAConfig重复build导致,即RSAConfig要保证是 单例 才不会导致报错。 参数说明 mchId:商户号 privateKey:商户号密钥 mchSerialNo:商户证书号 apiV3Key:apiV3密钥 建议 可以把商户配置参数使用数据库保存,服务启动的时

    2024年02月11日
    浏览(45)
  • 微信小程序uniapp+springboot实现小程序服务通知

    2.1 开通订阅消息,并选用模板 如果点击订阅消息让开启消息订阅开启后就可以出现以下页面,我本次使用的模板是 月卡到期提醒 模板,点击选用即可 2.2 查看模板字段类型 TemplateId后续会使用,复制出来,点击详情查看模板信息 2.3 查询对应字段类型的限制 订阅消息参数值内容限制

    2024年02月10日
    浏览(33)
  • Java医院3D人体智能导诊系统源码 Uniapp+springboot 微信小程序

    “智能导诊”以人工智能手段为依托,为人们提供智能分诊、问病信息等服务,在一定程度上满足了人们自我健康管理、精准挂号等需求。 智能导诊可根据描述的部位和病症,给出适合病症的科室参考。 智能导诊 页面会显示男性或女性的身体结构图,可切换正面/背面。通过

    2024年02月04日
    浏览(38)
  • 基于SpringBoot+Vue+uniapp微信小程序的微信小程序书店的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月17日
    浏览(45)
  • 基于SpringBoot+Vue+uniapp微信小程序的校园反诈骗微信小程序的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月22日
    浏览(73)
  • 基于java SpringBoot和Vue uniapp的汽车充电桩微信小程序

            随着电池性能的不断提高,电动汽车得到了广泛的应用,未来车辆发展的主要趋势将是电动汽车数量逐年增加,部分取代内燃机车,与此同时,随着电动汽车技术的日益成熟,电动汽车的管理和服务也更加规范。电动汽车标准也将作为系列化和标准化的现代车型出

    2024年02月09日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包