1. 程式人生 > >微信小程式、app整合微信支付

微信小程式、app整合微信支付

一、微信小程式支付

申請小程式開發者賬號,進行微信認證,獲取appid,開通微信支付,即繫結申請的微信支付商戶號。

1.小程式支付流程:

å°ç¨åºæ¯ä»æ¶åºå¾

2.商戶系統和微信支付系統主要互動:

      1、小程式內呼叫登入介面,獲取到使用者的openid。

      2、商戶呼叫支付介面統一下單。

      3、商戶再次簽名後返回發起支付需要的引數。

      4、商戶調起微信支付控制元件後付款,並接收支付通知處理賬單狀態。

3.微信統一下單:

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

然後編寫我們自己的支付服務呼叫該下單介面。

支付服務:

@Service
public class PayService  {

    @Resource
    private IPayDetailService payDetailService;

    /**
     * 小程式appid
     */
    @Value("${wx.applet.appid}")
    String wx_applet_appid;

    /**
     * APP移動應用端appid
     */
    @Value("${app.appid}")
    String app_appid;
    /**
     * 微信小程式商戶平臺商戶號
     */
    @Value("${wx.sh.mch_id}")
    String mchId;

    /**
     * 微信app商戶平臺商戶號
     */
    @Value("${wx.app.mch_id}")
    String appMchId;

    /**
     * 付款
     *
     * @param userId
     * @param openid
     * @param body
     * @param tradeType    交易型別 小程式取值如下:JSAPI  APP--app支付
     * @param request
     * @return
     */
    @Transactional
    @Override
    public ServiceResult<Object> payment(String userId, String openid, String body, String tradeType, HttpServletRequest request) {
        //生成訂單號
        String orderNumber = GenerateNum.getInstance().GenerateOrder();
                //微信支付呼叫統一下單介面
                //小程式端
                if (!StringUtils.isEmpty(tradeType) && PayConstant.TRADE_TYPE_WX_APPLET.equals(tradeType)) {
                    return payDetailService.wxPaybill(wx_applet_appid, mchId, orderNumber, PayConstant.WX_PAY_NOTIFY_URL, PayConstant.TRADE_TYPE_WX_APPLET,
                            openid, order.getActuallyPayMoney(), body, IPUtil.getIpAddr(request));
                    //app端
                } else if (!StringUtils.isEmpty(tradeType) && PayConstant.TRADE_TYPE_APP.equals(tradeType)) {
                    return payDetailService.wxPaybill(app_appid, appMchId, orderNumber, PayConstant.WX_PAY_NOTIFY_URL, PayConstant.TRADE_TYPE_APP,
                            null, order.getActuallyPayMoney(), body, IPUtil.getIpAddr(request));
                }
            } 
        return ServiceResultHelper.genResultWithFaild();
    }

下單服務:

