目录
1、添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency> <!--日期序列化--> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.9.3</version> </dependency> <!--自动生成get set 构造方法所需的依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!--微信支付--> <!-- 生成二维码工具 --> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.2.0</version> </dependency> <!-- 微信支付所需sdk --> <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency> <!--实现http请求的依赖包--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <!--swagger依赖--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.9.1.RELEASE</version> </dependency>
2、封装工具类
2.1 HttpUtil
public class HttpUtil {
private String url;
private Map<String, String> param;
private int statusCode;//状态码
private String content;//主体部分
private String xmlParam;//生成的xml
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public String getXmlParam() {
return xmlParam;
}
public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}
public HttpUtil(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpUtil(String url) {
this.url = url;
}
public void setParameter(Map<String, String> map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null)
param = new HashMap<String, String>();
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
setEntity(http);
execute(http);
}
public void get() throws ClientProtocolException, IOException {
if (param != null) {
StringBuilder url = new StringBuilder(this.url);
boolean isFirst = true;
for (String key : param.keySet()) {
if (isFirst)
url.append("?");
else
url.append("&");
url.append(key).append("=").append(param.get(key));
}
this.url = url.toString();
}
HttpGet http = new HttpGet(url);
execute(http);
}
/**
* set http post,put param
*/
private void setEntity(HttpEntityEnclosingRequestBase http) {
if (param != null) {
List<NameValuePair> nvps = new LinkedList<NameValuePair>();
for (String key : param.keySet())
nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
}
if (xmlParam != null) {
http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
}
}
private void execute(HttpUriRequest http) throws ClientProtocolException,
IOException {
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
.build();
} else {
httpClient = HttpClients.createDefault();
}
CloseableHttpResponse response = httpClient.execute(http);
try {
if (response != null) {
if (response.getStatusLine() != null)
statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
// 响应内容
content = EntityUtils.toString(entity, Consts.UTF_8);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
}
public int getStatusCode() {
return statusCode;
}
public String getContent() throws ParseException, IOException {
return content;
}
}
2.2 WebServletUtil
可以根据上下文获取Reques和Response对象
public class WebServletUtil {
private static ServletRequestAttributes servletRequestAttributes;
static {
servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
}
public static HttpServletRequest getRequest(){
return servletRequestAttributes.getRequest();
}
public static HttpServletResponse getResponse(){
return servletRequestAttributes.getResponse();
}
}
2.3 Swagger2Conf
为了更方便测试接口集成了swagger文档
@Configuration
@EnableSwagger2//开启swagger
public class Swagger2Conf {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.gzh.weixinpay.controller"))
//过滤掉所有error或error.*页面
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("权限管理API文档")
.description("本文档描述了权限管理接口定义")
.version("1.0")
.contact(new Contact("gzh", "http://taobao.com", "1984274233@qq.com"))
.build();
}
}
2.4 向微信发送请求并获取code_url
package com.gzh.weixinpay.conf;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.gzh.weixinpay.util.HttpUtil;
import com.gzh.weixinpay.util.WebServletUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* TODO
*
* @author DELL
* @version 1.0
* @since 2022-08-12 21:00:56
*/
@Data
@Component("config")
@ConfigurationProperties(prefix = "weixin")//让配置参数能分离出来
@PropertySource("classpath:payconfig.properties")
public class WxConfig implements Serializable {
@Value("${APPID}")
private String APPID;
@Value("${MCH_ID}")
private String MCH_ID;
@Value("${KEY}")
private String KEY;
@Value("${SPBILL_CREATE_IP}")
private String SPBILL_CREATE_IP;
@Value("${NOTIFY_URL}")
private String NOTIFY_URL;
@Value("${TRADE_TYPE}")
private String TRADE_TYPE;
@Value("${PLACEANORDER_URL}")
private String PAY_URL;
@Value("${QUERY_URL}")
private String QRY_URL;
/**
*
* @param out_trade_no 订单编号
* @param total_fee 总金额
* @param expireTimeMin 失效时间
* @return
*/
public Map createNative(String out_trade_no, int total_fee , Integer expireTimeMin) throws JsonProcessingException {
Map<String, String> param = new HashMap();
param.put("appid", APPID);
param.put("mch_id", MCH_ID);
param.put("nonce_str", WXPayUtil.generateNonceStr());
param.put("body", "测试支付功能"); //定义订单内容
param.put("out_trade_no", out_trade_no); //商户订单编号
param.put("fee_type", "CNY");
param.put("total_fee", new ObjectMapper().writeValueAsString(total_fee)); //总金额
param.put("spbill_create_ip", SPBILL_CREATE_IP);
param.put("notify_url", NOTIFY_URL); //回调地址
param.put("trade_type", "NATIVE");
Date date = new Date();
long time = date.getTime();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String timeStart = simpleDateFormat.format(time);
String timeEnd = simpleDateFormat.format(time + expireTimeMin * 60000);
param.put("time_start", timeStart); //订单创建时间
param.put("time_expire", timeEnd); //定义失效时间
try {
String xmlParam = WXPayUtil.generateSignedXml(param, KEY); //参数转换
HttpUtil client = new HttpUtil(PAY_URL);
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
String result = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
if (resultMap.get("err_code") != null && resultMap.get("err_code").equals("ORDERPAID")) {
System.out.println("<--"+out_trade_no+"--> :订单已经支付!");
}
Map<String, String> map = new HashMap<>();
String code_url = resultMap.get("code_url");
map.put("code_url", code_url);
map.put("total_fee", String.valueOf(total_fee));
map.put("out_trade_no", out_trade_no);
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @param out_trade_no 订单id
* @return
*/
public Map queryPayStatus(String out_trade_no) {
Map param = new HashMap();
param.put("appid", APPID);
param.put("mch_id", MCH_ID);
param.put("out_trade_no", out_trade_no);
param.put("nonce_str", WXPayUtil.generateNonceStr());
try {
String xmlParam = WXPayUtil.generateSignedXml(param, KEY);
HttpUtil client = new HttpUtil(QRY_URL);
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
String result = client.getContent();
Map<String, String> map = WXPayUtil.xmlToMap(result);
if (map.get("err_code") != null && map.get("err_code").equals("ORDERNOTEXIST")) {
System.out.println("订单不存在!");
}
if (map.get("trade_state").equals("SUCCESS")) {
//支付成功,未支付为:NOTPAY
}
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
3、控制层
@RestController
@CrossOrigin
@RequestMapping("/order")
public class PayController {
@Resource
WxConfig config;
@PostMapping("/createPay")
public BusiResult createPayOrder(@RequestBody PayVo payVo) throws JsonProcessingException {
//向微信发起调用生成预付链接
Map res = config.createNative(payVo.getOrderId(),
payVo.getFee(),
payVo.getExpirTime());
System.out.println(res);
return BusiResult.success(res);
}
//生成二维码图片
@GetMapping("getImg")
public void getImg(String code_url){
if(code_url == null) throw new NullPointerException();
try {
//生成二维码配置
Map<EncodeHintType, Object> hints = new HashMap<>();
// 设置纠错等级
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
// 编码类型
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix bitMatrix = new MultiFormatWriter().encode(code_url, BarcodeFormat.QR_CODE, 400, 400, hints);
OutputStream outputStream = WebServletUtil.getResponse().getOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "png", outputStream);
} catch (Exception e){
e.printStackTrace();
}
}
@PostMapping("/queryPayStatus")
public BusiResult queryPayStatus(@RequestBody PayVo payVo){
//向微信发起调用生成预付链接
Map res = config.queryPayStatus(payVo.getOrderId());
System.out.println(res);
return BusiResult.success(res);
}
}
4、配置文件
payconfig.properties
#公众号appleId APPID=? #商户号 MCH_ID=? #商户密钥 KEY=? #APP和网页支付提交用户端ip, Native支付填调用微信支付API的机器IP, 即:服务器ip地址 SPBILL_CREATE_IP=127.0.0.1 #接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。(需要配置) NOTIFY_URL=http://225m5x.natappfree.cc/page/notify #支付方式,取值如下:JSAPI,NATIVE,APP TRADE_TYPE=NATIVE # 微信支付 - 统一下单地址 PLACEANORDER_URL=https://api.mch.weixin.qq.com/pay/unifiedorder QUERY_URL=https://api.mch.weixin.qq.com/pay/orderquery
5、测试
5.1、访问doc.html
5.2、获取二维码