微信支付申请及简单使用

申请微信支付

一、微信支付首先需要先去申请资格,申请条件及步骤如下:

1、注册公众号(服务号-企业才能注册),可以根据营业执照类型选择:个体商户 | 企业/公司 | 政府 | 媒体 | 其他类型。

2、认证公众号(认证费:300元/年)

3、提交资料申请微信支付(审核时间为1-5个工作日内)。

 4、开户成功,登录商户平台进行验证。

资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。

二、获取公众号/商户信息支付信息

1、微信公众账号或开放平台APP的唯一标识 - appid

2、财付通平台的商户账号 - mch_id

3、财付通平台的商户密钥 - partnerkey

 4、启用Native

5、根据API进行开发:https://pay.weixin.qq.com/wiki/doc/api/index.html

微信支付基本使用

一、创建二维码

后端需要根据订单id和支付金额来返回一个Map集合给前端,前端通过qrcode创建一个支付二维码。

先在微信支付相关操作的类里面注入商户信息(商户信息在properties配置文件中)

// 微信公众账号或开放平台APP的唯一标识
@Value("${appid}")
private String appid;
// 财付通平台的商户账号
@Value("${partner}")
private String partner;
// 财付通平台的商户秘钥
@Value("${partnerkey}")
private String partnerkey;
// 回调地址
@Value("${notifyurl}")
private String notifyurl;

1、后端代码

①Controller层代码 - 根据自己的业务来做

/**
 * 获取当前登录用户名,
 * 根据用户名获取redis中的支付日志对象,
 * 根据支付日志对象中的支付单号和总金额
 * 调用微信统一下单接口, 生成支付链接返回
 * @return
 */
@RequestMapping("/createNative")
public Map createNative() {
	//1. 获取当前登录用户的用户名 - SpringSecurity
	String userName = SecurityContextHolder.getContext().getAuthentication().getName();
	//2. 根据用户名获取支付日志对象 - 根据自己业务调用
	PayLog payLog = orderService.getPayLogByUserName(userName);
	if (payLog != null) {
		//3. 调用统一下单接口生成支付链接 - 订单号、价格(单位:分)
		Map map = payService.createNative(payLog.getOutTradeNo(), "1");
		return map;
	}
	return new HashMap();
}

②Service层代码 - 根据自己的业务来做,传一些其他附加参数

**
 * 支付创建二维码
 * @param outTradeNo 订单号
 * @param totalFee   支付金额
 * @return 前端需要的相关信息
 */
public Map createNative(String outTradeNo, String totalFee){
	// 创建参数Map
	Map<String, String> payParam = new HashMap<>();
	payParam.put("appid",appid);	// 微信公众账号或开放平台APP的唯一标识
	payParam.put("mch_id",partner);	// 财付通平台的商户账号
	payParam.put("body","可口可乐250ML");	// 商品描述
	payParam.put("nonce_str",WXPayUtil.generateNonceStr()); // 随机字符串
	payParam.put("out_trade_no",outTradeNo);	// 订单号
	payParam.put("total_fee",totalFee);	  // 支付金额(单位:分)
	payParam.put("spbill_create_ip","127.0.0.1");	// APP和网页支付提交用户端ip
	payParam.put("notify_url",notifyurl);	// 回调地址
	payParam.put("trade_type","NATIVE");	// 交易类型 - 二维码支付
	try{
		// 生成要发送的xml,调用微信SDK的API接口将封装的Map数据转换成Xml格式字符串
		String xmlParam = WXPayUtil.generateSignedXml(payParam, partnerkey);  // 参数2:财付通平台的商户密钥
		// 使用HttpClient发送请求 - 参数为支付请求地址
		HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
		client.setHttps(true);
		client.setXmlParam(xmlParam);
		client.post();
		// 获得结果 - xml字符串
		String result = client.getContent();
		// 调用微信SDK的API接口将Xml数据转换成Map对象
		Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
		// 创建返回给前端去创建二维码的结果Map
		Map<String, String> returnMap = new HashMap<>();
		returnMap.put("code_url",resultMap.get("code_url"));  // 支付地址
		returnMap.put("total_fee",totalFee);		// 总金额
		returnMap.put("out_trade_no",outTradeNo);	// 订单号
	}catch(Excption e){
		e.printStackTrace();
        return new HashMap<>();
	}
}

2、前端代码(Vue) - 请求创建二维码

/**
 * 创建支付二维码
 */
createNative: function () {
	let _this = this;
	axios.get('/pay/createNative.do').then(function (response) {
		// 显示金额
		_this.money = (response.data.total_fee / 100).toFixed(2);
		// 显示订单号
		_this.out_trade_no = response.data.out_trade_no;
		// 使用QRcode创建二维码
		let qrcode = new QRCode(document.getElementById("qrcode"), {
			width: 250,
			height: 250
		});
		qrcode.makeCode(response.data.code_url);
		// 查询支付结果 - 这里采用的是长轮询方式查询支付结果
		_this.queryPayStatus(); 
	}).catch(function (reason) {
		console.log(reason);
	})
},

二、查询订单支付结果

1、后端代码

①Controller层代码 - 根据自己的业务来做

/**
 * 调用查询订单接口, 查询是否支付成功
 * @param out_trade_no 订单号
 * @return
 */
