1. 程式人生 > >微信小程式支付(Java)

微信小程式支付(Java)

相信進行過微信公眾號支付的同學對於微信小程式支付的開發上手很快,如下是微信官方對三種接入方式的對比


注意坑一:發起支付必須是HTTPS

流程

然後我們整理下發起訂單的思路。如下是官方給的流程圖,發起支付已經做了標註。


由此可見,伺服器端發起訂單需要以下五小步,我們來各個擊破。

第一步:獲取openid

第二步:生成商戶訂單

第三步:呼叫支付統一下單API

第四步:獲取預支付會話標識 prepayId

第五步:將組合資料簽名並返回

第一步:獲取openId(跳過)

獲取openId不做贅述,自行百度。

第二步:生成商戶訂單

這裡由我們系統內部生成。官方API如下(官方建議用當前系統時間加隨機序列來生成訂單號):


生成商戶訂單號程式碼如下(僅供參考)

public static String getRandomOrderId() {
        Random random = new Random(System.currentTimeMillis());
        int value = random.nextInt();
        while (value < 0) {
            value = random.nextInt();
        }
        return value + "";
    }

第三步:呼叫統一下單支付API

下面進行第三步呼叫支付統一下單API(將必傳引數整理成xml格式,傳送給下單url):

注意坑二:引數名按照字典序排序、引數值為空不參與簽名、區分大小寫、sign引數不參與簽名!

注意坑三:在結尾拼接要key、進行簽名運算後的sign要轉化成大寫!詳見官方“簽名演算法說明”

官方API說明:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1

簽名演算法說明:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3

整理的格式如下,然後傳送到下單url。程式碼如下

<xml>
   <appid>wx2421b1c4370ec43b</appid>


   <attach>支付測試</attach>
   <body>JSAPI支付測試</body>
   <mch_id>10000100</mch_id>
   <detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"蘋果手機" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"蘋果手機" } ] }]]></detail>
   <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
   <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
   <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
   <out_trade_no>1415659990</out_trade_no>
   <spbill_create_ip>14.23.150.211</spbill_create_ip>
   <total_fee>1</total_fee>
   <trade_type>JSAPI</trade_type>
   <sign>0CB01533B8C1EF103065174F50BCA001</sign>

</xml>

程式碼如下,僅供參考

    /**
     * 呼叫統一下單介面
     * @param openId
     */
    private String unifiedOrder(String openId, String clientIP, String randomNonceStr) {

        try {

            String url = Constant.URL_UNIFIED_ORDER;

            PayInfo payInfo = createPayInfo(openId, clientIP, randomNonceStr);
            String md5 = getSign(payInfo);
            payInfo.setSign(md5);

            log.error("md5 value: " + md5);

            String xml = CommonUtil.payInfoToXML(payInfo);
            xml = xml.replace("__", "_").replace("<![CDATA[1]]>", "1");
            //xml = xml.replace("__", "_").replace("<![CDATA[", "").replace("]]>", "");
            log.error(xml);

            StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml);
            log.error("unifiedOrder request return body: \n" + buffer.toString());
            Map<String, String> result = CommonUtil.parseXml(buffer.toString());


            String return_code = result.get("return_code");
            if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) {

                String return_msg = result.get("return_msg");
                if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) {
                    //log.error("統一下單錯誤!");
                    return "";
                }

                String prepay_Id = result.get("prepay_id");
                return prepay_Id;

            } else {
                return "";
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

/**
     * 生成訂單資訊
     * @param openId
	 * @param clientIP
	 * @param randomNonceStr
     */
    private PayInfo createPayInfo(String openId, String clientIP, String randomNonceStr) {

        Date date = new Date();
        String timeStart = TimeUtils.getFormatTime(date, Constant.TIME_FORMAT);
        String timeExpire = TimeUtils.getFormatTime(TimeUtils.addDay(date, Constant.TIME_EXPIRE), Constant.TIME_FORMAT);

        String randomOrderId = CommonUtil.getRandomOrderId();

        PayInfo payInfo = new PayInfo();
        payInfo.setAppid(Constant.APP_ID);
        payInfo.setMch_id(Constant.MCH_ID);
        payInfo.setDevice_info("WEB");
        payInfo.setNonce_str(randomNonceStr);
        payInfo.setSign_type("MD5");  //預設即為MD5
        payInfo.setBody("JSAPI支付測試");
        payInfo.setAttach("支付測試4luluteam");
        payInfo.setOut_trade_no(randomOrderId);
        payInfo.setTotal_fee(1);
        payInfo.setSpbill_create_ip(clientIP);
        payInfo.setTime_start(timeStart);
        payInfo.setTime_expire(timeExpire);
        payInfo.setNotify_url(Constant.URL_NOTIFY);
        payInfo.setTrade_type("JSAPI");
        payInfo.setLimit_pay("no_credit");
        payInfo.setOpenid(openId);

        return payInfo;
    }
/**
     * 對訂單資訊簽名
     * @param payInfo
     */
    private String getSign(PayInfo payInfo) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append("appid=" + payInfo.getAppid())
                .append("&attach=" + payInfo.getAttach())
                .append("&body=" + payInfo.getBody())
                .append("&device_info=" + payInfo.getDevice_info())
                .append("&limit_pay=" + payInfo.getLimit_pay())
                .append("&mch_id=" + payInfo.getMch_id())
                .append("&nonce_str=" + payInfo.getNonce_str())
                .append("¬ify_url=" + payInfo.getNotify_url())
                .append("&openid=" + payInfo.getOpenid())
                .append("&out_trade_no=" + payInfo.getOut_trade_no())
                .append("&sign_type=" + payInfo.getSign_type())
                .append("&spbill_create_ip=" + payInfo.getSpbill_create_ip())
                .append("&time_expire=" + payInfo.getTime_expire())
                .append("&time_start=" + payInfo.getTime_start())
                .append("&total_fee=" + payInfo.getTotal_fee())
                .append("&trade_type=" + payInfo.getTrade_type())
                .append("&key=" + Constant.APP_KEY);

        log.error("排序後的拼接引數:" + sb.toString());

        return CommonUtil.getMD5(sb.toString().trim()).toUpperCase();
    }


