微信刷臉支付全部流程(普通商戶號)
本文着重是 開發前的準備工作和開發過程中遇到的一些坑,不涉及 優化 ,訂單查詢 ,退款和 具體商戶後臺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版本了。
使用方式:
- 安裝人臉App。WxPayFace 微信刷臉支付SDK
- 商戶接入人臉SDK。項目中引入1中的aar包。商戶APP demo
- 商戶server .商戶server demo 此server是在商戶自己開發整套流程的參考demo ,如果只是跑通商戶App demo 則不需要此server。
4.刷臉支付流程
名詞解釋
人臉授權 :通過人臉識別,返回微信用戶信息(openid, face_code)。
face_code:人臉憑證。常用於人臉支付,作爲訂單的支付憑證。
時序圖
注:
- 初始化 initWxpayface, 只需要在程序啓動時調用;
- 釋放資源 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);
}
這裏需要注意的很多 :
- now 參數 注意是 10位的 時間戳 。並且拼接字符串a 時用的now 和 最後請求Body中xml內容中的Now的值一定要相同,兩個地方切勿直接用 System.currentTimeMillis()/1000L
- 獲取sign值 拼接的字符串a 一定要按照參數名ASCII碼從小到大排序。sign值可以用簽名校驗工具驗證
- 請求接口 https://payapp.weixin.qq.com/face/get_wxpayface_authinfo
- 返回值不是標準的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();
}
}
});
}
請求成功後一定要 調用更新支付結果的方法(不然一直卡在 支付等待界面),調用後會根據結果提示支付成功或者失敗。 至此整個支付流程就完事了。
最後可以去商戶平臺 查看訂單信息: