cn.springboot
best-pay-sdk
1.1.0
org.springframework.boot
spring-boot-starter-freemarker
wechat:
# 公众账号, 授权
mpAppId: wxd898fcb01713c658
mpAppSecret:
# 开放平台, 卖家扫码登录用
openAppId: wx6ad144e54af67d87
openAppSecret: 91a2ff6d38a2bbccfb7e9xxxxxx
# 支付/商户号
mchId: 1483469312
mchKey: 06C56A89949D617xxxxxxxxxxx
# 发起支付不需要证书, 退款需要
keyPath:
notifyUrl: http://sell.natapp4.cc/sell/pay/notify
templateId:
orderStatus: e-Cqq67QxD6YNI41iRiqawEYdFavW_7pc7LyEMb-yeQ
package com.imooc.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
/**
* 公众平台id
*/
private String mpAppId;
/**
* 公众平台密钥
*/
private String mpAppSecret;
/**
* 开放平台id
*/
private String openAppId;
/**
* 开放平台密钥
*/
private String openAppSecret;
/**
* 商户号
*/
private String mchId;
/**
* 商户密钥
*/
private String mchKey;
/**
* 商户证书路径
*/
private String keyPath;
/**
* 微信支付异步通知地址
*/
private String notifyUrl;
/**
* 微信模版id
*/
private Map templateId;
}
package com.imooc.config;
import com.lly835.bestpay.config.WxPayH5Config;
import com.lly835.bestpay.service.impl.BestPayServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class WechatPayConfig {
@Autowired
private WechatAccountConfig accountConfig;
@Bean
public BestPayServiceImpl bestPayService() {
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayH5Config(wxPayH5Config());
return bestPayService;
}
@Bean
public WxPayH5Config wxPayH5Config() {
WxPayH5Config wxPayH5Config = new WxPayH5Config();
wxPayH5Config.setAppId(accountConfig.getMpAppId());
wxPayH5Config.setAppSecret(accountConfig.getMpAppSecret());
wxPayH5Config.setMchId(accountConfig.getMchId());
wxPayH5Config.setMchKey(accountConfig.getMchKey());
wxPayH5Config.setKeyPath(accountConfig.getKeyPath());
wxPayH5Config.setNotifyUrl(accountConfig.getNotifyUrl());
return wxPayH5Config;
}
}
package com.imooc.service;
import com.imooc.dto.OrderDTO;
import com.lly835.bestpay.model.PayResponse;
import com.lly835.bestpay.model.RefundResponse;
public interface PayService {
PayResponse create(OrderDTO orderDTO);
PayResponse notify(String notifyData);
RefundResponse refund(OrderDTO orderDTO);
}
package com.imooc.service.impl;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.service.OrderService;
import com.imooc.service.PayService;
import com.imooc.utils.JsonUtil;
import com.imooc.utils.MathUtil;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import com.lly835.bestpay.model.PayRequest;
import com.lly835.bestpay.model.PayResponse;
import com.lly835.bestpay.model.RefundRequest;
import com.lly835.bestpay.model.RefundResponse;
import com.lly835.bestpay.service.impl.BestPayServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class PayServiceImpl implements PayService {
private static final String ORDER_NAME = "微信点餐订单";
@Autowired
private BestPayServiceImpl bestPayService;
@Autowired
private OrderService orderService;
@Override
public PayResponse create(OrderDTO orderDTO) {
PayRequest payRequest = new PayRequest();
payRequest.setOpenid(orderDTO.getBuyerOpenid());
payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
payRequest.setOrderId(orderDTO.getOrderId());
payRequest.setOrderName(ORDER_NAME);
payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("【微信支付】发起支付, request={}", JsonUtil.toJson(payRequest));
PayResponse payResponse = bestPayService.pay(payRequest);
log.info("【微信支付】发起支付, response={}", JsonUtil.toJson(payResponse));
return payResponse;
}
@Override
public PayResponse notify(String notifyData) {
//1. 验证签名
//2. 支付的状态
//3. 支付金额
//4. 支付人(下单人 == 支付人)
// bestPayService.asyncNotify 已经验证了签名和支付状态
PayResponse payResponse = bestPayService.asyncNotify(notifyData);
log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));
//查询订单
OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());
//判断订单是否存在
if (orderDTO == null) {
log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
//判断金额是否一致(0.10 0.1)
if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
payResponse.getOrderId(),
payResponse.getOrderAmount(),
orderDTO.getOrderAmount());
throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
}
//修改订单的支付状态
orderService.paid(orderDTO);
return payResponse;
}
/**
* 退款
* @param orderDTO
*/
@Override
public RefundResponse refund(OrderDTO orderDTO) {
RefundRequest refundRequest = new RefundRequest();
refundRequest.setOrderId(orderDTO.getOrderId());
refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
RefundResponse refundResponse = bestPayService.refund(refundRequest);
log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));
return refundResponse;
}
}
package com.imooc.controller;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.service.OrderService;
import com.imooc.service.PayService;
import com.lly835.bestpay.model.PayResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.Map;
@Controller
@RequestMapping("/pay")
public class PayController {
@Autowired
private OrderService orderService;
@Autowired
private PayService payService;
@GetMapping("/create")
public ModelAndView create(@RequestParam("orderId") String orderId,
@RequestParam("returnUrl") String returnUrl,
Map map) {
//1. 查询订单
OrderDTO orderDTO = orderService.findOne(orderId);
if (orderDTO == null) {
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
//2. 发起支付
PayResponse payResponse = payService.create(orderDTO);
map.put("payResponse", payResponse);
map.put("returnUrl", returnUrl);
return new ModelAndView("pay/create", map);
}
/**
* 微信异步通知
* @param notifyData
*/
@PostMapping("/notify")
public ModelAndView notify(@RequestBody String notifyData) {
payService.notify(notifyData);
//返回给微信处理结果
return new ModelAndView("pay/success");
}
}
Freemarker 目录结构
// create
// success
微信一旦成功发起了支付,这时,你在数据库改价格,发现再一次调用接口,还是原先未改的价格。这是因为一旦成功发起,微信服务器就会记录该订单信息,所以需要重新修改订单号等相关信息。
订单状态业务处理后需要返回给微信处理结果,否则微信会定时调用异步回调方法。
模板引擎:Freemarker,之所以要用这个,没有前后端分离,是因为前端可能反复需要写这么一段JSSDK,当然前端也可以去封装,这里只不过也可以在后端封装中间这个过程。