第四步:獲取prepayId

第三步如果沒問題,return_code 和result_code都為SUCCESS。則返回<prepay_id>(就是我們想要的預處理會話標識)如下所示:

<xml>
   <return_code><![CDATA[SUCCESS]]></return_code>
   <return_msg><![CDATA[OK]]></return_msg>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
   <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[JSAPI]]></trade_type>

</xml>

這裡提供下思路,將xml格式轉化成map。然後根據key=prepay_id 獲取prepayId

工具類程式碼如下,僅供參考

public class CommonUtil {

    public static String getRandomOrderId() {
        // UUID.randomUUID().toString().replace("-","")
        Random random = new Random(System.currentTimeMillis());
        int value = random.nextInt();
        while (value < 0) {
            value = random.nextInt();
        }
        return value + "";
    }

    private static XStream xstream = new XStream(new XppDriver() {
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                //增加CDATA標記
                boolean cdata = true;
                @SuppressWarnings("rawtypes")
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }
                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });

    public static String payInfoToXML(PayInfo pi) {
        xstream.alias("xml", pi.getClass());
        return xstream.toXML(pi);
    }

    @SuppressWarnings("unchecked")
    public static Map<String, String> parseXml(String xml) throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        Document document = DocumentHelper.parseText(xml);
        Element root = document.getRootElement();
        List<Element> elementList = root.elements();
        for (Element e : elementList)
            map.put(e.getName(), e.getText());
        return map;
    }
第五步:將組合資料簽名並返回

相關推薦

程式支付Java

相信進行過微信公眾號支付的同學對於微信小程式支付的開發上手很快,如下是微信官方對三種接入方式的對比注意坑一:發起支付必須是HTTPS流程然後我們整理下發起訂單的思路。如下是官方給的流程圖,發起支付已經做了標註。由此可見,伺服器端發起訂單需要以下五小步,我們來各個擊破。第一步:

程式支付thinkphp

之前一直想學下微信支付,這次終於有機會來操作一下,還是記錄下來,跟大家分享分享。 一、首先,我們要在微信官方網站上去下載支付介面,然後改個名方便呼叫(例如:WeiXinpay),然後將下載的檔案放入thinkphp的Vendor檔案下面; 二、然後去官網下載商戶操作證書:https://pa

程式登入Java

1、思路圖解2、思路描述(1)小程式呼叫wx.login獲取code(2)小程式呼叫wx.getUserInfo得到rawData, signatrue, encryptData.(3)小程式呼叫Server並傳入前兩步獲取的code、rawData、signature、en

程式支付PHP後臺支付統一下單“介面的坑

按照微信的文件來看確實流程是什麼樣的,但某些資料卻神一般的缺少說明,硬生生調了一天才知道完整的使用資料。 小程式提交訂單後就需要後臺請求兩次API,一次為獲取openid(某文件說是在小程式內獲取不安全,所以丟給後臺來獲取),後面一次為獲取prepay_id。最後那

程式JAVA實戰」程式簡介

轉自:https://idig8.com/2018/08/09/xiaochengxu-chuji-01/ 一直想學習小程式,苦於比較忙,加班比較多沒時間,其實這都是理由,很多時候習慣了搬磚,習慣了固定的圈子很難,也不想涉足其他的領域,但是也不能老錯過一波又一波新鮮的東西吧。09年就接觸微信,2013

