java微信支付創(chuàng)單APP、H5示例

以下是本人寫的微信支付創(chuàng)單APP、H5的demo,沒有什么封裝,簡易示例。
代碼部分沒有那么規(guī)范,只是練習(xí)使用的,例如:XMLUtil、MD5Encryption這樣的命名不符合阿里P3C規(guī)約。望讀者閱讀后自行整改。

1.首先下載相關(guān)的包

log4j:請自行下載

jdom:https://mvnrepository.com/artifact/org.jdom/jdom2

如有其他缺少的包,請自行下載,下面主要看看代碼部分。

2.WeChatPayUtil

package com.alipay.api.test.my;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.erlan.common.base.http.HttpRequest;
import com.erlan.common.util.MD5Encryption;
import com.erlan.common.util.XMLUtil;

/**
 * @Description: 微信支付工具類
 * @author admin
 * @date 2017年12月30日 下午1:55:21
 * @version V1.0
 */
public class WeChatPayUtil {

    private static Logger log = Logger.getLogger(WeChatPayUtil.class);

    public static void main(String[] args) {

        // APP支付測試
        Map<String, Object> appMap = unifiedorderAPP("1000010", "1", null, "這里填寫統(tǒng)一下單回調(diào)接口(關(guān)于回調(diào)接口,下面有示例)");

        // H5支付測試(創(chuàng)單成功后,返回的url必須在H5申請的支付域名之下訪問,這點(diǎn)要特別注意。而且我測試的是必須手機(jī)訪問才成功,手機(jī)!手機(jī)!手機(jī)!三遍了,老鐵們!),關(guān)于H5支付創(chuàng)單失敗的問題,我其他文章有提到
        Map<String, Object> mwebMap = unifiedorderMWEB("1000012", "1", null, "這里填寫統(tǒng)一下單回調(diào)接口(關(guān)于回調(diào)接口,下面有示例)");
    }

