项目初始化
使用 Spring Initializer 进行项目初始化, 记得后面选上 lombok、spring web、mybatis、mysql driver
在 application.yml 中对数据库连接进行配置:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/mall?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 123456
对接微信Native支付
1、引入作者的maven支付包
<dependency>
<groupId>cn.springboot</groupId>
<artifactId>best-pay-sdk</artifactId>
<version>1.3.0</version>
</dependency>
2、编写接口
package com.imooc.pay.service;
import com.lly835.bestpay.model.PayResponse;
import java.math.BigDecimal;
public interface IPayService {
/**
* 创建/发起字符
*/
PayResponse create(String orderId, BigDecimal amount);
}
3、编写实现类
package com.imooc.pay.service.impl;
import com.imooc.pay.service.IPayService;
import com.lly835.bestpay.config.WxPayConfig;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import com.lly835.bestpay.model.PayRequest;
import com.lly835.bestpay.model.PayResponse;
import com.lly835.bestpay.service.impl.BestPayServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.xml.ws.Response;
import java.math.BigDecimal;
@Slf4j
@Service
public class PayService implements IPayService {
/**
*
* @param orderId
* @param amount
*/
@Override
public PayResponse create(String orderId, BigDecimal amount) {
WxPayConfig wxPayConfig = new WxPayConfig();
wxPayConfig.setAppId("wxd898fcb01713c658"); //设置AppId
wxPayConfig.setMchId("1483469312"); //设置商户Id
wxPayConfig.setMchKey("7mdApPMfXddfWWbbP4DUaVYm2wjyh3v3"); //设置商户密钥
wxPayConfig.setNotifyUrl("http://localhost");
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayConfig(wxPayConfig);
PayRequest request = new PayRequest();
request.setOrderName("9442081-支付测试");
request.setOrderId(orderId);
request.setOrderAmount(amount.doubleValue());
request.setPayTypeEnum(BestPayTypeEnum.WXPAY_NATIVE);
PayResponse response = bestPayService.pay(request);
log.info("response={}", response);
return response;
}
}
4、编写测试代码
package com.imooc.pay.service.impl;
import com.imooc.pay.PayApplication;
import com.imooc.pay.PayApplicationTests;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import static org.junit.Assert.*;
public class PayServiceTest extends PayApplicationTests {
@Autowired
private PayService payService;
@Test
public void create() {
payService.create("chenxin123456", BigDecimal.valueOf(0.01));
}
}
运行结果:
红线的部分生成二维码就可以完成支付!!
使用程序实现将支付链路转换为二维码
1、引入 spring-boot-starter-freemarker 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2、编写controller类
package com.imooc.pay.controller;
import com.imooc.pay.service.impl.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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/pay")
public class PayController {
@Autowired
private PayService payService;
@GetMapping("/create")
public ModelAndView create(@RequestParam("orderId") String orderId,
@RequestParam("amount") BigDecimal amount) {
PayResponse response = payService.create(orderId, amount);
Map map = new HashMap<>();
map.put("codeUrl", response.getCodeUrl());
return new ModelAndView("create", map);
}
}
3、在templates下编写create.ftl。两端 jquery 代码是从 https://www.bootcdn.cn/ 中拷贝的。qrcode可以将一段字符串变成一张二维码来进行显示。
<html>
<head>
<meta charset="utf-8">
<title>支付</title>
</head>
<body>
<div id="myQrcode"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
<script>
jQuery('#myQrcode').qrcode({
text:"${codeUrl}"
});
</script>
</body>
</html>
微信异步通知
- notify_url 要在微信后台设置吗?
- notify_url 一定要用域名吗?
1、在 NATAPP.cn 网站购买一个隧道,可以将127.0.0.1映射到公网,启动下载的natapp.exe可获得一个公网地址:
2、更改NotifyUrl的值:
wxPayConfig.setNotifyUrl("http://xiaoxin-mall.natapp1.cc/pay/notify");
3、在PayController.java中编写方法:
@PostMapping("/notify")
public void asyncNotify(@RequestBody String notifyData) {
log.info("notifyData={}", notifyData);
}
4、支付一笔订单,可以发现,在控制台,可以收到多次重复的返回信息。
5、现在我们收到返回信息后在本地进行验证,并将验证成功地消息返回微信,这样微信就不会再给我们发信息了。
PayService.java:
/**
* 异步通知处理
* @param notifyData
*/
@Override
public String asyncNotify(String notifyData) {
//1、签名校验
PayResponse payResponse = bestPayService.asyncNotify(notifyData);
log.info("payResponse = {}", payResponse);
//2、金额校验(从数据库查订单)
//3、修改订单支付状态
//4、告诉微信我已经收到
return "<xml>\n" +
" <return_code><![CDATA[SUCCESS]]></return_code>\n" +
" <return_msg><![CDATA[OK]]></return_msg>\n" +
"</xml>";
}
PayController.java:
@PostMapping("/notify")
@ResponseBody
public String asyncNotify(@RequestBody String notifyData) {
log.info("notifyData={}", notifyData);
return payService.asyncNotify(notifyData);
}
支付宝支付
1、支付宝密钥说明
- 一共涉及到4个密钥,分别是:商户应用公钥、商户应用私钥、支付宝公钥、支付宝私钥。
- 用户在本地生成商户应用公钥和商户应用私钥,然后将商户应用公钥与支付宝公钥进行绑定,支付宝的私钥存储在支付宝的后台,这个用户看不到。
- 发起支付:商户(商户应用私钥签名)—> 支付宝(商户应用公钥验签)
- 异步通知:支付宝(支付宝私钥签名)----> 商户(支付宝公钥验签)
- 注意:RSA签名不等于RAS加密。签名和加密的代码是不一样的。
2、编写代码,加入支付宝支付
接口IPayService:
public interface IPayService {
/**
* 创建/发起支付
*/
PayResponse create(String orderId, BigDecimal amount, BestPayTypeEnum bestPayTypeEnum);
/**
* 异步通知处理
*/
String asyncNotify(String notifyData);
}
BestPayConfig.java:
@Component
public class BestPayConfig {
@Bean
public BestPayService bestPayService() {
//微信支付配置
WxPayConfig wxPayConfig = new WxPayConfig();
wxPayConfig.setAppId("wxd898fcb01713c658"); //设置AppId
wxPayConfig.setMchId("1483469312"); //设置商户Id
wxPayConfig.setMchKey("7mdApPMfXddfWWbbP4DUaVYm2wjyh3v3"); //设置商户密钥
wxPayConfig.setNotifyUrl("http://xiaoxin-mall.natapp1.cc/pay/notify");
//支付宝配置
AliPayConfig aliPayConfig = new AliPayConfig();
aliPayConfig.setAppId("2018062960540016");
aliPayConfig.setPrivateKey("MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDUcbUUBaTJA4ngFx1xED2WHrkS4YCi3BRDPu/qU+MXj7VNy/ip+VKSK9aGPd2dRswkt/4DOoOxnnesJDcve+2W9y2gPoOdSVjGlEwTE6MqeB5f3l+RO9Kcb2zL4JwCRxlE0HHLeRdWtnmbelh1rg8zTFwGnoi1pbQajAT/FGDFqIpdI6FrEYbyeR/VNxFXIGK16Z7gfnWRnS4TP93O5ckcuaAcE8tqW16G7u41bKsnsv2149mucBM2hFSEVD/KcGYYke3pD7VpQdk0+WhxITSsa5I8DfSFRKokN7iJoKjYWjI4gN3fxFWLbymeIumoOWdJflwYI5oL+GdVIHfQ0ETLAgMBAAECggEBALaqurdnjuQkfcXIOlGAVGQjMKFycmgWcfnMQQAsdyRINe2Zx8tnDL+QoBm3UjmsqVWdOvVNt/TevCmwzh6vIYBgMsQJXKO+cG33D16L0Q1wUTW/gE7hsFtAV70J+TrgJXMNA/ufuBigN/oe/bbaHknOi4ZJhGUkALOe16D4xajNjHcYdvkfw5Zkv9nAt+GjFphe1lt35KIx6ahFzMcnzzHYy4ezDFdQWPkT7CqrL8Nh9KPm6Sjbzw2JP3RH91OU1q44gPj7yyLTs1oxt4gEqOgVnczJWXoH+eKRmxRoIvodnB4kf1W9LTzAc4Qs7OneDccvhrOoTD3MGhqW0poXZFkCgYEA6dxBCLnU5oeoctoltnh42DXCzEv/M/gkv9HMY/FFnuM6e0qU5EcFjQtTPRRW1S45ctZaHHhpGppHF7RG+okdALj4x2OC1NG9DL4HboVW3CJ4TzmEeZRkR+LWRWw7FA8eXefDBTC4ojAwqkmbvYP3dm6FjSfNBGYs43Wm6qKTcB0CgYEA6I5rPmAv6rB1BBXF8riNhuoDntbHFHOC9kCwuVJooDBBnxXNCqA9mTdnwE8hjgCgbO5lQkeeIRYiZwFYdCLPW78j6Np5CaX0/ZxcVSUQBFYc3d0e3po+rqpX6BGOj7qGhzDXvAs+HScu7ERy0Kbq6b/EbuLlR3oCKIGncGIaxAcCgYBJyWrjm+6mxgrKIjZf+mb2oQ/Tce8VsKe3tjRtHEVBOqTLHd8Yn6gKtpYO4Yn8PVd2+lb4QK247RCdVA5JIlX6UmJ8VtOC3qJtkM+7eWrMjjuzk4xO6Bkz7Uh6IwoI7DRCoMuRqau30MiqEguHoknEHl8ZCIPRbYOgSRDfW2h1qQKBgF9ODnFPphN+IVZ9PdRNAeMqgDVWO9wLwr38oPAx76LGY/44RwF1zgi+hgxv4YZ6h0RdJq5U/1773TltebyOj4BAAw1oi3YCxzYwID7co4XDbK0X85CykcGvGbuHhm8st/krcR4lVV1JM5esLYmI/nixGGWBIwl53OyQxffunJ19AoGBAOX9cBWFIWh5EHVXYvW90HRBQoo1AYtsdrxTedqrij6cWQms6ACeQZPd5O0V6/Lhz3Lw5NRnUl9MbzwOC7BBSBvhWwNzBOzFnaoOvBABa5nFZsdF5jSI3LJHbnxmMvxnKjCgogSGf+hfgR042b/WKDDBEA965MQJ59tCVhALD81z");
aliPayConfig.setAliPayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtojdtkETo4OEsQLeyyPwtWK9ZqYJANq6jjXC74vk9n/r88yW577y7VdxcK9X/F/wvR7D8of7lndYdhg6xZro0eO2skPZTU+A549J7tfzahVbIBAS+x1WPFJwPtVrfBBvkwHL8PT+YnMcxKyBxOa6wo8fzJs1NgU1+qnDCpwUFyv59GUfdzBvTPL1fY3ZzvRHFHbapevVltbO/jNV0thb8dafmcJXl8lnjQy3XlH3eTH28tlVfqickacfRl/WSD8WN3dGgF7dTDKYfSR7YB7jsHe6VzoHM3UnD9/yQbi/Z3ZrL7yOxEjq4tfrKlZIW7ZCoUpOU4QdPIRhLeC6nWyGrQIDAQAB");
aliPayConfig.setNotifyUrl("http://xiaoxin-mall.natapp1.cc/pay/notify");
aliPayConfig.setReturnUrl("http://127.0.0.1");
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayConfig(wxPayConfig);
bestPayService.setAliPayConfig(aliPayConfig);
return bestPayService;
}
}
PayService.java:
@Slf4j
@Service
public class PayService implements IPayService {
@Autowired
private BestPayService bestPayService;
/**
*
* @param orderId
* @param amount
*/
@Override
public PayResponse create(String orderId, BigDecimal amount, BestPayTypeEnum bestPayTypeEnum) {
PayRequest request = new PayRequest();
request.setOrderName("9442081-支付测试");
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("payResponse = {}", payResponse);
//2、金额校验(从数据库查订单)
//3、修改订单支付状态
//4、告诉微信我已经收到
if(payResponse.getPayPlatformEnum() == BestPayPlatformEnum.WX) {
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("异步通知中错误的支付平台");
}
}
PayController.java:
@Controller
@RequestMapping("/pay")
@Slf4j
public class PayController {
@Autowired
private PayService payService;
@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());
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) {
log.info("notifyData={}", notifyData);
return payService.asyncNotify(notifyData);
}
}
createForAlipayPC.ftl:
<html>
<head>
<meta charset="utf-8">
<title>支付</title>
</head>
<body>
${body}
</body>
</html>