文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java后端如何对接微信支付

2023-06-22 03:05

关注

本篇内容主要讲解“Java后端如何对接微信支付”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java后端如何对接微信支付”吧!

首先我们要明确目标,我们点击微信支付官网,我们主要聚焦于这三种支付方式,其中JSPAI与APP主要与uniapp开发微信小程序与APP对接,而NATIVE主要与网页端扫码支付对接

Java后端如何对接微信支付

1.三种支付统一准备工作

建议导入这个jar,里面一些提供map和xml互转以及生成签名的函数,使用非常方便。

<dependency>    <groupId>com.github.wxpay</groupId>    <artifactId>wxpay-sdk</artifactId>    <version>0.0.3</version></dependency>

ps:下面代码涉及到的WXPayUtil下面的函数都是这个工具包里来的,所以不要再问这个函数代码在哪

商户号与支付结果通知回调地址

Java后端如何对接微信支付

回调url配置途径:

微信商户平台(pay.weixin.qq.com)-->产品中心-->开发配置-->支付配置

Java后端如何对接微信支付

既然涉及到维信小程序和app,那么它们的app_id是必须的

Java后端如何对接微信支付

统一下单、查单、退款的API地址,去官方文档找即可,我这里直接写好了

public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; public static final String ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";  public static final String ORDER_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

API密钥

用于签名算法

获取途径

微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->设置API密钥

Java后端如何对接微信支付

Java后端如何对接微信支付

 2.JSAPI统一下单

这种支付的使用场景,我们这里是对接uniapp开发的微信小程序

我们仔细阅读文档并提取信息,其实访问接口的很多参数都不是必填的,我们尽量去关注必填参数

归纳如下:

appid微信小程序app_idmch_id商户号nonce_str随机字符串,可以调用WXPayUtil下generateNonceStr方法生成sign

可以调用WXPayUtil下generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。

appid微信小程序app_id
mch_id商户号
nonce_str随机字符串,可以调用WXPayUtil下generateNonceStr方法生成
sign

可以调用WXPayUtil下generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。

body商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值
out_trade_no我们自己生成的订单号,保证同一商号下唯一即可
total_fee金额,注意单位是分
spbill_create_ip用户客户端ip
notify_url支付结果通知回调地址
trade_type交易类型,我们这里填JSAPI
openid因为是JSAPI方式,所以必传,openid其实很好获取,参考地址如何获取openid

但是下完单之后,怎么样才能让前端调用微信支付呢?这里需要查看uniapp官方文档微信小程序支付

Java后端如何对接微信支付

我们发现其实主要是拿到预支付id prepay_id以及签名

代码实现:

