微信支付java版本之Native付款
最近工作中接觸到一些關(guān)于微信支付方面的東西,看到給的DEMO都是PHP版本的,再加上微信支付文檔寫的確實(shí)不敢恭維,趟過不少坑之后閑下來做個(gè)總結(jié)。
一、前期準(zhǔn)備
做微信開發(fā)首先要申請一個(gè)公共賬號(hào),申請成功后會(huì)以郵件形式發(fā)給你一些必要信息,公共賬號(hào)中有開發(fā)文檔以及開發(fā)中必要信息,以及測試的數(shù)據(jù)查詢。

二、工具類
1.MD5加密工具類
package com.pay.utils.weixin;
import java.security.MessageDigest;
public class MD5Util {
public final static String MD5(String s) {
char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
try {
byte[] btInput = s.getBytes();
// 獲得MD5摘要算法的 MessageDigest 對象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字節(jié)更新摘要
mdInst.update(btInput);
// 獲得密文
byte[] md = mdInst.digest();
// 把密文轉(zhuǎn)換成十六進(jìn)制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
2.CommonUtil工具類,用于裝換成微信所需XML。以下return new String(xml.toString().getBytes(),"ISO8859-1");將工具類中的utf-8改成iso8859-1,否則微信訂單中的中文會(huì)出現(xiàn)亂碼,改后可以正確顯示。
package com.pay.utils.weixin;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.Map.Entry;
public class CommonUtil {
public static String CreateNoncestr(int length) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < length; i++) {
Random rd = new Random();
res += chars.indexOf(rd.nextInt(chars.length() - 1));
}
return res;
}
public static String CreateNoncestr() {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < 16; i++) {
Random rd = new Random();
res += chars.charAt(rd.nextInt(chars.length() - 1));
}
return res;
}
public static String FormatQueryParaMap(HashMap<String, String> parameters)
throws SDKRuntimeException {
String buff = "";
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(
parameters.entrySet());
Collections.sort(infoIds,
new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1,
Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(
o2.getKey());
}
});
for (int i = 0; i < infoIds.size(); i++) {
Map.Entry<String, String> item = infoIds.get(i);
if (item.getKey() != "") {
buff += item.getKey() + "="
+ URLEncoder.encode(item.getValue(), "utf-8") + "&";
}
}
if (buff.isEmpty() == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
throw new SDKRuntimeException(e.getMessage());
}
return buff;
}
public static String FormatBizQueryParaMap(HashMap<String, String> paraMap,
boolean urlencode) throws SDKRuntimeException {
String buff = "";
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(
paraMap.entrySet());
Collections.sort(infoIds,
new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1,
Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(
o2.getKey());
}
});
for (int i = 0; i < infoIds.size(); i++) {
Map.Entry<String, String> item = infoIds.get(i);
//System.out.println(item.getKey());
if (item.getKey() != "") {
String key = item.getKey();
String val = item.getValue();
if (urlencode) {
val = URLEncoder.encode(val, "utf-8");
}
buff += key.toLowerCase() + "=" + val + "&";
}
}
if (buff.isEmpty() == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
throw new SDKRuntimeException(e.getMessage());
}
return buff;
}
public static boolean IsNumeric(String str) {
if (str.matches("\\d *")) {
return true;
} else {
return false;
}
}
public static String ArrayToXml(HashMap<String, String> arr) {
String xml = "<xml>";
Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> entry = iter.next();
String key = entry.getKey();
String val = entry.getValue();
if (IsNumeric(val)) {
xml += "<" + key + ">" + val + "</" + key + ">";
} else
xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
}
xml += "</xml>";
try {
return new String(xml.toString().getBytes(),"ISO8859-1");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}
3.ClientCustomSSL工具類,用于生成sign以及創(chuàng)建微信訂單package com.pay.utils.weixin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.util.StringUtils;
/**
* This example demonstrates how to create secure connections with a custom SSL
* context.
*/
public class ClientCustomSSL {
public static String GetBizSign(HashMap<String, String> bizObj)
throws SDKRuntimeException {
HashMap<String, String> bizParameters = new HashMap<String, String>();
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(
bizObj.entrySet());
System.out.println(infoIds);
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1,
Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
System.out.println("--------------------");
System.out.println(infoIds);
for (int i = 0; i < infoIds.size(); i++) {
Map.Entry<String, String> item = infoIds.get(i);
if (item.getKey() != "") {
bizParameters.put(item.getKey().toLowerCase(), item.getValue());
}
}
//bizParameters.put("key", "12345678123456781234567812345671");
String bizString = CommonUtil.FormatBizQueryParaMap(bizParameters,
false);
bizString += "&key=12345678123456781234567812345671";
System.out.println("***************");
System.out.println(bizString);
// return SHA1Util.Sha1(bizString);
return MD5Util.MD5(bizString);
}
/**
* 微信創(chuàng)建訂單
* @param nonceStr
* @param orderDescribe
* @param orderNo
* @param price
* @param timeStart
* @param timeExpire
* @return
* @throws SDKRuntimeException
*/
public static String CreateNativePackage(String nonceStr,String orderDescribe,String orderNo,String price,String timeStart,String timeExpire) throws SDKRuntimeException {
HashMap<String, String> nativeObj = new HashMap<String, String>();
nativeObj.put("appid", "見公眾賬號(hào)"); //公眾賬號(hào)Id
nativeObj.put("mch_id", "見郵件"); //商戶號(hào)
nativeObj.put("nonce_str", nonceStr); //隨機(jī)字符串
nativeObj.put("body", orderDescribe); //商品描述
nativeObj.put("attach", "tradeno"); //附加數(shù)據(jù)
nativeObj.put("out_trade_no", orderNo); //商戶訂單號(hào)(全局唯一)
nativeObj.put("total_fee", price); //總金額(單位為分,不能帶小數(shù)點(diǎn))
nativeObj.put("spbill_create_ip","192.168.0.144"); //終端Ip
nativeObj.put("time_start", timeStart); //交易起始時(shí)間
nativeObj.put("time_expire", timeExpire); //交易結(jié)束時(shí)間
nativeObj.put("notify_url", CustomizedPropertyPlaceholderConfigurer.getContextProperty("wxurl")+"/weixin_callback/weixinCallback/init.action"); //回調(diào)通知地址
nativeObj.put("trade_type", "NATIVE"); //交易類型
String sign = GetBizSign(nativeObj);
nativeObj.put("sign", sign.toUpperCase());
return CommonUtil.ArrayToXml(nativeObj);
} /**
* 微信訂單支付查詢
* @param nonceStr
* @param orderDescribe
* @param orderNo
* @param price
* @param timeStart
* @param timeExpire
* @return
* @throws SDKRuntimeException
*/
public static String SearchNativePackage(String transactionId,String outTradeNo,String nonceStr) throws SDKRuntimeException {
HashMap<String, String> nativeObj = new HashMap<String, String>();
nativeObj.put("appid", "見公眾共賬號(hào)"); //公眾賬號(hào)Id
nativeObj.put("mch_id", "見郵件");//商戶號(hào)
nativeObj.put("nonce_str", nonceStr);//隨機(jī)字符串
if(!StringUtils.isEmpty(transactionId)){
nativeObj.put("transaction_id", transactionId);
}
if(!StringUtils.isEmpty(outTradeNo)){
nativeObj.put("out_trade_no", outTradeNo);//隨機(jī)字符串
}
String sign = GetBizSign(nativeObj);
nativeObj.put("sign", sign.toUpperCase());
return CommonUtil.ArrayToXml(nativeObj);
/**
* 微信退款
* @param outTradeNo
* @param outRefundNo
* @param totalFee
* @param refundFee
* @return
* @throws SDKRuntimeException
*/
public static String RefundNativePackage(String outTradeNo,String outRefundNo,String totalFee,String refundFee,String nonceStr) throws SDKRuntimeException {
HashMap<String, String> nativeObj = new HashMap<String, String>();
nativeObj.put("appid", "見公眾賬號(hào)");//公眾賬號(hào)Id
nativeObj.put("mch_id", "見郵件");//商戶號(hào)
nativeObj.put("nonce_str", nonceStr);//隨機(jī)字符串
nativeObj.put("out_trade_no", outTradeNo);//商戶訂單號(hào)(全局唯一)
nativeObj.put("out_refund_no", outRefundNo);//商戶退款單號(hào)(全局唯一)
nativeObj.put("total_fee", totalFee);//總金額(單位為分,不能帶小數(shù)點(diǎn))
nativeObj.put("refund_fee", refundFee);//退款金額(單位為分,不能帶小數(shù)點(diǎn))
nativeObj.put("op_user_id", "郵件");
String sign = GetBizSign(nativeObj);
nativeObj.put("sign", sign.toUpperCase());
return CommonUtil.ArrayToXml(nativeObj);
}
/**
* 微信待支付
* @param nonceStr
* @param orderDescribe
* @param orderNo
* @param price
* @param timeStart
* @param timeExpire
* @return
* @throws SDKRuntimeException
*/
public static String CreateJsApiPackage(String nonceStr,String orderDescribe,String orderNo,String price,String timeStart,String timeExpire,String openId) throws SDKRuntimeException {
HashMap<String, String> nativeObj = new HashMap<String, String>();
nativeObj.put("appid", "見公眾賬號(hào)");//公眾賬號(hào)Id
nativeObj.put("openid", openId);//公眾賬號(hào)Id
nativeObj.put("mch_id", "見郵件")//商戶號(hào)
nativeObj.put("nonce_str", nonceStr);//隨機(jī)字符串
nativeObj.put("body", orderDescribe);//商品描述
nativeObj.put("attach", "tradeno");//附加數(shù)據(jù)
nativeObj.put("out_trade_no", orderNo);//商戶訂單號(hào)(全局唯一)
nativeObj.put("total_fee", price);//總金額(單位為分,不能帶小數(shù)點(diǎn))
nativeObj.put("spbill_create_ip","192.168.0.144");//終端Ip
nativeObj.put("time_start", timeStart);//交易起始時(shí)間
nativeObj.put("time_expire", timeExpire)//交易結(jié)束時(shí)間
nativeObj.put("notify_url",CustomizedPropertyPlaceholderConfigurer.getContextProperty("wxurl")+"/weixin_callback/weixinCallback/init.action");//通知地址
nativeObj.put("trade_type", "JSAPI");//交易類型
String sign = GetBizSign(nativeObj);
nativeObj.put("sign", sign.toUpperCase());
return CommonUtil.ArrayToXml(nativeObj);
}
/**
* 微信關(guān)閉訂單
* @param nonceStr
* @param orderDescribe
* @param orderNo
* @param price
* @param timeStart
* @param timeExpire
* @param openId
* @return
* @throws SDKRuntimeException
*/
public static String CreateCloseOrder(String outTradeNo,String nonceStr) throws SDKRuntimeException {
HashMap<String, String> nativeObj = new HashMap<String, String>();
nativeObj.put("appid", "見公眾賬號(hào)");//公眾賬號(hào)Id
nativeObj.put("mch_id", "見郵件");//商戶號(hào)
nativeObj.put("out_trade_no", outTradeNo);//商戶訂單號(hào)(全局唯一)
nativeObj.put("nonce_str", nonceStr);//隨機(jī)字符串
String sign = GetBizSign(nativeObj);
nativeObj.put("sign", sign.toUpperCase());
return CommonUtil.ArrayToXml(nativeObj);
}
}
4.調(diào)用微信支付接口
package com.pay.controller.weixin;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.pay.bo.PayHist;
import com.pay.constants.PayHistoryPayStatus;
import com.pay.constants.PayHistoryPayType;
import com.pay.service.WeiXinPayService;
import com.pay.utils.weixin.ClientCustomSSL;
import com.pay.utils.weixin.CloseWeiXinOrderUtils;
import com.pay.utils.weixin.CustomizedPropertyPlaceholderConfigurer;
@RestController
@RequestMapping("/Pay")
public class WeiXinPayController {
@Autowired
WeiXinPayService weiXinPayService;
private static long standardTime = 1662652800000L;
/**
* 返回生成二維碼的url
* @param request
* @param response
* @return
*/
@RequestMapping(value="/getUrl",method=RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public Object getUrl(HttpServletResponse response,@RequestBody String body){
try{
JSONObject jsonO = JSONObject.fromObject(body);
PayHist ph = null;
// List<Map<String,Object>> td = weiXinPayService.getTrade(orderNo);
Date dt = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String nonceStr = sdf.format(dt).toString();
Date now = new Date();
String tradePayNo = jsonO.get("orderNo").toString()+String.format("%10d",standardTime - now.getTime()).substring(0, 10);
System.out.println("訂單標(biāo)號(hào)orderNo======="+jsonO.get("orderNo").toString());
System.out.println("10位隨機(jī)數(shù)======="+String.format("%10d",standardTime - now.getTime()).substring(0, 10));
String price = Math.round(Float.valueOf(jsonO.get("price").toString())*100)+"";
Long timeExpireStrOld = dt.getTime();
Long timeNew = Long.parseLong(CustomizedPropertyPlaceholderConfigurer.getContextProperty("weixin.send2finish.overtime").toString());
Long timeExpireNew = timeExpireStrOld+timeNew;
Date dtTimeExpire = new Date(timeExpireNew);
SimpleDateFormat dtSdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timeExpire = dtSdf.format(dtTimeExpire).toString();
System.out.println("nonceStr=="+nonceStr);
System.out.println("orderNo=="+jsonO.get("orderNo").toString());
System.out.println("price=="+price);
System.out.println("timeStart=="+nonceStr);
System.out.println("timeExpire=="+timeExpire);
JSONObject result = (JSONObject) setUrl(nonceStr,"訂單",tradePayNo,price,nonceStr,timeExpire);
if(result.get("status").toString().equals("success")){
ph = new PayHist();
ph.setTradePayUrl(result.getString("weixinPayUrl"));//此字段為支付鏈接,可以此鏈接生成二維碼掃碼支付
ph.setPayTradeNo(jsonO.get("orderNo").toString());
ph.setTradePayNo(tradePayNo);
ph.setPayStatus(PayHistoryPayStatus.WECHAT_PAY_STATUS_WAIT);
ph.setPayType(PayHistoryPayType.WECHAT);
ph.setAppKey(jsonO.getString("appKey").toString());
ph.setPayAmount(price);
result.put("payTradeNo", ph.getPayTradeNo());
result.put("tradePayNo", ph.getTradePayNo());
result.put("payStatus", ph.getPayStatus());
result.put("payType", ph.getPayType());
}
return result;
}catch(Exception e){
e.printStackTrace();
JSONObject result = new JSONObject();
result.put("status","error");
result.put("msg",e.getMessage());
// return result.toString();
}
return null;
}
public Object setUrl(String nonceStr,String orderDescribe,String orderNo,String price,String timeStart,String timeExpire) {
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(微信證書絕對路徑));
try {
keyStore.load(instream, "商戶ID".toCharArray());
}finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,<span style="font-family: Arial, Helvetica, sans-serif;">商戶ID</span>.toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf).build();
// HttpGet httpget = new
// HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund");
HttpPost httppost = new HttpPost(
"https://api.mch.weixin.qq.com/pay/unifiedorder");
String xml = ClientCustomSSL.CreateNativePackage(nonceStr,orderDescribe,orderNo,price,timeStart,timeExpire);
try {
StringEntity se = new StringEntity(xml);
httppost.setEntity(se);
System.out.println("executing request" + httppost.getRequestLine());
CloseableHttpResponse responseEntry = httpclient.execute(httppost);
try {
HttpEntity entity = responseEntry.getEntity();
System.out.println("----------------------------------------");
System.out.println(responseEntry.getStatusLine());
if (entity != null) {
System.out.println("Response content length: "
+ entity.getContentLength());
/* BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(entity.getContent()));
String text;
while ((text = bufferedReader.readLine()) != null) {
System.out.println("======="+text);
}*/
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(entity.getContent());
Element rootElt = document.getRootElement();
System.out.println("根節(jié)點(diǎn):" + rootElt.getName());
System.out.println("==="+rootElt.elementText("result_code"));
System.out.println("==="+rootElt.elementText("return_msg"));
String resultCode = rootElt.elementText("result_code");
JSONObject result = new JSONObject();
Document documentXml =DocumentHelper.parseText(xml);
Element rootEltXml = documentXml.getRootElement();
if(resultCode.equals("SUCCESS")){
System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id"));
System.out.println("=================sign===================="+ rootEltXml.elementText("sign"));
result.put("weixinPayUrl", rootElt.elementText("code_url"));
result.put("prepayId", rootElt.elementText("prepay_id"));
result.put("status","success");
result.put("msg","success");
}else{
result.put("status","false");
result.put("msg",rootElt.elementText("err_code_des"));
}
return result;
}
EntityUtils.consume(entity);
}
finally {
responseEntry.close();
}
}
finally {
httpclient.close();
}
return null;
}catch(Exception e){
e.printStackTrace();
JSONObject result = new JSONObject();
result.put("status","error");
result.put("msg",e.getMessage());
return result;
}
}
}
httpclient jar包和json jar包:下載地址。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringSession 請求與響應(yīng)重寫的實(shí)現(xiàn)
這篇文章主要介紹了SpringSession 請求與響應(yīng)重寫的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11
深入詳解Java中synchronized鎖升級(jí)的套路
synchronized鎖是啥?鎖其實(shí)就是一個(gè)對象,隨便哪一個(gè)都可以,Java中所有的對象都是鎖,換句話說,Java中所有對象都可以成為鎖。本文我們主要來聊聊synchronized鎖升級(jí)的套路,感興趣的可以收藏一下2023-04-04
Spring Data JPA實(shí)現(xiàn)分頁P(yáng)ageable的實(shí)例代碼
本篇文章主要介紹了Spring Data JPA實(shí)現(xiàn)分頁P(yáng)ageable的實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
java實(shí)現(xiàn)定制數(shù)據(jù)透視表的示例詳解
數(shù)據(jù)透視表(Pivot?Table)是一種數(shù)據(jù)分析工具,通常用于對大量數(shù)據(jù)進(jìn)行匯總、分析和展示,本文主要介紹了如何使用Java將計(jì)算項(xiàng)添加到數(shù)據(jù)透視表中,感興趣的可以了解下2023-12-12
Java @Transactional與synchronized使用的問題
這篇文章主要介紹了Java @Transactional與synchronized使用的問題,了解內(nèi)部原理是為了幫助我們做擴(kuò)展,同時(shí)也是驗(yàn)證了一個(gè)人的學(xué)習(xí)能力,如果你想讓自己的職業(yè)道路更上一層樓,這些底層的東西你是必須要會(huì)的2023-01-01