/**
     * 微信下單
     *
     * @param appId            APP端或者小程式appid
     * @param mchId            商戶號
     * @param orderNumber      訂單編號
     * @param notifyUrl        通知url
     * @param tradeType        交易型別
     * @param openid           使用者標識
     * @param totalFee         訂單總金額,單位為分
     * @param body             商品描述
     * @param spbill_create_ip APP和網頁支付提交使用者端ip
     * @return
     */
    @Override
    public ServiceResult<Object> wxPaybill(String appId, String mchId, String orderNumber, String notifyUrl, String tradeType,
                                           String openid, BigDecimal totalFee, String body, String spbill_create_ip) {
        try {
            if (StringUtils.isEmpty(orderNumber)) {
                return ServiceResultHelper.genResultWithFaild("訂單號不能為空", -1);
            }
            //生成的隨機字串
            String nonce_str = CharacterUtil.getRandomString(32);
            //組裝引數,使用者生成統一下單介面的簽名
            Map<String, Object> packageParams = new HashMap<>();
            if (!StringUtils.isEmpty(openid)) {
                packageParams.put("openid", openid);
            }
            packageParams.put("appid", appId);
            packageParams.put("mch_id", mchId);
            packageParams.put("nonce_str", nonce_str);
            packageParams.put("body", body);
            packageParams.put("out_trade_no", orderNumber);//訂單號
            packageParams.put("total_fee", totalFee.toString());//支付金額,這邊需要轉成字串型別,否則後面的簽名會失敗
            packageParams.put("spbill_create_ip", spbill_create_ip);
            packageParams.put("notify_url", notifyUrl);//支付成功後的回撥地址
            packageParams.put("trade_type", tradeType);//支付方式

            String prestr = PayUtil.createLinkString(packageParams); // 把陣列所有元素,按照“引數=引數值”的模式用“&”字元拼接成字串

            //MD5運算生成簽名,這裡是第一次簽名,用於呼叫統一下單介面
            String sign = PayUtil.sign(prestr, PayConstant.WX_PAY_PAY_KEY, "utf-8").toUpperCase();

            //拼接統一下單介面使用的xml資料,要將上一步生成的簽名一起拼接進去
            packageParams.put("sign", sign);
            String xml = MapXmlUtil.map2Xmlstring(packageParams);
            LOG.info("呼叫統一下單介面請求XML資料:" + xml);

            //呼叫統一下單介面,並接受返回的結果
            String result = PayUtil.httpRequest(PayConstant.WX_PAY_BILL_URL, "POST", xml);

            LOG.info("呼叫統一下單介面返回XML資料:" + result);
            if (StringUtils.isEmpty(result)) {
                return ServiceResultHelper.genResultWithFaild("呼叫統一下單介面返回結果為空", -1);
            }

            // 將解析結果儲存在HashMap中
            Map map = MapXmlUtil.xmlString2Map(result);
            String return_code = (String) map.get("return_code");//返回狀態碼
            Map<String, Object> data = new HashMap<>();//返回給小程式端需要的引數
            String prepay_id = (String) map.get("prepay_id");//返回的預付單資訊
            if (PayConstant.PAY_ORDER_SUCCESS.equals(return_code)) {
                //小程式的返回值
                if (!StringUtils.isEmpty(openid)) {
                    data.put("appId", appId);
                    data.put("nonceStr", nonce_str);
                    data.put("package", "prepay_id=" + prepay_id);
                    data.put("signType", "MD5");
                    Long timeStamp = System.currentTimeMillis() / 1000;
                    data.put("timeStamp", timeStamp + "");//這邊要將返回的時間戳轉化成字串,不然小程式端呼叫wx.requestPayment方法會報簽名錯誤
                    //拼接簽名需要的引數
                    String stringSignTemp = PayUtil.createLinkString(data);
                    //再次簽名,這個簽名用於小程式端呼叫wx.requesetPayment方法
                    String paySign = PayUtil.sign(stringSignTemp, PayConstant.WX_PAY_PAY_KEY, "utf-8").toUpperCase();
                    data.put("paySign", paySign);
                } else {
                    //app的返回值
                    data.put("appid", appId);
                    data.put("partnerid", mchId);
                    data.put("prepayid", prepay_id);
                    data.put("noncestr", nonce_str);
                    Long timeStamp = System.currentTimeMillis() / 1000;
                    data.put("timeStamp", timeStamp + "");
                    data.put("package", "Sign=WXPay");
                    String stringSignTemp = PayUtil.createLinkString(data);
                    String appSign = PayUtil.sign(stringSignTemp, PayConstant.WX_PAY_PAY_KEY, "utf-8").toUpperCase();
                    data.put("sign", appSign);
                }
                return ServiceResultHelper.genResultWithSuccess(data);
            }
            return ServiceResultHelper.genResultWithFaild(map.get("return_msg") != null ? map.get("return_msg").toString() : null, -1);
        } catch (Exception e) {
            LOG.info("下單異常:" + e.getMessage());
            return ServiceResultHelper.genResultWithFaild(e.getMessage(), -1);
        }

呼叫微信下單介面返回結果:

欄位名 變數名 必填 型別 示例值 描述
返回狀態碼 return_code String(16) SUCCESS

SUCCESS/FAIL

此欄位是通訊標識,非交易標識,交易是否成功需要檢視result_code來判斷

返回資訊 return_msg String(128) 簽名失敗

返回資訊,如非空,為錯誤原因

簽名失敗

引數格式校驗錯誤

paySign為再次簽名,這個簽名用於小程式端呼叫wx.requesetPayment方法,調起微信支付控制元件。

requesetPayment 引數說明:

引數 型別 必填 說明
timeStamp String 時間戳從1970年1月1日00:00:00至今的秒數,即當前的時間
nonceStr String 隨機字串,長度為32個字元以下。
package String 統一下單介面返回的 prepay_id 引數值,提交格式如:prepay_id=*
signType String 簽名型別,預設為MD5,支援HMAC-SHA256和MD5。注意此處需與統一下單的簽名型別一致
paySign String
success Function 介面呼叫成功的回撥函式
fail Function 介面呼叫失敗的回撥函式
complete Function 介面呼叫結束的回撥函式(呼叫成功、失敗都會執行)

呼叫wx.requestPayment(OBJECT)發起微信支付。所以在呼叫下單介面成功後要返回需要的引數。詳見下單服務程式碼小程式返回引數部分。

獲取ip地址工具:

import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;

public class IPUtil {
    public IPUtil() {
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
                InetAddress inet = null;

                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException var4) {
                    var4.printStackTrace();
                }

                ip = inet.getHostAddress();
            }
        }

        if (ip != null && ip.length() > 15 && ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }

        return ip;
    }
}

