1. 程式人生 > >Java實現JsApi方式的微信支付

Java實現JsApi方式的微信支付

要使用JsApi進行微信支付,首先要從微信獲得一個prepay_id,然後通過呼叫微信的jsapi完成支付,JS API的返回結果get_brand_wcpay_request:ok僅在使用者成功完成支付時返回。由於前端互動複雜,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以統一處理為使用者遇到錯誤或者主動放棄,不必細化區分。
示例程式碼如下:

function onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId" : "wx2421b1c4370ec43b",     //公眾號名稱,由商戶傳入     
           "timeStamp":" 1395712654",         //時間戳,自1970年以來的秒數     
           "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //隨機串     
           "package" : "u802345jgfjsdfgsdg888",     
           "signType" : "MD5",         //微信簽名方式:     
           "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在使用者支付成功後返回    ok,但並不保證它絕對可靠。 
       }
   ); 
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}

以上傳入的引數package,即為prepay_id
詳細文件見:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7

下面講的是獲得引數來呼叫jsapi
我們呼叫JSAPI時,必須獲得使用者的openid,(trade_type=JSAPI,openid為必填引數。)
首先定義一個請求的物件:

package com.unstoppedable.protocol;


import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.unstoppedable.common.Configure;
import com.unstoppedable.common.RandomStringGenerator;
import com.unstoppedable.common.Signature;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

@XStreamAlias("xml")
public class UnifiedOrderReqData {

    private String appid;
    private String mch_id;
    private String device_info;
    private String nonce_str;
    private String sign;
    private String body;
    private String detail;
    private String attach;
    private String out_trade_no;
    private String fee_type;
    private int total_fee;
    private String spbill_create_ip;
    private String time_start;
    private String time_expire;
    private String goods_tag;
    private String notify_url;
    private String trade_type;
    private String product_id;
    private String limit_pay;
    private String openid;

    private UnifiedOrderReqData(UnifiedOrderReqDataBuilder builder) {
        this.appid = builder.appid;
        this.mch_id = builder.mch_id;
        this.device_info = builder.device_info;
        this.nonce_str = RandomStringGenerator.getRandomStringByLength(32);
        this.body = builder.body;
        this.detail = builder.detail;
        this.attach = builder.attach;
        this.out_trade_no = builder.out_trade_no;
        this.fee_type = builder.fee_type;
        this.total_fee = builder.total_fee;
        this.spbill_create_ip = builder.spbill_create_ip;
        this.time_start = builder.time_start;
        this.time_expire = builder.time_expire;
        this.goods_tag = builder.goods_tag;
        this.notify_url = builder.notify_url;
        this.trade_type = builder.trade_type;
        this.product_id = builder.product_id;
        this.limit_pay = builder.limit_pay;
        this.openid = builder.openid;
        this.sign = Signature.getSign(toMap());
    }

    public String getAppid() {
        return appid;
    }

    public String getMch_id() {
        return mch_id;
    }

    public String getDevice_info() {
        return device_info;
    }

    public String getNonce_str() {
        return nonce_str;
    }

    public String getSign() {
        return sign;
    }

    public String getBody() {
        return body;
    }

    public String getDetail() {
        return detail;
    }

    public String getAttach() {
        return attach;
    }

    public String getOut_trade_no() {
        return out_trade_no;
    }

    public String getFee_type() {
        return fee_type;
    }

