Java-SSM+AngularJS-1实现微信扫码支付功能

微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景

1. 微信支付开发文档整体思路

微信支付接口调用的整体思路:

按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态

在线微信支付开发文档: https://pay.weixin.qq.com/wiki/doc/api/index.html
本Demo涉及到 统一下单和查询订单两组API,流程介绍看上面链接扫码支付,会有介绍

  • appid:微信公众账号或开放平台APP的唯一标识
  • mch_id:商户号 (配置文件中的partner)
  • partnerkey:商户密钥
  • sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性

2. 环境准备

  • 引入微信支付的Maven坐标
	<dependency>
		<groupId>com.github.wxpay</groupId>
		<artifactId>wxpay-sdk</artifactId>
		<version>0.0.3</version>
	</dependency>
  • 使用HttpClient模拟浏览器发送请求,这里提供工具类
package utils;

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请求客户端
 * 
 * @author Administrator
 * 
 */
public class HttpClient {
	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 HttpClient(String url, Map<String, String> param) {
		this.url = url;
		this.param = param;
	}

	public HttpClient(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;
	}

}

我们主要会用到微信支付SDK的以下功能:

  • 获取随机字符串 : WXPayUtil.generateNonceStr()
  • MAP转换为XML字符串(自动添加签名): WXPayUtil.generateSignedXml(param, partnerkey)
  • XML字符串转换为MAP: WXPayUtil.xmlToMap(result)

3. 接口编写-Maven-SSM

  • WxPayService 接口定义
package com.pyg.pay.service;

import java.util.Map;

/**
 * @ Description
 * @ auther          宁宁小可爱
 * @ create          2020-02-10 14:53
 */
public interface WxPayService {
    /*
    * 需求 : 向微信平台下单,申请获取支付功能,返回URL支付地址 ,生成二维码
    * 参数: 订单号 支付金额
    * 返回值: Map
    * */

    public Map creatQrCode(String order_no,String total_fee);

    /*
    * 需求: 根据订单号查询支付状态
    * 参数: String order_no
    * 返回值: map
    * */
    public Map queryOrderPayStatus(String orderNo);
}

  • WxPayServiceImpl实现类代码
package com.pyg.pay.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.github.wxpay.sdk.WXPayUtil;
import com.pyg.pay.service.WxPayService;
import org.springframework.beans.factory.annotation.Value;
import utils.HttpClient;

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

/**
 * @ Description
 * @ auther          宁宁小可爱
 * @ create          2020-02-10 15:13
 */
@Service
public class WxPayServiceImpl implements WxPayService {

    /*
     * 需求 : 向微信平台下单,申请获取支付功能,返回URL支付地址 ,生成二维码
     * 参数: 订单号 支付金额
     * 返回值: Map
     * 支付凭证;: 统一放入properties中
     * appid: 唯一标识
     * mch_id: 商户号
     * parterner: 支付秘钥
     * sign: 签名
     * 统一下单接口地址 https://api.mch.weixin.qq.com/pay/unifiedorder
     * */

    /*
    * 支付流程:
    * 1. 创建httpClient对象,向微信支付平台发送请求
    * 2. 准备传递的参数
    * 3. 获取返回值
    * 4. 返回支付地址
    * */

    // 注入唯一标识
    @Value("${appid}")
    private String appid;

    // 注入商户号
    @Value("${partner}")
    private String partner;

    // 注入秘钥
    @Value("${partnerkey}")
    private String partnerkey;

    // 注入统一下单地址
    @Value("${payUrl}")
    private String payUrl;

