Java结合微信支付 weixin-java-pay V3接口

Java整合微信支付

1.起先的打算是根据微信支付的官方文档来一步步的去操作,但是因为时间与效率的问题还是使用了集成的框架,话不多说,直接开干。

1.1 maven依赖

  <properties>
    <weixin-java.version>4.3.0</weixin-java.version>
  </properties>

  <dependencies>
    
            <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-miniapp</artifactId>
            <version>${weixin-java.version}</version>
        </dependency>
 
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.8</version>
        </dependency>
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
            <version>${weixin-java.version}</version>
        </dependency>
   </dependencies>

1.2 配置文件 (写不写都行,可以直接写在类中)

properties格式

#微信支付相关参数
wxpay.mch-id=
#商户API证书序列号
wxpay.mch-serial-no=
#私钥
wxpay.private-key-path=apiclient_key.pem
wxpay.key-path=apiclient_cert.p12
wxpay.private-cert-path=apiclient_cert.pem
#APIv3密钥
wxpay.api-v3-key=
wxpay.appid=
#微信服务器地址
wxpay.domain=https://api.mch.weixin.qq.com
#接收结果通知地址
wxpay.notify-domain=

yml格式

#微信支付相关参数
wechat:
  pay:
    mchid: 
    #商户API证书序列号
    mch-serial-no: 
    #私钥 证书 
    private-key-path=apiclient_key.pem
    key-path=apiclient_cert.p12
    private-cert-path=apiclient_cert.pem
    #APIv3密钥
    api-v3-key: 
    appid: 
    #微信服务器地址
    domain: https://api.mch.weixin.qq.com
    #接收结果通知地址
    notify-domain: 

工具类

import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;

public class WxPayUtil {

    public static WxPayService getWxPayService() {
        WxPayService payService = new WxPayServiceImpl();
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId("");
        payConfig.setMchId("");
        payConfig.setApiV3Key("");
        payConfig.setKeyPath("apiclient_cert.p12");//p12
        payConfig.setPrivateKeyPath("apiclient_key.pem");//私钥
        payConfig.setPrivateCertPath("apiclient_cert.pem");//证书
        payConfig.setUseSandboxEnv(false);
        payService.setConfig(payConfig);
        return payService;
    }
}

1.3下单

        //explain:所需参数 Payer、Amount、Description、OutTradeNo、TimeExpire、NotifyUrl。
        //explain:
        /*
            Payer:openid 支付人id
            Amount:Total:总额 单位分
            Currency :货币 CNY人民币
            Description:商品描述
            OutTradeNo:商品单号
            TimeExpire:过期时间 
            NotifyUrl:回调函数 外网可以访问的地址,跳过权限验证
            
         
            payService.createOrderV3(TradeTypeEnum.JSAPI, request);
        */
    
     public Map payOrder(){
        Map map = new HashMap<>();
        WxPayService payService = WxPayUtil.getWxPayService();
        try {
            WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
            WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
            payer.setOpenid();
            WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
            amount.setTotal();
            amount.setCurrency("CNY");
            request.setDescription();
            request.setOutTradeNo();
            Date nowDate = new Date();
            Date dateAfter = new Date(nowDate.getTime() + 300000);
            String format = DateUtil.format(dateAfter, "yyyy-MM-dd'T'HH:mm:ssXXX");
            request.setTimeExpire(format);
            request.setNotifyUrl("");
            request.setAmount();
            request.setPayer();
            map.put("data", payService.createOrderV3(TradeTypeEnum.JSAPI, request));
            return R.data(map);
        } catch (Exception e) {
            System.out.println("微信支付失败!订单号:{} " + feeOrder.getOrderNo() + ",原因:{}" + e.getMessage());
            e.getStackTrace();
            map.put("data", "支付失败,请稍后重试!");
        }
        return R.data(map);
}

