SpringBoot整合微信支付(Native)

2022-09-01 09:37:18

一、微信扫码支付流程

1、微信支付开发文档:微信支付开发文档
2、微信扫码支付业务流程

3、个人理解的支付流程
①用户在商户平台下单
②商户生成商户订单,并调起微信统一下单接口
③微信生成预支付订单,返回预支付的交易连接(code_url),我们根据code_url 生成二维码,提供给消费者扫码。值得注意的是:微信对于同一商户订单号且同一价格的商品可以生成多个预付单,我们可以设置预付单的有效时间(time_expire),预付单过期后,刷新二维码支付页面便会生成一个预付单,此时会发现二维码变了,即返回的预支付的交易连接(code_url)变了。
④用户扫描二维码,跳出支付页面
⑤用户确认支付,输入密码,支付成功
⑥交易成功后,微信返回支付成功页面。

前台界面编写

文档结构

支付页面:wxpay.html

qruous.js下载:

qruous.js下载:二维码生成工具及vue工具类-Node.js文档类资源-CSDN下载

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<!-- 引入vue组件进行调用 -->
		<script src="js/vue.js"></script>
		<script src="js/axios.min.js"></script>
		<!-- 二维码生成插件 -->
		<script src="js/qrious.js"></script>
	</head>
	<body>
		<input type="button" name="buy" id="buy" value="支付" onClick="buy(11)" />
		<input type="button" name="buy" id="buy" value="查询" onClick="queryPay(568718542894128)" />
		<div class="erweima" id = "erweima">
			<div class="hr">
				<div class="tc" @click="yinc"></div>
			</div>
			<img id="qrious">
			<div class="saosao">
				<p>请使用微信扫一扫</p>
				<p>扫描二维码支付</p>
			</div>
			<div id="zfcg">支付成功!</div>
		</div>
		
	</body>
	<script>
		
	  // 点击支付按钮调用后端代码生成预付链接,将预付链接信息生成到QRious value中动态生成二维码链接
	  function buy(orderId) {
	  	document.getElementById("qrious").setAttribute("src", "");
	  	document.getElementById("erweima").style.visibility= "visible";
	  	document.getElementById("erweima").style.width="600px";
	  	document.getElementById("erweima").style.opacity="1";
		
		let order = {};
		order.orderId = "568718542894128";
		order.fee = "1";//默认金额 单位分
		order.expirTime = 5; //失效时间
		
		axios.post(`http://localhost:8080/order/createPay`, order).then(res=>{
			let obj = res.data;
			console.log(obj)
			if (res.status === 200) {
				var qr = new QRious({
					element: document.getElementById('qrious'),
					size: 250,
					level: 'H',
					value: obj.data.code_url
				});
				setTimeout(() => {
					queryPay(orderId)
				}, 10000);
			}
		})
	}
	
	function queryPay(orderId){
		let order = {};
		order.orderId=orderId;
		axios.post(`http://localhost:8080/order/queryPayStatus`, order).then(res=>{
			let obj = res.data;
			console.log(obj)
			setTimeout(() => {
				location.href = "success.html";
			}, 10000);
		})
	}
	
	
	
	</script>
	
	<style>
		#buy {
			float: none;
		}
	
		.erweima {
			/* width: 600px; */
			width: 0px;
			height: 500px;
			background-color: gray;
			opacity: 0.98;
			position: absolute;
			left: 50%;
			top: 50%;
			transform: translate(-50%, -50%);
			z-index: 99;
			/* display: none; */
			visibility: hidden;
			opacity: 0;
			transition: 0.45s all;
		}
	
		.hr {
			height: 30px;
			background-color: #333;
			color: #fff;
		}
	
		.tc::after {
			content: "X";
			position: absolute;
			right: 0px;
			/* line-height: 30px; */
			/* background-color: red; */
			padding: 5px 8px;
			font-size: 15px;
			transition: 0.4s all;
		}
	
		.tc:hover::after {
			background-color: #f00;
		}
	
		#qrious {
			width: 250px;
			height: 250px;
			/* margin: 100px auto; */
			position: absolute;
			left: 50%;
			top: 50%;
			transform: translate(-50%, -50%);
			background-image: url(./images/jiaz.gif);
			background-position: center;
		}
	
		.saosao {
			position: absolute;
			left: 50%;
			top: 85%;
			transform: translate(-50%, -50%);
			text-align: center;
			color: #fff;
		}
	
		.saosao p {
			margin-top: 10px;
		}
	
		#zfcg {
			width: 250px;
			height: 250px;
			color: #0cdd0c;
			background-color: black;
			position: absolute;
			left: 50%;
			top: 50%;
			transform: translate(-50%, -50%);
			text-align: center;
			line-height: 250px;
			font-size: 28px;
			opacity: 0.85;
			font-weight: 600;
			display: none;
		}
	
		.bottom {
			height: 100px;
			width: 100%;
			float: left;
		}
	</style>
