SpringBoot微信扫码支付的实现示例

网友投稿 856 2023-02-16

SpringBoot微信扫码支付的实现示例

SpringBoot微信扫码支付的实现示例

一、首先导入生成二维码和微信支付环境

com.google.zxing

core

3.2.1

com.google.zxing

javase

3.2.0

com.github.wxpay

wxpay-sdk

0.0.3

二、在application.yml文件配置微信所有需的基本配置

1.导入

代码如下(示例):

# 服务器域名地址

server:

service-domain: //这里写你的域名地址

#微信app支付

pay:

wxpay:

app:

appID: 微信appid

mchID: 商户号

key: //这个key实在微信支付公众品台自己定义的key 要求36位

certPath: static/cert/wxpay/apiclient_cert.p12 # 从微信商户平台-的安全证书存放的路径、我放在resources下面,切记一定要看看target目录下的class文件下有没有打包apiclient_cert.p12文件

payNotifyUrl: # 微信支付成功的异步通知接口 这里引入你的回调接口。

//这里直接写https://域名:端口/接口地址,注意一定是线上的接口,因为微信访问不到你本地的接口

2.创建MyWXPayConfig类引入配置信息

代码如下(示例):

package com.example.gasstation.config;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

import java.io.InputStream;

@Data

@Component

@ConfigurationProperties(prefix = "pay.wxpay.app")

public class MyWXPayConfig implements WXPayConfig{

/**

* appID

*/

private String appID;

/**

* 商户号

*/

private String mchID;

/**

* API 密钥

*/

private String key;

/**

* API证书绝对路径 (本项目放在了 resources/cert/wxpay/apiclient_cert.p12")

*/

private String certRiFMhWPath;

/**

* HTTP(S) 连接超时时间,单位毫秒

*/

private int httpConnectTimeoutMs = 8000;

/**

* HTTP(S) 读数据超时时间,单位毫秒

*/

private int httpReadTimeoutMs = 10000;

/**

* 微信支付异步通知地址

*/

private String payNotifyUrl;

/**

* 微信退款异步通知地址

*/

private String refundNotifyUrl;

/**

* 统一下单url

*/

private final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

/** 这里实现了一个service层**/

@Override

public InputStream getCertStream() {

InputStream certStream =getClass().getClassLoader().getResourceAsStream(certPath);

return certStream;

}

//在同层级下面新建WXPayConfig service层

package com.example.gasstation.config;

import java.io.InputStream;

public interface WXPayConfig {

InputStream getCertStream();//不要问我为啥不另起一行,因为我懒

}

}

三、引入 WxPayServiceImpl 实现类

package com.example.gasstation.server.impl;

import com.example.gasstation.config.MyWXPayConfig;

import com.example.gasstation.entity.Result;

import com.example.gasstation.mapper.PayMapper;

import com.example.gasstation.model.Money_transfer;

import com.example.gasstation.model.Pay;

import com.example.gasstation.server.WxPayService;

import com.example.gasstation.util.HttpClientUtil;

import com.example.gasstation.util.WXPayUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.text.DecimalFormat;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Map;

import java.util.SortedMap;

import java.util.TreeMap;

@Service

public class WxPayServiceImpl implements WxPayService {

@Autowired

private MyWXPayConfig wxPayAppConfig;

@Autowired

private PayMapper payMapper;

@Override

public String save(String orderNo, double amount, String body,Integer uid) {

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式

// 1. 生成订单

// 订单号,流水号,金额,付款状态,创建时间

String product_id = WXPayUtils.generateUUID();

Pay pay = new Pay();//这里新建一个实体类 用处存入数据库

pay.setTradeNo(product_id);

pay.setOutTradeNo(orderNo);

pay.setBody(body);

pay.setPaystatus(1);

pay.setUid(uid);

pay.setTotalAmount(amount);

pay.setGmtCreate(df.format(new Date()));

pay.setTradeStatus("0");

pay.setAppId(wxPayAppConfig.getAppID());

// 生成预支付订单,保存到数据库

payMapper.insert(pay);

// 调用统一下单方法,返回 codeUrl 地址

String codeUrl = unifiedOrder(product_id,orderNo,amount,body);

return codeUrl;

}

private String unifiedOrder(String product_id, String orderNo, double amount, String body){

// 生成签名

try{

SortedMap params = new TreeMap<>();

params.put("appid",wxPayAppConfig.getAppID());

params.put("mch_id",wxPayAppConfig.getMchID());

params.put("nonce_str", WXPayUtils.generateUUID());

params.put("body",body); // 商品描述

params.put("out_trade_no",orderNo); // 商户订单号

params.put("total_fee",String.valueOf((int)(amount*100))); // 标价金额(单位为分)

params.put("spbill_create_ip", "这里写服务器IP"); // 终端IP

params.put("notify_url", wxPayAppConfig.getPayNotifyUrl()); // 异步接收微信支付结果通知的回调地址

params.put("trade_type","NATIVE"); // 交易类型

params.put("product_id",product_id); // 微信支付要求NATIVE支付,此参数必填

// sign 签名

String sign = WXPayUtils.createSign(params, wxPayAppConfig.getKey());

params.put("sign",sign);

System.out.println(sign);

// map转xml

String payXml = WXPayUtils.mapToXml(params);

System.out.println(payXml);

// 统一下单

String s = HttpClientUtil.doPost(wxPayAppConfig.getUNIFIED_ORDER_URL(), payXml, 10000);

if(null == s){

return null;

}

Map unifiedOrderMap = WXPayUtils.xmlToMap(s);

System.out.println(unifiedOrderMap.toString());

if(unifiedOrderMap != null){

// 前台添加定时器,进行轮询操作,直到支付完毕

return unifiedOrderMap.get("code_url");

}

} catch (Exception e){

e.printStackTrace();

}

return null;

}

}

