栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java对接JsApi和H5下单支付,查询,退款(使用官方SDK)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java对接JsApi和H5下单支付,查询,退款(使用官方SDK)

本篇文章只展示了对接微信支付的部分内容,主要参考了官方文档以及其SDK

https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient

和绅士1993博主的内容  http://t.csdn.cn/HHLZs

并且是在该博主的内容上面二次开发,去掉了三方的包(IJPay),删除了多余代码,需要源码可以@我或者你可以直接使用该博主的代码,验证过有效!

我对其代码做了稍微调整。目录如下:主要是替换了html的位置,因为申请域名导致的问题

例如我申请的域名地址为 baidu.com/wxpay

 

添加依赖官方依赖

        
        
            
            
            
        
        
        
            cn.hutool
            hutool-all
            5.4.0
        
        
            com.google.code.gson
            gson
        

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

当你拿到了这些如下配置后,可参考博主绅士1993的方法,需要注意的点是 appId mchId  apiKey3 和domain(微信申请的域名)

通用工具类和方法:

我抽取了三方包的一些代码,和官方的部分代码:
放在CommonUtil中,这里代码过多就不展示了,有需要可以私信

另外引用官方SDK中的代码,构建通用httpClient,之后根据httpClient完成发送请求功能

   private WechatPayHttpClientBuilder  getWechatPayHttpClientBuilder() throws Exception{
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new FileInputStream(wxPayV3Bean.getKeyPath()));

        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(wxPayV3Bean.getMchId(), new WechatPay2Credentials(wxPayV3Bean.getMchId(),
                new PrivateKeySigner(CommonUtil.getSerialNumber(wxPayV3Bean.getCertPath()), merchantPrivateKey)), wxPayV3Bean.getApiKey3().getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号,可继续调用putMerchant添加商户信息

        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(wxPayV3Bean.getMchId());
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

        X509Certificate certificate = verifier.getValidCertificate();
        List certificates = new ArrayList<>();
        certificates.add(certificate);

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(wxPayV3Bean.getMchId(), CommonUtil.getSerialNumber(wxPayV3Bean.getCertPath()), merchantPrivateKey)
                .withWechatPay(certificates);
    // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

    // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
        return builder;
    }
H5下单支付(外部浏览器)

H5支付是最简单的,不需要繁琐的配置,根据官方需要的参数构建入参URl即可

    
    @RequestMapping("/wxpay/v3/h5Pay")
    @ResponseBody
    public ResponseInfo myselfH5(HttpServletRequest request) throws Exception{
        WechatPayHttpClientBuilder builder = getWechatPayHttpClientBuilder();
        CloseableHttpClient httpClient = builder.build();

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/h5");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        String outTradeNo = CommonUtil.generateStr();
        log.info("订单号是:{}",outTradeNo);
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid",wxPayV3Bean.getMchId())
                .put("out_trade_no", outTradeNo)
                .put("appid", wxPayV3Bean.getAppId())
                .put("description", "Image形象店-深圳腾大-QQ公仔")
                .put("notify_url", wxPayV3Bean.getDomain().concat("/v3/payNotify"));
        rootNode.putObject("amount")
                .put("total", 1)
                .put("currency", "CNY");
        rootNode.putObject("scene_info")
                .put("payer_client_ip", CommonUtil.getIpAddress(request))
                .putObject("h5_info")
                .put("type","Wap");

        System.out.println("rootNode是:" + rootNode.toString());
        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String result =EntityUtils.toString(response.getEntity());
        log.info("reuslt是:" + result);
        return new ResponseInfo(result);
    }

JsApi下单支付(微信内部浏览器)

1.根据微信用户获取code

2.根据用户code获取OpenId

3.获取openId后调用下单方法

package com.example.wxpay.controller.wxpay;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.example.wxpay.domain.WxPayV3Bean;
import com.example.wxpay.utils.CommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;

import java.util.Map;


@Controller
public class WxGZHController {

    private static final Logger log = LoggerFactory.getLogger(WxGZHController.class);