    public int getTotal_fee() {
        return total_fee;
    }

    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }

    public String getTime_start() {
        return time_start;
    }

    public String getTime_expire() {
        return time_expire;
    }

    public String getGoods_tag() {
        return goods_tag;
    }

    public String getNotify_url() {
        return notify_url;
    }

    public String getTrade_type() {
        return trade_type;
    }

    public String getProduct_id() {
        return product_id;
    }

    public String getLimit_pay() {
        return limit_pay;
    }

    public String getOpenid() {
        return openid;
    }

    public Map<String, Object> toMap() {
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            Object obj;
            try {
                obj = field.get(this);
                if (obj != null) {
                    map.put(field.getName(), obj);
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }


    public static class UnifiedOrderReqDataBuilder {
        private String appid;
        private String mch_id;
        private String device_info;
        private String body;
        private String detail;
        private String attach;
        private String out_trade_no;
        private String fee_type;
        private int total_fee;
        private String spbill_create_ip;
        private String time_start;
        private String time_expire;
        private String goods_tag;
        private String notify_url;
        private String trade_type;
        private String product_id;
        private String limit_pay;
        private String openid;

        /**
         * 使用配置中的 appid 和  mch_id
         *
         * @param body
         * @param out_trade_no
         * @param total_fee
         * @param spbill_create_ip
         * @param notify_url
         * @param trade_type
         */
        public UnifiedOrderReqDataBuilder(String body, String out_trade_no, Integer total_fee,
                                          String spbill_create_ip, String notify_url, String trade_type) {

            this(Configure.getAppid(), Configure.getMchid(), body, out_trade_no, total_fee,
                    spbill_create_ip, notify_url, trade_type);

        }

        public UnifiedOrderReqDataBuilder(String appid, String mch_id, String body, String out_trade_no, Integer total_fee,
                                          String spbill_create_ip, String notify_url, String trade_type) {
            if (appid == null) {
                throw new IllegalArgumentException("傳入引數appid不能為null");
            }
            if (mch_id == null) {
                throw new IllegalArgumentException("傳入引數mch_id不能為null");
            }
            if (body == null) {
                throw new IllegalArgumentException("傳入引數body不能為null");
            }
            if (out_trade_no == null) {
                throw new IllegalArgumentException("傳入引數out_trade_no不能為null");
            }
            if (total_fee == null) {
                throw new IllegalArgumentException("傳入引數total_fee不能為null");
            }
            if (spbill_create_ip == null) {
                throw new IllegalArgumentException("傳入引數spbill_create_ip不能為null");
            }
            if (notify_url == null) {
                throw new IllegalArgumentException("傳入引數notify_url不能為null");
            }
            if (trade_type == null) {
                throw new IllegalArgumentException("傳入引數trade_type不能為null");
            }
            this.appid = appid;
            this.mch_id = mch_id;
            this.body = body;
            this.out_trade_no = out_trade_no;
            this.total_fee = total_fee;
            this.spbill_create_ip = spbill_create_ip;
            this.notify_url = notify_url;
            this.trade_type = trade_type;
        }

        public UnifiedOrderReqDataBuilder setDevice_info(String device_info) {
            this.device_info = device_info;
            return this;
        }

        public UnifiedOrderReqDataBuilder setDetail(String detail) {
            this.detail = detail;
            return this;
        }

        public UnifiedOrderReqDataBuilder setAttach(String attach) {
            this.attach = attach;
            return this;
        }

        public UnifiedOrderReqDataBuilder setFee_type(String fee_type) {
            this.fee_type = fee_type;
            return this;
        }

        public UnifiedOrderReqDataBuilder setTime_start(String time_start) {
            this.time_start = time_start;
            return this;
        }

        public UnifiedOrderReqDataBuilder setTime_expire(String time_expire) {
            this.time_expire = time_expire;
            return this;
        }

        public UnifiedOrderReqDataBuilder setGoods_tag(String goods_tag) {
            this.goods_tag = goods_tag;
            return this;
        }

        public UnifiedOrderReqDataBuilder setProduct_id(String product_id) {
            this.product_id = product_id;
            return this;
        }

        public UnifiedOrderReqDataBuilder setLimit_pay(String limit_pay) {
            this.limit_pay = limit_pay;
            return this;
        }

        public UnifiedOrderReqDataBuilder setOpenid(String openid) {
            this.openid = openid;
            return this;
        }


        public UnifiedOrderReqData build() {

            if ("JSAPI".equals(this.trade_type) && this.openid == null) {
                throw new IllegalArgumentException("當傳入trade_type為JSAPI時,openid為必填引數");
            }
            if ("NATIVE".equals(this.trade_type) && this.product_id == null) {
                throw new IllegalArgumentException("當傳入trade_type為NATIVE時,product_id為必填引數");
            }
            return new UnifiedOrderReqData(this);
        }
    }


}

因為有些引數為必填,有些引數為選填。而且sign要等所有引數傳入之後才能計算的出,所以這裡用了builder模式。關於builder模式。關於每個引數的定義,參考說明文件https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

我們選用httpclient進行網路傳輸。

package com.unstoppedable.common;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
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 javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyStore;

/**
 * Created by hupeng on 2015/7/28.
 */
public class HttpService {
    private static Log logger = LogFactory.getLog(HttpService.class);

    private static CloseableHttpClient httpClient = buildHttpClient();

    //連線超時時間,預設10秒
    private static int socketTimeout = 5000;

    //傳輸超時時間,預設30秒
    private static int connectTimeout = 5000;

    private static int requestTimeout = 5000;

    public static CloseableHttpClient buildHttpClient() {

        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));//載入本地的證書進行https加密傳輸
            try {
                keyStore.load(instream, Configure.getCertPassword().toCharArray());//設定證書密碼
            } finally {
                instream.close();
            }


            // Trust own CA and all self-signed certs
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray())
                    .build();
            // Allow TLSv1 protocol only
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[]{"TLSv1"},
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(requestTimeout)
                    .setSocketTimeout(socketTimeout).build();

            httpClient = HttpClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .setSSLSocketFactory(sslsf)
                    .build();

            return httpClient;
        } catch (Exception e) {
            throw new RuntimeException("error create httpclient......", e);
        }
    }



    public static String doGet(String requestUrl) throws Exception {
        HttpGet httpget = new HttpGet(requestUrl);
        try {


            logger.debug("Executing request " + httpget.getRequestLine());
            // Create a custom response handler
            ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

                @Override
                public String handleResponse(
                        final HttpResponse response) throws ClientProtocolException, IOException {
                    int status = response.getStatusLine().getStatusCode();
                    if (status >= 200 && status < 300) {
                        HttpEntity entity = response.getEntity();
                        return entity != null ? EntityUtils.toString(entity) : null;
                    } else {
                        throw new ClientProtocolException("Unexpected response status: " + status);
                    }
                }

            };

            return httpClient.execute(httpget, responseHandler);
        } finally {
            httpget.releaseConnection();
        }
    }

    public static String doPost(String url, Object object2Xml) {

        String result = null;

        HttpPost httpPost = new HttpPost(url);

        //解決XStream對出現雙下劃線的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));

        //將要提交給API的資料物件轉換成XML格式資料Post給API
        String postDataXML = xStreamForRequestPostData.toXML(object2Xml);

        logger.info("API,POST過去的資料是:");
        logger.info(postDataXML);

        //得指明使用UTF-8編碼,否則到API伺服器XML的中文不能被成功識別
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);

        //設定請求器的配置

        logger.info("executing request" + httpPost.getRequestLine());

        try {
            HttpResponse response = httpClient.execute(httpPost);

            HttpEntity entity = response.getEntity();

            result = EntityUtils.toString(entity, "UTF-8");

        } catch (ConnectionPoolTimeoutException e) {
            logger.error("http get throw ConnectionPoolTimeoutException(wait time out)", e);

        } catch (ConnectTimeoutException e) {
            logger.error("http get throw ConnectTimeoutException", e);

        } catch (SocketTimeoutException e) {
            logger.error("http get throw SocketTimeoutException", e);

        } catch (Exception e) {
            logger.error("http get throw Exception", e);

        } finally {
            httpPost.abort();
        }

        return result;
    }
}

