5支付(freemarker)

1概念

在这里插入图片描述
支付结果以异步为准

2微信支付文档

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4

2.1 简单测试一下统一下单的接口

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

在这里插入图片描述

主要看签名算法
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3

3支付demo(封装好,直接用):

https://github.com/Pay-Group/best-pay-sdk

下边的demo的分解学习;

4简单的使用

4.1引入依赖

<dependency>
            <groupId>cn.springboot</groupId>
            <artifactId>best-pay-sdk</artifactId>
            <version>1.3.0</version>
        </dependency>

4.2编写

在这里插入图片描述

在这里插入图片描述
每次调用返回的二维码文本是不一样的。

在这里插入图片描述
在这里插入图片描述

4.3 前端生成二维码

用这个js就可以做到

4.3.1 引入freemarker

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

4.3.2编写controller

在这里插入图片描述

4.3.3 编写页面

在这里插入图片描述

4.3.4测试




注意:

只要订单号不变,不会重复支付的



4.3.5修改controller,获取二维码给页面

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.3.6提取配置代码

因为异步通知也需要校验签名,创建支付也需要,提取出来:
在这里插入图片描述
修改代码:
在这里插入图片描述

添加啦@bean ,项目启动啦就执行啦代码

4.3.7异步通知

在这里插入图片描述

4.3.8支付宝

在这里插入图片描述

5 最终代码

5.1 controller

package com.imooc.pay.controller;

import com.imooc.pay.pojo.PayInfo;
import com.imooc.pay.service.impl.PayService;
import com.lly835.bestpay.config.WxPayConfig;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import com.lly835.bestpay.model.PayResponse;
import lombok.extern.slf4j.Slf4j;
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.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by 廖师兄
 */
@Controller
@RequestMapping("/pay")
@Slf4j
public class PayController {
    
    

	@Autowired
	private PayService payService;

	@Autowired
	private WxPayConfig wxPayConfig;

	@GetMapping("/create")
	public ModelAndView create(@RequestParam("orderId") String orderId,
							   @RequestParam("amount") BigDecimal amount,
							   @RequestParam("payType") BestPayTypeEnum bestPayTypeEnum
							   ) {
    
    
		PayResponse response = payService.create(orderId, amount, bestPayTypeEnum);

		//支付方式不同,渲染就不同, WXPAY_NATIVE使用codeUrl,  ALIPAY_PC使用body
		Map<String, String> map = new HashMap<>();
		if (bestPayTypeEnum == BestPayTypeEnum.WXPAY_NATIVE) {
    
    
			map.put("codeUrl", response.getCodeUrl());
			map.put("orderId", orderId);
			map.put("returnUrl", wxPayConfig.getReturnUrl());
			return new ModelAndView("createForWxNative", map);
		}else if (bestPayTypeEnum == BestPayTypeEnum.ALIPAY_PC) {
    
    
			map.put("body", response.getBody());
			return new ModelAndView("createForAlipayPc", map);
		}

		throw new RuntimeException("暂不支持的支付类型");
	}

	@PostMapping("/notify")
	@ResponseBody
	public String asyncNotify(@RequestBody String notifyData) {
    
    
		return payService.asyncNotify(notifyData);
	}

	@GetMapping("/queryByOrderId")
	@ResponseBody
	public PayInfo queryByOrderId(@RequestParam String orderId) {
    
    
		log.info("查询支付记录...");
		return payService.queryByOrderId(orderId);
	}
}

5.2 service

package com.imooc.pay.service.impl;

import com.imooc.pay.dao.PayInfoMapper;
import com.imooc.pay.enums.PayPlatformEnum;
import com.imooc.pay.pojo.PayInfo;
import com.imooc.pay.service.IPayService;
import com.lly835.bestpay.enums.BestPayPlatformEnum;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import com.lly835.bestpay.enums.OrderStatusEnum;
import com.lly835.bestpay.model.PayRequest;
import com.lly835.bestpay.model.PayResponse;
import com.lly835.bestpay.service.BestPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * Created by 廖师兄
 */
@Slf4j
@Service
public class PayService implements IPayService {
    
    

	@Autowired
	private BestPayService bestPayService;

	@Autowired
	private PayInfoMapper payInfoMapper;

	/**
	 * 创建/发起支付
	 *
	 * @param orderId
	 * @param amount
	 */
	@Override
	public PayResponse create(String orderId, BigDecimal amount, BestPayTypeEnum bestPayTypeEnum) {
    
    
		//写入数据库
		PayInfo payInfo = new PayInfo(Long.parseLong(orderId),
				PayPlatformEnum.getByBestPayTypeEnum(bestPayTypeEnum).getCode(),
				OrderStatusEnum.NOTPAY.name(),
				amount);
		payInfoMapper.insertSelective(payInfo);

		PayRequest request = new PayRequest();
		request.setOrderName("4559066-最好的支付sdk");
		request.setOrderId(orderId);
		request.setOrderAmount(amount.doubleValue());
		request.setPayTypeEnum(bestPayTypeEnum);

		PayResponse response = bestPayService.pay(request);
		log.info("发起支付 response={}", response);

		return response;

	}