.NET Core 程式支付——統一下單

最近公司研發了幾個電商小程式,還有一個核心的電商直播,只要是電商一般都會涉及到交易資訊,離不開支付系統,這裡我們統一實現小程式的支付流程(與服務號實現步驟一樣)。 目錄1、開通小程式的支付能力2、商戶後臺繫結同一主體的APPID並授權3、預先設定回撥地址,商戶後臺設定開發的配置4、程式碼實現統一支付5、微信

程式-01筆記

微信小程式和Vue有點相似。 首先下載開發工具,直接去官網找工具就可以了  然後安裝使用就可以了 點選選擇小程式,來到這裡 點選測試號小程式 ,會自動生成AppID。然後建立專案就可以了 介面就是這樣、可以選擇各種機型以及屬於你的糞×。點選編譯或者直接

程式——操作

首先,你需要從官網上https://mp.weixin.qq.com/cgi-bin/wx下載“微信web開發者工具”,如下圖 雙擊開啟 設定專案路徑,AppId或者點選小程式,入門就建立快速啟動模版,確定開啟。 開啟程式後,我們先認識一下目錄:如圖所示, pages資料夾放的是你每

程式 筆記

navigateTo, redirectTo 只能開啟非 tabBar 頁面。 switchTab 只能開啟 tabBar 頁面。 reLaunch 可以開啟任意頁面。 頁面底部的 tabBar 由頁面決定,即只要是定義為 tabB

程式learning - 1簡易教程

官方連結:https://developers.weixin.qq.com/miniprogram/dev/ 學習微信小程式,重點標記,只做記錄,自行看官方文件       全域性配置 小程式根目錄下的 app.json 檔案用來對微信

程式學習18 —— 上拉載入和下拉重新整理

在微信小程式上實現下拉重新整理、上拉載入的效果 使用系統提供的onPullDownRefresh、onReachBottom這2個事件, 前提需要在app.json或page.json配置檔案中設定,才能使用。 app.json是全應用的頁面都可以使用該事件

程式新聞文章釋出系統前後臺完整程式碼

1:建表 。分類表,新聞表 2:後臺。新增新聞,管理新聞。 3:小程式介面。列表頁,詳情頁。 4:後臺介面 5:小程式程式碼 //////////////////////////////////////////////

從零開始學程式開發

從我學習開發小程式到現在,已經有好幾款上線了。雖然都是一些益智類的小程式,不難,但是也從裡面學到了一些知識。 1.準備工作 首先需要去官網註冊一個小程式的帳號。註冊好賬號以後,就可以設定補充小程式的資訊

程式 - debug除錯

微信小程式除錯的方式是基於Chrome.     1. 常見console.log除錯(可以具體參考console.log這個函式使用,它可不止這一個作用!)         2.使用NETWORK(我們可以查詢到訪問了那些介面以

程式入門3:移動端訪問PC

上一篇講了web-view的使用,然而你會發現這隻能訪問一些百度、淘寶、騰訊等一些存在的網站,但如何訪問自己本地ip以來訪問自己寫的一些網站呢? 注意:這篇文章是利用Python開發的web,可能對一些其他僅有HTML、css、js的靜態不太適用 對於Django部分本篇不做過多講解,以後

程式入門2:web-view的使用

上一篇:簡單介面的實現 web-view是最近微信推出的元件,最開始用的時候對於我來說,就是一個內嵌網頁,相當於HTML裡的a標籤。然而這個元件卻不像想象中的那麼好用,不過困擾我十多天後,終於找到一個小方法來“解決”了,看完勿噴。 首先,你得是用企業賬戶或其它賬戶申請註冊的

程式入門1:簡單介面的實現

原始碼我已經放在GitHub上了https://github.com/A666AHL/pupil 1.安裝 微信web開發者工具 不多BB,直接從安裝IDE開始 首先,你得進入微信公眾平臺官網(https://mp.weixin.qq.com) 點選底部的小程式並檢視詳情

程式實戰---實現登入介面

昨天小程式第一天公測,就下載個小程式自帶IDE玩了玩,看了看API,擼出了個登入介面給大家分享下。 下面是主介面和程式碼。 index.wxml <view class="container"> <view class="usermotto">

程式把玩scroll-view元件

scroll-view為滾動檢視,分為水平滾動和垂直滾動。注意滾動檢視垂直滾動時一定要設定高度否則的話scroll-view不會生效。滾動檢視常用的地方一般都是Item項比較多的介面,比如我的模組

程式筆記

微信小程式的初步使用   昨個寫了個Vue的小練習,今天再來個微信小程式的練習。資料繫結上大致跟vue的用法差不多,可惜了不會CSS,要不寫起來能順溜不少。這麼一看還是寫Android順手。 1、建立