微信扫码支付(java)
项目中引入微信支付,按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
1.导入SDK依赖和httpClient
httpclient用于在后端发送请求
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
2.编写配置文件properties
# 微信公众账号或开放平台APP的唯一标识
appid=******XXX
# 财付通平台的商户账号
partner=******XXX
# 财付通平台的商户密钥
partnerkey=******XXX
3.httpClient工具类
package com.pyg.util;
import java.io.IOException;
import java.security.GeneralSecurityException;
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;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
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.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
/**
* 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;
}
}
4.后端controller
package com.pyg.cart.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.pyg.pay.service.WeixinPayService;
import com.pyg.util.IdWorker;
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 java.util.Map;
/**
* @Title: PayController
* @ProjectName PYG
* @Description: TODO
*/
@RestController
@RequestMapping("/pay")
public class PayController {
@Reference
private WeixinPayService weixinPayService;
@Autowired
private IdWorker idWorker;
@RequestMapping("/createNative")
public Map createNative( String out_trade_no,String total_fee){
//返回 订单号 支付金额 code_url(支付地址)
return weixinPayService.createNative(out_trade_no,total_fee);
}
@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
Result result=null;
//限时5分钟
int x=0;
while (true){
Map map = weixinPayService.queryPayStatus(out_trade_no);
if(map==null){
result=new Result(false,"支付出错");
break;
}
if(map.get("trade_state").equals("SUCCESS")){
result=new Result(true,"支付成功");
break;
}
try{
Thread.sleep(3000);//每隔3秒 发送请求确认订单状态
}catch (Exception e){
e.printStackTrace();
}
x++;
if(x>=100){
result=new Result(false,"二维码超时");
}
}
return result;
}
}
5.服务层接口
package com.pyg.pay.service;
import java.util.Map;
/**
* @Title: WeixinPayService
* @ProjectName PYG
* @Description: TODO
*/
public interface WeixinPayService {
/**
* @Description: 生成微信支付二维码
* @param out_trade_no 订单号
* @param total_fee 金额(单位: 分)
* @return java.util.Map
*/
Map createNative(String out_trade_no,String total_fee);
/**
* @Description: 查询支付状态
* @param out_trade_no 订单号
* @return java.util.Map
*/
Map queryPayStatus(String out_trade_no);
/**
* @Description: 关闭订单
* @param out_trade_no
* @return java.util.Map<java.lang.String,java.lang.String>
*/
Map<String,String> closePay(String out_trade_no);
}
6.服务层实现类
(1)获取随机字符串
WXPayUtil.generateNonceStr()
(2)MAP转换为XML字符串(自动添加签名)
WXPayUtil.generateSignedXml(param, partnerkey)
(3)XML字符串转换为MAP
WXPayUtil.xmlToMap(result)
我们要生成支付二维码,就需要一个支付地址, 支付地址在预支付响应中–> code_url
package com.pyg.pay.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.github.wxpay.sdk.WXPayUtil;
import com.pyg.pay.service.WeixinPayService;
import com.pyg.util.HttpClient;
import org.springframework.beans.factory.annotation.Value;
import sun.security.action.PutAllAction;
import java.util.HashMap;
import java.util.Map;
/**
* @Title: WeixinPayServiceImpl
* @ProjectName PYG
* @Description: TODO
*/
@Service(timeout = 10000)
public class WeixinPayServiceImpl implements WeixinPayService{
@Value("${appid}")
private String appid;
@Value("${partner}")
private String partner;
@Value("${partnerkey}")
private String partnerKey;
/**
* @param out_trade_no 订单号
* @param total_fee 金额(分)
* @return java.util.Map
* @Description: 生成微信支付二维码
*/
@Override
public Map createNative(String out_trade_no, String total_fee) {
//需要发送xml 我们就先定义map数据 然后使用wxpayUtil 进行转换
Map<String,String> param=new HashMap<>();
param.put("appid",appid);//公众号
param.put("mch_id",partner);//商户号
param.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
param.put("body","QuietHR旗舰店");//商品描述
param.put("out_trade_no",out_trade_no);//商户订单号
param.put("total_fee",total_fee);//总金额(分)
param.put("spbill_create_ip","127.0.0.1");//IP
param.put("notify_url","http://47.93.49.119/");//回调地址
param.put("trade_type","NATIVE");//交易类型
try{
//生成发送的xml
String xmlParam = WXPayUtil.generateSignedXml(param, partnerKey);
//发送post请求
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
//获得预支付响应
String result = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
//重新封装数据到前端 前端需要什么就传什么
Map<String,String> map=new HashMap<>();
map.put("code_url",resultMap.get("code_url"));//返回的支付地址 根据这个url生成二维码
map.put("total_fee",total_fee);
map.put("out_trade_no",out_trade_no);
return map;
}catch (Exception e){
e.printStackTrace();
return new HashMap();
}
}
/**
* @param out_trade_no 订单号
* @return java.util.Map
* @Description: 查询支付状态
*/
@Override
public Map queryPayStatus(String out_trade_no) {
Map param=new HashMap();
param.put("appid",appid);
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";
try{
String xmlParam = WXPayUtil.generateSignedXml(param, partnerKey);
HttpClient client = new HttpClient(url);
client.setHttps(true);
client.setXmlParam(xmlParam);
client.post();
String result = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
System.out.println(result);
return resultMap;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* @Description: 关闭订单
* @param out_trade_no
* @return java.util.Map<java.lang.String,java.lang.String>
*/
@Override
public Map<String, String> closePay(String out_trade_no) {
Map param=new HashMap();
param.put("appid",appid);
param.put("mch_id",partner);
param.put("out_trade_no",out_trade_no);
param.put("nonce_str",WXPayUtil.generateNonceStr());
try {
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/closeorder");
client.setHttps(true);
String signedXml = WXPayUtil.generateSignedXml(param, partnerKey);
client.setXmlParam(signedXml);
client.post();
String content = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(content);
System.out.println(resultMap);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
发送的xml和接收的xml
<!--统一下单API 预支付发送的xml-->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xml>
<nonce_str>62fe30a4646b4eb4b46eefd25f1ac4d3</nonce_str>
<out_trade_no>1059415625195061248</out_trade_no>
<appid>******XXX</appid>
<total_fee>1</total_fee>
<sign>06CE9EE392E771955E9F748667273FCF</sign>
<trade_type>NATIVE</trade_type>
<mch_id>******XXX</mch_id>
<body>QuietHR旗舰店</body>
<notify_url>http://47.93.49.119/</notify_url>
<spbill_create_ip>127.0.0.1</spbill_create_ip>
</xml>
<!--统一下单API 预支付响应的xml-->
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[******XXX]]></appid>
<mch_id><![CDATA[******XXX]]></mch_id>
<nonce_str><![CDATA[yyUe8Zez8djJhTiw]]></nonce_str>
<sign><![CDATA[E0EDEC1D6B6268DE6B1172FFFBE3FCD9]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx0520022407695355d74a5cb20329414550]]></prepay_id>
<trade_type><![CDATA[NATIVE]]></trade_type>
<code_url><![CDATA[weixin://wxpay/bizpayurl?pr=nE1oWY9]]></code_url>
</xml>
<!--查询订单API 返回的xml-->
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[******XXX]]></appid>
<mch_id><![CDATA[******XXX]]></mch_id>
<nonce_str><![CDATA[GUXcF2078yXq3JV9]]></nonce_str>
<sign><![CDATA[44152FA2993AA49BBE1176CB9F30574C]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<out_trade_no><![CDATA[1059415625195061248]]></out_trade_no>
<trade_state><![CDATA[NOTPAY]]></trade_state>
<trade_state_desc><![CDATA[订单未支付]]></trade_state_desc>
</xml>
<!--查询订单API 返回的xml-->
<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[******XXX]]></appid>
<mch_id><![CDATA[******XXX]]></mch_id>
<nonce_str><![CDATA[PUTtEeWs3l8wxt2i]]></nonce_str>
<sign><![CDATA[2FAFE2328050760166FEBC87786A5E2D]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<openid><![CDATA[******XXX]]></openid>
<is_subscribe><![CDATA[N]]></is_subscribe>
<trade_type><![CDATA[NATIVE]]></trade_type>
<bank_type><![CDATA[CFT]]></bank_type>
<total_fee>1</total_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<transaction_id><![CDATA[4200000218201811057232858782]]></transaction_id>
<out_trade_no><![CDATA[1059415625195061248]]></out_trade_no>
<attach><![CDATA[]]></attach>
<time_end><![CDATA[20181105200235]]></time_end>
<trade_state><![CDATA[SUCCESS]]></trade_state>
<cash_fee>1</cash_fee>
<trade_state_desc><![CDATA[支付成功]]></trade_state_desc>
</xml>