Java 微信native扫码支付 亲测有用

最近在网上总结了spring cloud 微信扫码支付的流程,
本人是刚入行的小白,有不对的地方请大家指出
也欢迎大家来多多交流

我的商户APPID和秘钥的一些配置信息,是公司的 这些需要微信的商户认证
在这里插入图片描述

一.微信支付类型

微信native扫码支付

链接: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=1_1.
在这里插入图片描述

二. native扫码支付

2.1. 业务流程
在这里插入图片描述
(1)商户后台系统根据用户选购的商品生成订单。

(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;

(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

(4)商户后台系统根据返回的code_url生成二维码。

(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

(8)微信支付系统根据用户授权完成支付交易。

(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(12)商户确认订单已支付后给用户发货。

2.2. 后端代码编写

2.2.1 引入依赖
<!--微信支付-->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>RELEASE</version>
        </dependency>

编写application.properties
#微信
#APPID
wx.AppID=
#商户ID
wx.MchID=
#API秘钥
wx.Key=
#异步接收微信支付结果通知的回调地址(此处的值随意填写)
wx.NotifyUrl=
2.2.2 编写WxPayConfig 配置类
package com.hcloud.wxconfig;

import java.io.InputStream;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.github.wxpay.sdk.WXPayConfig;

@Component
public class WxPayConfig implements WXPayConfig{

	@Value("${wx.AppID}")
	private String AppID;
	
	@Value("${wx.MchID}")
	private String MchID;
	
	@Value("${wx.Key}")
	private String Key;
	
	@Value("${wx.NotifyUrl}")
	private String NotifyUrl;
	
	/**
     * 获取 App ID
     *
     * @return App ID
     */
    @Override
    public String getAppID() {
        return AppID;
    }

    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    @Override
    public String getMchID() {
        return MchID;
    }

    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    @Override
    public String getKey() {
        return Key;
    }

    /**
     * 异步接收微信支付结果通知的回调地址
     *
     * @return
     */
    public String getNotifyUrl() {
        return NotifyUrl;
    }

    /**
     * 获取商户证书内容
     *
     * @return 商户证书内容
     */
    @Override
    public InputStream getCertStream() {
        return null;
    }

    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     * @return
     */
    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     *
     * @return
     */
    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }

	
	
}
2.2.3 编写WxNativeService 扫码支付(这里调用微信的[统一下单]API)
package com.hcloud.payservice;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

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

import com.github.wxpay.sdk.WXPay;
import com.hcloud.wxconfig.WxPayConfig;

@Service
public class WxNativeService {

	 @Autowired
	 WxPayConfig payConfig;
	 /**
	     * 统一下单接口(用于返回二维码的url)
	     *
	     * @param money      金额
	     * @param outTradeNo 商户订单号
	     * @param ip         终端IP
	     * @return
	     */

	    public Map<String, String> unifiedOrder(String money, String outTradeNo, String ip) {
	        Map<String, String> paramMap = new LinkedHashMap<>(8);
	        WXPay wxPay = new WXPay(payConfig);
	        //入参 根据微信开放文档的接口入参规则填写即可
	        paramMap.put("device_info", "WEB");//自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
	        paramMap.put("body", "华远科技");//商品名称
	        paramMap.put("out_trade_no", outTradeNo);//微信支付订单号
	        paramMap.put("total_fee", money);//支付金额
	        paramMap.put("spbill_create_ip", ip);//支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
	        paramMap.put("notify_url", payConfig.getNotifyUrl());//支付成功回调路径
	        paramMap.put("trade_type", "NATIVE");//支付方式  NATIVE扫码支付 
	        paramMap.put("product_id", "000000000");//商品ID

	        HashMap<String, String> result = new HashMap<>(2);
	        try {
	            Map<String, String> map = wxPay.unifiedOrder(paramMap);
	            //return_code是通信标识,非交易标识,交易是否成功需要查看result_code来判断
	            if ("SUCCESS".equals(map.get("return_code"))) {
	                if ("SUCCESS".equals(map.get("result_code"))) {
	                    result.put("status", "success");
	                    result.put("msg", "请求成功");
	                    result.put("code_url", map.get("code_url"));
	                    result.put("prepay_id", map.get("prepay_id"));
	                    result.put("trade_type", map.get("trade_type"));
	                    result.put("money", money);
	                    result.put("out_trade_no", outTradeNo);
	                    return result;
	                } else {
	                    result.put("status", "fail");
	                    result.put("msg", map.get("err_code_des"));
	                    return result;
	                }
	            } else {
	                result.put("status", "fail");
	                result.put("msg", map.get("return_msg"));
	                return result;
	            }

	        } catch (Exception e) {
	            e.printStackTrace();
	        }

	        result.put("status", "fail");
	        result.put("msg", "未知异常");
	        return result;
	    }

	    /**
	     * 查询订单
	     *
	     * @param outTradeNo 商户订单号
	     * @return
	     */

	    public Map<String, String> orderQuery(String outTradeNo) {
	        Map<String, String> paramMap = new LinkedHashMap<>(1);
	        WXPay wxPay = new WXPay(payConfig);
	        paramMap.put("out_trade_no", outTradeNo);
	        Map<String, String> result = new HashMap<>();
	        try {
	            Map<String, String> map = wxPay.orderQuery(paramMap);
	            if ("SUCCESS".equals(map.get("return_code"))) {
	                if ("SUCCESS".equals(map.get("result_code"))) {
	                    //result.put("status", "success");
	                    result.put("out_trade_no", map.get("out_trade_no")); //商户订单号
	                    switch (map.get("trade_state")) {

	                        case "SUCCESS":
	                            result.put("trade_type", map.get("trade_type")); //支付类型
	                            result.put("trade_state", map.get("trade_state")); //支付状态
	                            result.put("transaction_id", map.get("transaction_id")); //微信支付订单号
	                            result.put("total_fee", map.get("total_fee")); //支付金额
	                            result.put("time_end", map.get("time_end")); //支付完成时间
	                            result.put("msg", "支付成功");
	                            break;
	                        case "REFUND":
	                            result.put("msg", "转入退款");
	                            break;
	                        case "NOTPAY":
	                            map.put("msg", "未支付");
	                            break;
	                        case "CLOSED":
	                            map.put("msg", "已关闭");
	                            break;
	                        case "REVOKED":
	                            map.put("msg", "已撤销");
	                            break;
	                        case "USERPAYING":
	                            map.put("msg", "用户支付中");
	                            break;
	                        case "PAYERROR":
	                            map.put("msg", "支付失败");
	                            break;
	                        default:
	                            map.put("msg", "支付失败");
	                            break;
	                    }

	                    return result;
	                } else {
	                    result.put("status", "fail");
	                    result.put("out_trade_no", map.get("out_trade_no")); //商户订单号
	                    result.put("msg", map.get("err_code_des"));
	                    return result;
	                }
	            } else {
	                result.put("status", "fail");
	                result.put("msg", map.get("return_msg"));
	                return result;
	            }
	        } catch (Exception e) {
	            e.printStackTrace();
	        }

	        result.put("status", "fail");
	        result.put("msg", "未知异常");
	        return result;
	    }
	
}

2.2.4 编写WxIpUtil 生成终端IP工具类
package com.hcloud.payutil;

import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.servlet.http.HttpServletRequest;

/*
 * @className: IpUtil
 * @description 获取终端IP
 * @since JDK1.8
 * @author ljh
 * @createdAt  2020/8/30 0030
 * @version 1.0.0
 **/
public class WxIpUtil {
	public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();

        return "0:0:0:0:0:0:0:1".equals(ipAddress) ? "127.0.0.1" : ipAddress;
    }
}

2.2.5 编写PayController
package com.hcloud.paycontroller;


import java.math.BigDecimal;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

import com.github.wxpay.sdk.WXPayUtil;
import com.hcloud.payservice.WxNativeService;
import com.hcloud.payutil.WxIpUtil;


@Controller
@EnableEurekaClient
public class AliPayController {

	@Autowired
    private WxNativeService nativeService;
	
	/**
	 * 微信支付页面
	 * @return
	 */
	
	public String WxPay(String total, Map<String, Object> paramMap) {
		paramMap.put("total", total);
		return "/Pay/WxPay";
	}
	
	/**
	 * 调用微信接口生成二维码
	 * @param request
	 * @param money
	 * @return
	 */
	@ResponseBody
	@RequestMapping("/Wxpay/createNative")
    public Map<String, String> createNative(HttpServletRequest request, String money) {
		
        return nativeService.unifiedOrder(money, WXPayUtil.generateNonceStr(), WxIpUtil.getIpAddr(request));

    }
	/**
	 * 定时查询微信是否支付成功
	 * @param outTradeNo
	 * @return
	 */
	@ResponseBody
    @RequestMapping("/Wxpay/orderQuery")
    public Map<String, String> orderQuery(String outTradeNo) {

        int i=0;

        while (true) {
            Map<String, String> map = nativeService.orderQuery(outTradeNo);
            i++;
            if ("SUCCESS".equals(map.get("trade_state"))) {
                return map;
            }

            try {
                //因为这里是循环调用查询订单,所以如果前台超过5分钟没有支付 就会让刷新页面重新生成二维码
                Thread.sleep(3000);
                if (i>=100){
                    System.out.println(i);
                    map.put("status","fail");
                    map.put("msg","二维码已超时");
                    return map;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                map.put("msg", "支付失败");
                return map;
            }
        }

    }
	
	/**
     *  微信支付成功的回调
     * */
	@RequestMapping("/WxPay/fallback")
    public String WxFallback (HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        System.err.println(parameterMap);
        System.out.println("进入了微信回调");
        //写更改数据库的方法  逻辑
        
        
        return "/Pay/WxPayReturn";
    }
	
}

2.3 前端代码编写

微信扫码页面

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE">
  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>
  <title>微信支付页</title>
  <link rel="stylesheet" type="text/css" href="css/webbase.css"/>
  <link rel="stylesheet" type="text/css" href="css/pages-weixinpay.css"/>
  <!--引入jQuery和qrious相关类库-->
  <script src="js/jquery.min.js"></script>
  <script src="js/qrious.js"></script>


</head>

<body>
<!--head-->
<div>
  <!--主内容-->
  <div class="pay">
    <div class="checkout-tit">
      <h4 class="fl tit-txt">
        <span class="success-icon"></span><span class="success-info">订单提交成功,请您及时付款!订单号:<span id="out_trade_no"></span></span>
      </h4>
      <span class="fr"><em class="sui-lead">应付金额:</em><em class="orange money"></em><span id="money"></span></span>
      <div class="clearfix"></div>
    </div>
    <div class="checkout-steps">
      <div class="fl weixin">微信支付</div>
      <div class="fl sao">
        <p class="red"></p>
        <div class="fl code">
          <img id="qrious">
          <div class="saosao">
            <p>请使用微信扫一扫</p>
            <p>扫描二维码支付</p>
          </div>
        </div>
        <div class="fl phone">

        </div>

      </div>
    </div>
  </div>

</div>


<script type="text/javascript">

    //页面一加载就调用
    window.onload = function() {
        createNative()
    }

    /*统一下单接口调用*/
    function createNative() {
        $.ajax({
            type: 'get',
            url: 'http://localhost:8080/createNative',
            success: function(data) {
                console.log(data);
                $("#money").html((data.money / 100).toFixed(2))
                $("#out_trade_no").html(data.out_trade_no)
                /*调用后端统一下单接口根据返回的code_url 用qrious生成二维码*/
                var qr = new QRious({
                    element: document.getElementById('qrious'),
                    size: 250,
                    level: 'H', //二维码容错级别 从小到大 : L M Q H
                    value: data.code_url
                });
                orderQuery(data.out_trade_no)
            }
        })
    }

    /*查询订单接口调用*/
    function orderQuery(out_trade_no) {
        $.ajax({
            type: 'get',
            url: 'http://localhost:8080/orderQuery?outTradeNo=' + out_trade_no,
            success: function(data) {
                console.log(data);
	
				//支付成功后跳转成功页面
                if (data.trade_state == "SUCCESS") {
                    location.href = 'paysuccess.html?money=' + ((data.total_fee) / 100).toFixed(2)
                } else {
                    if (data.msg == "二维码已超时") {
                        console.log(data.msg);
                        //二维码超时后重新生成二维码
                        createNative();
                    } else {
						//跳转失败页面
                        location.href = 'payfail.html'
                    }
                }

            }
        })
    }


</script>
</body>

</html>

成功页面paysuccess.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE">
  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>
  <title>支付页-成功</title>


  <link rel="stylesheet" type="text/css" href="css/webbase.css"/>
  <link rel="stylesheet" type="text/css" href="css/pages-paysuccess.css"/>
  <script src="js/jquery.min.js"></script>

</head>

<body>
<!--head-->

<div class="cart">
  <!--logoArea-->
  <div class="logoArea">
    <div class="fl logo"><span class="title">支付页</span></div>
  </div>
  <!--主内容-->
  <div class="paysuccess">
    <div class="success">
      <h3><img src="img/right.png" width="48" height="48"> 恭喜您,支付成功啦!</h3>
      <div class="paydetail">
        <p>支付方式:微信支付</p>
        <p>支付金额:¥<span id="money"></span></p>
        <p class="button">
          <a href="myOrder.html" class="sui-btn btn-xlarge btn-danger">查看订单</a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="index.html" class="sui-btn btn-xlarge ">继续购物</a>
        </p>
      </div>
    </div>

  </div>
</div>
</body>
<script>
    window.onload = function() {
        var money = getUrlParam("money");
        $("#money").html(money)
    }

    //解析跳转过来时url携带的参数
    function getUrlParam(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
        var r = window.location.search.substr(1).match(reg);  //匹配目标参数
        if (r != null) return unescape(r[2]);
        return null; //返回参数值
    }
</script>
</html>

支付失败页面payfail.html

<!DOCTYPE html>
<html>

	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE">
		<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
		<title>支付页-失败</title>

		
	
    <link rel="stylesheet" type="text/css" href="css/webbase.css" />
    <link rel="stylesheet" type="text/css" href="css/pages-payfail.css" />
</head>

	<body>
		<!--head-->

		<div class="cart">
			<!--logoArea-->
			<div class="logoArea">
				<div class="fl logo"><span class="title">支付页</span></div>
			</div>
			<!--主内容-->
			<div class="payfail">
				<div class="fail">
					<h3><img src="img/fail.png" width="48" height="48"> 支付失败,请稍后再试</h3>
					<div class="fail-text">
					<p>失败原因:不能使用金币购买!</p>
					<p class="button"><a href="" class="sui-btn btn-xlarge btn-danger">重新支付</a></p>
				    </div>
				</div>
				
			</div>
		</div>

<!--页面底部END-->

</body>

</html>

成功啦
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃铁板鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值