Andorid微信刷臉支付使用過程解析


本文着重是 開發前的準備工作和開發過程中遇到的一些坑,不涉及 優化 ,訂單查詢 ,退款和 具體商戶後臺server的開發。
開發環境: rk開發板 +華捷艾米A200 camera
最終實現 用自己的應用和商戶信息成功進行了一筆刷臉支付過程的demo 並且不需要搭建自己的商戶後臺server。

1.創建移動應用

微信開放平臺
在微信開放平臺中創建 移動應用,填寫 包名 和 簽名信息 等。創建需要等審覈完成,一般審覈過程很快。

在這裏插入圖片描述
然後開通微信支付,未認證用戶需要進行認證。認證過程也需要審覈,這個比創建應用的時候要慢。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
認證過程需要填寫一些企業資料 和收取一定費用 ,好像是 300/年 。

2.申請商戶號, 開通APP支付和刷臉支付,關聯APPID

微信商戶號申請

在這裏插入圖片描述
點擊APP支付 申請開通
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
開通後 在APPID授權管理 標籤頁中 關聯 步驟1 中所申請的 APPID (必須已經完成認證並且開通APP支付權限)。
在這裏插入圖片描述
如果APPID未認證 會提示
在這裏插入圖片描述
如果未開通APP支付權限
在這裏插入圖片描述
所以必須先要認證並且開通支付權限。

正常情況:
在這裏插入圖片描述
然後去微信開放平臺 對應的APP設置中確認 關聯。
在這裏插入圖片描述
在這裏插入圖片描述
至此綁定完成


3.開發指引 | 微信刷臉支付

微信刷臉支付 開發指引
微信刷臉支付SDK 目前應該也在快速迭代中,前幾天還是1.30版本,現在就已經更新2.10版本了。
使用方式:

  1. 安裝人臉App。WxPayFace 微信刷臉支付SDK
  2. 商戶接入人臉SDK。項目中引入1中的aar包。商戶APP demo
  3. 商戶server .商戶server demo 此server是在商戶自己開發整套流程的參考demo ,如果只是跑通商戶App demo 則不需要此server。


4.刷臉支付流程

名詞解釋

人臉授權 :通過人臉識別,返回微信用戶信息(openid, face_code)。

face_code:人臉憑證。常用於人臉支付,作爲訂單的支付憑證。

時序圖

商戶APP微信人臉sdk商戶server微信支付後臺API1. 程序啓動時初始化 initWxpayfacedoInitWxpayface(返回初始化結果)2. 獲取數據 getWxpayfaceRawdatadoGetWxpayfaceRawdata(返回rawdata)3.獲取SDK調用憑證3.get_wxpayface_authinfo(rawdata)返回authinfo返回authinfo4. 進行人臉識別 getWxpayfaceCode啓動人臉識別activity進行人臉識別完成人臉識別回調返回人臉識別結果(face_code, openid)doWxPayfaceCodeCallback(返回人臉識別結果)進行支付5. 發起訂單人臉支付發起訂單支付facepay(face_code, orderinfo)返回支付結果返回支付結果查詢支付結果查詢訂單狀態queryorder返回訂單狀態loop[ 直到:返回確定的訂單狀態(成功/失敗/訂單不存在)/超過輪詢時間 ]返回支付結果撤銷交易,以避免用戶扣款,而沒有發貨的情況(撤銷可後臺異步進行)3. 撤銷交易reverseorder返回撤銷結果loop[ 撤銷交易直到成功 ]opt[ 輪詢結束仍然沒有支付成功 ]alt[ 支付成功/失敗 ][ 支付結果未明(比如:支付中/網絡超時) ]6. 更新支付結果updateWxpayfacePayResult(callback)用戶確認支付結果關閉人臉應用界面doUpdatePayResultCallBack(界面關閉,觸發回調)程序退出(...)6. 釋放資源 releaseWxpayface商戶APP微信人臉sdk商戶server微信支付後臺API

注:

  1. 初始化 initWxpayface, 只需要在程序啓動時調用;
  2. 釋放資源 releaseWxpayface,只需要在程序退出時調用;
    每個方法的具體參數可在文檔內查看。接口文檔