訂單號生成工具:


import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 訂單號生成工具
 */
public class GenerateNum {

    // 使用單例模式,不允許直接建立例項
    private GenerateNum() {
    }

    // 建立一個空例項物件,類需要用的時候才賦值
    private static GenerateNum g = null;

    // 單例模式--懶漢模式
    public static synchronized GenerateNum getInstance() {
        if (g == null) {
            g = new GenerateNum();
        }
        return g;
    }

    // 全域性自增數
    private static int count = 0;
    // 每毫秒秒最多生成多少訂單(最好是像9999這種準備進位的值)
    private static final int total = 9999;
    // 格式化的時間字串
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");

    // 獲取當前時間年月日時分秒毫秒字串
    private static String getNowDateStr() {
        return sdf.format(new Date());
    }

    // 記錄上一次的時間,用來判斷是否需要遞增全域性數
    private static String now = null;

    /*
     * 生成一個訂單號
     */
    public synchronized String GenerateOrder() {
        String datastr = getNowDateStr();
        if (datastr.equals(now)) {
            count++;// 自增
        } else {
            count = 1;
            now = datastr;
        }
        int countInteger = String.valueOf(total).length() - String.valueOf(count).length();// 算補位
        String bu = "";// 補字串
        for (int i = 0; i < countInteger; i++) {
            bu += "0";
        }
        bu += String.valueOf(count);
        if (count >= total) {
            count = 0;
        }
        return datastr + bu;
    }
}

簽名字串工具:用於MD5運算生成簽名,呼叫統一下單介面,並接受返回的結果


import org.apache.commons.codec.digest.DigestUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SignatureException;
import java.util.*;

public class PayUtil {
    /**
     * 簽名字串
     *
     * @param text 需要簽名的字串
     * @param key  金鑰
     * @param input_charset 編碼格式
     * @return 簽名結果
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    /**
     * 簽名字串
     *
     * @param text 需要簽名的字串
     * @param sign          簽名結果
     * @param key 金鑰
     * @param input_charset 編碼格式
     * @return 簽名結果
     */
    public static boolean verify(String text, String sign, String key, String input_charset) {
        text = text + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
        if (mysign.equals(sign)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws SignatureException
     * @throws UnsupportedEncodingException
     */
    public static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5簽名過程中出現錯誤,指定的編碼集不對,您目前指定的編碼集是:" + charset);
        }
    }

    private static boolean isValidChar(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
            return true;
        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
            return true;// 簡體中文漢字編碼
        return false;
    }

    /**
     * 除去陣列中的空值和簽名引數
     *
     * @param sArray 簽名引數組
     * @return 去掉空值與簽名引數後的新簽名引數組
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 把陣列所有元素排序,並按照“引數=引數值”的模式用“&”字元拼接成字串
     *
     * @param params 需要排序並參與字元拼接的引數組
     * @return 拼接後字串
     */
    public static String createLinkString(Map<String, Object> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value =(String) params.get(key);
            if (i == keys.size() - 1) {// 拼接時,不包括最後一個&字元
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }



    public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
        // 建立SSLContext
        StringBuffer buffer = null;
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            //往伺服器端寫內容
            if (null != outputStr) {
                OutputStream os = conn.getOutputStream();
                os.write(outputStr.getBytes("utf-8"));
                os.close();
            }
            // 讀取伺服器端返回的內容
            InputStream is = conn.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            buffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                buffer.append(line);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return buffer.toString();
    }
}

Map和xml互相轉換工具:


import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.util.*;

public class MapXmlUtil {