四、引入WxPayService层

package com.example.gasstation.server;

import com.example.gasstation.model.Money_transfer;

public interface WxPayService {

String save(String orderNo, double amount, String body,Integer uid);

boolean callBackPayUpdate(String outTradeNo,String totalFee);

}

五、引入Util类

package com.example.gasstation.util;

import com.github.wxpay.sdk.WXPayUtil;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.w3c.dom.Node;

import org.w3c.dom.NodeList;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.transform.OutputKeys;

import javax.xml.transform.Transformer;

import javax.xml.transform.TransformerFactory;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;

import java.io.ByteArrayInputStream;

import java.io.InputStream;

import java.io.StringWriter;

import java.util.*;

/**

* @Author qjp

*/

public class WXPayUtils {

/**

* XML格式字符串转换为Map

*

* @param strXML XML字符串

* @return XML数据转换后的Map

* @throws Exception

*/

public static Map xmlToMap(String strXML) throws Exception {

try {

Map data = new HashMap();

DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();

InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));

org.w3c.dom.Document doc = documentBuilder.parse(stream);

doc.getDocumentElement().normalize();

NodeList nodeList = doc.getDocumentElement().getChildNodes();

for (int idx = 0; idx < nodeList.getLength(); ++idx) {

Node node = nodeList.item(idx);

if (node.getNodeType() == Node.ELEMENT_NODE) {

org.w3c.dom.Element element = (org.w3c.dom.Element) node;

data.put(element.getNodeName(), element.getTextContent());

}

}

try {

stream.close();

} catch (Exception ex) {

// do nothing

}

return data;

} catch (Exception ex) {

WXPayUtils.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);

throw ex;

}

}

/**

* 将Map转换为XML格式的字符串

*

* @param data Map类型数据

* @return XML格式的字符串

* @throws Exception

*/

public static String mapToXml(Map data) throws Exception {

Document document = WXPayXmlUtil.newDocument();

Element root = document.createElement("xml");

document.appendChild(root);

for (String key: data.keySet()) {

String value = data.get(key);

if (value == null) {

value = "";

}

value = value.trim();

Element filed = document.createElement(key);

filed.appendChild(document.createTextNode(value));

root.appendChild(filed);

}

TransformerFactory tf = TransformerFactory.newInstance();

Transformer transformer = tf.newTransformer();

DOMSource source = new DOMSource(document);

transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

transformer.setOutputProperty(OutputKeys.INDENT, "yes");

StringWriter writer = new StringWriter();

StreamResult result = new StreamResult(writer);

transformer.transform(source, result);

String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");

try {

writer.close();

}

catch (Exception ex) {

}

return output;

}

/**

* 生成微信支付sign

*/

public static String createSign(SortedMap params, String key){

StringBuilder sb = new StringBuilder();

Set> es = params.entrySet();

Iterator> it = es.iterator();

while(it.hasNext()){

Map.Entry entry = it.next();

String k = entry.getKey();

String v = entry.getValue();

if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){

sb.append(k + "=" + v + "&");

}

}

sb.append("key=").append(key);

String sign = MD5Util.MD5(sb.toString()).toUpperCase();

return sign;

}

/**

* 校验签名

* @param params

* @param key

* @return

*/

public static Boolean isCorrectSign(SortedMap params, String key){

String sign = createSign(params, key);

String wxPaySign = params.get("sign").toUpperCase();

return wxPaySign.equals(sign);

}

/**

* 获取有序map

* @param map

*/

public static SortedMap getSortedMap(Map map){

SortedMap sortedMap = new TreeMap<>();

Iterator it = map.keySet().iterator();

while(it.hasNext()){

String key = it.next();

String value = map.get(key);

String temp = "";

if(null != value){

temp = value.trim();

}

sortedMap.put(key, value);

}

return sortedMap;

}

/**

* 日志

* @return

*/

public static Logger getLogger() {

Logger logger = LoggerFactory.getLogger("wxpay java sdk");

return logger;

}

/**

* 获取当前时间戳,单位秒

* @return

*/

public static long getCurrentTimestamp() {

return System.currentTimeMillis()/1000;

}

/**

* 获取当前时间戳,单位毫秒

* @return

*/

public static long getCurrentTimestampMs() {

return System.currentTimeMillis();

}

/**

* 生成UUID(用来表示一笔订单)

* @return

*/

public static String generateUUID(){

String uuid = UUID.randomUUID().toString()

.replaceAll("-","")

.substring(0,32);

return uuid;

}

}

引入WXPayXmlUtil类

package com.example.gasstation.util;

import javax.xml.XMLConstants;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;

public RiFMhWfinal class WXPayXmlUtil {

public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);

documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

documentBuilderFactory.setXIncludeAware(false);

documentBuilderFactory.setExpandEntityReferences(false);

return documentBuilderFactory.newDocumentBuilder();

}

public static Document newDocument() throws ParserConfigurationException {

return newDocumentBuilder().newDocument();

}

}

六、引入WxPayController类

提示:到这里没有报错咱们已经成功一半啦

@RestController

@RequestMapping("/wxPay")

public class WxPayController {

@Autowired

private WxPayService wxPayService;

@Autowired

private MyWXPayConfig wxPayConfig;

@Autowired

private WebMvcConfigurer webMvcConfigurer;

/**

* 微信支付 生成二维码

*

* @param money

* @return

*/

@GetMapping("/pay")

public void wxPay(Double money,String body,Integer uid ,HttpServletResponse response){

Double amount = money;//金额

SimpleDateFormat date = new SimpleDateFormat("yyyyMMddHHmmss");

String orderNo = date.format(new Date()) + WXPayUtils.getCurrentTimestampMs();

String url_code = wxPayService.save(orderNo, amount, body,uid);

System.out.println("url_code:----------"+url_code);

if(url_code == null){

throw new NullPointerException();

}

try {

// 生成二维码配置

Map hints = new HashMap<>();

// 设置纠错等级

hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);

// 编码类型

hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");

BitMatrix bitMatrix = new MultiFormatWriter().encode(url_code, BarcodeFormat.QR_CODE, 400, 400, hints);

OutputStream outputStream = response.getOutputStream();

MatrixToImageWriter.writeToStream(bitMatrix, "png", outputStream);

} catch (Exception e){

e.printStackTrace();

}

}

/**

* 微信支付回调接口

*/

@RequestMapping("/callback")

public void OrderCallBack(HttpServletRequest request, HttpServletResponse response) {

InputStream inputStream = null;

try {

inputStream = request.getInputStream();

// BufferedReader是包装设计模式,性能更高

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));

StringBuffer stringBuffer = new StringBuffer();

String line;

while ((line = bufferedReader.readLine()) != null) {

stringBuffer.append(line);

}

bufferedReader.close();

inputStream.close();

Map callBackMap = WXPayUtils.xmlToMap(stringBuffer.toString());

System.out.println(callBackMap.toString());

SortedMap sortedMap = WXPayUtils.getSortedMap(callBackMap);

// 校验签名是否正确

if (WXPayUtils.isCorrectSign(sortedMap, wxPayConfig.getKey())) {

System.out.println("签名校验成功!");

// 更新订单状态

if ("SUCCESS".equals(sortedMap.get("result_code"))) {

String outTradeNo = sortedMap.get("out_trade_no"); // 流水号

String totalFee = sortedMap.get("total_fee"); // 交易金额

if (wxPayService.callBackPayUpdate(outTradeNo, totalFee)) { // 通知微信订单处理成功

response.setContentType("text/xml");

response.setContentType("content-type");

response.getWriter().println(" <![CDATA[SUCCESS]]> <![CDATA[OK]]> ");

//这里说明告诉微信你已经成功啦,别给老子重复回调我的方法啦,这里有一个坑,

response.setContentType("text/xml");

response.getWriter().println("SUCCESS")

//本身我就只有这两句话,然后就导致微信一直回调我的方法,废了半天的劲才搞好啦,

//原因就是格式不对,给他返回的值他不认识,这里可以看一下微信的支付开发文档,虽然文档写的很垃圾

}

}

// 未成功,就都处理为失败订单

response.setContentType("text/html");

response.getWriter().println("fail");

}

} catch (IOException e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

}

七、MD5加密

@Slf4j

public class MD5Util {

public static String MD5(String source) {

return encodeMd5(source.getBytes());

}

private static String encodeMd5(byte[] source) {

try {

return encodeHex(MessageDigest.getInstance("MD5").digest(source));

} catch (NoSuchAlgorithmException e) {

throw new IllegalStateException(e.getMessage(), e);

}

}

private static String encodeHex(byte[] bytes) {

StringBuffer buffer = new StringBuffer(bytes.length * 2);

for (int i = 0; i < bytes.length; i++) {

if(((int) bytes[i] & 0xff) < 0x10) {

buffer.append("0");

}

buffer.append(Long.toString((int) bytes[i] & 0xff, 16));

}

return buffer.toString();

}

}

总结

有什么不对的地方欢迎各位码友指出问题,因为我也是第一次做这个微信扫码支付

回调方法返回类型是void 你设置其他返回类型,就会跟你 给微信返的起冲突,就会导致报错

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微信小程序投票行为分析(小程序投票套路)
下一篇:SpringBoot日志框架如何使用
相关文章

 发表评论

暂时没有评论,来抢沙发吧~