java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java接入微信支付

Java接入微信支付详细实现步骤

作者:阿海打酱油

在Java中接入微信支付,通常需要通过微信提供的微信支付开放平台来进行集成,这篇文章主要介绍了Java接入微信支付的详细实现步骤,文中通过代码介绍的非常详细,需要的朋友可以参考下

一:主要流程

(1)前期准备工作

①注册认证微信公众号/小程序

②申请微信商户号

③配置API安全

(2)环境搭建和主要流程理解

①引入依赖

②确定支付方式,分析其流程

(3)核心代码开发

①配置参数

②向微信发送请求

二:注意事项

(1)个人类型账号无法开通微信支付

(2)切勿泄露APIV3秘钥

三:步骤实现

官方文档(以Native为例):产品介绍_Native支付|微信支付商户文档中心

先查看具体流程

(1)先注册对应的公众号/小程序

官网链接:https://mp.weixin.qq.com/

(2)认证对应的公众号/小程序

①方法一:

②方法二:

(3)注册商户号,开通对应的Native支付权限

官网链接:https://pay.weixin.qq.com/

开通步骤指引链接:申请Native支付权限指引_Native支付|微信支付商户文档中心

(4)绑定商户号与APPID

对应指引链接:管理商户号绑定的APPID账号_Native支付|微信支付商户文档中心

(5)获取对应参数开发

对应指引链接:开发必要参数说明_通用规则|微信支付商户文档中心

(6)核心代码开发

①推荐使用官方SDK:https://github.com/wechatpay-apiv3/wechatpay-java

②引入依赖

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.4.13</version> <!-- 注意检查最新版本 -->
</dependency>

③代码实现

配置类:

@Configuration
public class WeChatPayConfig {

    @Value("${wechat.pay.mchid}")
    private String mchId;

    @Value("${wechat.pay.mchSerialNo}") // 你的商户API证书序列号
    private String mchSerialNo;

    @Value("${wechat.pay.privateKeyPath}") // 你的.p12证书中的私钥文件路径 (.pem格式更佳)
    private String privateKeyPath;

    @Value("${wechat.pay.apiV3Key}")
    private String apiV3Key;

    @Value("${wechat.pay.certPath}") // 下载的微信平台证书路径
    private String certPath;

    /**
     * 创建私钥签名对象
     */
    @Bean
    public PrivateKey getPrivateKey() throws Exception {
        return PemUtil.loadPrivateKey(
            new FileInputStream(privateKeyPath));
    }

    /**
     * 创建自动更新的Verifier (验证微信响应签名)
     */
    @Bean
    public Verifier getVerifier() throws Exception {
        // 方式1: 使用本地已下载的平台证书 (简单,需手动更新)
        // X509Certificate certificate = PemUtil.loadCertificate(new FileInputStream(certPath));
        // return new StaticVerifier(mchId, mchSerialNo, certificate);

        // 方式2: 使用自动更新机制 (推荐)
        ScheduledUpdateCertificatesVerifier verifier = 
            new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, getPrivateKey())),
                apiV3Key.getBytes(StandardCharsets.UTF_8));
        return verifier;
    }

    /**
     * 创建HttpClient (用于调用微信API)
     */
    @Bean
    public CloseableHttpClient getWxPayClient() throws Exception {
        return WechatPayHttpClientBuilder.create()
            .withMerchant(mchId, mchSerialNo, getPrivateKey())
            .withValidator(new WechatPay2Validator(getVerifier()))
            .build();
    }
}

服务层(统一下单)示例与代码(需调整)

@Override
public String createNativeOrder(String outTradeNo, int totalFee, String body) {
    // ... 构建请求参数 (类似JSAPI,但去掉payer,增加scene_info等)
    json.put("appid", appid); // Native也需要appid
    json.put("mchid", mchId);
    json.put("description", body);
    json.put("out_trade_no", outTradeNo);
    json.put("notify_url", "https://yourdomain.com/api/wxpay/notify");

    JSONObject amount = new JSONObject();
    amount.put("total", totalFee);
    amount.put("currency", "CNY");
    json.put("amount", amount);

    // 场景信息 (可选)
    JSONObject sceneInfo = new JSONObject();
    sceneInfo.put("payer_client_ip", "用户IP"); // 必须传真实IP
    json.put("scene_info", sceneInfo);

    // 发送请求到 Native 接口
    try {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
        // ... 同上设置请求头和实体
        // 解析响应,获取 code_url
        JSONObject result = JSON.parseObject(responseString);
        return result.getString("code_url"); // 返回二维码链接
    } catch (Exception e) {
        throw new RuntimeException("创建Native订单失败", e);
    }
}