    @Resource
    WxPayV3Bean wxPayV3Bean;


    private static final String stateCashout = "pay";
    private static final String weixinGzhSecret = "60b4299dde678770b98f1xxxxxxxxx";//开发者密码(AppSecret) 根据实际替换
    private static final String jsApiPayUrl = "/wxpay/jsApiPay.html";//使用openId的html页面



    
    //1.先查询code
    @RequestMapping("/wxgzh/redirecttocashout")
    public String redirectToCashout() {

        log.info("准备获取code");
        String urlFir = "redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid=";

        // 微信申请的域名 也就是  wxPayV3Bean.getDomain() 根据实际替换 这里是baidu.com
        String domain = "http://baidu.com/wxpay";
        String redirectMethod = "/wxgzh/weixinoauth";

        // (重要!!!) 则拼接地址为: http://baidu.com/wxpay/wxgzh/weixinoauth  编码后为; http%3A%2F%2Fbaidu.com%2Fwxpay%2Fwxgzh%2Fweixinoauth
        // 通过该地址,会进入weixinOauth方法,同时会得到微信返回的code 和 自己添加的入参state
        String encoderUrl = CommonUtil.getURLEncoderString(domain + redirectMethod);

        String state = "pay";
        // 静默
        String scopeBase = "snsapi_base";
        // 需要手动点击 (可根据官网介绍选择)
        String scopeUser = "snsapi_userinfo";

        log.info(urlFir + wxPayV3Bean.getAppId() + "&redirect_uri=" + encoderUrl +"&response_type=code&scope=" + scopeUser + "&state=" + state + "#wechat_redirect");
        return urlFir + wxPayV3Bean.getAppId() + "&redirect_uri=" + encoderUrl +"&response_type=code&scope=" + scopeUser + "&state=" + state + "#wechat_redirect";
    }

    //2.根据code获取openId
    @GetMapping("/wxpay/wxgzh/weixinoauth")
    public String weixinOauth(@RequestParam String code,@RequestParam String state) throws Exception {
        log.info("获取code:{}",code);
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                + wxPayV3Bean.getAppId() + "&secret=" + weixinGzhSecret + "&code=" + code + "&grant_type=authorization_code";
        Map paramMap = null;
        String res = HttpUtil.get(url, paramMap);
        System.out.println(res);
        String openid = JSONObject.parseObject(res).getString("openid");
        log.info("根据code查询得到openId:{}",openid);
        String redirect = "";
        switch (state){
            case stateCashout:
                redirect =jsApiPayUrl + "?openId=" + openid;
                break;
        }
        log.info("准备调起jsApi支付,url:{}",redirect);
        return "redirect:" + redirect;
    }

}

4.封装url和请求参数,根据前端Js和后端返回的参数唤醒微信支付

  
    @RequestMapping("/wxpay/v3/jsApiPay")
    @ResponseBody
    public String jsApiPay(@RequestParam(value = "openId", required = false, defaultValue = "oNB9p1BpVJEqu0xvwt2X3i93G1A4") String openId) throws Exception{

        WechatPayHttpClientBuilder builder = getWechatPayHttpClientBuilder();
        CloseableHttpClient httpClient = builder.build();

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        // OutputStream流用来保存请求数据
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        // 订单失效时间(可填可不填)
        String timeExpire = CommonUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 60);
        // 随机生成订单号
        String outTradeNo = CommonUtil.generateStr();
        log.info("订单号是:{}",outTradeNo);
        ObjectNode rootNode = objectMapper.createObjectNode();
        // 还有其他字段,例如attach 可填可不填
        rootNode.put("mchid",wxPayV3Bean.getMchId())
                .put("appid", wxPayV3Bean.getAppId())
                .put("description", "Image形象店-深圳腾大-QQ公仔")
                .put("notify_url", wxPayV3Bean.getDomain().concat("/v3/payNotify"))
                .put("out_trade_no", outTradeNo)
                .put("time_expire", timeExpire);
        rootNode.putObject("amount")
                .put("total", 1);
        rootNode.putObject("payer")
                .put("openid", openId);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        System.out.println("下单参数是:" + bos.toString("UTF-8"));

        // 发出请求得到响应结果
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String result = EntityUtils.toString(response.getEntity());
        JSONObject jsonObject = JSONUtil.parseObj(result);
        String prepayId = jsonObject.getStr("prepay_id");
        log.info("结果是:{}", result);
        Map map = CommonUtil.buildPayMap(wxPayV3Bean.getAppId(),prepayId, wxPayV3Bean.getKeyPath());
        log.info("唤起支付参数:{}", map);
        return JSONUtil.toJsonStr(map);
    }