5.接口調用踩坑

(1)初始化(initWxpayface)

這個一般放在自己定義的Application#onCreate()中調用就可以了。官方示例copy即可

//對人臉SDK進行初始化
WxPayFace.getInstance().initWxpayface(this, new IWxPayfaceCallback() {
			@Override
			public void response(Map info) throws RemoteException {
				if (info == null) {
					new RuntimeException("調用返回爲空").printStackTrace();
					return ;
				}
				String code = (String) info.get("return_code");
				String msg = (String) info.get("return_msg");
				Log.d(TAG, "response info :: " + code + " | " + msg);
				if (code == null || !code.equals("SUCCESS")) {
					new RuntimeException("調用返回非成功信息: " + msg).printStackTrace();
					return ;
				}
				Log.d(TAG, "調用返回成功");
			}
		});

(2)獲取數據 (getWxpayfaceRawdata)

此過程 一定要保證 兩點:
1.設備能聯網,應用要添加<uses-permission android:name="android.permission.INTERNET"/>權限,
官方demo中還添加以下權限:

   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
   <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name="android.permission.READ_PHONE_STATE" />
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

2.設備有SN號 。在android設備上,開機後進入setting->about devices->status->Serial number查看
當時使用的是 rk開發板,IMG沒有寫入SN號。當時在論壇上找的寫入SN號工具只能寫入 "ro.boot.serialno"的值,"ro.serialno"還是空的,所以最後只能修改源代碼,重新編譯燒錄版本。

    /** 2. 人臉識別第二步 獲取raw data*/
    private void getWxpayfaceRawdata() {
        WxPayFace.getInstance().getWxpayfaceRawdata(new IWxPayfaceCallback() {
            @Override
            public void response(Map info) throws RemoteException {
                if (info == null) {
                    new RuntimeException("調用返回爲空").printStackTrace();
                    return;
                }
                String code = (String) info.get("return_code");
                String msg = (String) info.get("return_msg");
                rawdata = info.get("rawdata").toString();
                Log.d(TAG, "rawdata ==" + rawdata);
                if (code == null || rawdata == null || !code.equals("SUCCESS")) {
                    new RuntimeException("調用返回非成功信息,return_msg:" + msg + "   ").printStackTrace();
                    return ;
                }
       	        /**
       	         在這裏處理您自己的業務邏輯
                 可以緊接着執行第三步 獲取調用憑證getAuthInfo,
                 這應該是向 商戶server 發起請求。
       	         */
       	        getAuthInfo(rawdata);
            }
        });
    }

(3)獲取調用憑證(get_wxpayface_authinfo)

這是一個後端調用接口 採用xml格式
因爲demo爲了省事,省去商戶後臺server的開發,所以這一步也是在Android端直接調用。
獲取憑證需要很多的 參數

