5 payment (freemarker)

1 concept

Insert picture description here
Payment results are subject to asynchronous

2WeChat payment document

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

2.1 Simply test the unified order interface

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

Insert picture description here

Mainly look at the signature algorithm
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3

3 payment demo (packaged and used directly):

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

Decomposition and study of the demo below;

4 simple to use

4.1 Introducing dependencies

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

4.2 Writing

Insert picture description here

Insert picture description here
The text of the QR code returned by each call is different.

Insert picture description here
Insert picture description here

4.3 QR code generation on the front end

It can be done with this js

4.3.1 Introduce freemarker

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

4.3.2 Write controller

Insert picture description here

4.3.3 Writing pages

Insert picture description here

4.3.4 Test




note:

As long as the order number remains the same, there will be no double payment



4.3.5 Modify the controller and get the QR code to the page

Insert picture description here

Insert picture description here

Insert picture description here

4.3.6 Extract configuration code

Because asynchronous notification also needs to verify the signature, and create payment also needs to extract it:
Insert picture description here
modify the code:
Insert picture description here

@Bean is added, the code is executed when the project is started

4.3.7 Asynchronous notification

Insert picture description here

4.3.8 Alipay

Insert picture description here

5 Final code

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 Configuration file

  
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 Process description

To apply for a WeChat merchant account and activate the payment function, we mainly get two attributes: one is the WeChat payment merchant account, and the other is the WeChat payment API key.

a The user goes to our website, selects a product, clicks to buy, and then visits our back-end interface. The logic that this interface needs to process is to first generate an order in our system, and then call the WeChat unified order interface, and WeChat unified order placement The interface needs to generate signatures and encrypted transmissions according to some parameters specified in the document. The WeChat backend system will return us a code_url link. After our backend code gets this code_url link, it will be converted into a QR code image and returned to the front end for display , And then the user uses WeChat to scan for payment;

The interface address of the payment callback has been specified in the configuration class

b. After the user pays, WeChat will call back our interface to tell us the result of the payment, so we need to write another WeChat payment callback
interface. The general logic of this interface should be: Get the payment successful status to update the order in our system The status in the table is paid, and then we will return the information that we have received the notification to WeChat; if our interface does not return the information that we have received the notification to WeChat, there will be a certain strategy on WeChat, which will periodically Try to adjust our interface once, until it succeeds, or until it reaches a certain number of times or reaches a certain time, it will stop calling.
Also , if we do not receive WeChat callback for more than a certain time, we can also actively adjust WeChat Interface to query the corresponding payment status

Guess you like

Origin blog.csdn.net/Insist___/article/details/109101652