    /**
     * 統(tǒng)一下單(APP)
     *
     * @param outTradeNo 系統(tǒng)訂單號
     * @param totalFee 金額(分)
     * @param attach 附加數(shù)據(jù)
     * @param notifyUrl 回調(diào)地址
     * @author admin
     * @date 2017年12月30日 下午2:18:29
     * @version V1.0
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> unifiedorderAPP(String outTradeNo, String totalFee, String attach, String notifyUrl) {
        Map<String, Object> resultMap = new HashMap<String, Object>(8);
        resultMap.put("return_code", "FAIL");

        // ======================================
        // 以下有注釋的代碼,需要更改對應(yīng)的值-Begin
        // ======================================

        // 連接商戶key
        String key = "W8FFB955CA32319C3AAAE96286CC6666";

        // 統(tǒng)一下單接口
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        // 應(yīng)用ID(微信開放平臺審核通過的應(yīng)用APPID)
        String appid = "******************";
        // 商戶號(微信支付分配的商戶號)
        String mch_id = "2506666250";
        // 隨便的一個MD5加密字符串就行(工具類有提供,見下面)
        String nonce_str = "49ba59abbe56e057";
        String sign = "";
        // 支付的商品信息(測試隨便輸入,支付后在微信里面看下付費(fèi)賬單信息就明白了)
        String body = "**-微信支付";
        String detail = "**-微信支付";
        String out_trade_no = outTradeNo;
        String total_fee = totalFee;
        // 終端IP(改成你的服務(wù)器IP就行)
        String spbill_create_ip = "10.10.10.110";
        String notify_url = notifyUrl;
        String trade_type = "APP";
        // 這個值可以不給,如果不給的話,下面不要拼接,我下面有拼接此參數(shù),所以給它一個值,詳情官方文檔說明
        attach = StringUtils.isBlank(attach) ? "no" : attach;

        // ======================================
        // 以上有注釋的代碼,需要更改對應(yīng)的值-End
        // ======================================

        // 對參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序生成字符串
        String signStr = "appid=" + appid + "&attach=" + attach + "&body=" + body + "&detail=" + detail + "&mch_id=" + mch_id + "&nonce_str="
                + nonce_str + "&notify_url=" + notify_url + "&out_trade_no=" + out_trade_no + "&spbill_create_ip=" + spbill_create_ip + "&total_fee="
                + total_fee + "&trade_type=" + trade_type;
        // 連接商戶key
        signStr += ("&key=" + key);
        System.out.println("signStr:" + signStr);
        // 生成sign并轉(zhuǎn)成大寫
        sign = MD5Encryption.toMD5(signStr).toUpperCase();
        System.out.println(sign);

        // 最終的提交xml:
        String params = "<xml><appid><![CDATA[" + appid + "]]></appid><attach><![CDATA[" + attach + "]]></attach><body><![CDATA[" + body
                + "]]></body><detail><![CDATA[" + detail + "]]></detail><mch_id><![CDATA[" + mch_id + "]]></mch_id><nonce_str><![CDATA[" + nonce_str
                + "]]></nonce_str><notify_url><![CDATA[" + notify_url + "]]></notify_url><out_trade_no><![CDATA[" + out_trade_no
                + "]]></out_trade_no><spbill_create_ip><![CDATA[" + spbill_create_ip + "]]></spbill_create_ip><total_fee><![CDATA[" + total_fee
                + "]]></total_fee><trade_type><![CDATA[" + trade_type + "]]></trade_type><sign>" + sign + "</sign></xml>";

        try {
            System.out.println(params);
            String result = HttpRequest.sendPost(url, params);
            System.out.println(result);
            Map<String, String> map = null;
            map = XMLUtil.doXMLParse(result);
            // System.out.println("return_code:" + map.get("return_code"));
            if (map.get("return_code").equals("SUCCESS")) {
                // 時間戳(秒)
                Date date = new Date();
                long time = date.getTime();
                time = (time / 1000);
                String timestamp = time + "";
                String resultAppid = map.get("appid").toString();
                String resultPartnerid = map.get("mch_id").toString();
                String resultPrepayid = map.get("prepay_id").toString();
                String resultPackage = "Sign=WXPay";
                String resultNoncestr = map.get("nonce_str").toString();
                // 對參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序生成字符串
                String resultSignStr = "appid=" + resultAppid + "&noncestr=" + resultNoncestr + "&package=" + resultPackage + "&partnerid="
                        + resultPartnerid + "&prepayid=" + resultPrepayid + "&timestamp=" + timestamp;
                // 連接商戶key
                resultSignStr += ("&key=" + key);
                System.out.println("resultSignStr:" + resultSignStr);
                // 生成sign并轉(zhuǎn)成大寫
                String resultSign = MD5Encryption.toMD5(resultSignStr).toUpperCase();
                System.out.println("resultSign:" + resultSign);
                // APP支付所需參數(shù)(需要把這些信息返給APP開發(fā)者)
                resultMap.put("return_code", "SUCCESS");
                resultMap.put("appid", resultAppid);
                resultMap.put("partnerid", resultPartnerid);
                resultMap.put("prepayid", resultPrepayid);
                resultMap.put("package", resultPackage);
                resultMap.put("noncestr", resultNoncestr);
                resultMap.put("timestamp", timestamp);
                resultMap.put("sign", resultSign);
                System.out.println(resultMap.toString());
            }
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            log.error("APP統(tǒng)一下單異常");
            e.printStackTrace();
        }

        return resultMap;
    }

    /**
     * 統(tǒng)一下單(H5)
     *
     * @param outTradeNo 系統(tǒng)訂單號
     * @param totalFee 金額(分)
     * @param attach 附加數(shù)據(jù)
     * @param notifyUrl 回調(diào)地址
     * @author admin
     * @date 2017年12月30日 下午2:18:29
     * @version V1.0
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> unifiedorderMWEB(String outTradeNo, String totalFee, String attach, String notifyUrl) {
        Map<String, Object> resultMap = new HashMap<String, Object>(8);
        resultMap.put("return_code", "FAIL");

        // ======================================
        // 以下有注釋的代碼,需要更改對應(yīng)的值-Begin
        // ======================================

        // 連接商戶key
        String key = "W8FFB955CA32319C3AAAE96286CC6666";

        // 統(tǒng)一下單接口
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        // 應(yīng)用ID(微信開放平臺審核通過的應(yīng)用APPID)
        String appid = "******************";
        // 商戶號(微信支付分配的商戶號)
        String mch_id = "2506666250";
        // 隨便的一個MD5加密字符串就行(工具類有提供,見下面)
        String nonce_str = "49ba59abbe56e057";
        String sign = "";
        // 支付的商品信息(測試隨便輸入,支付后在微信里面看下付費(fèi)賬單信息就明白了)
        String body = "**-微信支付";
        String detail = "**-微信支付";
        String out_trade_no = outTradeNo;
        String total_fee = totalFee;
        // 終端IP(改成你的服務(wù)器IP就行)
        String spbill_create_ip = "10.10.10.110";
        String notify_url = notifyUrl;
        String trade_type = "APP";

        // 商戶平臺申請開通H5支付,對應(yīng)的信息,詳見官方文檔(bundle_id的值在微信開放平臺-管理中心-對應(yīng)的產(chǎn)品里面查看)
        String scene_info = "{\"h5_info\": {\"type\":\"IOS\",\"app_name\": \"你的產(chǎn)品名稱\",\"bundle_id\": \"cn.**.**\"}}";

        attach = StringUtils.isBlank(attach) ? "no" : attach;
        // ======================================
        // 以上有注釋的代碼,需要更改對應(yīng)的值-End
        // ======================================

        // 對參數(shù)按照key=value的格式,并按照參數(shù)名ASCII字典序排序生成字符串
        String signStr = "appid=" + appid + "&attach=" + attach + "&body=" + body + "&detail=" + detail + "&mch_id=" + mch_id + "&nonce_str="
                + nonce_str + "&notify_url=" + notify_url + "&out_trade_no=" + out_trade_no + "&scene_info=" + scene_info + "&spbill_create_ip="
                + spbill_create_ip + "&total_fee=" + total_fee + "&trade_type=" + trade_type;
        // 連接商戶key
        signStr += ("&key=" + key);
        System.out.println("signStr:" + signStr);
        // 生成sign并轉(zhuǎn)成大寫
        sign = MD5Encryption.toMD5(signStr).toUpperCase();
        System.out.println(sign);

        // 最終的提交xml:
        String params = "<xml><appid><![CDATA[" + appid + "]]></appid><attach><![CDATA[" + attach + "]]></attach><body><![CDATA[" + body
                + "]]></body><detail><![CDATA[" + detail + "]]></detail><mch_id><![CDATA[" + mch_id + "]]></mch_id><nonce_str><![CDATA[" + nonce_str
                + "]]></nonce_str><notify_url><![CDATA[" + notify_url + "]]></notify_url><out_trade_no><![CDATA[" + out_trade_no
                + "]]></out_trade_no><scene_info><![CDATA[" + scene_info + "]]></scene_info><spbill_create_ip><![CDATA[" + spbill_create_ip
                + "]]></spbill_create_ip><total_fee><![CDATA[" + total_fee + "]]></total_fee><trade_type><![CDATA[" + trade_type
                + "]]></trade_type><sign>" + sign + "</sign></xml>";

        try {
            System.out.println(params);
            String result = HttpRequest.sendPost(url, params);
            System.out.println(result);
            Map<String, String> map = null;
            map = XMLUtil.doXMLParse(result);
            System.out.println("return_code:" + map.get("return_code"));
            if (map.get("return_code").equals("SUCCESS")) {
                String mwebUrl = map.get("mweb_url").toString();
                // H5支付所需要的URL(必須在H5申請的支付域名之下訪問,而且我測試的是必須手機(jī)訪問才成功,手機(jī)!手機(jī)!手機(jī)!三遍了,老鐵們!)
                resultMap.put("url", mwebUrl);
                resultMap.put("return_code", "SUCCESS");
                System.out.println(resultMap.toString());
            }
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            log.error("H5統(tǒng)一下單異常");
            e.printStackTrace();
        }

        return resultMap;
    }

}

3.MD5Encryption

package com.alipay.api.test.my;

import java.security.MessageDigest;

/**
 * @Description: MD5工具類
 * @author admin
 * @date 2018年1月12日 上午9:10:39
 * @version V1.0
 */
public class MD5Encryption {

