保姆级教程:使用wechatpay-java SDK实现微信支付V3的APP支付与退款
保姆级教程使用wechatpay-java SDK实现微信支付V3的APP支付与退款在移动应用生态中支付功能如同血液循环系统般重要。作为国内主流的支付解决方案微信支付V3版本通过其开放平台提供了更安全、更高效的支付能力。本文将手把手带你完成从零开始集成wechatpay-java SDK的全过程涵盖APP支付、回调处理到退款功能的完整闭环。1. 环境准备与基础配置开始编码前我们需要搭建好开发环境。微信支付V3要求使用Java 8及以上版本推荐使用Maven进行依赖管理。与V2版本相比V3最大的变化是采用了自动更新平台证书机制开发者不再需要手动更换证书。在pom.xml中添加核心依赖dependency groupIdcom.github.wechatpay-apiv3/groupId artifactIdwechatpay-java/artifactId version0.4.5/version /dependency注意请始终使用官方GitHub仓库发布的最新稳定版本避免潜在的安全风险。配置商户信息时需要准备以下关键参数商户号mch_idAPIv3密钥32位随机字符串商户证书序列号商户私钥文件建议将这些敏感信息存储在环境变量或配置中心而非硬编码在项目中。以下是推荐的目录结构src/ ├── main/ │ ├── java/ │ │ └── com/yourcompany/wechatpay/ │ │ ├── config/ │ │ ├── service/ │ │ └── util/ │ └── resources/ │ └── cert/ │ └── apiclient_key.pem2. APP支付全流程实现2.1 初始化支付服务微信支付V3的SDK采用了建造者模式进行配置初始化相比V2版本更加清晰public class WeChatPayConfig { private static final String PRIVATE_KEY_PATH /path/to/apiclient_key.pem; public static RSAAutoCertificateConfig initConfig() { return new RSAAutoCertificateConfig.Builder() .merchantId(System.getenv(WECHAT_MCH_ID)) .privateKeyFromPath(PRIVATE_KEY_PATH) .merchantSerialNumber(System.getenv(WECHAT_SERIAL_NO)) .apiV3Key(System.getenv(WECHAT_API_V3_KEY)) .build(); } }2.2 构建支付请求创建预支付订单时需要注意金额单位转换为分且所有必填字段都需要完整public PrepayResponse createPrepay(String orderNo, String goodsName, int amount, String notifyUrl) { AppService service new AppService.Builder() .config(WeChatPayConfig.initConfig()) .build(); Amount paymentAmount new Amount(); paymentAmount.setTotal(amount); PrepayRequest request new PrepayRequest(); request.setAppid(System.getenv(WECHAT_APP_ID)); request.setMchid(System.getenv(WECHAT_MCH_ID)); request.setDescription(goodsName); request.setOutTradeNo(orderNo); request.setNotifyUrl(notifyUrl); request.setAmount(paymentAmount); return service.prepay(request); }返回给客户端的签名数据需要严格按照微信要求的字段顺序字段名说明示例appId应用IDwx8888888888888888timeStamp时间戳1414561699nonceStr随机字符串5K8264ILTKCH16CQ2502SI8ZNMTM67VSpackage固定值SignWXPaysignType签名类型RSApaySign签名串oR9d8PuhnIcYZ8cB...2.3 客户端调起支付Android端调起支付的代码示例fun callWeChatPay(parameters: MapString, String) { val req PayReq().apply { appId parameters[appid] partnerId parameters[partnerid] prepayId parameters[prepayid] packageValue parameters[package] nonceStr parameters[noncestr] timeStamp parameters[timestamp] sign parameters[sign] } IWXAPI.sendReq(req) }3. 支付回调处理微信支付成功后的回调通知是交易确认的关键环节。V3版本的回调采用了全新的签名验证机制RestController RequestMapping(/api/payment) public class PaymentCallbackController { PostMapping(/wechat/notify) public String handleNotify(HttpServletRequest request) { try { String signature request.getHeader(Wechatpay-Signature); String nonce request.getHeader(Wechatpay-Nonce); String timestamp request.getHeader(Wechatpay-Timestamp); String serial request.getHeader(Wechatpay-Serial); String body request.getReader().lines() .collect(Collectors.joining()); RequestParam param new RequestParam.Builder() .serialNumber(serial) .nonce(nonce) .signature(signature) .timestamp(timestamp) .body(body) .build(); NotificationParser parser new NotificationParser( WeChatPayConfig.initConfig()); Transaction transaction parser.parse(param, Transaction.class); // 处理业务逻辑 paymentService.processPayment(transaction); return success; } catch (Exception e) { log.error(处理微信支付回调异常, e); return fail; } } }常见回调问题排查表问题现象可能原因解决方案无法收到回调网络不通/URL错误检查服务器外网可达性签名验证失败APIv3密钥不匹配核对商户平台配置重复通知未及时返回success确保5秒内响应解密失败证书过期检查自动更新机制4. 退款功能实现微信支付V3的退款接口相比V2版本有了显著改进支持部分退款和多次退款。退款请求需要特别注意金额精度public Refund createRefund(String transactionId, String refundNo, int totalAmount, int refundAmount) { RefundService service new RefundService.Builder() .config(WeChatPayConfig.initConfig()) .build(); AmountReq amount new AmountReq(); amount.setTotal(totalAmount); amount.setRefund(refundAmount); amount.setCurrency(CNY); CreateRequest request new CreateRequest(); request.setTransactionId(transactionId); request.setOutRefundNo(refundNo); request.setAmount(amount); return service.create(request); }退款状态查询的最佳实践public void checkRefundStatus(String refundNo) { RefundService service new RefundService.Builder() .config(WeChatPayConfig.initConfig()) .build(); QueryByOutRefundNoRequest request new QueryByOutRefundNoRequest(); request.setOutRefundNo(refundNo); Refund refund service.queryByOutRefundNo(request); switch (refund.getStatus()) { case SUCCESS: // 退款成功处理 break; case CLOSED: // 退款关闭处理 break; case PROCESSING: // 退款处理中 break; case ABNORMAL: // 退款异常处理 break; } }5. 调试与问题排查开发过程中难免会遇到各种问题以下是几个实用技巧日志记录在初始化配置时开启DEBUG日志LoggerContext loggerContext (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.getLogger(com.wechat.pay).setLevel(Level.DEBUG);沙箱环境使用测试商户号进行开发wechat.mch.id1230000109 wechat.api.v3.key0123456789abcdef0123456789abcdef常见错误代码错误码含义解决方案PARAM_ERROR参数错误检查必填字段SIGN_ERROR签名错误验证签名算法NO_AUTH无权限检查接口权限NOTENOUGH余额不足商户账户充值在实际项目中我们发现最容易出错的是签名生成环节。建议将签名逻辑封装为独立工具类并在单元测试中验证各种边界情况。例如测试不同字符集的商品描述、极端金额值等情况下的签名一致性。