    @Override
    public Map creatQrCode(String order_no, String total_fee) {
        try {
            // 创建MAP对象 封装参数
            Map paramMap = new HashMap();
            // appid
            paramMap.put("appid",appid);
            // 商户号
            paramMap.put("mch_id",partner);
            // 随机字符串 这是微信sdk提供的工具类
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            // 支付描述
            paramMap.put("body","微信扫码支付");
            // 商户订单号
            paramMap.put("out_trade_no",order_no);
            // 支付金额
            paramMap.put("total_fee",total_fee);
            // 终端IP
            paramMap.put("spbill_create_ip","127.0.0.1");
            // 设置通知地址
            paramMap.put("notify_url","https://www.baidu.com");
            // 交易类型
            paramMap.put("trade_type","NATIVE ");

            // 生成具有签名的XML格式参数
            String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
            // 创建httpclient对象 ,向微信支付平台发送下单请求
            HttpClient httpClient = new HttpClient(payUrl);
            // 设置请求方式 https
            httpClient.setHttps(true);
            // 设置请求参数
            httpClient.setXmlParam(xmlParam);
            // 设置请求方式 post
            httpClient.post();
            // 获取请求结果 返回XML格式
            String result = httpClient.getContent();

            // 返回支付地址 我们需要结果中的code_url 把返回值的xml转换Map 用sdk的utils
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);

            // 返回支付页面需要的数据 支付地址 支付金额  订单号 前台页面需要显示
            resultMap.put("order_no",order_no);
            resultMap.put("total_fee",total_fee);

            return resultMap;


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

    /*
     * 需求: 根据订单号查询支付状态
     * 参数: String order_no
     * 返回值: map
     *
     * 业务步骤:
     * 1. 组装要传递的参数
     * 2. 使用HttpClient调用微信平台提供的查询接口
     * 3. 获取返回值
     * 4. 返回支付状态
     * */
    // 注入查询地址
    @Value("${queryUrl}")
    private String queryUrl;
    @Override
    public Map queryOrderPayStatus(String orderNo) {
        try {
            // 创建MAP对象 封装参数
            Map paramMap = new HashMap();
            // appid
            paramMap.put("appid",appid);
            // 商户号
            paramMap.put("mch_id",partner);
            // 随机字符串 这是微信sdk提供的工具类
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            // 商户订单号
            paramMap.put("out_trade_no",orderNo);
            // 生成具有签名的XML参数
            String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);

            // 创建httpclient对象 ,向微信支付平台发送下单请求
            HttpClient httpClient = new HttpClient(queryUrl);
            // 设置请求方式 https
            httpClient.setHttps(true);
            // 设置请求参数
            httpClient.setXmlParam(xmlParam);
            // 设置请求方式 post
            httpClient.post();
            // 获取请求结果 返回XML格式
            String result = httpClient.getContent();

            // 返回支付地址 我们需要结果中的code_url 把返回值的xml转换Map 用sdk的utils
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);

            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • properties配置文件
appid=你的唯一标识
partner=商户号
partnerkey=秘钥
notifyurl=http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify
# 统一下单接口地址
payUrl=https://api.mch.weixin.qq.com/pay/unifiedorder
# 查询接口
queryUrl=https://api.mch.weixin.qq.com/pay/orderquery
  • controller层代码 ,我这里用了dubbo所以用refrence远程注入了
package com.pyg.pay.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.pyg.order.service.OrderService;
import com.pyg.pay.service.WxPayService;
import com.pyg.pojo.TbOrder;
import entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import utils.IdWorker;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * @ Description
 * @ auther          宁宁小可爱
 * @ create          2020-02-10 15:46
 */
@RestController
@RequestMapping("/wxpay")
public class WxPayController {
    /*
     * 需求 : 向微信平台下单,申请获取支付功能,返回URL支付地址 ,生成二维码
     * 参数: 订单号 支付金额
     * 返回值: Map
     * 支付凭证;: 统一放入properties中
     * appid: 唯一标识
     * mch_id: 商户号
     * parterner: 支付秘钥
     * sign: 签名
     * 统一下单接口地址 https://api.mch.weixin.qq.com/pay/unifiedorder
     * */

    @Reference
    private WxPayService wxPayService;

    // 注入订单服务
    @Reference
    private OrderService orderService;

    @RequestMapping("creatQrCode")
    public Map creatQrCode(HttpServletRequest request){
        String userId = request.getRemoteUser();
        // 根据用户名查询订单, 一个用户多个订单
        List<TbOrder> orderList =  orderService.findOrderByUserId(userId);
        // 调用服务层 统一下单  生成支付号
        IdWorker idWorker = new IdWorker();
        double totalFee =  0;
        // 计算总金额
        for (TbOrder tbOrder : orderList) {
            totalFee += tbOrder.getPayment().doubleValue();
        }
        // 由于是测试 支付金额就设置一分钱
        Map map = wxPayService.creatQrCode(idWorker.nextId() + "", "1");
        return map;

    }

     /*
     * 需求: 根据订单号查询支付状态
     * 参数: String order_no
     * 返回值: map
     * 请求; /queryOrderPayStatus
     */
     @RequestMapping("queryOrderPayStatus")
    public Result queryOrderPayStatus(String orderNo){
         try {
             int count = 0;
             // 循环检测二维码的支付状态 每隔三秒检测一次
             while (true){
                 Map map = wxPayService.queryOrderPayStatus(orderNo);
                 if (map == null){
                     return new Result(false,"支付失败");
                 }

                 if (map.get("trade_state").equals("SUCCESS")){
                     return new Result(true,"支付成功");
                 }

                 // 每隔三秒循环一次
                 Thread.sleep(3000);
                 // 超过五分钟未支付 支付超时
                 count++;
                 if (count >100 ){
                     return new Result(false,"支付超时");
                 }
             }
         } catch (Exception e) {
             e.printStackTrace();
             return new Result(false,"支付失败");
         }
     }


}

  • AngularJs1 controller代码
//控制层
app.controller('payController', function ($scope, $controller, payService) {
    //搜索
    $scope.createNative = function () {
        payService.creatQrCode().success(
            function (response) {
                if (response.result_code == 'SUCCESS'){
                    // 支付订单号
                    $scope.out_trade_no = response.order_no;
                    // 支付金额
                    $scope.totolFee = response.total_fee;
                    // 成功后生成二维码
                    new QRious({
                        element:document.getElementById('qrious'),
                        size:200,
                        background:'white',
                        foreground:'black',
                        level:'H',
                        value:response.code_url
                    });

                    // 调用查询二维码支付状态
                    this.queryOrderPayStatus($scope.out_trade_no);
                }
            }
        );
    }
    // 定义查询二维码状态方法
    queryOrderPayStatus = function (out_trade_no) {
        // 调用服务层代码
        payService.queryOrderPayStatus(out_trade_no).success(function (response) {
            if (response.success){
                location.href = "paysuccess.html"
            }else {
                location.href = "payfail.html"
            }
        })
    }

});	

  • AngularJs1 service代码
//服务层
app.service('payService', function ($http) {

    //搜索
    this.creatQrCode = function () {
        return $http.get('../wxpay/creatQrCode');
    }

    this.queryOrderPayStatus = function (out_trade_no) {
        return $http.get("../wxpay/queryOrderPayStatus?orderNo="+out_trade_no);
    }
});

  • 结果展示

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

叫我三胖哥哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值