	/**
	 * 异步通知处理
	 *
	 * @param notifyData
	 */
	@Override
	public String asyncNotify(String notifyData) {
    
    
		//1. 签名检验
		PayResponse payResponse = bestPayService.asyncNotify(notifyData);
		log.info("异步通知 response={}", payResponse);

		//2. 金额校验(从数据库查订单)
		//比较严重(正常情况下是不会发生的)发出告警:钉钉、短信
		PayInfo payInfo = payInfoMapper.selectByOrderNo(Long.parseLong(payResponse.getOrderId()));
		if (payInfo == null) {
    
    
			//告警
			throw new RuntimeException("通过orderNo查询到的结果是null");
		}
		//如果订单支付状态不是"已支付"
		if (!payInfo.getPlatformStatus().equals(OrderStatusEnum.SUCCESS.name())) {
    
    
			//Double类型比较大小,精度。1.00  1.0
			if (payInfo.getPayAmount().compareTo(BigDecimal.valueOf(payResponse.getOrderAmount())) != 0) {
    
    
				//告警
				throw new RuntimeException("异步通知中的金额和数据库里的不一致,orderNo=" + payResponse.getOrderId());
			}

			//3. 修改订单支付状态
			payInfo.setPlatformStatus(OrderStatusEnum.SUCCESS.name());
			payInfo.setPlatformNumber(payResponse.getOutTradeNo());
			payInfoMapper.updateByPrimaryKeySelective(payInfo);
		}

		//TODO pay发送MQ消息,mall接受MQ消息

		if (payResponse.getPayPlatformEnum() == BestPayPlatformEnum.WX) {
    
    
			//4. 告诉微信不要再通知了
			return "<xml>\n" +
					"  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
					"  <return_msg><![CDATA[OK]]></return_msg>\n" +
					"</xml>";
		}else if (payResponse.getPayPlatformEnum() == BestPayPlatformEnum.ALIPAY) {
    
    
			return "success";
		}

		throw new RuntimeException("异步通知中错误的支付平台");
	}

	@Override
	public PayInfo queryByOrderId(String orderId) {
    
    
		return payInfoMapper.selectByOrderNo(Long.parseLong(orderId));
	}
}

5.3BestPayConfig

package com.imooc.pay.config;

import com.lly835.bestpay.config.AliPayConfig;
import com.lly835.bestpay.config.WxPayConfig;
import com.lly835.bestpay.service.BestPayService;
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;

/**
 * Created by 廖师兄
 */
@Component
public class BestPayConfig {
    
    

	@Autowired
	private WxAccountConfig wxAccountConfig;

	@Autowired
	private AlipayAccountConfig alipayAccountConfig;

	@Bean
	public BestPayService bestPayService(WxPayConfig wxPayConfig) {
    
    
		AliPayConfig aliPayConfig = new AliPayConfig();
		aliPayConfig.setAppId(alipayAccountConfig.getAppId());
		aliPayConfig.setPrivateKey(alipayAccountConfig.getPrivateKey());
		aliPayConfig.setAliPayPublicKey(alipayAccountConfig.getPublicKey());
		aliPayConfig.setNotifyUrl(alipayAccountConfig.getNotifyUrl());
		aliPayConfig.setReturnUrl(alipayAccountConfig.getReturnUrl());

		BestPayServiceImpl bestPayService = new BestPayServiceImpl();
		bestPayService.setWxPayConfig(wxPayConfig);
		bestPayService.setAliPayConfig(aliPayConfig);
		return bestPayService;
	}

	@Bean
	public WxPayConfig wxPayConfig() {
    
    
		WxPayConfig wxPayConfig = new WxPayConfig();
		wxPayConfig.setAppId(wxAccountConfig.getAppId());
		wxPayConfig.setMchId(wxAccountConfig.getMchId());
		wxPayConfig.setMchKey(wxAccountConfig.getMchKey());
		//192.168.50.101 同一局域网可访问
		//125.121.56.227 云服务器可行,家庭宽带不行(路由器、光猫)
		wxPayConfig.setNotifyUrl(wxAccountConfig.getNotifyUrl());
		wxPayConfig.setReturnUrl(wxAccountConfig.getReturnUrl());
		return wxPayConfig;
	}
}

5.4配置文件

  
wechat:
  mpAppId: wxd898xxxx
  mchId: 1483xxxx
  mchKey: C5245D70xxxxx
  keyPath: /var/weixin_cert/wxpay.p12
  notifyUrl: http://xxx.com/notify
  miniAppId: wxxxxxxxxxxx6bf9b
  miniAppSecret: xxbc6xxxxxxxxxxxxxx9c49d
  appAppId: wxxxxxxxxxxxx43b0

alipay:
  appId: appId
  privateKey: 商户私钥
  aliPayPublicKey: 支付宝公钥
  notifyUrl: http://xxx.com/notify
  returnUrl: http://xxx.com/return
  sandbox: true #是否使用沙箱

6过程说明

申请微信商家账号和开通支付功能,我们主要是为了拿到两个属性:一个是微信支付商户号,另一个是微信支付API秘钥。

a用户去我们的网站选择了一个商品点击购买,然后访问我们后台的接口,这个接口需要处理的逻辑是首先在我们系统里生成一条订单,然后调用微信的统一下单接口,微信的统一下单接口需要根据文档上指定的一些参数进行生成签名和加密传输,微信后台系统会给我们返回一个code_url链接,我们后台的代码拿到这个code_url链接后将它转成二维码图片,返给前端展示,然后用户使用微信扫一扫进行支付;

在配置类已经指定啦支付回调的接口地址

b、用户支付后,微信会回调我们的接口,告诉我们支付的结果,所以我们需要再写一个微信支付回调的接口
这个接口的大概逻辑应该是:拿到支付成功的状态去更新我们系统里订单表里的状态为已支付,然后给微信返回我们已经收到通知的信息;如果我们的接口没给微信返回已经收到通知的信息,微信那边会有一定的策略,它会每隔一段时间去调一次我们的接口试一下,直到成功或者直到达到一定的次数或者达到一定的时间,才会停止调用
还有就是我们这边超过一定的时间没收到微信的回调的话,我们也可以主动调微信的接口去查询相应的支付状态

猜你喜欢

转载自blog.csdn.net/Insist___/article/details/109101652