小程序微信支付,微信支付
目录
1:下载相应的jar包
链接: 点我下载jar包 提取码: pk2y
2:对数据进行加密
①:设置相应参数
public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
//小程序id
reqData.put("appid", wxPayConfigVlue.getAppID());
//商户号
reqData.put("mch_id", wxPayConfigVlue.getMchID());
//小程序openid
reqData.put("openid", "XXXXXXXXXXXXXXXXXXXXXXX");
//随机字符串
reqData.put("nonce_str", WXPayUtil.generateNonceStr());
//商品描述
reqData.put("body", "会员充值");
//商户订单号
reqData.put("out_trade_no", "123456");
//标价金额
reqData.put("total_fee", "1");
//终端ip
reqData.put("spbill_create_ip", "XXXXXXXXXXXXXXXXX");
//通知地址
reqData.put("notify_url", "http://www.weixin.qq.com/wxpay/pay.php");
//交易类型
reqData.put("trade_type", "JSAPI");
//加密类型
reqData.put("sign_type", "MD5");
//wxPayConfigVlue.getKey() 商户支付秘钥(可登录微信支付平台获取)
reqData.put("sign", WXPayUtil.generateSignature(reqData, wxPayConfigVlue.getKey()));
return reqData;
}
②:加密工具类
public final class WXPayXmlUtil {
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
return documentBuilderFactory.newDocumentBuilder();
}
public static Document newDocument() throws ParserConfigurationException {
return newDocumentBuilder().newDocument();
}
}
public class WXPayUtil {
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(“sign”)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
return MD5(sb.toString()).toUpperCase();
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
throw ex;
}
}
}
3:发送请求
①:无证书请求
/**
* 不需要证书的请求
* @param urlSuffix String
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 超时时间,单位是毫秒
* @param readTimeoutMs 超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public String requestWithoutCert(String urlSuffix, Map<String, String> reqData,
int connectTimeoutMs, int readTimeoutMs) throws Exception {
String msgUUID = reqData.get("nonce_str");
String reqBody = WXPayUtil.mapToXml(reqData);
String resp = String resp = new WXPayRequest().requestOnce(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, true);
return resp;
}
②:创建WXPayRequest
public class WXPayRequest {
private WXPayConfigVlue wxPayConfigVlue = new WXPayConfigVlue();
public static final String WXPAYSDK_VERSION = "WXPaySDK/3.0.9";
public static final String USER_AGENT = WXPAYSDK_VERSION +
" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();
public WXPayRequest(){}
/**
* 请求,只请求一次,不做重试
* @param urlSuffix 请求地址
* @param uuid
* @param data
* @param connectTimeoutMs
* @param readTimeoutMs
* @param useCert 是否使用证书,针对退款、撤销等操作
* @return
* @throws IOException
* @throws ClientProtocolException
* @throws Exception
*/
public String requestOnce(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert) throws ClientProtocolException, IOException {
BasicHttpClientConnectionManager connManager;
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(),
null,
null,
null
);
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connManager)
.build();
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(data, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.addHeader("User-Agent", USER_AGENT + " " + wxPayConfigVlue.getMchID());
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
return EntityUtils.toString(httpEntity, "UTF-8");
}
}
③:创建WXPayXmlUtil
public class WXPayXmlUtil {
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
return documentBuilderFactory.newDocumentBuilder();
}
public static Document newDocument() throws ParserConfigurationException {
return newDocumentBuilder().newDocument();
}
}
4:将xml转成map集合
/**
* 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
* @param xmlStr API返回的XML格式数据
* @return Map类型数据
* @throws Exception
*/
public Map<String, String> processResponseXml(String xmlStr) throws Exception {
String RETURN_CODE = "return_code";
String return_code;
Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
if (respData.containsKey(RETURN_CODE)) {
return_code = respData.get(RETURN_CODE);
}
else {
throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
}
if (return_code.equals("FAIL")) {
return respData;
}
else if (return_code.equals("SUCCESS")) {
return respData;
}
else {
throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
}
}
5:对数据进行二次加密
//二次加密
public void seconEncryption(Map map){
Map<String, String> seconMap = new HashMap<String,String>();
String timestamp = System.currentTimeMillis()+"";
System.out.println("生成的时间戳为");
System.out.println(timestamp);
//小程序appid
seconMap.put("appId", (String) map.get("appid"));
//随机字符串
seconMap.put("nonceStr", (String) map.get("nonce_str"));
//数据包
seconMap.put("package", "prepay_id="+map.get("prepay_id"));
//签名方式
seconMap.put("signType", "MD5");
//当前时间戳
seconMap.put("timeStamp", timestamp);
//加密
try {
String secondMsg = WXPayUtil.generateSignature(seconMap, wxPayConfigVlue.getKey());
System.out.println("二次加密后返回的值为");
System.out.println(secondMsg);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
6:前端根据后端传来的二次加密数据调起微信支付
//调起微信支付
wx.requestPayment(
{
'timeStamp': "XXXXXXXXXXX", //时间戳
'nonceStr': 'XXXXXXXXXXXX', //随机字符串
'package': 'prepay_id=XXXXXXXXXXXXX', //统一下单接口返回的 prepay_id 参数值
'signType': 'MD5',
'paySign': "XXXXXXXXXXXXXXXXXX",
'success': function (res) {
console.log("调起支付成功")
console.log(res);
},
'fail': function (res) {
console.log("调起支付发生失败")
console.log(res);
},
'complete': function (res) {
console.log("调起支付结束");
console.log(res);
}
})