public Map<String, String> unifiedOrderByJsp(Order order, String clientIp) throws Exception {        long begin = System.currentTimeMillis();        //使用map封装微信支付需要的固定参数        Map<String, String> m = new HashMap<>();        //设置支付参数        m.put("appid", OrderUtils.WX_APP_ID);        m.put("mch_id", OrderUtils.MCH_ID);        m.put("nonce_str", WXPayUtil.generateNonceStr());        //商品描述 例如:天天爱消除-游戏充值        m.put("body", "换芯易-" + order.getGoodsModel() + "购买");        //订单号        m.put("out_trade_no", order.getOrderSn());        m.put("total_fee", order.getActualPrice().multiply(new BigDecimal("100")).longValue() + "");        m.put("spbill_create_ip", clientIp);        m.put("notify_url", OrderUtils.NOTIFY_URL);        m.put("trade_type", "JSAPI");        //trade_type=JSAPI时(即JSAPI支付),必须要openid        UserThreeDao userThreeDao = new UserThreeDaoImpl();        UserThree userThree = userThreeDao.queryOpenid(order.getUserId());        if (userThree == null) {            return null;        }        m.put("openid", userThree.getThreeOpenid());        //生成签名        m.put("sign", WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));         //发送请求,请求路径:UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"        HttpClient client = new HttpClient(OrderUtils.UNIFIED_ORDER_URL);        //设置xml格式的参数,要把map转为xml        client.setXmlParam(WXPayUtil.mapToXml(m));        //设置支持https        client.setHttps(true);        //执行请求发送        client.post();         //xml转为map接受结果        Map<String, String> response = WXPayUtil.xmlToMap(client.getContent());        long end = System.currentTimeMillis();        System.out.println("请求https://api.mch.weixin.qq.com/pay/unifiedorder耗时:" + (end - begin) + "ms");        System.out.println("请求结果:" + JSON.toJSONString(response));         if ("SUCCESS".equals(response.get("return_code")) && "SUCCESS".equals(response.get("result_code"))) {             Map<String, String> param = new HashMap<>();            //返回结果格式参照https://uniapp.dcloud.io/api/plugins/payment?id=requestpayment            //随机字符串            //时间戳,但是单位为s,不是毫秒            param.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));            param.put("nonceStr", WXPayUtil.generateNonceStr());             param.put("package", "prepay_id=" + response.get("prepay_id"));            param.put("signType", "MD5");             param.put("paySign", WXPayUtil.generateSignature(param, OrderUtils.APP_KEY));            return param;        }        //为空代表下单失败        return null;    }

3.NATIVE统一下单

主要应用场景是PC端扫码支付

必填参数归纳如下:

appid微信小程序app_id
mch_id商户号
nonce_str随机字符串,可以调用WXPayUtil下generateNonceStr方法生成
sign

可以调用WXPayUtil下generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。

body商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值
out_trade_no我们自己生成的订单号,保证同一商号下唯一即可
total_fee金额,注意单位是分
spbill_create_ip用户客户端ip
notify_url支付结果通知回调地址
trade_type交易类型,我们这里填NATIVE
product_idtrade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,我们可以自行定义。

代码如下:

public Map<String, String> unifiedOrderByNative(Order order,  String clientIp) throws Exception {        long begin = System.currentTimeMillis();        //使用map封装微信支付需要的固定参数        Map<String, String> m = new HashMap<>();        //1、设置支付参数        m.put("appid", OrderUtils.WX_APP_ID);        m.put("mch_id", OrderUtils.MCH_ID);        m.put("nonce_str", WXPayUtil.generateNonceStr());        //商品描述 例如:天天爱消除-游戏充值        m.put("body", "换芯易-" + order.getGoodsModel() + "购买");        //订单号        m.put("out_trade_no", order.getOrderSn());        m.put("total_fee", order.getActualPrice().multiply(new BigDecimal("100")).longValue() + "");        m.put("spbill_create_ip", clientIp);        m.put("notify_url", OrderUtils.NOTIFY_URL);        m.put("trade_type", "NATIVE");        //trade_type=NATIVE时,此参数必传        m.put("product_id", order.getId());        //生成签名        m.put("sign", WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));         //发送请求,请求路径:UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"        HttpClient client = new HttpClient(OrderUtils.UNIFIED_ORDER_URL);        //设置xml格式的参数,要把map转为xml        client.setXmlParam(WXPayUtil.mapToXml(m));        //设置支持https        client.setHttps(true);        //执行请求发送        client.post();         //xml转为map接受结果        Map<String, String> response = WXPayUtil.xmlToMap(client.getContent());        long end = System.currentTimeMillis();        System.out.println("请求https://api.mch.weixin.qq.com/pay/unifiedorder耗时:" + (end - begin) + "ms");        System.out.println("请求结果:" + JSON.toJSONString(response));         if ("SUCCESS".equals(response.get("return_code")) && "SUCCESS".equals(response.get("result_code"))) {             Map<String, String> param = new HashMap<>();            //二维码地址            param.put("code_url", response.get("code_url"));            param.put("order_sn", order.getOrderSn());            param.put("order_id", order.getId());            param.put("total_fee", order.getActualPrice().multiply(new BigDecimal("100")).longValue() + "");            return param;        }        //为空代表下单失败        return null;    }

 上面代码中最重要的返回结果就是code_url。即生成的支付二维码地址,然后用微信扫码并付款即可。

4.APP统一下单

应用场景是APP端调用微信支付,对接uniapp开发的app

归纳如下:

appidAPP的app_id
mch_id商户号
nonce_str随机字符串,可以调用WXPayUtil下generateNonceStr方法生成
sign

可以调用WXPayUtil下generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。

body商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值
out_trade_no我们自己生成的订单号,保证同一商号下唯一即可
total_fee金额,注意单位是分
spbill_create_ip用户客户端ip
notify_url支付结果通知回调地址
trade_type交易类型,我们这里填APP

但是下完单之后,怎么样才能让前端调用微信支付呢?这里需要查看uniapp官方文档微信小程序支付:

Java后端如何对接微信支付

我们可以看到常规的比如appid、商户号需要,充要的是预支付id prepayid和签名

代码实现如下: 

public Map<String, String> unifiedOrderByApp(Order order, String clientIp) throws Exception {        long begin = System.currentTimeMillis();        //使用map封装微信支付需要的固定参数        Map<String, String> m = new HashMap<>();        //1、设置支付参数        m.put("appid", OrderUtils.APP_APP_ID);        m.put("mch_id", OrderUtils.MCH_ID);        m.put("nonce_str", WXPayUtil.generateNonceStr());        //商品描述 例如:天天爱消除-游戏充值        m.put("body", "换芯易-" + order.getGoodsModel() + "购买");        //订单号        m.put("out_trade_no", order.getOrderSn());        m.put("total_fee", order.getActualPrice().multiply(new BigDecimal("100")).longValue() + "");        m.put("spbill_create_ip", clientIp);        m.put("notify_url", OrderUtils.NOTIFY_URL);        m.put("trade_type", "APP");        //生成签名        m.put("sign", WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));         //发送请求,请求路径:UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"        HttpClient client = new HttpClient(OrderUtils.UNIFIED_ORDER_URL);        //设置xml格式的参数,要把map转为xml        client.setXmlParam(WXPayUtil.mapToXml(m));        //设置支持https        client.setHttps(true);        //执行请求发送        client.post();         //xml转为map接受结果        Map<String, String> response = WXPayUtil.xmlToMap(client.getContent());        long end = System.currentTimeMillis();        System.out.println("请求https://api.mch.weixin.qq.com/pay/unifiedorder耗时:" + (end - begin) + "ms");        System.out.println("请求结果:" + JSON.toJSONString(response));         if ("SUCCESS".equals(response.get("return_code")) && "SUCCESS".equals(response.get("result_code"))) {             Map<String, String> param = new HashMap<>();            param.put("appid", OrderUtils.APP_APP_ID);            //随机字符串            param.put("noncestr", WXPayUtil.generateNonceStr());            //固定值            param.put("package", "Sign=WXPay");            param.put("partnerid", OrderUtils.MCH_ID);            param.put("prepayid", response.get("prepay_id"));             //时间戳,但是单位为s,不是毫秒            param.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));            //param.put("package", response.get("sign"));            param.put("sign", WXPayUtil.generateSignature(param, OrderUtils.APP_KEY));            return param;        }        //为空代表下单失败        return null;    }

按照uniapp官方要求的格式封装返回结果给前端。

5.三种支付方式返回结果

对于上面三种支付方式,他们的返回结果我都没有细细分析,因为官方文档写得很详细,我们非常需要关注的也就预支付id这个返回结果。所以贴一下官方文档,有兴趣可以仔细看看(重要的我会标注)

共有

Java后端如何对接微信支付

其中APP和JSAPI得着重关注预支付id

 Java后端如何对接微信支付

native扫码支付主要关注code_url

 Java后端如何对接微信支付

6.查单

我们实际业务中,过于依赖微信支付的回调结果来判断订单状态显然是不太合适的,所以自己去查单这种操作十分常见。查单需要的必填参数归纳如下:

appidapp_id
mch_id商户号
nonce_str随机字符串,可以调用WXPayUtil下generateNonceStr方法生成
sign

可以调用WXPayUtil下generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。

out_trade_no我们自己生成的订单号,保证同一商号下唯一即可,这个订单号需要和我们当时调用统一下单传过去的单号一致

ps:这里的out_trade_no可以用transaction_id代替,但是一般我们查单的时候可能还没有transaction_id,所以只能用我们自己程序业务订单号out_trade_no去查,因为transaction_id需要调查单接口才能得到,也就是说如果我们以后"二次查单"可以用这个参数。

代码实现入下:

public Map<String, String> queryOrderStatus(String orderSn) throws Exception {        long beginTime = System.currentTimeMillis();        //1、封装参数        Map<String, String> m = new HashMap<>();        m.put("appid", OrderUtils.APP_APP_ID);        m.put("mch_id", OrderUtils.MCH_ID);        m.put("out_trade_no", orderSn);        m.put("nonce_str", WXPayUtil.generateNonceStr());         //生成签名        m.put("sign", WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));         //发送请求,请求路径:UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery"        HttpClient client = new HttpClient(OrderUtils.ORDER_QUERY_URL);        client.setXmlParam(WXPayUtil.mapToXml(m));        client.setHttps(true);        client.post();        //3、返回第三方的数据        Map<String, String> result = WXPayUtil.xmlToMap(client.getContent());        long endTime = System.currentTimeMillis();        System.out.println("请求https://api.mch.weixin.qq.com/pay/orderquery耗时:" + (endTime - beginTime) + "ms");        System.out.println("请求结果:" + JSON.toJSONString(result));        return result;    }

返回结果参数部分如下,重要的标注起来:

Java后端如何对接微信支付

Java后端如何对接微信支付

 一般情况下主要是利用trade_state来判断用户是否支付成功从而更新订单或记录交易成功物流等业务操作。

Map<String, String> map = orderDao.queryOrderStatus(orderSn);if (map == null) {    return ApiResponse.createApiResponse(ApiResponse.HTTP_STATE_400_ERROR_10001, "微信支付出错");} //微信if ("SUCCESS".equals(map.get("trade_state"))) {    //更新订单或记录交易成功物流等业务操作}

7.退款

言简意赅,就是方便(rnm,退钱)

重要参数

appidapp_id
mch_id商户号
nonce_str随机字符串,可以调用WXPayUtil下generateNonceStr方法生成
sign

可以调用WXPayUtil下generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。

out_trade_no我们自己生成的订单号,保证同一商号下唯一即可,这个订单号需要和我们当时调用统一下单传过去的单号一致
transaction_id和上面的out_trade_no二选一,官方推荐transaction_id
out_refund_no其实和统一下单的时候的订单号原理差不多,我们自己随机生成一个,保证同一商户系统下唯一即可
total_fee要退款的订单的金额
refund_fee实际要退款的金额

代码实现如下:

public Map<String, String> refundOrder(RefundTrace refundTrace, Order order) throws Exception {        long begin = System.currentTimeMillis();        //使用map封装微信支付需要的固定参数        Map<String, String> m = new HashMap<>();        //1、设置支付参数        m.put("appid", OrderUtils.WX_APP_ID);        m.put("mch_id", OrderUtils.MCH_ID);        m.put("nonce_str", WXPayUtil.generateNonceStr());        //微信支付订单号        m.put("transaction_id", order.getPayId());        //商户退款单号        m.put("out_refund_no", refundTrace.getRefundSn());        //订单金额        m.put("total_fee", order.getActualPrice().multiply(new BigDecimal("100")).longValue() + "");        //退款金额 即实际退款金额        m.put("refund_fee", refundTrace.getRefundAmount().multiply(new BigDecimal("100")).longValue() + "");        //退款原因        m.put("refund_desc", refundTrace.getRefundReason());         m.put("notify_url", OrderUtils.NOTIFY_URL);        //生成签名        m.put("sign", WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));         发送请求,请求路径:ORDER_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"        String content = HttpRequestUtils.refundRequest(WXPayUtil.mapToXml(m));          //xml转为map接受结果        Map<String, String> response = WXPayUtil.xmlToMap(content);        long end = System.currentTimeMillis();        System.out.println("请求https://api.mch.weixin.qq.com/secapi/pay/refund耗时:" + (end - begin) + "ms");        System.out.println("请求结果:" + JSON.toJSONString(response));         if ("SUCCESS".equals(response.get("return_code")) && "SUCCESS".equals(response.get("result_code"))) {             Map<String, String> param = new HashMap<>();            param.put("refund_id", response.get("refund_id"));            param.put("refund_fee", response.get("refund_fee"));            return param;        }        //为空代表退款失败        return null;    }

这里注意一下!!!!因为操作涉及到把商户方的钱转回到买方的操作,所以安全系数比较高,调用退款api官方要求证书

Java后端如何对接微信支付

证书申请途径如下:

Java后端如何对接微信支付

 微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全-->申请API证书

申请到证书,需要放到项目下:

Java后端如何对接微信支付

那么我们这里的http请求就不能用之前的了,需要配置该证书

 上面代码中使用的refundRequest函数细节如下:

public static String refundRequest(String order) throws Exception {        try {            KeyStore clientStore = KeyStore.getInstance("PKCS12");            // 读取项目存放的PKCS12证书文件            FileInputStream instream = new FileInputStream("apiclient_cert.p12");            try {                // 指定PKCS12的密码(商户ID)                clientStore.load(instream, OrderUtils.MCH_ID.toCharArray());            } finally {                instream.close();            }            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(clientStore, OrderUtils.MCH_ID.toCharArray()).build();            // 指定TLS版本            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null,                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());            // 设置httpclient的SSLSocketFactory            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();            try {                HttpPost httpost = new HttpPost(OrderUtils.ORDER_REFUND_URL); // 设置响应头信息//                httpost.addHeader("Connection", "keep-alive");//                httpost.addHeader("Accept", "*    private void setEntity(HttpEntityEnclosingRequestBase http) {        if (param != null) {            List<NameValuePair> nvps = new LinkedList<NameValuePair>();            for (String key : param.keySet())                nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数            http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数        }        if (xmlParam != null) {            http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));        }    }     private void execute(HttpUriRequest http) throws ClientProtocolException,            IOException {        CloseableHttpClient httpClient = null;        try {            if (isHttps) {                SSLContext sslContext = new SSLContextBuilder()                        .loadTrustMaterial(null, new TrustStrategy() {                            // 信任所有                            @Override                            public boolean isTrusted(X509Certificate[] chain,                                                     String authType)                                    throws CertificateException {                                return true;                            }                        }).build();                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(                        sslContext);                httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)                        .build();            } else {                httpClient = HttpClients.createDefault();            }            CloseableHttpResponse response = httpClient.execute(http);            try {                if (response != null) {                    if (response.getStatusLine() != null)                        statusCode = response.getStatusLine().getStatusCode();                    HttpEntity entity = response.getEntity();                    // 响应内容                    content = EntityUtils.toString(entity, Consts.UTF_8);                }            } finally {                response.close();            }        } catch (Exception e) {            e.printStackTrace();        } finally {            httpClient.close();        }    }     public int getStatusCode() {        return statusCode;    }     public String getContent() throws ParseException, IOException {        return content;    } }

到此,相信大家对“Java后端如何对接微信支付”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