项目背景
微信小程序中调用微信支付
微信支付开通
申请微信支付过程。。。
申请证书和秘钥
登录微信支付平台-API安全按照指示操作,v3秘钥可以不申请
导包
使用封装好的微信支付的jar
<!-- 微信支付 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.6.0</version>
</dependency>
支付代码
wx:
appId:
appSecret:
mchId:
mchSerialNo:
apiKey:
apiV3Key:
keyPath:
notifyUrl:
refundNotify:
@Component
@ConfigurationProperties(prefix = "wx")
@Data
@ToString
public class WxPayProperties {
//小程序appid
private String appId;
//小程序秘钥
private String appSecret;
//商户号
private String mchId;
//证书序列号
private String mchSerialNo;
//api秘钥-v2
private String apiKey;
//apiV3秘钥
private String apiV3Key;
//回调接口地址
private String notifyUrl;
//证书地址
private String keyPath;
//退款回调地址
private String refundNotify;
}
配置
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
@Resource
private WxPayProperties wxPayProperties;
@Bean
@ConditionalOnMissingBean
public WxPayService wxPayService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.wxPayProperties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.wxPayProperties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.wxPayProperties.getApiKey()));
payConfig.setKeyPath(StringUtils.trimToNull(this.wxPayProperties.getKeyPath()));
payConfig.setApiV3Key(StringUtils.trimToNull(this.wxPayProperties.getApiV3Key()));
payConfig.setNotifyUrl(wxPayProperties.getNotifyUrl());
//交易类型
payConfig.setTradeType("JSAPI");
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
创建预支付订单
try {
String tradeNo = seqUtil.getIncrByTime("WM");
String start = DateUtil.getCurrentDate("yyyyMMddHHmmss");
String expire = DateUtil.dateToStr("yyyyMMddHHmmss", DateUtil.add(DateUtil.getNow(), 1, Calendar.HOUR));
// 创建预支付订单的请求对象
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
//订单备注
orderRequest.setBody("膳食点餐");
//订单号
orderRequest.setOutTradeNo(tradeNo);
//元转成分
orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(realAmount.toString()));
orderRequest.setOpenid(openId);
orderRequest.setSpbillCreateIp(ip);
orderRequest.setTimeStart(start);
orderRequest.setTimeExpire(expire);
Object order = wxPayService.createOrder(orderRequest);
JSONObject res = JSONUtil.parseObj(order);
//处理业务逻辑
return new JsonResultResponse<>(res);
} catch (Exception e) {
log.error("微信支付失败!订单号:{},原因:{}", tradeNo, e.getMessage());
e.printStackTrace();
return new JsonResultResponse<>(ReturnCode.EXCEPTION.getCode(), "支付失败:" + e.getMessage());
}
支付回调
@Transactional(rollbackFor = Exception.class)
public JsonResultResponse payNotify(HttpServletRequest req) {
try {
log.info("进入微信支付回调");
String xmlResult = IOUtils.toString(req.getInputStream(), req.getCharacterEncoding());
WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(xmlResult);
log.info("微信支付回调结果:{}", result);
// 加入自己处理订单的业务逻辑,需要判断订单是否已经支付过,否则可能会重复调用
String orderId = result.getOutTradeNo();
String transactionId = result.getTransactionId();
String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
MealsOrder mealsOrder = mealsOrderDao.selectByCode(orderId);
if (mealsOrder == null) {
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), orderId + "订单不存在");
}
if (mealsOrder.getStatus() != CommonEnum.OrderStatus.NOPAY.getId()) {
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), orderId + "已经进行回调,不可重复回调");
}
mealsOrder.setStatus(CommonEnum.OrderStatus.PAY.getId());
mealsOrder.setOrderDate(DateUtil.getNow());
mealsOrder.setTransactionId(transactionId);
mealsOrderDao.updateById(mealsOrder);
return new JsonResultResponse("处理成功!");
} catch (Exception e) {
log.error("微信支付回调结果异常,异常原因{}", e.getMessage());
e.printStackTrace();
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "微信支付回调结果异常,异常原因:" + e.getMessage());
}
}
申请退款
@Override
@Transactional(rollbackFor = Exception.class)
public JsonResultResponse orderRefund(Integer id) {
String tradeNo = seqUtil.getIncrByDateTime("TK");
MealsOrder mealsOrder = mealsOrderDao.selectById(id);
if (mealsOrder == null) {
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "订单不存在");
}
if (mealsOrder.getStatus() != CommonEnum.OrderStatus.PAY.getId() && mealsOrder.getStatus() != CommonEnum.OrderStatus.NOPAY.getId()) {
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "订单已经制作不能取消,请联系商家");
}
try {
WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
wxPayRefundRequest.setOutTradeNo(mealsOrder.getCode());
wxPayRefundRequest.setTransactionId(wxPayRefundRequest.getOpUserId());
wxPayRefundRequest.setOutRefundNo(tradeNo);
wxPayRefundRequest.setRefundFee(BaseWxPayRequest.yuanToFen(mealsOrder.getRealAmount().toString()));
wxPayRefundRequest.setTotalFee(BaseWxPayRequest.yuanToFen(mealsOrder.getRealAmount().toString()));
wxPayRefundRequest.setNotifyUrl(wxPayProperties.getRefundNotify());
WxPayRefundResult refund = this.wxPayService.refund(wxPayRefundRequest);
mealsOrder.setOutRefundNo(tradeNo);
mealsOrder.setStatus(CommonEnum.OrderStatus.CANCEL.getId());
mealsOrderDao.updateById(mealsOrder);
return new JsonResultResponse("取消成功,等待退款");
} catch (Exception e) {
e.printStackTrace();
log.error("取消订单失败,订单号{}", mealsOrder.getCode());
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "取消失败");
}
}
退款回调
@Override
@Transactional(rollbackFor = Exception.class)
public JsonResultResponse refundNotify(String jsonData) {
try {
log.info("进入微信退款回调{}", jsonData);
WxPayRefundNotifyResult result = wxPayService.parseRefundNotifyResult(jsonData);
String outRefundNo = result.getReqInfo().getOutRefundNo();
String transactionId = result.getReqInfo().getTransactionId();
MealsOrder mealsOrder = mealsOrderDao.selectByTransactionId(transactionId);
log.info("退款回调结果:{}", result);
log.info("transactionId:{}", transactionId);
if (mealsOrder == null) {
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "退款失败,找不到该订单");
}
if ("SUCCESS".equals(result.getReqInfo().getRefundStatus())) {
if (mealsOrder.getStatus() == CommonEnum.OrderStatus.REFUND.getId()) {
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "该订单已退款");
}
//已退款状态
mealsOrder.setStatus(CommonEnum.OrderStatus.REFUND.getId());
mealsOrder.setOutRefundInfo(JSONUtil.toJsonStr(result.getReqInfo()));
mealsOrderDao.updateById(mealsOrder);
} else {
mealsOrder.setOutRefundInfo(JSONUtil.toJsonStr(result.getReqInfo()));
mealsOrderDao.updateById(mealsOrder);
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "退款失败" + result.getErrCodeDes());
}
return new JsonResultResponse("退款成功");
} catch (Exception e) {
e.printStackTrace();
log.error("退款回调异常,异常原因{}", e.getMessage());
return new JsonResultResponse(ReturnCode.EXCEPTION.getCode(), "退款失败" + e.getMessage());
}
}
发布坑
微信退款的过程中出现本地可以解密,线上一直报错解密失败
最后采用更换JDK的jar包解决
问题分析:微信相关API接入过程需要进行AES解密操作,Sun公司通过权限文件(local_policy.jar、US_export_policy.jar)做了相应限制,在解密操作过程中可能出现问题。因此Oracle在官网提供无政策全线文件进行下载。
下载地址:
JDK8下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
其他版本下载地址 :http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
以下是Linux系统下处理方式,其他系统同理
如果安装了JRE,将下载后文件中的两个jar文件放到%JRE_HOME%libsecurity目录下覆盖原来的文件 如果安装了JDK,将下载后文件中的两个jar文件放到%JDK_HOME%jrelibsecurity目录下覆盖原来文件