依赖引入
证书申请和下载
- 官方文档: 直达
配置类
- 参数配置类
/**
* @Description: 微信支付商户信息配置类
*/
@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "wechat.pay")
public class WeChatPayProperties {
/**
* 微信小程序或者微信公众号appId
*/
private String appid;
/**
* 商户号
*/
private String mchId;
/**
* 商户密钥
*/
private String mchKey;
/**
* 商户证书序列号
*/
private String serialNo;
/**
* apiV3Key
*/
private String apiV3Key;
/**
* 证书
*/
private String keyPath;
/**
* 商户私钥文件
*/
private String privateKeyPath;
/**
* apiclient_cert.pem证书文件
*/
private String privateCertPath;
/**
* 交易类型
* JSAPI--公众号支付
* NATIVE--原生扫码支付
* APP--app支付
*/
private String tradeType;
/**
* 支付结果异步通知回调地址
*/
private String notifyUrl;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 微信支付配置类
/**
* @Description: 微信支付配置类
*/
@Component
@ConditionalOnClass(WxPayService.class)
@RequiredArgsConstructor
public class WeChatPayConfig {
private final WeChatPayProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayConfig payConfig() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppid()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getSerialNo()));
payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiV3Key()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));
payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
payConfig.setTradeType(StringUtils.trimToNull(this.properties.getTradeType()));
return payConfig;
}
@Bean
public WxPayService wxPayService(WxPayConfig payConfig) {
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 自定义的微信预支付返回信息类
- 工具类
/**
* @Description: 微信支付相关工具类
*/
public class WxPayUtil {
public static String sign(String signStr, PrivateKey privateKey) {
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKey.getEncoded(), null);
return Base64.encode(sign.sign(signStr.getBytes()));
}
public static String buildMessage(String appId, String timeStamp, String nonceStr, String body) {
return appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + body + "\n";
}
public static AutoUpdateCertificatesVerifier getVerifier(WxPayConfig wxPayConfig) throws IOException {
PrivateKey privateKey = PemUtils.loadPrivateKey(new ClassPathResource("apiclient_key.pem").getInputStream());
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WxPayCredentials(wxPayConfig.getMchId(), new PrivateKeySigner(wxPayConfig.getCertSerialNo(), privateKey))
, wxPayConfig.getApiV3Key().getBytes("utf-8")
);
return verifier;
}
/**
* 验证签名
*
* @param certificate
* @param message
* @param signature
* @return
*/
public static boolean verify(AutoUpdateCertificatesVerifier certificate, byte[] message, String signature) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initVerify(certificate.getValidCertificate());
sign.update(message);
return sign.verify(java.util.Base64.getDecoder().decode(signature));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
throw new RuntimeException("签名验证过程发生了错误", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("无效的证书", e);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 接口层和服务处理层代码
- 重点注意:微信支付回调结果通知不要使用JSONObject去做接收
- 接口层
@RestController
@RequestMapping("/请求路径")
@RequiredArgsConstructor
public class AppCustomerUserRechargeOrderController {
private final IAppCustomerUserRechargeOrderService rechargeOrderService;
@PostMapping("/preOrder")
public Result<WxPayInfoVO> preOrder(@RequestParam("goodId") String goodId) {
return Result.success(rechargeOrderService.preOrder(goodId));
}
// 支付回调
@PostMapping("/back")
public Map<String,String> back(@RequestBody Map body,HttpServletRequest request) {
return rechargeOrderService.formalOrder(body, request);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 处理层
@Override
public WxPayInfoVO preRecharge(String goodId) {
try {
//看看是否有其他的业务逻辑
// 调用微信的统一下单接口获取预支付单返回前端 createOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request)
WxPayUnifiedOrderV3Request payRequest = new WxPayUnifiedOrderV3Request();
payRequest.setOutTradeNo(rechargeNo);//系统充值订单号
payRequest.setDescription(goodsPackage.getPackageName());
//订单失效时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
payRequest.setTimeExpire(dateFormat.format(DateUtil.offset(new Date(), DateField.HOUR_OF_DAY, 1)));
WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
payer.setOpenid(userAccount.getOpenId());
payRequest.setPayer(payer);
WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
amount.setTotal(rechargeOrder.getTotalAmount().multiply(BigDecimal.valueOf(100)).intValue());
amount.setCurrency("CNY");
payRequest.setAmount(amount);
payRequest.setNotifyUrl(weChatPayProperties.getNotifyUrl());
WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, payRequest);
String encode = WxPayUtil.sign(WxPayUtil.buildMessage(weChatPayProperties.getAppid(), jsapiResult.getTimeStamp(), jsapiResult.getNonceStr(), jsapiResult.getPackageValue())
, PemUtils.loadPrivateKey(new ClassPathResource("apiclient_key.pem").getInputStream()));
WxPayInfoVO payInfoVO = PojoUtil.exchangePojo(jsapiResult, WxPayInfoVO.class);
payInfoVO.setPaySign(encode);
return payInfoVO;
} catch (WxPayException e) {
e.printStackTrace();
throw new BizException("微信支付失败", e);
} catch (IOException e) {
e.printStackTrace();
throw new BizException("微信支付失败", e);
}
}
@Override
public Map<String, String> createFormalOrder(Map body, HttpServletRequest request) {
try {
Map<String, String> resultMap = new HashMap<>();
resultMap.put("code", "FAIL");
ObjectMapper objectMapper = new ObjectMapper();
try {
//官方文档中有说明切记不要改变原始报文主体,如果使用JSONObject接收的话,不能使用JSON转换出来的字符串,会出现验签错误的情况,请注意
String data = objectMapper.writeValueAsString(body);
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"));
WxPayNotifyV3Result notifyV3Result =wxPayService.parseOrderNotifyV3Result(data, header);
WxPayNotifyV3Result.DecryptNotifyResult decryptNotifyResult = notifyV3Result.getResult();//解密后的数据
if ("SUCCESS".equalsIgnoreCase(decryptNotifyResult.getTradeState())) {
resultMap.put("code", "SUCCESS");
}
} catch (Exception e) {
e.printStackTrace();
}
return resultMap;
} catch (Exception e) {
e.printStackTrace();
throw new BizException("微信支付成功回调失败", e);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 到此这篇关于SpringBoot整合weixin-java-pay实现微信小程序支付的示例代码的文章就介绍到这了