1.对接准备
需要的参数包括开发者ID(appid)、商户号、商户api私钥、商户证书序列号、商户APIV3密钥、回调地址。
在微信公众平台https://mp.weixin.qq.com/ 注册应用,类型只选择“公众号/小程序/企业微信”,注册完成后需完成”微信认证“(微信收取300元),在基础配置中拿到开发者ID(APPID)。
在微信支付商户平台https://pay.weixin.qq.com注册商户,在账户中心-api安全中设置APIv3密钥,微信支付 APIv3 使用由 证书授权机构(Certificate Authority,简称CA)签发的证书,商户申请证书时,证书工具会生成商户私钥,并保存在本地证书文件夹的件 apiclient_key.pem
中。开发和部署,需要区分环境,加载 apiclient_key.pem
文件。
在账户中心-商户信息中拿到微信支付商户号,即商户ID (收钱的商家ID);
在产品中心-appid账号管理中,将申请的下来的APPID绑定到商户号下。
Certificate Downloader 是 Java 微信支付 APIv3 平台证书的命令行下载工具。该工具可从https://api.mch.weixin.qq.com/v3/certificates
接口获取商户可用证书。
2.maven引入jar包
<!-- 微信支付SDK --><dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-java</artifactId> <version>0.2.9</version></dependency>
3.yml文件配置微信参数,工具类读取
@Componentpublic class WechatPayConfig { /** * 应用ID */ public static String appid; /** * 商户号 */ public static String merchantId; /** * 商户API私钥路径 */ public static String privateKeyPath; /** * 商户证书序列号 */ public static String merchantSerialNumber; /** * 商户APIV3密钥 */ public static String apiV3Key; /** * 通知地址(有效性:1. HTTPS;2. 不允许携带查询串。) */ public static String notifyUrl; /** * 微信支付配置 */ public static Config config; /** * 订单支付超时时间/分钟 */ public static final Integer ORDER_PAY_TIME_OUT = 1440; private WechatPayConfig() {} @Value("${wechat.pay.appid}") public void setAppid(String appid) { WechatPayConfig.appid = appid; } @Value("${wechat.pay.merchantId}") public void setMerchantId(String merchantId) { WechatPayConfig.merchantId = merchantId; } @Value("${wechat.pay.privateKeyPath}") public void setPrivateKeyPath(String privateKeyPath) { String classPath = System.getProperty("java.class.path"); // 是否运行在开发环境 boolean isRunningFromIde = classPath.toLowerCase().contains("eclipse") || classPath.toLowerCase().contains("idea"); // 开发环境与部署jar包环境 不同获取私钥路径 if (isRunningFromIde){ WechatPayConfig.privateKeyPath = getClass().getClassLoader().getResource("apiclient_key.pem").getPath(); }else { WechatPayConfig.privateKeyPath = privateKeyPath; } } @Value("${wechat.pay.merchantSerialNumber}") public void setMerchantSerialNumber(String merchantSerialNumber) { WechatPayConfig.merchantSerialNumber = merchantSerialNumber; } @Value("${wechat.pay.apiV3Key}") public void setApiV3Key(String apiV3Key) { WechatPayConfig.apiV3Key = apiV3Key; } @Value("${wechat.pay.notifyUrl}") public void setNotifyUrl(String notifyUrl) { WechatPayConfig.notifyUrl = notifyUrl; } @PostConstruct public void initializeConfig() { // 初始化微信配置 config = new RSAAutoCertificateConfig.Builder() .merchantId(merchantId) .privateKeyFromPath(privateKeyPath) .merchantSerialNumber(merchantSerialNumber) .apiV3Key(apiV3Key) .build(); }}
4.对接支付接口
/** * 微信 Native 支付下单 * * @param amt 金额 * @param orderNumber 订单号 * @param description 描述 * @return 二维码链接 */ public static AjaxResult nativePay(BigDecimal amt, String orderNumber, String description) { // 当前时间 Date currentDate = new Date(); // 将金额转换为分 int total = amt.multiply(new BigDecimal("100")).intValue(); try { // 构建service NativePayService service = new NativePayService.Builder().config(WechatPayConfig.config).build(); // 构建请求对象 PrepayRequest request = new PrepayRequest(); request.setAppid(WechatPayConfig.appid); request.setMchid(WechatPayConfig.merchantId); request.setNotifyUrl(WechatPayConfig.notifyUrl); // 设置金额 Amount amount = new Amount(); amount.setTotal(total); request.setAmount(amount); request.setDescription(description); request.setOutTradeNo(orderNumber); // 计算订单失效时间 Calendar calendar = DateUtils.toCalendar(currentDate); calendar.add(Calendar.MINUTE, WechatPayConfig.ORDER_PAY_TIME_OUT); request.setTimeExpire(DateUtils.parseDateToStr("yyyy-MM-dd'T'HH:mm:ss+08:00", calendar.getTime())); return AjaxResult.success(service.prepay(request).getCodeUrl()); } catch (Exception e){ log.error(e.getMessage(),e); return AjaxResult.error("支付订单创建失败"); } }
5.回调地址
/** * 微信支付回调 * * @return 回调结果 */ @PostMapping("/wechat") public ResponseEntity.BodyBuilder wechat(@RequestBody String body, HttpServletRequest request) { try { // 构造 RequestParam RequestParam requestParam = new RequestParam.Builder() // 序列号 .serialNumber(request.getHeader("Wechatpay-Serial")) // 随机数 .nonce(request.getHeader("Wechatpay-Nonce")) // 签名 .signature(request.getHeader("Wechatpay-Signature")) // 时间戳 .timestamp(request.getHeader("Wechatpay-Timestamp")) .body(body) .build(); // 初始化解析器 NotificationParser parser = new NotificationParser((NotificationConfig) WechatPayConfig.config); // 验签、解密并转换成 Transaction Transaction transaction = parser.parse(requestParam, Transaction.class); // 校验交易状态 if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) { // 支付成功,根据订单编号查询订单信息 // 1.查询订单信息 Order orderOld = orderService.selectForUpdateByOrderNumber(transaction.getOutTradeNo()); // 校验金额 if (orderOld != null && orderOld.getPayAmount().equals(transaction.getAmount().getTotal())) { // 金额相等 完成支付 更新订单状态 WechatPayUtil.success(orderOld,transaction); } else { // 金额异常 执行退款 WechatPayUtil.refunded(new WechatPayRedis(transaction.getOutTradeNo(), transaction.getAmount().getTotal(), null)); } } } catch (ValidationException e) { // 签名验证失败,返回 401 UNAUTHORIZED 状态码 return ResponseEntity.status(HttpStatus.UNAUTHORIZED); } // 处理成功,返回 200 OK 状态码 return ResponseEntity.status(HttpStatus.OK); } /** * 支付成功 */ public static void success( Order orderOld,Transaction transaction) { try { // 支付完成时间 Date payTime = DateUtils.parseDate(transaction.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ss+08:00"); // 校验订单信息 & 支付结果 & 订单有效期止日期 if (orderOld != null && orderOld.getPayResult() == 0 && payTime.compareTo(orderOld.getExpirationDate()) < 1) { // 构建修改对象 Order updateOrder = new Order(); updateOrder.setId(orderOld.getId()); updateOrder.setPayMethod(PayMethodEnum.W.name()); updateOrder.setPayChannel(PayChannelEnum.P.name()); updateOrder.setPayDate(payTime); updateOrder.setPayResult(PayResultEnum.PAID.getCode()); updateOrder.setPayType(PayTypeEnum.U.name()); updateOrder.setSerialNumber(transaction.getTransactionId()); if (orderService.editPayResult(updateOrder,orderOld) == 1) { // 删除Redis订单支付信息 SpringUtils.getBean(RedisCache.class).deleteObject(WechatPayConfig.ORDER_PAY_REDIS_PREFIX + transaction.getOutTradeNo()); } } else { // 订单信息不存在 执行退款 WechatPayUtil.refunded(new WechatPayRedis(transaction.getOutTradeNo(), transaction.getAmount().getTotal(), null)); } } catch (Exception e) { log.error(e.getMessage(), e); } } /** * 执行退款 */ public static void refunded(WechatPayRedis wechatPay) { try { // 构建退款Service RefundService service = new RefundService.Builder().config(WechatPayConfig.config).build(); // 构建请求对象 CreateRequest request = new CreateRequest(); request.setOutTradeNo(wechatPay.getOrderNumber()); request.setOutRefundNo(wechatPay.getOrderNumber()); // 支付总金额(分) long total = wechatPay.getTotal(); // 设置退款金额 AmountReq amount = new AmountReq(); amount.setRefund(total); amount.setTotal(total); amount.setCurrency("CNY"); request.setAmount(amount); // 请求API申请退款 Refund refund = service.create(request); // 校验退款结果 if (refund != null && Status.SUCCESS.equals(refund.getStatus())) { // 退款成功 log.info("微信退款成功:{}", wechatPay); } } catch (Exception e) { log.error(e.getMessage(), e); } }