根据订单号tradeNo 进行订单查询,通用:

    
    @GetMapping("/wxpay/v3/searchJsApi")
    @ResponseBody
    public String searchJsApi() throws Exception{
        WechatPayHttpClientBuilder builder = getWechatPayHttpClientBuilder();

        CloseableHttpClient httpClient = builder.build();

        String result = "";
        String tradeNo = "6d259dd0d9344be1a97c9dc55964591d";
        String mchid = wxPayV3Bean.getMchId();

        try {  // "https://api.mch.weixin.qq.com/v3/pay/transactions/id/4200000889202103303311396384?mchid=1230000109"
            URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"+ tradeNo +"?mchid=" + mchid);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            CloseableHttpResponse response = httpClient.execute(httpGet);

            result = EntityUtils.toString(response.getEntity());

            JSONObject jsonObject = JSONUtil.parseObj(result);

            String code = jsonObject.getStr("trade_state");

            System.out.println(result);
            System.out.println("结果:" + code);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

根据订单号进行退款。通用:

    
    @GetMapping("/v3/refund")
    public String refund() throws Exception {
        String tradeNo = "6d259dd0d9344be1a97c9dc55964591d";
        // 商家内部的退款单号
        String myRefundNo ="123456789";
        WechatPayHttpClientBuilder builder = getWechatPayHttpClientBuilder();

        CloseableHttpClient httpClient = builder.build();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
        httpPost.addHeader("Accept","application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("out_trade_no",tradeNo);
        rootNode.put("out_refund_no", myRefundNo);
        rootNode.putObject("amount")
                .put("refund",1)
                .put("total", 1)
                .put("currency", "CNY");
        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String result = EntityUtils.toString(response.getEntity());
        log.info("结果是:{}", result);
        return result;

    }

下面是下载证书 和 回调通知地址的实现,只是替换了使用官方的SDK

    // 获取平台证书certificates
    @GetMapping("/getPlatformCert")
    @ResponseBody
    public void getCa() throws Exception{

        WechatPayHttpClientBuilder builder = getWechatPayHttpClientBuilder();

        CloseableHttpClient httpClient = builder.build();

        URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/certificates");
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpResponse response = httpClient.execute(httpGet);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println("证书是: " + bodyAsString);

        JSONObject jsonObject = JSONUtil.parseObj(bodyAsString);
        JSONArray dataArray = jsonObject.getJSONArray("data");
        // 默认认为只有一个平台证书
        JSONObject encryptObject = dataArray.getJSONObject(0);
        JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
        String associatedData = encryptCertificate.getStr("associated_data");
        String cipherText = encryptCertificate.getStr("ciphertext");
        String nonce = encryptCertificate.getStr("nonce");
        String serialNo = encryptObject.getStr("serial_no");
        final String platSerialNo = CommonUtil.savePlatformCert(wxPayV3Bean.getApiKey3(), associatedData, nonce, cipherText, wxPayV3Bean.getPlatformCertPath());
        log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
    }


    @RequestMapping(value = "/wxpay/v3/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
    @ResponseBody
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        log.info("收到支付成功的通知");
        Map map = new HashMap<>(12);
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = CommonUtil.readData(request);
            log.info("支付通知密文 {}", result);

            // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
            String plainText = CommonUtil.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());

            log.info("支付通知明文 {}", plainText);

            if (StrUtil.isNotEmpty(plainText)) {
                response.setStatus(200);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/951255.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号