    public static String toMD5(String plainText) {
        try {
            if ("".equals(plainText) || plainText == null) {
                return "erlan";
            }
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(plainText.getBytes());
            byte b[] = md.digest();

            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            return buf.toString();
            // System.out.println("32λ: " + buf.toString());// 32位
            // System.out.println("16λ: " + buf.toString().substring(8, 24));// 16位
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // public static void main(String[] args) {
    // MD5Encryption md5 = new MD5Encryption();
    // String md52 = md5.toMD5("123456ERlan");
    // System.out.println(md52.toUpperCase());
    // }

}

4.XMLUtil

package com.erlan.common.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

/**
 * @Description: XML解析工具類
 * @author admin
 * @date 2018年1月12日 上午9:10:10
 * @version V1.0
 */
public class XMLUtil {
    
    /**
     * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節(jié)點(diǎn),則此節(jié)點(diǎn)的值是子節(jié)點(diǎn)的xml數(shù)據(jù)。
     * 
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();

        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = XMLUtil.getChildrenText(children);
            }

            m.put(k, v);
        }

        // 關(guān)閉流
        in.close();

        return m;
    }

    /**
     * 獲取子結(jié)點(diǎn)的xml
     * 
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(XMLUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    /**
     * 獲取xml編碼字符集
     * 
     * @param strxml
     * @return
     * @throws IOException
     * @throws JDOMException
     */
    public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
        InputStream in = HttpClientUtil.String2Inputstream(strxml);
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        in.close();
        return (String) doc.getProperty("encoding");
    }

    /**
     * 支付成功,返回微信那服務(wù)器
     * 
     * @param return_code
     * @param return_msg
     * @return
     */
    public static String setXML(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }

    public static String createXML(Map<String, Object> map) {
        Set<Entry<String, Object>> set = map.entrySet();
        set.iterator();
        return null;
    }

}

4.WeChatController
這里要注意了!例如你的這個接口是針對APP支付成功后的業(yè)務(wù)處理,訪問你這個接口的地址是:http://192.168.0.110:8080//wechat/successRechargeNotify
那么上面提到的unifiedorderAPP方法里的參數(shù)notifyUrl的值就是這個接口地址,例如:
unifiedorderAPP("1000010", "1", null, "http://192.168.0.110:8080//wechat/successRechargeNotify");

package com.erlan.mobile.controller.pay;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.jdom2.JDOMException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.erlan.api.service.TbOrderService;
import com.erlan.api.service.TbUserService;
import com.erlan.common.util.XMLUtil;
import com.erlan.common.util.wxpay.WeChatPayConfig;

/**
 * @Description: 微信支付成功回調(diào)
 * @author admin
 * @date 2017年12月29日 上午9:19:25
 * @version V1.0
 */
@RestController
@RequestMapping("/wechat")
public class WeChatController {