參數 必填 類型 說明
store_id string(32) 門店編號, 由商戶定義, 各門店唯一。
store_name string(128) 門店名稱,由商戶定義。(可用於展示)
device_id string(32) 終端設備編號,由商戶定義。
attach string 附加字段。字段格式使用Json
rawdata string(2048) 初始化數據。由微信人臉SDK的接口返回。
獲取方式參見:
[獲取數據 getWxpayfaceRawdata](#獲取數據 getWxpayfaceRawdata)
[獲取數據 getWxpayfaceRawdata](#獲取數據 getWxpayfaceRawdata)
appid string(32) 商戶號綁定的公衆號/小程序 appid
mch_id string(32) 商戶號
sub_appid string(32) 子商戶綁定的公衆號/小程序 appid(服務商模式)
sub_mch_id string(32) 子商戶號(服務商模式)
now int 取當前時間,10位unix時間戳。 例如:1239878956
version string 版本號。固定爲1
sign_type string 簽名類型,目前支持HMAC-SHA256和MD5,默認爲MD5
nonce_str string(32) 隨機字符串,不長於32位
sign string 參數簽名。詳見微信支付簽名算法
Sign參數獲取

簽名算法

簽名生成的通用步驟如下:

第一步,設所有發送或者接收到的數據爲集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

特別注意以下重要規則:

參數名ASCII碼從小到大排序(字典序)
◆ 如果參數的值爲空不參與簽名;
◆ 參數名區分大小寫;
◆ 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值作校驗。
◆ 微信接口可能增加字段,驗證簽名時必須支持增加的擴展字段
第二步,在stringA最後拼接上key得到stringSignTemp字符串,並對stringSignTemp進行MD5運算,再將得到的字符串所有字符轉換爲大寫,得到sign值signValue。

◆ key設置路徑:微信商戶平臺(pay.weixin.qq.com)–>賬戶設置–>API安全–>密鑰設置
,獲取方法:
在這裏插入圖片描述
安裝插件 ,瀏覽器最好別用QQ瀏覽器。在這裏插入圖片描述
自家的插件不知道爲什麼喚不起來。重啓N遍無用,推薦Chrome.
密鑰是32位字符串 。
在上面第二步代碼回調成功後緊接着 可以調用第三步。

    private void getAuthInfo(String rawdata){
        //now 參數 是unix 10位時間戳
        long  time = System.currentTimeMillis()/1000L;
        //爲了對比起來方便 下面所有請求 都是用 字符串拼接的方式。
        //注意參數順序 參數名ASCII碼從小到大排序(字典序).
        String a ="appid=appid應用ID&device_id=DEV001&mch_id=mch_id商戶號&nonce_str=V37ZHZVf2OrwsUV7kXTjTguP74c0byvE&now="+time+"&rawdata="+rawdata+"&sign_type=MD5&store_id=IMG001&store_name=門店名稱&version=1";
        String stringSignTemp=a+"&key=32位的字符串";//注:key爲商戶平臺設置的密鑰key
        String sign= md5(stringSignTemp).toUpperCase(); //注:MD5簽名方式
        Log.d(TAG, "sign : " +sign);
        String  finalStr = "<xml>\n" +
                "     <appid>appid應用ID</appid>\n" +
                "     <device_id>DEV001</device_id>\n" +
                "     <nonce_str>V37ZHZVf2OrwsUV7kXTjTguP74c0byvE</nonce_str>\n" +
                "     <now>"+time+"</now>\n" +
                "     <mch_id>mch_id商戶號</mch_id>\n" +
                "     <rawdata>"+rawdata+"</rawdata>\n" +
                "     <store_id>IMG001</store_id>\n" +
                "     <store_name>門店名稱</store_name>\n" +
                "     <sign_type>MD5</sign_type>\n" +
                "     <version>1</version>\n" +
                "     <sign>"+sign+"</sign>\n" +
                "</xml>";
       
        //SSL可以不用管
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }
                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }
                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };
            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient client = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory)
                    .hostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                            return true;
                        }
                    })
                    .build();
                    
            //請求格式xml ,請求方式post
            RequestBody body=RequestBody.create(MediaType.parse("application/xml"),finalStr);
            Request request = new Request.Builder()
                    .url("https://payapp.weixin.qq.com/face/get_wxpayface_authinfo")//後臺接口地址,具體見後臺開發文檔
                    .post(body)
                    .build();

            client.newCall(request)
                    .enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            Log.d(TAG, "onFailure | getAuthInfo " + e.toString());
                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            try {
                                String bodyStr = response.body().string();
                                Log.d(TAG, "onResponse | getAuthInfo " + bodyStr);
                                //這裏返回的不是標準的xml格式,缺少 開始標籤 ,拼接後用XmlPullParser解析
                                String  xmlStr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+bodyStr;
                                //ReturnXMLParser 類在官方的demo中
                                //最終獲取到 AuthInfo信息。
                                mAuthInfo = ReturnXMLParser.parseGetAuthInfoXML( new ByteArrayInputStream(xmlStr.getBytes()));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    

這裏需要注意的很多 :

  1. now 參數 注意是 10位的 時間戳 。並且拼接字符串a 時用的now 和 最後請求Body中xml內容中的Now的值一定要相同,兩個地方切勿直接用 System.currentTimeMillis()/1000L
  2. 獲取sign值 拼接的字符串a 一定要按照參數名ASCII碼從小到大排序。sign值可以用簽名校驗工具驗證
  3. 請求接口 https://payapp.weixin.qq.com/face/get_wxpayface_authinfo
  4. 返回值不是標準的xml格式,不包括"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"部分,所以可以先拼接在解析。

就這樣最終拿到了AuthInfo字符串 。


(4)獲取人臉支付憑證(getWxpayfaceCode)

啓動人臉APP主界面入口,開啓人臉識別,獲取用戶信息(openid)和支付憑證()。

參數 必填 類型 說明
appid string 商戶號綁定的公衆號/小程序 appid
mch_id string 商戶號
sub_appid string(32) 子商戶綁定的公衆號/小程序 appid(可不填)
sub_mch_id string(32) 子商戶號(非服務商模式不填)
store_id string 門店編號
telephone string 用戶手機號。用於傳遞會員手機,此手機將作爲默認值, 填寫到手機輸入欄。
out_trade_no string 商戶訂單號,須與調用支付接口時字段一致,該字段在在face_code_type爲"1"時可不填,爲"0"時必填
total_fee string 訂單金額(數字), 單位分. 該字段在在face_code_type爲"1"時可不填,爲"0"時必填
face_authtype string 可選值:
FACEPAY: 人臉憑證,常用於人臉支付
FACEPAY_DELAY: 延遲支付(提供商戶號信息聯繫微信支付開通權限)
authinfo string 調用憑證。獲取方式參見: get_wxpayface_authinfo
ask_face_permit string 支付成功頁是否需要展示人臉識別授權項。
展示:1
不展示:0
人臉識別授權項:
用戶授權後用於1:N識別,可返回用戶信息openid,建議商戶有自己會員系統時,填1。
ask_ret_page string 是否展示微信支付成功頁,可選值:“0”,不展示;“1”,展示
face_code_type string 目標face_code類型,可選值:“0”,人臉付款碼:數字字母混合,通過「刷臉支付」接口完成支付;“1”,刷卡付款碼:18位數字,通過「付款碼支付/被掃支付」接口完成支付。如果不填寫則默認爲"0"
ignore_update_pay_result string 商戶端是否對SDK返回支付結果,可選值:“0”,返回支付結果,商戶需在確認⽀付結果後調⽤[updateWxpayfacePayResult]通知SDK;“1”,不返回支付結果。如果不填寫則默認爲"0"。
    private void getWXPayFaceCode(String mAuthInfo){
        if (mAuthInfo== null){
            Log.d(TAG, "mAuthInfo 爲 null");
            return;
        }
        HashMap params = new HashMap();
        params.put(BaseUtils.FACE_AUTHTYPE, "FACEPAY");
        params.put(BaseUtils.APPID, "wxefawffetat56");
        params.put(BaseUtils.MCH_ID, "11534ef6121");
        params.put(BaseUtils.STORE_ID, "IMG001");
        //訂單號 ,注意後後面支付pay中的訂單號要一致
        out_trade_no = "" +(System.currentTimeMillis() / 100000); 
        params.put(BaseUtils.OUT_TRADE_NO, out_trade_no);
        //訂單金額
        params.put(BaseUtils.TOTAL_FEE, "1");
        //第三步中獲取的AuthInfo
        params.put(BaseUtils.AUTHINFO, mAuthInfo);

        WxPayFace.getInstance().getWxpayfaceCode(params, new IWxPayfaceCallback() {
            @Override
            public void response(final Map info) throws RemoteException {
//                if (!isSuccessInfo(info)) {
//                    return;
//                }
                Log.d(TAG, "response | getWxpayfaceCode" );

                final String code = (String)info.get(BaseUtils.RETURN_CODE);
                // open id  , face code
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //請求成功
                        if (TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_SUCCESS)) {
                            //獲取openid 和 face_code 
                            face_code = (String)info.get("face_code");
                            openID = (String)info.get("openid");
                            ip = "174.207.250.66";
                            try {
                                Thread.sleep(2000);
                                //第5步 支付
                                pay(openID,face_code,ip);
                            } catch (Exception e) {
                            }
                        } else if (TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_USER_CANCEL)) {
                            Log.d(TAG, "run: 用戶取消");
                        } else if (TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_SCAN_PAYMENT)) {
                            Log.d(TAG, "run: 掃碼支付");
                        } else if (TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_ERROR)) {
                            Log.d(TAG, "run: 發生錯誤");
                        }
                    }
                });
            }
        });
    }

