這個就直接貼代碼了,反正暫時沒法驗證對錯
錯誤工具類
import plugins.pay.wechat.domain.WechatPayError;
import java.util.HashMap;
import java.util.Map;
/**
* 錯誤工具類,這裏用枚舉類會更好,不過我真的懶得去看文檔改了,這個類也不是很重要,不搞了
* @author zhang.shushan
*/
public class WeChatPayErrorUtil {
private static Map<String, WechatPayError> errs = new HashMap<>();
static {
errs.put("NOAUTH", WechatPayError.builder().err_code_des("商戶無此接口權限").err_cause("商戶未開通此接口權限")
.err_resolve("請商戶前往申請此接口權限").build());
errs.put("NOTENOUGH", WechatPayError.builder().err_code_des("餘額不足").err_cause("用戶帳號餘額不足")
.err_resolve("用戶帳號餘額不足,請用戶充值或更換支付卡後再支付").build());
errs.put("ORDERPAID", WechatPayError.builder().err_code_des("商戶訂單已支付").err_cause("商戶訂單已支付,無需重複操作")
.err_resolve("商戶訂單已支付,無需更多操作").build());
errs.put("ORDERCLOSED", WechatPayError.builder().err_code_des("訂單已關閉").err_cause("當前訂單已關閉,無法支付")
.err_resolve("當前訂單已關閉,請重新下單").build());
errs.put("SYSTEMERROR",
WechatPayError.builder().err_code_des("系統錯誤").err_cause("系統超時").err_resolve("系統異常,請用相同參數重新調用").build());
errs.put("APPID_NOT_EXIST", WechatPayError.builder().err_code_des("APPID不存在").err_cause("參數中缺少APPID")
.err_resolve("請檢查APPID是否正確").build());
errs.put("MCHID_NOT_EXIST", WechatPayError.builder().err_code_des("MCHID不存在").err_cause("參數中缺少MCHID")
.err_resolve("請檢查MCHID是否正確").build());
errs.put("APPID_MCHID_NOT_MATCH", WechatPayError.builder().err_code_des("appid和mch_id不匹配")
.err_cause("appid和mch_id不匹配").err_resolve("請確認appid和mch_id是否匹配").build());
errs.put("LACK_PARAMS",
WechatPayError.builder().err_code_des("缺少參數").err_cause("缺少必要的請求參數").err_resolve("請檢查參數是否齊全").build());
errs.put("OUT_TRADE_NO_USED", WechatPayError.builder().err_code_des("商戶訂單號重複 ").err_cause("同一筆交易不能多次提交")
.err_resolve("請覈實商戶訂單號是否重複提交").build());
errs.put("SIGNERROR", WechatPayError.builder().err_code_des("簽名錯誤").err_cause("參數簽名結果不正確")
.err_resolve("請覈實商戶訂單號是否重複提交").build());
errs.put("XML_FORMAT_ERROR", WechatPayError.builder().err_code_des("XML格式錯誤").err_cause("XML格式錯誤")
.err_resolve("請檢查XML參數格式是否正確").build());
errs.put("REQUIRE_POST_METHOD", WechatPayError.builder().err_code_des("請使用post方法").err_cause("未使用post傳遞參數")
.err_resolve("請檢查請求參數是否通過post方法提交").build());
errs.put("POST_DATA_EMPTY", WechatPayError.builder().err_code_des("post數據爲空").err_cause("post數據不能爲空")
.err_resolve("請檢查post數據是否爲空").build());
errs.put("NOT_UTF8", WechatPayError.builder().err_code_des("編碼格式錯誤").err_cause("未使用指定編碼格式")
.err_resolve("請使用NOT_UTF8編碼格式").build());
}
public static String getErrorMsg(String result_code) {
// TODO Auto-generated method stub
return errs.get(result_code).getErr_cause()+":"+errs.get(result_code).getErr_resolve();
}
}
錯誤封裝對象
public class WechatPayError {
private String err_code_des;
private String err_cause;
private String err_resolve;
public WechatPayError(String err_code_des,String err_cause,String err_resolve){
this.err_code_des=err_code_des;
this.err_cause=err_cause;
this.err_resolve=err_resolve;
}
public static WechatPayErrorBuilder builder(){
return new WechatPayErrorBuilder();
}
public static class WechatPayErrorBuilder{
private String err_code_des;
private String err_cause;
private String err_resolve;
public WechatPayErrorBuilder err_code_des(String err_code_des){
this.err_code_des=err_code_des;
return this;
}
public WechatPayErrorBuilder err_cause(String err_cause){
this.err_cause=err_cause;
return this;
}
public WechatPayErrorBuilder err_resolve(String err_resolve){
this.err_resolve = err_resolve;
return this;
}
@Override
public String toString(){
return "WechatPayErrorBuilder[err_code_des="+this.err_code_des+",err_cause="+this.err_cause+",err_resolve="+err_resolve+"]";
}
public WechatPayError build(){
return new WechatPayError(this.err_code_des, this.err_cause, this.err_resolve);
}
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
public String getErr_cause() {
return err_cause;
}
public void setErr_cause(String err_cause) {
this.err_cause = err_cause;
}
public String getErr_resolve() {
return err_resolve;
}
public void setErr_resolve(String err_resolve) {
this.err_resolve = err_resolve;
}
}
XML工具類
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/**
* 2018/7/3
*/
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();
}
}
微信訂單返回信息封裝
/**
* 微信預支付訂單返回信息
* @author zhang.shushan
* @date 2018年1月19日
*/
public class WechatPreOrderInfo {
//返回狀態碼
private String return_code;
//返回信息
private String return_msg;
//應用APPID
private String appid;
//商戶號
private String mch_id;
//設備號
private String device_info;
//隨機字符串
private String nonce_str;
//簽名
private String sign;
//業務結果
private String result_code;
//錯誤代碼
private String err_code;
//錯誤代碼描述
private String err_code_des;
//交易類型
private String trade_type;
//預支付交易會話標識
private String prepay_id;
//掃碼支付返回字段,用於生成二維碼
private String code_url;
/**
* 連接是否成功
* @return
*/
public boolean isContact(){
return "SUCCESS".equals(this.return_code);
}
/**
* 業務是否成功
* @return
*/
public boolean isSuccess(){
if(isContact()){
return "SUCCESS".equals(this.result_code);
}
return false;
}
/**
* 固定字段
* @return
*/
public String getPackage(){
return "Sign=WXPay";
}
/**
* 時間戳
* @return
*/
public Long getTimestamp(){
return System.currentTimeMillis()/1000;
}
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_code_des() {
return err_code_des;
}
public void setErr_code_des(String err_code_des) {
this.err_code_des = err_code_des;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getPrepay_id() {
return prepay_id;
}
public void setPrepay_id(String prepay_id) {
this.prepay_id = prepay_id;
}
public String getCode_url() {
return code_url;
}
public void setCode_url(String code_url) {
this.code_url = code_url;
}
}
微信支付返回信息封裝
/**
* 微信支付返回信息類
* @author zhang.shushan
* @date 2018年1月24日
*/
public class WechatPayRet {
//返回狀態碼
private String return_code;
//返回信息
private String return_msg;
//應用ID
private String appid;
//商戶號
private String mch_id;
//設備號
private String device_info;
//隨機字符串
private String nonce_str;
//簽名
private String sign;
//業務結果
private String result_code;
//錯誤代碼
private String err_code;
//錯誤代碼描述
private String err_des;
//用戶標識
private String openid;
//是否關注公衆賬號
private String is_subscribe;
//交易類型
private String trade_type;
//付款銀行
private String bank_type;
//總金額
private int total_fee;
//貨幣種類
private String fee_type;
//現金支付金額
private int cash_fee;
//現金支付貨幣類型
private String cash_fee_type;
//代金券金額
private int coupon_fee;
//代金券使用數量
private int coupon_count;
//微信支付訂單號
private String transaction_id;
//商戶訂單號
private String out_trade_no;
//商家數據包
private String attach;
//支付完成時間
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date time_end;
/**
* 連接是否成功
* @return
*/
public boolean isContact(){
return "SUCCESS".equals(this.return_code);
}
/**
* 業務是否成功
* @return
*/
public boolean isSuccess(){
if(isContact()){
return "SUCCESS".equals(this.result_code);
}
return false;
}
public String getReturn_code() {
return return_code;
}
public void setReturn_code(String return_code) {
this.return_code = return_code;
}
public String getReturn_msg() {
return return_msg;
}
public void setReturn_msg(String return_msg) {
this.return_msg = return_msg;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getResult_code() {
return result_code;
}
public void setResult_code(String result_code) {
this.result_code = result_code;
}
public String getErr_code() {
return err_code;
}
public void setErr_code(String err_code) {
this.err_code = err_code;
}
public String getErr_des() {
return err_des;
}
public void setErr_des(String err_des) {
this.err_des = err_des;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getIs_subscribe() {
return is_subscribe;
}
public void setIs_subscribe(String is_subscribe) {
this.is_subscribe = is_subscribe;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getBank_type() {
return bank_type;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public int getTotal_fee() {
return total_fee;
}
public void setTotal_fee(int total_fee) {
this.total_fee = total_fee;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public int getCash_fee() {
return cash_fee;
}
public void setCash_fee(int cash_fee) {
this.cash_fee = cash_fee;
}
public String getCash_fee_type() {
return cash_fee_type;
}
public void setCash_fee_type(String cash_fee_type) {
this.cash_fee_type = cash_fee_type;
}
public int getCoupon_fee() {
return coupon_fee;
}
public void setCoupon_fee(int coupon_fee) {
this.coupon_fee = coupon_fee;
}
public int getCoupon_count() {
return coupon_count;
}
public void setCoupon_count(int coupon_count) {
this.coupon_count = coupon_count;
}
public String getTransaction_id() {
return transaction_id;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public Date getTime_end() {
return time_end;
}
public void setTime_end(Date time_end) {
this.time_end = time_end;
}
}
下單信息封裝
import com.alibaba.fastjson.JSON;
import java.io.IOException;
/**
* 微信支付信息實體類
* @date 2018年1月19日
*/
public class WechatPayInfo {
// 應用ID
private String appid;
// 商戶號
private String mch_id;
// 終端設備號(門店號或收銀設備ID),默認請傳"WEB"
private String device_info = "WEB";
// 隨機字符串
private String nonce_str;
// 簽名,信息填充完整後使用工具類設置簽名
private String sign;
// 簽名類型,目前支持HMAC-SHA256和MD5,默認爲MD5
private String sign_type = "MD5";
/**
* 商品描述交易字段格式根據不同的應用場景按照以下格式:
* APP——需傳入應用市場上的APP名字-實際商品名稱,
* 天天愛消除-遊戲充值。
*/
private String body;
// 附加數據,在查詢API和支付通知中原樣返回,該字段主要用於商戶攜帶訂單的自定義數據
private String attach;
// 商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|*@ ,且在同一個商戶號下唯一
private String out_trade_no;
// 符合ISO 4217標準的三位字母代碼,默認人民幣:CNY,其他值列表詳見
private String fee_type = "CNY";
// 訂單總金額,單位爲分
private int total_fee;
//訂單詳情,用於單個商品的優惠,設置成對象的json
private String detail;
// 用戶端實際ip
private String spbill_create_ip;
// 接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數。
private String notify_url;
// 交易類型
private String trade_type;
// 該字段用於統一下單時上報場景信息,目前支持上報實際門店信息,設置成對象的json
private String scene_info;
//微信公衆號支付必填
private String openid;
//限制使用信用卡支付
private String limit_pay;
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getLimit_pay() {
return limit_pay;
}
public void setLimit_pay(String limit_pay) {
this.limit_pay = limit_pay;
}
/**
* 設置限制使用信用卡
*/
public void configureLimitPay(){
this.limit_pay="no_credit";
}
/**
* 設置必填的自定義參數
*
* @param body
* @param out_trade_no
* @param total_fee
* @param suffix
* @param trade_type
* @throws IOException
*/
public WechatPayInfo(String body, String out_trade_no, String suffix,
int total_fee,
String trade_type, String spbill_create_ip) throws IOException {
this.body = "開放平臺APP name" + "-" + body;
this.out_trade_no = out_trade_no;
this.notify_url = "商戶後臺回調接口地址";
this.trade_type = trade_type;
this.spbill_create_ip = spbill_create_ip;
this.total_fee = total_fee;
}
/**
* 設置必填的自定義參數
*
* @param body
* @param out_trade_no
* @param total_fee
* @param notify_url
* @param trade_type
* @throws IOException
*/
public WechatPayInfo(String body, String out_trade_no,
int total_fee, String notify_url,
String trade_type, String spbill_create_ip) throws IOException {
this.body = 開放平臺APP name + "-" + body;
this.out_trade_no = out_trade_no;
this.notify_url = notify_url;
this.trade_type = trade_type;
this.spbill_create_ip = spbill_create_ip;
this.total_fee = total_fee;
}
/**
* 設置單品優惠信息
*
* @param orderInfos
*/
public void configDetail(OrderInfos orderInfos) {
this.detail = JSON.toJSONString(orderInfos);
}
/**
* 設置實際門店信息
*
* @param sceneInfo
*/
public void configScene_info(SceneInfo sceneInfo) {
this.scene_info = JSON.toJSONString(sceneInfo);
}
/**
* 設置必填的自定義參數
*
* @param body
* @param out_trade_no
* @param total_fee
* @param notify_url
* @param trade_type
* @throws IOException
*/
public void configMajorParameters(String body, String out_trade_no,
int total_fee, String notify_url,
String trade_type, String spbill_create_ip) throws IOException {
this.body = "開放平臺APP name" + "-" + body;
this.out_trade_no = out_trade_no;
this.notify_url = notify_url;
this.trade_type = trade_type;
this.spbill_create_ip = spbill_create_ip;
this.total_fee = total_fee;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getSign_type() {
return sign_type;
}
public void setSign_type(String sign_type) {
this.sign_type = sign_type;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getScene_info() {
return scene_info;
}
public void setScene_info(String scene_info) {
this.scene_info = scene_info;
}
public void setTotal_fee(int total_fee) {
this.total_fee = total_fee;
}
}
下單和回調接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alipay.api.internal.util.StringUtils;
import plugins.pay.wechat.domain.WechatPayInfo;
import plugins.pay.wechat.domain.WechatPayRet;
import plugins.pay.wechat.domain.WechatPreOrderInfo;
import plugins.pay.wechat.sdk.WXPayXmlUtil;
import plugins.pay.wechat.utils.WeChatPayErrorUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
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.DocumentException;
import org.dom4j.DocumentHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URI;
import java.security.MessageDigest;
import java.util.*;
@RestController
@Api(tags="支付接口")
@RequestMapping("/Indent")
public class H5IndentController {
@GetMapping("/addWechat")
@ApiOperation(value = "微信新增訂單", notes = "微信新增訂單")
public Map<String,Object> addWechat(HttpServletRequest request, HttpServletResponse response){
// 返回結果
Map<String,Object> map = null;
try {
//創建微信預支付返回對象,主要是爲了返回給APP時數據結構不變
WechatPreOrderInfo wechatPreOrderInfo = new WechatPreOrderInfo();
//創建預支付請求信息,18是價格
WechatPayInfo payInfo = new WechatPayInfo("會員充值", "商戶訂單id",
18, "商戶回調接口地址",
"APP", request.getRemoteAddr());
// 向微信發起請求,這裏需要調起各種配置信息
wechatPreOrderInfo = preOrder(payInfo);
// 加簽
map = sign4App(wechatPreOrderInfo);
// TODO 保存數據
// 給返回結果添加訂單id
map.put("indentId", "商戶訂單id");
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
// 微信APP收款回調
@PostMapping("/wechatNotify")
public void wechatNotify(HttpServletRequest request, HttpServletResponse response)throws Exception{
//解析數據
WechatPayRet ret = parseRequest(request);
String return_code = ret.getReturn_code();
if(return_code.equals("SUCCESS")){
// 開始進行訂單處理,ret.getOut_trade_no()就是商戶訂單號,delIndent()是業務處理邏輯自己完善
String result = delIndent(ret.getOut_trade_no());
System.out.println("----------------------------------------------------------------------交易完成!");
if(result.equals("success")){
//成功之後要應答,讓微信別調了。但是還是會有重入的可能,所以必須做好數據鎖
echo(response);
}else{
return;
}
}else{
return;
}
}
// APP支付二次加簽
public static Map<String,Object> sign4App(WechatPreOrderInfo preOrderInfo)throws Exception{
TreeMap<String, Object> map = new TreeMap<>();
map.put("appid", preOrderInfo.getAppid());
map.put("partnerid", preOrderInfo.getMch_id());
map.put("prepayid", preOrderInfo.getPrepay_id());
map.put("package", preOrderInfo.getPackage());
map.put("noncestr", generator(32, "QWERTYUIPASDFGHJKLZXCVBNMqwertyuipasdfghjklzxcvbnm01234567890".toCharArray()));
map.put("timestamp", preOrderInfo.getTimestamp());
map.put("sign", signMap(map));
return map;
}
// 加簽
private static String signMap(TreeMap<String,Object> map) throws Exception{
Set<String> keys = map.keySet();
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append("=").append(map.get(key)).append("&");
}
sb.append("key").append("=").append("商戶號密鑰");
return DigestUtils.md5Hex(sb.toString()).toUpperCase();
}
// 生成數據和字母組成的隨機字符串
private static String generator(int count, char[] arr) {
if (count <= 0) {
count = 6;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
double d = Math.random();
int index = (int) Math.floor(d * arr.length);
sb.append(arr[index]);
}
return sb.toString();
}
public static WechatPreOrderInfo preOrder(WechatPayInfo payInfo) throws Exception {
if(payInfo.getTrade_type().equals("JSAPI")&& StringUtils.isEmpty(payInfo.getOpenid())){
throw new RuntimeException("公衆號支付openid不能爲空,請填入正確的openid");
}
payInfo.setAppid("開放平臺APPID");
payInfo.setMch_id("商戶平臺商戶號");
payInfo.setNonce_str(generator(32, "QWERTYUIPASDFGHJKLZXCVBNMqwertyuipasdfghjklzxcvbnm01234567890".toCharArray()).toUpperCase());
payInfo.setSign(generateSign(payInfo));
Document doc = convertMap2Xml(getSignMap(payInfo, WechatPayInfo.class));
URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com").setPath("/pay/unifiedorder")
.build();
String xml = connectWithXMLByPost(uri, doc);
WechatPreOrderInfo info = (WechatPreOrderInfo) convertXml2Bean(xml, WechatPreOrderInfo.class);
if (!info.isContact()) {
String msg = info.getReturn_code() + ":" + info.getReturn_msg();
msg = new String(msg.getBytes("iso-8859-1"), "utf-8");
throw new RuntimeException(msg);
}
if (!info.isSuccess()) {
String msg = info.getResult_code() + ":" + WeChatPayErrorUtil.getErrorMsg(info.getErr_code());
throw new RuntimeException(msg);
}
return info;
}
// xml字符串轉對象
public static Object convertXml2Bean(String xmlString, Class<?> clazz) {
Document document = null;
try {
document = DocumentHelper.parseText(xmlString);
} catch (DocumentException e) {
throw new RuntimeException("獲取Document異常" + xmlString);
} // 獲取根節點
return convertXml2Bean2(document, clazz);
}
// xml文檔Document轉對象
@SuppressWarnings("unchecked")
public static Object convertXml2Bean2(Document document, Class<?> clazz) {
Map<String,String> map = new HashMap<>();
// 獲取根節點
org.dom4j.Element root = document.getRootElement();
try {
List<org.dom4j.Element> properties = root.elements();
for (org.dom4j.Element pro : properties) {
String propName = pro.getName();
String propValue = pro.getText();
map.put(propName, propValue);
}
} catch (Exception e) {
e.printStackTrace();
}
//處理map裏的JSON字符串字段,防止解析錯誤
Map<String,Object> objMap = new TreeMap<>();
Set<String> keys = map.keySet();
for (String key : keys) {
String str = map.get(key);
try {
//如果是JSON字符串,則轉換成對象,再添加到objMap中
objMap.put(key, JSON.parse(str));
} catch (JSONException e) {
//如果不是JSON字符串,則直接添加到objMap中
objMap.put(key, str);
} catch (Exception e){
//其餘錯誤拋出
e.printStackTrace();
}
}
return JSON.parseObject(JSON.toJSONString(map),clazz);
}
// 使用post方法發送xml
public static String connectWithXMLByPost(URI uri, Document doc) throws Exception {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost post = new HttpPost(uri);
post.addHeader("Content-Type", "text/xml;charset=UTF-8");
StringEntity xml = new StringEntity(doc.asXML(), "UTF-8");
post.setEntity(xml);
try (CloseableHttpResponse response = httpClient.execute(post)) {
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
// 將獲取的Map轉換成xml
public static Document convertMap2Xml(Map<String,Object> map){
Document doc = DocumentHelper.createDocument();
try {
org.dom4j.Element root = doc.addElement("xml");
Set<String> keys = map.keySet();
for (String key : keys) {
org.dom4j.Element ele = root.addElement(key);
ele.addCDATA(map.get(key).toString());
}
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
// 設置簽名
private static String generateSign(Object obj) throws Exception {
TreeMap<String, Object> map = getSignMap(obj, obj.getClass());
return signMap(map);
}
// 獲取按順序整理好的非空值字段
@SuppressWarnings("unchecked")
private static TreeMap<String, Object> getSignMap(Object obj, Class<?> clz) throws Exception {
if (obj == null) {
throw new RuntimeException("支付對象不能爲空");
}
Field[] fields = clz.getDeclaredFields();
// 使用treeMap將字段按要求排序
TreeMap<String, Object> map = new TreeMap<>();
for (Field field : fields) {
map.put(field.getName(), clz.getMethod("get" + uperFirst(field.getName())).invoke(obj));
}
// 使用fastjson過濾掉null的字段,愛死fastjson了
String json = JSON.toJSONString(map);
map = JSON.parseObject(json, TreeMap.class);
return map;
}
// 首字母大寫
private static String uperFirst(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 應答微信回調
public static void echo(HttpServletResponse response) throws Exception {
response.setContentType("application/xml");
ServletOutputStream os = response.getOutputStream();
os.print("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
}
// 解析微信後臺返回的數據
public static WechatPayRet parseRequest(HttpServletRequest request) throws Exception {
String xml = readXmlFromRequest(request);
Map map = xmlToMap(xml);
WechatPayRet ret = new WechatPayRet();
ret.setReturn_code((String) map.get("result_code"));
ret.setOut_trade_no((String) map.get("out_trade_no"));
return ret;
}
// XML格式字符串轉換爲Map
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) {
getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
// 日誌
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
// 從request讀取xml
public static String readXmlFromRequest(HttpServletRequest request) {
StringBuilder xmlSb = new StringBuilder();
try(
ServletInputStream in = request.getInputStream();
InputStreamReader inputStream = new InputStreamReader(in);
BufferedReader buffer = new BufferedReader(inputStream);
){
String line = null;
while((line=buffer.readLine())!=null){
xmlSb.append(line);
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlSb.toString();
}
// MD5加密
public static String md5(String key) {
System.out.println(key);
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
try {
byte[] btInput = key.getBytes("UTF-8");
// 獲得MD5摘要算法的 MessageDigest 對象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字節更新摘要
mdInst.update(btInput);
// 獲得密文
byte[] md = mdInst.digest();
// 把密文轉換成十六進制的字符串形式
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];
}
String s = new String(str);
s = s.toLowerCase();
return s;
} catch (Exception e) {
return null;
}
}
}
好了,我暫時沒法驗證是否有錯。請諒解