然後是我們的總入口:

package com.unstoppedable.service;

import com.unstoppedable.common.Configure;
import com.unstoppedable.common.HttpService;
import com.unstoppedable.common.XMLParser;
import com.unstoppedable.protocol.UnifiedOrderReqData;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.Map;

/**
 * Created by hupeng on 2015/7/28.
 */
public class WxPayApi {

    public static Map<String,Object> UnifiedOrder(UnifiedOrderReqData reqData) throws IOException, SAXException, ParserConfigurationException {
        String res  = HttpService.doPost(Configure.UNIFIED_ORDER_API, reqData);
        return XMLParser.getMapFromXML(res);
    }

    public static void main(String[] args) throws Exception {
        UnifiedOrderReqData reqData = new UnifiedOrderReqData.UnifiedOrderReqDataBuilder("appid", "mch_id", "body", "out_trade_no", 1, "spbill_create_ip", "notify_url", "JSAPI").setOpenid("openid").build();
        System.out.println(UnifiedOrder(reqData));


    }
}

返回的xml為:

<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>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

return_code 和result_code都為SUCCESS的時候會返回我們需要的prepay_id。。。

然後在jsapi中使用他就可以了。。

程式碼託管在https://github.com/hupengcool/wxpay_jsapi ,歡迎指正

相關推薦

Java後臺實現小程式微支付

本人第一篇部落格,之前筆記一直放在有道雲,想寫部落格很久了,一直沒下定決心,也沒靜下心好好寫,今天突然跟朋友談 到平時工作根本沒時間學習、整理、總結balabala,工作中遇到的問題很多會經常遇到,不整理總結第二次碰到又要半天,就這麼扯吧扯吧,扯完之後,不知道哪來的決心,就下手了,哈哈,廢話

Java實現JsApi方式的微支付

要使用JsApi進行微信支付,首先要從微信獲得一個prepay_id,然後通過呼叫微信的jsapi完成支付,JS API的返回結果get_brand_wcpay_request:ok僅在使用者成功完成支付時返回。由於前端互動複雜,get_brand_wcpay_request:cancel或者get_bran

小程式微支付程式碼實現

小程式端 觸發呼叫微信支付介面: // 微信支付 goPay : function(e){ var that = this; var pay = e.currentTarget.dataset.pay; var uid = that.data.uid;

java服務端,微支付功能的實現

     接入微信的支付功能在java服務端,現在記錄下來這個過程,方便以後用到。程式碼都是參考網路,功能可以實現      實際參考https://github.com/wxpay/WXPay-SDK-Java     ①,寫一個介面,作為引數配置的介面,當然也可以不用介

小程式微支付java服務端