</html>

说明:上面静态代码已将订单编号写死,应根据实际业务动态填写相应订单编号;设置10秒自动跳转,无法判断是否已支付。

成功后跳转页面:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h1>支付成功跳转页面</h1>
	</body>
</html>

后台代码:

引入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


        <!--微信支付依赖包-->
        <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>

vo类:

支付定义实体

package com.weiah.wxpay.vo;

import lombok.Data;

@Data
public class PayVo {
    private String orderId;//订单编号
    private String fee; //金额
    private Integer expirTime; //失效时间
}

定义返回结果:

package com.weiah.wxpay.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class BusiResult implements Serializable {
    private Integer status; //200成功  201失败
    private String  msg;    //提示信息
    private Object  data;   //服务器返回值数据

    public static BusiResult fail(){

        return new BusiResult(201,"业务调用失败!!",null);
    }

    public static BusiResult fail(int code, String msg){

        return new BusiResult(code, msg, null);
    }

    public static BusiResult success(){

        return new BusiResult(200,"业务调用成功!!",null);
    }


    public static BusiResult success(Object data){

        return new BusiResult(200,"业务调用成功!!",data);
    }

    public static BusiResult success(String msg,Object data){

        return new BusiResult(200,msg,data);
    }
}

新建Http请求工具类:

package com.weiah.wxpay.vo;


import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


/**
 * http请求客户端
 */
public class HttpUtil {
    private String url;
    private Map<String, String> param;
    private int statusCode;
    private String content;
    private String xmlParam;
    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;
    }

}

定义微信支付公共工具类:

package com.weiah.wxpay.util;

import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Value;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

public class WXPayPubUtil {

    private static final String APP_ID = "wx8397f8696b538317";

    private static final String PARTNER = "1473426802";

    private static final String PARTNER_KEY = "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb";

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

    private static final String QRY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";

    /**
     *
     * @param out_trade_no 订单编号
     * @param total_fee   总金额
     * @param expireTimeMin  失效时间
     * @return
     */
    public static Map createNative(String out_trade_no, String total_fee , Integer expireTimeMin) {
        Map<String, String> param = new HashMap();
        param.put("appid", APP_ID);
        param.put("mch_id", PARTNER);
        param.put("nonce_str", WXPayUtil.generateNonceStr());
        param.put("body", "测试支付功能"); //定义订单内容
        param.put("out_trade_no", out_trade_no);  //商户订单编号
        param.put("total_fee", total_fee);  //总金额
        param.put("spbill_create_ip", "127.0.0.1");
        param.put("notify_url", "http://www.tedu.cn"); //回调地址
        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, PARTNER_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<>();
            map.put("code_url", resultMap.get("code_url"));
            map.put("total_fee", 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 static Map queryPayStatus(String out_trade_no) {

        Map param = new HashMap();
        param.put("appid", APP_ID);
        param.put("mch_id", PARTNER);
        param.put("out_trade_no", out_trade_no);
        param.put("nonce_str", WXPayUtil.generateNonceStr());
        try {
            String xmlParam = WXPayUtil.generateSignedXml(param, PARTNER_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;
        }
    }


}

controller类:

package com.weiah.wxpay.controller;

import com.weiah.wxpay.util.WXPayPubUtil;
import com.weiah.wxpay.vo.BusiResult;
import com.weiah.wxpay.vo.PayVo;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@CrossOrigin
@RequestMapping("/order")
public class PayController {

    @PostMapping("/createPay")
    public BusiResult createPayOrder(@RequestBody PayVo payVo){
        //向微信发起调用生成预付链接
        Map res = WXPayPubUtil.createNative(payVo.getOrderId(),
                                            payVo.getFee(),
                                            payVo.getExpirTime());
        System.out.println(res);
        return BusiResult.success(res);
    }


    @PostMapping("/queryPayStatus")
    public BusiResult queryPayStatus(@RequestBody PayVo payVo){
        //向微信发起调用生成预付链接
        Map res = WXPayPubUtil.queryPayStatus(payVo.getOrderId());
        System.out.println(res);
        return BusiResult.success(res);
    }

}


以上可实现简单支付查询功能。

  • 作者:尘风随缘
  • 原文链接:https://blog.csdn.net/qq_29305715/article/details/123521227
    更新时间:2022-09-01 09:37:18