④回调处理

注意:此路径必须是公网能访问的路径

@RestController
@RequestMapping("/api/wxpay")
public class WeChatPayController {

    @Autowired
    private WeChatPayService weChatPayService;

    @PostMapping("/create-jsapi-order")
    public ResponseEntity<Map<String, String>> createJsapiOrder(@RequestBody PayRequest request) {
        // 获取用户openid (通常由前端通过登录态传递或后端通过code换取)
        String openid = getUserOpenidFromSessionOrToken(request.getToken());
        Map<String, String> result = weChatPayService.createJsapiOrder(
            openid, request.getOutTradeNo(), request.getTotalFee(), request.getBody());
        return ResponseEntity.ok(result);
    }

    @PostMapping("/notify")
    public ResponseEntity<String> handleNotify(@RequestBody String notifyData, 
                                              HttpServletRequest request) {
        try {
            // 1. 验证通知的签名和有效性 (SDK提供了工具类)
            // 这里需要解析请求头中的 Wechatpay-Signature, Wechatpay-Nonce, Wechatpay-Timestamp
            String signature = request.getHeader("Wechatpay-Signature");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String body = notifyData; // 请求体

            // 使用之前配置的Verifier来验证
            // boolean isValid = verifier.verify(timestamp, nonce, body, signature);
            // if (!isValid) {
            //     return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("签名验证失败");
            // }

            // 2. 解密通知数据 (APIv3通知体是加密的)
            AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
            String plainText = aesUtil.decryptToString(
                nonce.getBytes(StandardCharsets.UTF_8),
                associatedData.getBytes(StandardCharsets.UTF_8), // 关联数据
                ciphertext.getBytes(StandardCharsets.UTF_8)); // 密文

            // 3. 解析解密后的JSON数据
            JSONObject notifyDataObj = JSON.parseObject(plainText);
            String eventType = notifyDataObj.getString("event_type");
            JSONObject resource = notifyDataObj.getJSONObject("resource");
            String ciphertext = resource.getString("ciphertext"); // 加密的数据包
            String associatedData = resource.getString("associated_data");
            String nonce = resource.getString("nonce");

            // 再次解密ciphertext得到最终的订单信息
            String orderInfo = aesUtil.decryptToString(nonce.getBytes(), 
                associatedData.getBytes(), ciphertext.getBytes());
            JSONObject orderData = JSON.parseObject(orderInfo);

            String outTradeNo = orderData.getString("out_trade_no");
            String transactionId = orderData.getString("transaction_id");
            String tradeState = orderData.getString("trade_state"); // SUCCESS为支付成功

            // 4. 业务处理
            if ("SUCCESS".equals(tradeState)) {
                // TODO: 更新你的数据库订单状态为"已支付"
                // TODO: 执行发货、积分发放等后续业务逻辑
                // TODO: 记录日志
            } else if ("CLOSED".equals(tradeState) || "REVOKED".equals(tradeState)) {
                // 支付失败或关闭
            }

            // 5. 返回成功响应 (必须是200且内容为"success")
            return ResponseEntity.ok().body("{\"code\":\"SUCCESS\",\"message\":\"成功\"}");
        } catch (Exception e) {
            // 记录详细的错误日志
            log.error("处理微信支付通知失败", e);
            // 返回失败,微信会在一段时间后重试
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
        }
    }
}

工具类

public class WeChatPayUtil {
    // 生成JSAPI调用所需参数的签名 (注意:这是RSA-SHA256,不是APIv3的HMAC-SHA256)
    public static String sign(Map<String, String> params, String apiKey) {
        // 1. 参数按key ASCII排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);

        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (value != null && !value.trim().isEmpty() && !"sign".equals(key)) {
                sb.append(key).append("=").append(value).append("&");
            }
        }
        sb.append("key=").append(apiKey);

        // 2. 计算MD5 (旧版) 或 HMAC-SHA256 (新版推荐,但JSAPI仍多用MD5?实际看文档)
        // 注意:微信JSAPI目前主流仍是MD5,但请查阅最新官方文档确认!
        String stringA = sb.toString();
        String sign = DigestUtils.md5Hex(stringA).toUpperCase();

        return sign;
    }
}

四:总结

对接微信支付主要繁琐的是前期商户号、证书、配置,需仔细阅读文档,后续对接SDK非常简单,注意处理幂等性

到此这篇关于Java接入微信支付的文章就介绍到这了,更多相关Java接入微信支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文