這個過程沒什麼說的,就是用第三步的AuthInfo ,還有就是訂單號 要和後面第五步支付過程的訂單號要保持一致。

(5)支付 (pay),並更新支付結果(updateWxpayfacePayResult)

1. pay

支付 同樣是一個後端server接口 後端server接口
接口地址 :https://api.mch.weixin.qq.com/pay/facepay
請求方式 POST、XML

參數 必填 類型 說明
appid string 商戶號綁定的公衆號/小程序 appid
mch_id string 商戶號
sub_appid string(32) 子商戶綁定的公衆號/小程序 appid(可不填)
sub_mch_id string(32) 子商戶號(非服務商模式不填)
device_info string(32) 終端設備號(商戶自定義,如門店編號)。
out_trade_no string(32) 商戶系統內部的訂單號,32個字符內、可包含字母;更換授權碼必須要換新的商戶訂單號 其他說明見商戶訂單號
total_fee string 訂單金額(數字), 單位分. 該字段在在face_code_type爲"1"時可不填,爲"0"時必填
nonce_str string(32) 隨機字符串,不長於32位。推薦隨機數生成算法
sign string(32) 參數簽名。詳見微信支付簽名算法
body String(128) 商品或支付單簡要描述,格式要求:門店品牌名-城市分店名-實際商品名稱
total_fee Int 訂單總金額,單位爲分,只能爲整數,詳見支付金額
spbill_create_ip String(16) 調用微信支付API的機器IP
openid String(128) 用戶在商戶appid 下的唯一標識
face_code String(128) 人臉憑證,用於刷臉支付