    /**
     * Map轉換成Xml
     *
     * @param map
     * @return
     */
    public static String map2Xmlstring(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer("");
        sb.append("<xml>");

        Set<String> set = map.keySet();
        for (Iterator<String> it = set.iterator(); it.hasNext(); ) {
            String key = it.next();
            Object value = map.get(key);
            sb.append("<").append(key).append(">");
            sb.append(value);
            sb.append("</").append(key).append(">");
        }
        sb.append("</xml>");
        return sb.toString();
    }


    /**
     * Xml string轉換成Map
     *
     * @param xmlStr
     * @return
     */
    public static Map<String, Object> xmlString2Map(String xmlStr) {
        Map<String, Object> map = new HashMap<String, Object>();
        Document doc;
        try {
            doc = DocumentHelper.parseText(xmlStr);
            Element el = doc.getRootElement();
            map = recGetXmlElementValue(el, map);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 迴圈解析xml
     *
     * @param ele
     * @param map
     * @return
     */
    @SuppressWarnings({"unchecked"})
    private static Map<String, Object> recGetXmlElementValue(Element ele, Map<String, Object> map) {
        List<Element> eleList = ele.elements();
        if (eleList.size() == 0) {
            map.put(ele.getName(), ele.getTextTrim());
            return map;
        } else {
            for (Iterator<Element> iter = eleList.iterator(); iter.hasNext(); ) {
                Element innerEle = iter.next();
                recGetXmlElementValue(innerEle, map);
            }
            return map;
        }
    }
}

常量配置:



public class PayConstant {

    /**
     * 標價幣種 預設人民幣:CNY
     */
    public static final String FEE_TYPE_DEFAULT = "CNY";

    /**
     * 交易型別 小程式取值如下:JSAPI  APP--app支付
     */
    public static final String TRADE_TYPE_WX_APPLET = "JSAPI";

    public static final String TRADE_TYPE_APP = "APP";

    /**
     * 統一下單呼叫介面
     */
    public static final String PAY_ORDER_SUCCESS = "SUCCESS";

    public static final String PAY_ORDER_MSG = "OK";

    public static final String PAY_ORDER_FAIL="FAIL";

    /**
     * 支付方式  1:微信  2:支付寶
     */
    public static final Integer PAY_WAY_WX = 1;
    public static final Integer PAY_WAY_ZFB = 2;

    /**
     * 支付結果
     */
    public static final Integer PAY_RESULT_SUCCESS = 1;
    public static final Integer PAY_RESULT_FAIL = 0;//失敗

    /**
     * 下單
     */
    public static final String WX_PAY_BILL_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     * 微信伺服器支付結果回撥通知url
     */
    public static String WX_PAY_NOTIFY_URL="https://applet.cnjpkj.com/wx-api/pay/wxPayBillCallback";

    /**
     * 商戶平臺API金鑰
     */
    public static final String WX_PAY_PAY_KEY="GURkraWQOG5rrQFeQdXuACLwsCEGievV";


    /**
     * 訂單狀態 0:刪除 1:已完成 2:未付款  3:待發貨 4:待收貨 5:待評價 6:已取消
     */
    public static final Integer ORDER_STATUS_DEL = 0;
    public static final Integer ORDER_STATUS_FINISH = 1;
    public static final Integer ORDER_STATUS_NO_PAY = 2;
    public static final Integer ORDER_STATUS_WAIT_DELIVERY = 3;
    public static final Integer ORDER_STATUS_ALREADY_DELIVERY = 4;
    public static final Integer ORDER_STATUS_WAIT_EVALUATION = 5;
    public static final Integer ORDER_STATUS_CANCLE = 6;

}

二、app支付

1.app支付流程圖:

APPæ¯ä»æ¶åºå¾

商戶系統和微信支付系統主要互動說明:

步驟1:使用者在商戶APP中選擇商品,提交訂單,選擇微信支付。

步驟2:商戶後臺收到使用者支付單,呼叫微信支付統一下單介面。

步驟3:統一下單介面返回正常的prepay_id,再按簽名規範重新生成簽名後,將資料傳輸給APP。參與簽名的欄位名為appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式為Sign=WXPay

步驟4:商戶APP調起微信支付。

步驟5:商戶後臺接收支付通知。

步驟6:商戶後臺查詢支付結果

app支付呼叫下單服務時openid傳值null,openid用於微信小程式下單。

2.支付服務、下單服務

這部分的程式碼和小程式的程式碼放在一起做了封裝,詳見上文。

app端調起支付控制元件所需的引數和小程式不一樣。

下單成功後所需引數返回。

APP端調起支付的引數列表

欄位名 變數名 型別 必填 示例值 描述
應用ID appid String(32) wx8888888888888888 微信開放平臺稽核通過的應用APPID
商戶號 partnerid String(32) 1900000109 微信支付分配的商戶號
預支付交易會話ID prepayid String(32) WX1217752501201407033233368018 微信返回的支付交易會話ID
擴充套件欄位 package String(128) Sign=WXPay 暫填寫固定值Sign=WXPay
隨機字串 noncestr String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 隨機字串,不長於32位。推薦隨機數生成演算法
時間戳 timestamp String(10) 1412000000
簽名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 簽名,詳見簽名生成演算法注意:簽名方式一定要與統一下單介面使用的一致

三、微信支付回撥介面:


    /**
     * 微信支付回撥
     *
     * @param resultXMLEntity 微信支付結果非同步推送XML資料包實體
     * @return
     */
    @Transactional
    @Override
    public String wxPayBillCallback(PayResultXMLEntity resultXMLEntity) {
        Map<String, Object> result = new HashMap<>();
        LOG.info("resultXMLEntity:" + resultXMLEntity.toString());
        if (resultXMLEntity == null) {
            LOG.error("微信支付結果非同步推送XML資料包實體為空");
            result.put("return_code", PayConstant.PAY_ORDER_FAIL);
            result.put("return_msg", "微信支付結果非同步推送XML資料包實體為空");
            return MapXmlUtil.map2Xmlstring(result);
        }
        if (!StringUtils.isEmpty(resultXMLEntity.getOutTradeNo())) {
            ServiceResult<PayDetailAO> payDetailRet = getPayDetailByOrderNumber(resultXMLEntity.getOutTradeNo());
            if (payDetailRet != null && payDetailRet.isSucceed() && payDetailRet.getData() != null) {
                //已經支付成功,再次通知則不做處理
                if (payDetailRet.getData().getPayStatus() != null && payDetailRet.getData().getPayStatus().equals(PayConstant.PAY_RESULT_SUCCESS)) {
                    result.put("return_code", PayConstant.PAY_ORDER_SUCCESS);
                    result.put("return_msg", PayConstant.PAY_ORDER_MSG);
                    return MapXmlUtil.map2Xmlstring(result);
                }
            } else {
                result.put("return_code", PayConstant.PAY_ORDER_FAIL);
                result.put("return_msg", "訂單號不存在");
                return MapXmlUtil.map2Xmlstring(result);
            }
        }
        if (!StringUtils.isEmpty(resultXMLEntity.getReturnCode())) {
            //生成訂單號
            PayDetailAO payDetail = new PayDetailAO();
            if (resultXMLEntity.getReturnCode().equals(PayConstant.PAY_ORDER_SUCCESS)) {
                //更新訂單狀態
                orderService.updateOrder(resultXMLEntity.getOutTradeNo(), PayConstant.ORDER_STATUS_WAIT_DELIVERY, resultXMLEntity.getTimeEnd());
                //更新支付狀態
                payDetail.setPayStatus(PayConstant.PAY_RESULT_SUCCESS);
            } else if (resultXMLEntity.getReturnCode().equals(PayConstant.PAY_ORDER_FAIL)) {
                payDetail.setPayStatus(PayConstant.PAY_RESULT_FAIL);
            }
            payDetail.setSerialNumber(resultXMLEntity.getTransactionId());//支付流水號
            payDetail.setUpdateTime(!StringUtils.isEmpty(resultXMLEntity.getTimeEnd()) ?
                    DateUtil.parseStrToDate(resultXMLEntity.getTimeEnd(), DateUtil.DATE_FORMAT_6) : null);
            //更新支付資訊
            PayDetailCriteria criteria = new PayDetailCriteria();
            criteria.createCriteria().andOrderNumberEqualTo(resultXMLEntity.getOutTradeNo());
            ServiceResult<Integer> payOrderRet = updateByCriteriaSelective(payDetail, criteria);
            if (payOrderRet != null && payOrderRet.isSucceed() && payOrderRet.getData() > 0) {
                LOG.info("微信支付成功");
                result.put("return_code", PayConstant.PAY_ORDER_SUCCESS);
                result.put("return_msg", PayConstant.PAY_ORDER_MSG);
                return MapXmlUtil.map2Xmlstring(result);
            }
        }
        result.put("return_code", PayConstant.PAY_ORDER_FAIL);
        result.put("return_msg", "微信支付結果通知處理失敗");
        return MapXmlUtil.map2Xmlstring(result);
    }