@RequestMapping("/queryPayStatus")
public MessageResult queryPayStatus(String out_trade_no) {
	String userName = SecurityContextHolder.getContext().getAuthentication().getName();
	MessageResult MessageResult = null;
	int flag = 1;
	// 长轮询的方式查询结果 - 建议使用WebSocket或通过消息中间件RabbitMQ Web STOMP来解决
	while(true) {
		// 1. 判断支付单号等于null
		if (out_trade_no == null) {
			MessageResult = new MessageResult(false, "二维码超时");
			break;
		}
		// 2. 调用查询接口查询支付是否成功
		Map map = payService.queryPayStatus(out_trade_no);
		if ("SUCCESS".equals(map.get("trade_state"))) {
			MessageResult = new MessageResult(true, "支付成功!");
			//3. 如果支付成功, 支付日志表和订单表的支付状态改为已支付, redis的支付日志对象删除
			orderService.updatePayStatus(userName);
			break;
		}
		try {
			Thread.sleep(3000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 如果5分钟没有支付则支付超时
		if (flag > 100) {
			MessageResult = new MessageResult(false, "二维码超时");
			break;
		}
		flag++;
	}
	return MessageResult;
}

②Service层代码 - 根据自己的业务来做

/**
 * 查询是否已经支付
 * @param out_trade_no 订单号
 * @return
 */
@Override
public Map queryPayStatus(String out_trade_no) {
	Map param = new HashMap();
	param.put("appid", appid);	// 微信公众账号或开放平台APP的唯一标识
	param.put("mch_id", partner); // 财付通平台的商户账号
	param.put("out_trade_no", out_trade_no); // 订单号
	param.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
	String url = "https://api.mch.weixin.qq.com/pay/orderquery";  // 查询url
	try {
		// 生成要发送的xml,调用微信SDK的API接口将封装的Map数据转换成Xml格式字符串
		String xmlParam = WXPayUtil.generateSignedXml(param, partnerkey); // 参数2:财付通平台的商户密钥
		// 使用HttpClient发送请求 - 参数为支付请求地址
		HttpClient client = new HttpClient(url);
		client.setHttps(true);
		client.setXmlParam(xmlParam);
		client.post();
		// 获得结果 - xml字符串
		String result = client.getContent();
		// 调用微信SDK的API接口将Xml数据转换成Map对象
		Map<String, String> map = WXPayUtil.xmlToMap(result);
		return map;
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
}

2、前端代码(Vue) - 查询支付结果

/**
 * 查询支付结果
 */
queryPayStatus: function () {
	let _this = this;
	axios.get("/pay/queryPayStatus.do?out_trade_no=" + this.out_trade_no).then(function (response) {
		if (response.data.success) {     // 跳转支付成功页面
			location.href = "paysuccess.html?money=" + _this.money;
		} else {
			if (response.data.message === '二维码超时') {
				// 重新生成二维码
				this.createNative();
			} else {	// 跳转支付失败页面
				location.href = "payfail.html"
			}
		}
	}).catch(function (reason) {
		console.log(reason);
	});
}

三、关闭订单操作

这里只提供后端服务层代码,因为我的业务里因为之前没考虑周全,是没有做这个功能的。但是这个功能在真正的项目中是必须要有的,假如某个人下单买了一个东西,他一直不支付怎么办?通常业务里都会有30分钟不支付,关闭订单的操作(含关闭微信支付)。如果真的要做的话,可以通过MQ延时队列来做。

/****
 * 关闭订单操作
 * @param out_trade_no 订单号
 * @return
 */
public Map closePay(String out_trade_no) {
	Map param=new HashMap();
	param.put("appid", appid);	 // 微信公众账号或开放平台APP的唯一标识
	param.put("mch_id", partner); // 财付通平台的商户账号
	param.put("out_trade_no", out_trade_no); // 订单号
	param.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
	// 关闭微信支付Url
	String url="https://api.mch.weixin.qq.com/pay/closeorder";
	try {
		// 生成要发送的xml,调用微信SDK的API接口将封装的Map数据转换成Xml格式字符串
		String xmlParam = WXPayUtil.generateSignedXml(param, partnerkey);
		HttpClient client=new HttpClient(url);
		client.setHttps(true);
		client.setXmlParam(xmlParam);
		client.post();
		// 获得结果 - xml字符串
		String result = client.getContent();
		// 调用微信SDK的API接口将Xml数据转换成Map对象
		Map<String, String> map = WXPayUtil.xmlToMap(result);
		return map;
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
}

HttpClient源码

/**
 * 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;
	}

}

经验记录:

1、二维码支付的时候查询是否支付成功的问题:

①Ajax短轮询,通过定时器异步刷新查询是否支付成功。

缺点:这种方式实时效果较差,而且对服务端的压力也较大。

②长轮询,后端循环查询是否支付成功。

缺点:长轮询服务端会长时间地占用资源,如果消息频繁发送的话会给服务端带来较大的压力。

③WebSocket 双向通信

RabbitMQ可以通过RabbitMQ  Web STOMP 插件来做。

2、订单超时关闭订单的问题:

下单的时候发送MQ延迟消息,30分钟未支付,则消费这个延迟消息做一些未支付的业务处理。

3、微信支付回调地址需要外网能访问,如果是本地的话,可以通过内网穿透去做。

发布了100 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40885085/article/details/104593558