更多不必要參數可查看文檔。
舉例如下:

<xml>
   <appid>wx2421b1c4370ec43b</appid>
   <attach>訂單額外描述</attach>
   <body>刷卡支付測試</body>
   <device_info>1000</device_info>
   <goods_tag></goods_tag>
   <mch_id>10000100</mch_id>
   <nonce_str>8aaee146b1dee7cec9100add9b96cbe2</nonce_str>
   <out_trade_no>1415757673</out_trade_no>
   <spbill_create_ip>14.17.22.52</spbill_create_ip>
   <time_expire></time_expire>
   <total_fee>1</total_fee>
   <openid><![CDATA[oUpF8uN95-Ptaags6E_roPHg7AG0]]></openid>
   <face_code>Qpoqwhsdjhfausrhqieofnq90w=w8233wdwjdjiwq</face_code>
   <sign>C29DB7DB1FD4136B84AE35604756362C</sign>
</xml>

pay方法中Http請求 返回結果請查看文檔
在這裏插入圖片描述

2. updateWxpayfacePayResult(final Map info, final IWxPayfaceCallback wxpayfaceCallBack)
接口參數
參數 必填 類型 說明
appid string 商戶綁定的公衆號/小程序 appid
mch_id string 商戶號
store_id string 門店編號
authinfo string 調用憑證。獲取方式參見: get_wxpayface_authinfo
payresult string 支付結果。可取值:
SUCCESS: 支付成功
ERROR: 支付失敗
接口返回
參數 必填 類型 說明
return_code string 錯誤碼。公共定義見 公共錯誤碼
return_msg string(128) 對錯誤碼的描述
實踐指引
  private void updateWxpayfacePayResult() {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("appid", "填您的公衆號"); // 公衆號,必填
        map.put("mch_id", "填您的商戶號"); // 商戶號,必填
        map.put("store_id", "填您的門店編號"); // 門店編號,必填
        map.put("authinfo", "填您的調用憑證"); // 調用憑證,必填
        map.put("payresult", "SUCCESS"); // 支付結果,SUCCESS:支付成功   ERROR:支付失敗   必填
        WxPayFace.getInstance().updateWxpayfacePayResult(map, new IWxPayfaceCallback() {
            @Override
            public void response(Map info) throws RemoteException {
                if (info == null) {
                    new RuntimeException("調用返回爲空").printStackTrace();
                    return;
                }
                String code = (String) info.get("return_code"); // 錯誤碼
                String msg = (String) info.get("return_msg"); // 錯誤碼描述
                if (code == null || !code.equals("SUCCESS")) {
                    new RuntimeException("調用返回非成功信息,return_msg:" + msg + "   ").printStackTrace();
                    return ;
                }
                /*
                在這裏處理您自己的業務邏輯:
                執行到這裏說明用戶已經確認支付結果且成功了,此時刷臉支付界面關閉,您可以在這裏選擇跳轉到其它界面
                 */
            }
        });
    }