    private static Logger log = Logger.getLogger(WeChatController.class);

    @Reference
    private TbOrderService tbOrderService;

    @Reference
    private TbUserService tbUserService;

    /**
     * 訂單支付成功回調(diào)
     *
     * @param param
     * @return
     * @author admin
     * @date 2017年12月27日 下午2:21:53
     * @version V1.0
     */
    @SuppressWarnings({ "unchecked" })
    @RequestMapping("/successPayNotify")
    public String successPayNotify(HttpServletRequest request, HttpServletResponse response) {
        log.info("pay......success");
        String notifyResult = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

        try {
            System.out.println("微信支付回調(diào)");
            // PrintWriter writer = response.getWriter();
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String result = new String(outSteam.toByteArray(), "utf-8");
            System.out.println("微信支付通知結(jié)果:" + result);
            Map<String, String> map = null;
            try {
                /**
                 * 解析微信通知返回的信息
                 */
                map = XMLUtil.doXMLParse(result);
            }
            catch (JDOMException e) {
                // TODO Auto-generated catch block
                log.error("微信支付回調(diào)解析異常");
                e.printStackTrace();
            }
            System.out.println("=========:" + result);
            // 若支付成功,則告知微信服務(wù)器收到通知
            if (map.get("return_code").equals("SUCCESS")) {

                // 這里可以寫你的系統(tǒng)業(yè)務(wù)邏輯......

            }
        }
        catch (Exception e) {
            // TODO: handle exception
            log.error("微信支付回調(diào)異常");
        }

        System.out.println("notifyResult:" + notifyResult);
        return notifyResult;
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。