关于准备工作,微信扫码支付模式二:官方文档地址在这 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 可以先看看,实际上需要准备的东西有以下几个:
将微信官方的wxpay-sdk-3.0.9打包并在自己项目根目录下拆关键libs文件夹放入文件夹内
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
<!-- 微信支付 开始-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.9</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/wxpay-sdk-3.0.9.jar</systemPath>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
开发开始
配置 WxPayConfig
配置文件格式见上图
package com.bootdo.payment.config;
import com.github.wxpay.sdk.IWXPayDomain;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.*;
/**
* 微信支付配置文件
*/
@Component
@ConfigurationProperties(prefix = "pay.wxpay.app")
public class WxPayConfig extends WXPayConfig {
/**
* appID
*/
private String appID;
/**
* 商户号
*/
private String mchID;
/**
* API 密钥
*/
private String key;
/**
* API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")
*/
private String certPath;
/**
* HTTP(S) 连接超时时间,单位毫秒
*/
private int httpConnectTimeoutMs = 8000;
/**
* HTTP(S) 读数据超时时间,单位毫秒
*/
private int httpReadTimeoutMs = 10000;
/**
* 微信支付异步通知地址
*/
private String payNotifyUrl;
/**
* 终端IP
*/
private String spbill_create_ip;
/**
* 微信退款异步通知地址
*/
private String refundNotifyUrl;
/**
* 微信小程序支付回回调
*/
private String appPayNotifyUrl;
/**
* 获取商户证书内容(这里证书需要到微信商户平台进行下载)
*
* @return 商户证书内容
*/
@Override
public InputStream getCertStream() {
InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);
return certStream;
}
@Override
public String getAppID() {
return appID;
}
public void setAppID(String appID) {
this.appID = appID;
}
@Override
public String getMchID() {
return mchID;
}
public void setMchID(String mchID) {
this.mchID = mchID;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
@Override
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getCertPath() {
return certPath;
}
public void setCertPath(String certPath) {
this.certPath = certPath;
}
@Override
public int getHttpConnectTimeoutMs() {
return httpConnectTimeoutMs;
}
public void setHttpConnectTimeoutMs(int httpConnectTimeoutMs) {
this.httpConnectTimeoutMs = httpConnectTimeoutMs;
}
@Override
public int getHttpReadTimeoutMs() {
return httpReadTimeoutMs;
}
public void setHttpReadTimeoutMs(int httpReadTimeoutMs) {
this.httpReadTimeoutMs = httpReadTimeoutMs;
}
public String getPayNotifyUrl() {
return payNotifyUrl;
}
public void setPayNotifyUrl(String payNotifyUrl) {
this.payNotifyUrl = payNotifyUrl;
}
public String getRefundNotifyUrl() {
return refundNotifyUrl;
}
public void setRefundNotifyUrl(String refundNotifyUrl) {
this.refundNotifyUrl = refundNotifyUrl;
}
public String getAppPayNotifyUrl() {
return appPayNotifyUrl;
}
public void setAppPayNotifyUrl(String appPayNotifyUrl) {
this.appPayNotifyUrl = appPayNotifyUrl;
}
@Override
public IWXPayDomain getWXPayDomain() {
IWXPayDomain iwxPayDomain = new IWXPayDomain() {
@Override
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
@Override
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
}
};
return iwxPayDomain;
}
}
创建 WxPayService 接口
package com.bootdo.payment.service;
import com.bootdo.common.utils.R;
import java.util.Map;
/**
* 微信支付服务接口
*/
public interface WxPayService {
/**
* 微信支付统一下单
* @param orderNo: 订单编号
* @param amount: 实际支付金额
* @param body: 订单描述
* @return
*/
R unifiedOrder(String orderNo, double amount, String body,Integer paymentType) ;
/**
* 订单支付异步通知
* @param notifyStr: 微信异步通知消息字符串
* @return
*/
String notify(String notifyStr) throws Exception;
/**
* 退款
* @param orderNo: 订单编号
* @param amount: 实际支付金额
* @param refundReason: 退款原因
* @return
*/
R refund(String orderNo, double amount, String refundReason) throws Exception;
/**
* 关闭订单
* @param orderNo 订单编号
* @return
*/
R closeOrder(String orderNo);
/**
* 查询订单
* @param orderNo 订单编号
* @return
*/
Map<String,Object> orderQuery(String orderNo);
}
实现类 WxPayServiceImpl
package com.bootdo.payment.service.impl;
import com.bootdo.common.utils.R;
import com.bootdo.common.utils.StringUtils;
import com.bootdo.payment.config.WxPayConfig;
import com.bootdo.payment.domain.SelfmachineDO;
import com.bootdo.payment.service.SelfmachineService;
import com.bootdo.payment.service.WxPayService;
import com.bootdo.payment.util.PayToolUtil;
import com.bootdo.payment.util.QrCodeUtil;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class WxPayServiceImpl implements WxPayService {
private final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
@Autowired
private WxPayConfig wxPayAppConfig;
@Autowired
private SelfmachineService selfmachineService;
@Override
public R unifiedOrder(String orderNo, double amount, String body,Integer paymentType) {
Map<String, String> returnMap = new HashMap<>();
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
try {
WXPay wxpay = new WXPay(wxPayAppConfig);
// 商品描述
requestMap.put("body", body);
// 商户订单号
requestMap.put("out_trade_no", orderNo);
// 总金额
requestMap.put("total_fee", String.valueOf((int)(amount*100)));
// 终端IP
requestMap.put("spbill_create_ip", wxPayAppConfig.getSpbill_create_ip());
//支付类型
requestMap.put("trade_type", "NATIVE");
// 接收微信支付异步通知回调地址
requestMap.put("notify_url", wxPayAppConfig.getPayNotifyUrl());
//统一下单接口
Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
//输出返回信息
resultMap.forEach((k,v) -> logger.info("订单key:{},订单value:{}",k, resultMap.get(k)));
//获取返回码
String returnCode = resultMap.get("return_code");
String returnMsg = resultMap.get("return_msg");
//若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
if ("SUCCESS".equals(returnCode)) {
String resultCode = resultMap.get("result_code");
String errCodeDes = resultMap.get("err_code_des");
if ("SUCCESS".equals(resultCode)) {
responseMap = resultMap;
}
}
if (responseMap == null || responseMap.isEmpty()) {
return R.error("获取预支付交易会话标识失败");
}
//生成二维码
returnMap.put("qrCode_img", QrCodeUtil.generateQrCode(resultMap.get("code_url")));
SelfmachineDO selfmachineDO = new SelfmachineDO(orderNo,paymentType,new BigDecimal(Double.valueOf(amount)),1,new Date());
if(selfmachineService.save(selfmachineDO)<0){
return R.error("订单信息保存失败,请刷新重试");
}
return R.ok().put("data", returnMap);
} catch (Exception e) {
logger.error("订单号:{},错误信息:{}", orderNo, e.getMessage());
return R.error("微信支付统一下单失败");
}
}
@Override
public String notify(String notifyStr) throws Exception {
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
// 转换成map
Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
WXPay wxpayApp = new WXPay(wxPayAppConfig);
if (wxpayApp.isResponseSignatureValid(resultMap)) {
//状态
String returnCode = resultMap.get("return_code");
//商户订单号
String outTradeNo = resultMap.get("out_trade_no");
if (returnCode.equals("SUCCESS")) {
if (! StringUtils.isEmpty(outTradeNo)) {
//根据业务流程,修改数据库订单支付状态,和其他数据的相应状态
logger.info("微信支付回调成功,订单号:{}", outTradeNo);
xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlBack;
}
@Override
public R refund(String orderNo, double amount, String refundReason) throws Exception {
if(StringUtils.isEmpty(orderNo)){
return R.error("订单编号不能为空");
}
if(amount <= 0){
return R.error("退款金额必须大于0");
}
Map<String, String> responseMap = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
WXPay wxpay = new WXPay(wxPayAppConfig);
requestMap.put("out_trade_no", orderNo);
//商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 自行手动生成
requestMap.put("out_refund_no", "");
requestMap.put("total_fee", "订单总金额");
//所需退款金额
requestMap.put("refund_fee", String.valueOf((int)(amount*100)));
requestMap.put("refund_desc", refundReason);
try {
responseMap = wxpay.refund(requestMap);
} catch (Exception e) {
e.printStackTrace();
}
responseMap.forEach((k, v) -> logger.info("订单key:{},订单value:{}",k, v));
//返回状态码
String return_code = responseMap.get("return_code");
//返回信息
String return_msg = responseMap.get("return_msg");
if ("SUCCESS".equals(return_code)) {
//业务结果
String result_code = responseMap.get("result_code");
//错误代码描述
String err_code_des = responseMap.get("err_code_des");
if ("SUCCESS".equals(result_code)) {
//表示退款申请接受成功,结果通过退款查询接口查询
//修改用户订单状态为退款申请中或已退款。退款异步通知根据需求,可选
return R.ok("退款申请成功");
} else {
logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
return R.error(err_code_des);
}
} else {
logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
return R.error(return_msg);
}
}
@Override
public R closeOrder(String orderNo) {
Map<String,String> map = new HashMap<>();
String returnMsg="";
//关单前先查询订单状态
Map<String,Object> mpas = orderQuery(orderNo);
if (!mpas.get("trade_state").equals("NOTPAY")) {
return R.error(mpas.get("trade_state_desc").toString());
}
try {
WXPay wxpay = new WXPay(wxPayAppConfig);
// 商户订单号
map.put("out_trade_no", orderNo);
//公众账号ID
map.put("appid", wxPayAppConfig.getAppID());
//商户号
map.put("mch_id", wxPayAppConfig.getMchID());
//商户订单号
map.put("out_trade_no", orderNo);
//签名
map.put("sign", WXPayUtil.generateSignature(map, wxPayAppConfig.getKey()));
//随机字符串
map.put("nonce_str", String.valueOf(PayToolUtil.buildRandom(32)));
Map<String, String> resultMap = wxpay.closeOrder(map);
resultMap.forEach((k,v) -> logger.info("订单key:{},订单value:{}",k, resultMap.get(k)));
//获取返回码
String returnCode = resultMap.get("return_code");
returnMsg = resultMap.get("return_msg");
//若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
if ("SUCCESS".equals(returnCode)) {
String resultCode = resultMap.get("result_code");
String errCodeDes = resultMap.get("err_code_des");
if ("SUCCESS".equals(resultCode)) {
return R.ok();
}
}
} catch (Exception e) {
e.printStackTrace();
logger.info("订单号:{}错误信息:{}", orderNo, returnMsg);
return R.error(returnMsg);
}
return R.error("订单关闭失败");
}
@Override
public Map<String,Object> orderQuery(String orderNo) {
Map<String ,Object> map = new HashMap<>();
Map<String, String> requestMap = new HashMap<>();
// 商户订单号
requestMap.put("out_trade_no", orderNo);
//公众账号ID
requestMap.put("appid", wxPayAppConfig.getAppID());
//商户号
requestMap.put("mch_id", wxPayAppConfig.getMchID());
//随机字符串
requestMap.put("nonce_str", String.valueOf(PayToolUtil.buildRandom(32)));
String returnMsg="";
try {
WXPay wxpay = new WXPay(wxPayAppConfig);
//签名
requestMap.put("sign", WXPayUtil.generateSignature(requestMap, wxPayAppConfig.getKey()));
Map<String, String> resultMap = wxpay.orderQuery(requestMap);
resultMap.forEach((k,v) -> logger.info("订单key:{},订单value:{}",k, resultMap.get(k)));
//获取返回码
String returnCode = resultMap.get("return_code");
returnMsg = resultMap.get("return_msg");
//若返回码为SUCCESS,则会返回一个result_code,再对该result_code进行判断
if ("SUCCESS".equals(returnCode)) {
String resultCode = resultMap.get("result_code");
String errCodeDes = resultMap.get("err_code_des");
if ("SUCCESS".equals(resultCode)) {
map.put("success",true);
map.put("trade_state",resultMap.get("trade_state"));
map.put("trade_state_desc",resultMap.get("trade_state_desc"));
return map;
}else{
System.out.println("-*****************************************");
map.put("trade_state",resultMap.get("result_code"));
return map;
}
}
} catch (Exception e) {
e.printStackTrace();
map.put("success",false);
return map;
}
map.put("success",false);
return map;
}
}
控制器层 WxPayController
package com.bootdo.payment.controller;
import com.bootdo.common.utils.R;
import com.bootdo.payment.domain.SelfmachineDO;
import com.bootdo.payment.service.SelfmachineService;
import com.bootdo.payment.service.WxPayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
/**
* 微信支付
* @author snow
*/
@Api(tags = "微信支付Controller")
@RestController
@RequestMapping("/api/wxPay")
@CrossOrigin
public class WxPayController{
@Autowired
private WxPayService wxPayService;
private final Logger logger = LoggerFactory.getLogger(WxPayController.class);
@Autowired
private SelfmachineService selfmachineService;
/**
* 微信扫码支付统一下单接口
* @param orderNo 订单号
* @param amount 订单金额
* @param body 商品名称
* @param paymentType 支付类型
* @param request
* @return
*/
@ApiOperation("微信扫码支付统一下单接口")
@ApiImplicitParams({ @ApiImplicitParam( dataType = "String", name = "orderNo", value = "订单号", required = true),
@ApiImplicitParam( dataType = "String", name = "amount", value = "订单金额", required = true),
@ApiImplicitParam( dataType = "double", name = "body", value = "商品名称", required = true)})
@GetMapping("/unifiedOrder")
public R unifiedOrder(@RequestParam("orderNo") String orderNo, @RequestParam("amount") double amount, @RequestParam("body") String body,@RequestParam("paymentType") Integer paymentType,
HttpServletRequest request) {
System.out.println(orderNo);
try {
// 1、验证订单是否存在
Map<String,Object> map = wxPayService.orderQuery(orderNo);
logger.info(map.toString());
//NOTPAY—未支付
//REVOKED—已撤销(付款码支付)
if(!map.get("trade_state").equals("NOTPAY")&&!map.get("trade_state").equals("REVOKED")&&!map.get("trade_state").equals("FAIL")){
return R.error("该订单"+map.get("trade_state_desc").toString());
}
System.out.println("*-*-*-*--**-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*");
// 2、开始微信支付统一下单
R resultMap = wxPayService.unifiedOrder(orderNo, amount, body,paymentType);
System.out.println(resultMap.get("data"));
return resultMap;
} catch (Exception e) {
logger.error(e.getMessage());
return R.error("运行异常,请联系管理员");
}
}
/**
* 微信扫码支付微信支付异步通知
*/
@RequestMapping(value = "/notify")
public String payNotify(HttpServletRequest request) {
InputStream is = null;
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
is = request.getInputStream();
// 将InputStream转换成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
xmlBack = wxPayService.notify(sb.toString());
} catch (Exception e) {
logger.error("微信手机支付回调通知失败:", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return xmlBack;
}
/**
* 微信扫码支付退款
* @param orderNo 订单号
* @param amount 退款金额
* @param refundReason 退款原因
* @return
* @throws Exception
*/
@ApiIgnore
@PostMapping("/refund")
public R refund(@RequestParam String orderNo, @RequestParam double amount,@RequestParam(required = false) String refundReason) throws Exception {
return wxPayService.refund(orderNo, amount, refundReason);
}
/**
* 微信扫码支付关闭订单
* @param orderNo 订单编号
* @return
*/
@PostMapping("/closeOrder")
public R closeOrder(@RequestParam String orderNo){
R r = wxPayService.closeOrder(orderNo);
if(Integer.parseInt(r.get("code").toString())==0){
//闭单成功修改自己的业务
return r.ok();
}
return r.error("订单撤销失败");
}
/**
* 微信扫码支付查询订单
* @param orderNo 订单编号
* @return
*/
@PostMapping("/orderQuery")
public R orderQuery(@RequestParam String orderNo){
Map<String,Object> map = wxPayService.orderQuery(orderNo);
if(map.isEmpty()){
return R.ok();
}
return R.error();
}
/**
* 微信扫码支付根据订单编号查询订单状态
* @param orderNo 订单编号
* @return
*/
@PostMapping("/getOrderInfo")
public R getOrderInfo(@RequestParam String orderNo){
SelfmachineDO selfmachineDO = selfmachineService.get(orderNo);
if (selfmachineDO!=null) {
return R.ok().put("data",selfmachineDO);
}
return R.error("订单状态获取失败");
}
@GetMapping("/getOrder")
public R getOrder(@RequestParam("orderNo") String orderNo){
SelfmachineDO selfmachineDO = selfmachineService.get(orderNo);
if (selfmachineDO != null) {
return R.ok().put("data",selfmachineDO);
}
return R.error("订单状态获取失败");
}
@PostMapping("/updata")
public R update(SelfmachineDO selfmachineDO){
if(selfmachineService.update(selfmachineDO) > 0){
R.ok();
}
return R.error("订单状态修改失败");
}
/**
* 放弃支付
* @param orderNo
* @return
*/
@PostMapping("/giveUpPayMent")
public R giveUpPayMent(@RequestParam String orderNo){
//1·查询支付订单状态
Map<String,Object> map = wxPayService.orderQuery(orderNo);
//NOTPAY—未支付
//REVOKED—已撤销(付款码支付)
if(map.get("trade_state").equals("NOTPAY")){
R r = wxPayService.closeOrder(orderNo);
//2·撤销订单
if(Integer.parseInt(r.get("code").toString())==0){
//闭单成功修改自己的业务
return r.ok("订单撤销成功!");
}
return r.ok("订单撤销成功!");
}
return R.error(map.get("trade_state").toString());
}
}
正常我们这里就从微信服务器获取了一个支付url,形如weixin://wxpay/bizpayurl?pr=pIxXXXX,之后我们就需要把这个url生成一个二维码,然后就可以使用自己手机微信端扫码支付了。关于二维码生成有很多种方法,各位各取所需吧,我这里提供一个google的二维码生成接口:
<!-- 二维码生成-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
<scope>compile</scope>
</dependency>
QrCodeUtil
package com.bootdo.payment.util;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
/**
* 二维码生成
*/
public class QrCodeUtil {
public static String generateQrCode(String code_url){
String code="data:image/png;base64,";
Base64.Encoder encoder = Base64.getEncoder();
try {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
//返回给应用层的 tempToken 是经过base64加密返回后端需要解密
BitMatrix bitMatrix = qrCodeWriter.encode(code_url, BarcodeFormat.QR_CODE, 430, 430);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
code += encoder.encodeToString(outputStream.toByteArray());
} catch (IOException | WriterException e) {
e.printStackTrace();
}
return code;
}
}
到此微信支付完成,此文章仅作为自己的开发经验记录,如有不合理之处还请给位指点一二,谢谢