先来看一下微信支付的流程,可见在整个支付流程中后台需要处理的事情有:
1、调用统一下单api
2、生成JSAPI页面调用的支付参数,并请求支付
3、异步通知商户支付结果
4、返回微信异步通知的处理结果
下面来实际编程实现以上的过程,这里面我们使用了第三方Sdk,best-pay-sdk
https://github.com/Pay-Group/best-pay-sdk
maven引入依赖
<dependency><groupId>cn.springboot</groupId><artifactId>best-pay-sdk</artifactId><version>1.3.0</version></dependency>
支付最核心的就是要出现微信支付的界面,那么就需要在商户页面调起支付,那么如何调起支付界面呢,根据开发文档,调起支付需要执行下面的JS,JS中需要传入相应的信息
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
那么思路比较明确,只需要在controller层执行支付的逻辑,调用统一下单的api,并返回JSAPI页面调用的支付参数动态注入到这段JS支付模板就可以了,那么每次调用controller层的接口时,就会调起支付。
@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<String,Object> map){
//查询订单
OrderDTO orderDTO=orderService.findOne(orderId);
if(orderDTO==null){
throw new OrderException(ResultEnum.ORDER_NOT_EXIST);}
//配置创建支付所需的必要信息
PayResponse payResponse=payService.create(orderDTO);
//把信息放入支付模板中
map.put("payResponse",payResponse);
//returnUrl为支付成功之后需要跳转到的页面
returnUrl="http://sell.com/#/order/"+orderId;
map.put("returnUrl",returnUrl);return new ModelAndView("pay/create",map);}}
接下来需要将后台传过来的支付参数动态注入到支付模板中,这里使用了freemarker
<script>function onBridgeReady(){
WeixinJSBridge.invoke('getBrandWCPayRequest',{"appId":"${payResponse.appId}", //公众号名称,由商户传入"timeStamp":"${payResponse.timeStamp}", //时间戳,自1970年以来的秒数"nonceStr":"${payResponse.nonceStr}", //随机串"package":"${payResponse.packAge}","signType":"MD5", //微信签名方式:"paySign":"${payResponse.paySign}" //微信签名},
function(res){
/* if(res.err_msg=="get_brand_wcpay_request:ok"){
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。}
*/
location.href="${returnUrl}";});}if(typeof WeixinJSBridge=="undefined"){
if( document.addEventListener){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);}elseif(document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);}}else{
onBridgeReady();}</script>
service层中的逻辑,bestPayService.pay(payRequest)调用了统一支付的API,并返回了JSAPI页面调用的支付参数
@Service
@Slf4j
public class PayServiceImpl implements PayService{
@Autowired
private OrderService orderService;
private static final String ORDER_NAME="微信订单";
@Autowired
private BestPayServiceImpl bestPayService;
@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_MP);
log.info("【微信支付】payRequest={}", JsonUtil.toJson(payRequest));
PayResponse payResponse=bestPayService.pay(payRequest);
log.info("【微信支付】payResponse={}",JsonUtil.toJson(payResponse));return payResponse;}}
配置JAVAconfig,设置商户信息
@Configuration
public class WechatPayConfig{
@Autowired
private WechatAccountConfig wechatAccountConfig;
@Bean WxPayConfig wxPayConfig(){
WxPayConfig wxPayConfig=new WxPayConfig();
wxPayConfig.setAppId(wechatAccountConfig.getMpAppId());
wxPayConfig.setAppSecret(wechatAccountConfig.getMpAppSecret());
wxPayConfig.setMchId(wechatAccountConfig.getMchId());
wxPayConfig.setMchKey(wechatAccountConfig.getMchKey());
wxPayConfig.setKeyPath(wechatAccountConfig.getKeyPath());
wxPayConfig.setNotifyUrl(wechatAccountConfig.getNotifyUrl());return wxPayConfig;}
@Bean
public BestPayServiceImpl bestPayService(){
BestPayServiceImpl bestPayService=new BestPayServiceImpl();
bestPayService.setWxPayConfig(wxPayConfig());return bestPayService;}}
@Data
@Component
@ConfigurationProperties(prefix="wechat")
public class WechatAccountConfig{
//公众号ID
private String mpAppId;
//公众号密钥
private String mpAppSecret;
//商户号
private String mchId;
//商户密钥
private String mchKey;
//商户证书路径
private String keyPath;
//异步回调接口
private String notifyUrl;}
然后在yml中配置相应的信息即可
此时打开微信端调用controller层的支付接口就可以实现支付了
支付完成之后还需要处理微信异步返回的结果,接收到异步返回的结果后可以处理业务逻辑,比如修改支付状态等,随后将处理结果返回微信,如果不处理请求,微信会一直返回异步通知。
controller层
@PostMapping("/notify")
public ModelAndView notify(@RequestBody String notifyData){
payService.notify(notifyData);return new ModelAndView("pay/success");}
service层
@Override
public PayResponse notify( String notifyData){
PayResponse payResponse=bestPayService.asyncNotify(notifyData);
log.info("【微信异步通知】,payResonse={}",JsonUtil.toJson(payResponse));
OrderDTO orderDTO=orderService.findOne(payResponse.getOrderId());
orderService.paid(orderDTO);return payResponse;}
支付成功后返回的JS代码,至此支付的所有流程结束
<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>
支付测试:
调用统一支付API之后返回的内容,可见调用了统一支付API后返回了之后生成JSAPI页面调用的所需要的支付参数
2020-02-25 03:39:22.518 INFO 22880 ---[nio-8889-exec-6] okhttp3.OkHttpClient:<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[wxd898fcb01713c658]]></appid><mch_id><![CDATA[1483469312]]></mch_id><nonce_str><![CDATA[g1K2MKyNdNkMkn7t]]></nonce_str><sign><![CDATA[61D46CFE319025CE457FC8F90B6CBADC]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[wx250337332209497ce17477f81982372500]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type></xml>
2020-02-25 03:39:22.518 INFO 22880 ---[nio-8889-exec-6] okhttp3.OkHttpClient:<-- END HTTP(456-byte body)
2020-02-25 03:39:22.531 INFO 22880 ---[nio-8889-exec-6] com.pers.food.service.PayServiceImpl:
【微信支付】payResponse={"appId":"wxd898fcb01713c658","timeStamp":"1582573162","nonceStr":"75htValspgU82402","packAge":"prepay_id\u003dwx250337332209497ce17477f81982372500","signType":"MD5","paySign":"855A556EBA7799B011235554A00268DB"}
支付成功后,接收异步通知后返回的信息,可见返回了支付金额,订单号等信息
2020-02-25 03:40:24.144 INFO 22880 ---[nio-8889-exec-4] com.pers.food.service.PayServiceImpl:
【微信异步通知】,payResonse={"orderAmount": 0.01,"orderId":"1582573104751271126","outTradeNo":"4200000512202002252531868211","payPlatformEnum":"WX"}