1.4退款

   public Map refund(){
   
    Map map = new HashMap<>();
        try {
            WxPayService wxPayService = WxPayUtil.getWxPayService();
            WxPayRefundV3Request request = new WxPayRefundV3Request();
            com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request.Amount amount = new             WxPayRefundV3Request.Amount();
      //退款单号 自己生成的单号
            request.setOutTradeNo();
      //使用内部单号
            request.setOutRefundNo();
            amount.setCurrency("CNY");
            amount.setRefund();
            amount.setTotal();
            request.setAmount(amount);
      //退款原因
            request.setReason();
            map.put("data", wxPayService.refundV3(request));
            return map;
        } catch (WxPayException e) {
            e.getMessage();
            map.put("data", "退款失败,请稍后重试!");
        }
        return map;
}

1.5微信支付回调

    @ApiOperation(value = "微信支付回调")
    @PostMapping("notify")
    public R notify() {
        WxPayService payService = WxPayUtil.getWxPayService();
        HttpServletRequest request = WebUtil.getRequest();
        SignatureHeader header = new SignatureHeader();
        header.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
        header.setNonce(request.getHeader("Wechatpay-Nonce"));
        header.setSignature(request.getHeader("Wechatpay-Signature"));
        header.setSerial(request.getHeader("Wechatpay-Serial"));
        Map map = new HashMap();
        try {
            String requestBody = WebUtil.getRequestBody(request.getInputStream());
            map.put("data", payService.parseOrderNotifyV3Result(requestBody, header));
            return R.data(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return R.data(map);
    }

注意要点: 1,Topay里面的参数要填好:appid,appsecret,mch_id,partnerkey,spbill_create_ip 2,openid 需要微信授权获取到 3,每次支付orderNo要不同 openid参考实例: 1,授权链接地址:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxba3445566677&redirect_uri=http://www.acc.com/weixin/pay/paydispatcher&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect 2,转向处理地址:通过第一个链接微信会把code传过来,之前参数获取就行 @RequestMapping(value = "/paydispatcher", method = { RequestMethod.GET }) public void payDispatcher(HttpServletRequest request, HttpServletResponse response) throws Exception { String code = request.getParameter("code"); String msg=""; if(code==null||code.equals("")){ msg="获取微信Code失败!"; request.setAttribute("msg" ,msg); request.getRequestDispatcher("/jsp/login.jsp").forward(request,response); }else{ WeixinUtil util = new WeixinUtil(); UserAccessToken token = (UserAccessToken) request.getSession().getAttribute("UserAccessToken"); if(null==token){ token = util.getAccessToken3(Constants.APPID, Constants.SECRET,code); request.getSession().setAttribute("UserAccessToken",token); } request.setAttribute("openid", token.getOpenid()); request.setAttribute("accessToken", token.getAccessToken()); request.setAttribute("refreshToken", token.getRefreshToken()); request.setAttribute("expiresIn", token.getExpiresIn()); request.getRequestDispatcher("/pay/index.jsp").forward(request,response); } } // 获取用户openid accesstoken public static UserAccessToken getAccessToken3(String appid , String appsecret,String code) { UserAccessToken accessToken = null; String requestUrl = Constants.GET_OPENID_ACCESSTOKEN_URL.replace("APPID" , appid).replace("APPSECRET" , appsecret).replace("CODE" , code); String json = httpRequest(requestUrl , "GET" , null); JSONObject jsonObject = JSONObject.fromObject(json); // 如果请求成功 if (null != jsonObject) { try { accessToken = new UserAccessToken(); accessToken.setAccessToken(jsonObject.getString("access_token")); accessToken.setRefreshToken(jsonObject.getString("refresh_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); accessToken.setOpenid(jsonObject.getString("openid")); accessToken.setScope(jsonObject.getString("scope")); } catch (Exception e) { accessToken = null; // 获取token失败 System.out.println("获取token失败 errcode:{} errmsg:{}"); } } return accessToken; }
### 获取和使用 `weixin-java-pay` 公钥 为了安全地完成微信支付集成,开发者需要验证来自微信服务器的通知签名以及解密敏感数据。这涉及到获取并正确配置微信支付公钥。 #### 1. 引入依赖项 对于希望利用第三方封装库简化操作的应用程序来说,可以考虑引入由 Binary Wang 维护的 `weixin-java-pay` 库[^2]: ```xml <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-pay</artifactId> <version>4.6.0</version> </dependency> ``` #### 2. 配置证书路径与商户私钥 在应用启动时初始化必要的参数,包括但不限于 API v3 密钥、商户号 (mchid) 和序列号 (serialNo),还有最重要的——设置用于存储平台证书文件的位置 (`certPath`) 及其对应的商户私钥 (`privateKey`)。这些信息通常保存在一个配置类中以便于管理。 ```java @Configuration public class WxPayConfig { @Value("${wx.pay.merchant-id}") private String mchId; @Value("${wx.pay.api-v3-key}") private String apiV3Key; @Bean(name = "wxPayService") public IWXPayService wxPayService() throws Exception { WXPayConfiguration config = new WXPayConfiguration(); // 设置API V3 Key, 商户ID等基本信息 config.setAppId("your-app-id"); config.setMchId(mchId); config.setApiKey(apiV3Key); // 加载本地证书(.pem格式), 并指定商户私钥 File certFile = ResourceUtils.getFile("classpath:apiclient_cert.pem"); config.setCertStream(new FileInputStream(certFile)); config.setPrivateKey(privateKey); return new DefaultWXPayServiceImpl(config); } } ``` #### 3. 下载并缓存微信支付平台公钥 当接收到微信推送过来的消息或回调通知时,需先下载最新的平台公钥来校验消息的真实性。此过程可通过调用微信提供的接口 `/v3/certificates` 来实现,并将返回的结果中的公钥部分提取出来作为后续加密运算的基础。 ```java @Service @Slf4j public class PublicKeyManager { private final RestTemplate restTemplate; private static Map<String, String> publicKeyCache = new ConcurrentHashMap<>(); @Autowired public PublicKeyManager(RestTemplateBuilder builder){ this.restTemplate = builder.build(); } /** * 更新并获取最新版本的微信支付平台公钥. */ public synchronized void refreshPublicKey(){ try{ ResponseEntity<String> responseEntity = restTemplate.getForEntity( "https://api.mch.weixin.qq.com/v3/certificates", String.class ); JSONObject jsonObject = JSON.parseObject(responseEntity.getBody()); JSONArray dataArr = jsonObject.getJSONArray("data"); for(int i=0;i<dataArr.size();i++){ JSONObject itemObj = dataArr.getJSONObject(i); String serialNumber = itemObj.getString("encrypt_certificate").getJSONObject("algorithm").getString("value"); String pubKeyContent = itemObj.getString("encrypt_certificate").getJSONObject("ciphertext").getString("value"); // 解码Base64编码后的字符串得到实际的PEM格式公钥内容 byte[] decodedBytes = Base64.decode(pubKeyContent.getBytes(StandardCharsets.UTF_8)); X509Certificate certificate = CertificateFactory.getInstance("X.509") .generateCertificate(new ByteArrayInputStream(decodedBytes)); publicKeyCache.put(serialNumber,certificate.getPublicKey().toString()); log.info("Updated WeChat Pay platform public key with SN={}", serialNumber); } }catch(Exception e){ throw new RuntimeException(e.getMessage(),e); } } /** * 根据给定的序列号查找已缓存的公钥实例. * * @param sn 序列号 * @return 对应的公钥对象;如果找不到则抛出异常 */ public PublicKey getPublicKeyBySerial(String sn){ if(!publicKeyCache.containsKey(sn)){ refreshPublicKey(); } return ((RSAPublicKey)(new RSAPublicKeySpec(BigInteger.valueOf(Long.parseLong(publicKeyCache.get(sn))),BigInteger.valueOf(65537)))); } } ``` 上述代码片段展示了如何通过 HTTP 请求访问微信公众平台所提供的公开 API 接口以取得当前有效的 RSA 公钥,并将其转换成适合 Java 使用的形式加以储存。每当应用程序重启或是距离上次更新时间过久之后都会触发一次新的请求从而确保所使用的总是最新版次的安全凭证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值