最後第五步整體過程代碼:

    private void pay(String openID,String faceCode,String ipAddress){
        Log.d(TAG, "-------------start pay-----------");
        String a ="appid=APPID&body=刷臉支付測試&device_info=DEV001&face_code="+faceCode+"&mch_id=MCH_ID&nonce_str=V37ZHZVf2OrwsUV7kXTjTguP74c0byvE&openid="+openID+"&out_trade_no="+out_trade_no+"&spbill_create_ip="+ipAddress+"&total_fee=1";
        String stringSignTemp=a+"&key=32KEY";//注:key爲商戶平臺設置的密鑰key
        String sign= md5(stringSignTemp).toUpperCase();
        String  finalStr = "<xml>\n" +
                "     <appid>APPID</appid>\n" +
                "     <body>刷臉支付測試</body>\n" +
                "     <device_info>DEV001</device_info>\n"+
                "     <face_code>"+faceCode+"</face_code>\n"+
                "     <mch_id>MCHID</mch_id>\n" +
                "     <nonce_str>V37ZHZVf2OrwsUV7kXTjTguP74c0byvE</nonce_str>\n" +
                "     <openid>"+openID+"</openid>\n"+
                "     <out_trade_no>"+out_trade_no+"</out_trade_no>\n"+
                "     <spbill_create_ip>"+ipAddress+"</spbill_create_ip>\n" +
                "     <total_fee>1</total_fee>\n"+
                "     <sign>"+sign+"</sign>\n" +
                "</xml>";

        OkHttpClient client = new OkHttpClient.Builder()
                .hostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                })
                .build();

        RequestBody body=RequestBody.create(MediaType.parse("application/xml"),finalStr);
        Request request = new Request.Builder()
                .url("https://api.mch.weixin.qq.com/pay/facepay")
                .post(body)
                .build();

        client.newCall(request)
                .enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        Log.d(TAG, "onFailure | getAuthInfo " + e.toString());
                    }
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        try {
                            String bodyStr = response.body().string();
                            Log.d(TAG, "支付結果  ====\n" + bodyStr);
                            String  xmlStr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+bodyStr;
//                            String retrunCode = ReturnXMLParser.parseCodeXML( new ByteArrayInputStream(xmlStr.getBytes()),"return_code");
                            String result_code = ReturnXMLParser.parseCodeXML( new ByteArrayInputStream(xmlStr.getBytes()),"result_code");
                            Map map =new HashMap();
                            map.put("appid","APPID應用ID");
                            map.put("mch_id",商戶號);
                            map.put("store_id","IMG001");
                            map.put("authinfo",mAuthInfo);
                            boolean resultStatus = TextUtils.equals(result_code, WxfacePayCommonCode.VAL_RSP_PARAMS_SUCCESS);
                            if (result_code!=null && resultStatus ){
                                map.put("payresult","SUCCESS");
                            }else {
                                map.put("payresult","ERROR");
                            }
							//更新支付狀態 
                            WxPayFace.getInstance().updateWxpayfacePayResult(map, new IWxPayfaceCallback() {
                                @Override
                                public void response(Map info) throws RemoteException {
                                    Log.d(TAG, "-------------更新支付狀態 -----------"+info.get("return_code"));
                                }
                            });
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
    }

請求成功後一定要 調用更新支付結果的方法(不然一直卡在 支付等待界面),調用後會根據結果提示支付成功或者失敗。 至此整個支付流程就完事了。

最後可以去商戶平臺 查看訂單信息:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章