1、首先可以通過服務端來獲取openid,openid可以作為自己平臺微信使用者身份的唯一標識。 /** * @Description: 獲取openId * @param: code 小程式授權後獲得的code * @Author: zh

java 實現簡單的短發送

下載 har ase 設置 request enc value pri ring 現在中國網建上註冊一個自己的賬戶, 然後裏面有代碼案例,也有相應的下載jar包的地址 代碼如下: public class Message { public static void main

Java實現IP/TCP通幫助類SocketSimple

[] ofo create tar 中央倉庫 thread get 講解 llb 新春伊始,上班碼代碼,看了一下自己年前的總結,發現有一個Socket通信的幫助庫SocketSimple,今天就介紹一下該庫的作用。 作用講解 SocketSimple庫主要是對Socket服

Java開發SSM框架微支付

etc ray 企業 展示 system 都在 pes generator 程序開發 微信小程序的Java支付開發一直是一塊坑,網上的教程也是琳瑯滿目。筆者六月的時候接觸到了微信的小程序開發摸到了微信支付方面的東西,騰訊的官方文檔也是一言難盡很多地方看不懂,而且官方也沒有提

PHP實現 APP端微支付功能

1.我封裝好的一個支付類檔案,多餘的東西都去除掉了,並且把配置引數放到了這個支付類中,只需要修改Weixinpayandroid方法內的幾個引數就可以直接複製使用: class Wxpayandroid { //引數配置 public $config = array( 'appid' =&

Java實現websocket與微小程式連線

微信的WebSocket介面和HTML5的WebSocket基本一樣,是HTTP協議升級來的,做為一個新的Socket在B/S上使用,它實現了瀏覽器與伺服器全雙工通訊。   在WebSocket出來之前,實現即時通訊通常使用Ajax來實現,而Ajax是通過輪詢的方式進行實時資料的獲

JAVA實現第三方網站微掃碼登入Demo

用JAVA Servlet實現的第三方網站微信掃碼登入的Demo,做這個確實很辛苦,很不容易實現,但是還是做出來了,這裡把程式碼貼一下,避免做微信登入開發的朋友們少走彎路 package com.wxlogin.common; import java.n

小程式微支付

需要引數 小程式 appid 小程式 appsecret 商戶 merchid 商戶apikey 證書 (企業付款到使用者需要證書) 支付類 tp5 放在 extends/weixin資料夾下 名稱空間 weixin <?php namespace wei

海外小程式微支付,微小程式跨境支付,小程式境外支付

在上一篇文章《167個國家和地區可以開通微信海外小程式》 你已經瞭解境外公司可以申請微信小程式, 可以把你的商品和服務, 在小程式中展示。 想要在小程式裡完成交易, 必須涉及到線上支付的問題, 很多人

Thinkphp 5.1 微小程式微支付

剛學習thinkPHP,小白一位,記錄一下,大神們不要笑! Pay.php <?php namespace app\index\controller; use think\exception\HttpResponseException; use think\Re

Java】快速整合微支付支付支付

本文介紹博主自己封裝的一個微信支付和支付寶支付的library 本專案解決的問題僅限於最後的支付環節,即你告訴我多少錢,我帶著這個多少錢的資訊發起微信支付或者支付寶支付,僅需簡單的配置資訊,然後

小程式微支付回撥

//非同步請求迴應微信支付是否成功 @Transactional public String weixinNotify(Str

小黑式爛代碼之微APP支付 + 退款(JAVA實現)

result 技術分享 文件 index tsig eat java ava fan 首先,你得先有微信開發平臺賬號密碼還需要開通應用,然後還有微信服務商平臺商戶版賬號(這些我都是給產品經理拿的) 其次我認為你先去看一看微信開發平臺的文檔! https://pay.wei

java實現沙箱測試環境支付支付(demo)和整合微支付支付支付到ssm

mar 文件 fun Go examples IT === throws 由於 文章有不當之處,歡迎指正,如果喜歡微信閱讀,你也可以關註我的微信公眾號:好好學java,獲取優質學習資源。 一、支付寶測試環境代碼測試 1.下載電腦網站的官方demo: 下載地址:https:

支付報"呼叫支付jsapi缺少引數 total_fee",實際Java統一支付介面,返回“body引數長度有誤”

之前幫朋友做過一次微信支付,記得裡面到處都是坑,今天朋友說出現問題提了:商品選擇3個及3個以內的時候,正常支付,選擇4個及以上的時候,就會支付失敗。並傳給我2張截圖:   明顯報錯資訊:呼叫支付jsapi缺少引數 total_fee 基於之前對微信支付的瞭解,這個報錯參考意義

網頁掃碼支付(公眾號)JAVA實現

今天我們來說說微信網頁掃碼支付,這個支付的步驟和微信公眾號網頁是差不多的,也和微信小程式的步驟是一致的,不過appid是微信公眾號的,我自己收集的微信開發文件希望對大家有用: https://blog.csdn.net/qq_41971087/article/d