本文講解了 android開發的原生態app集成了支付寶支付, 還提供了java后台服務器處理支付寶支付的加密代碼, app前端與java后台服務器使用json數據格式交互信息,java后台服務主要用來對支付數據進行加密和接受支付寶服務器的回調

注意: 本文即涉及到 android前端, 也涉及到 Java后台
准備條件:
1. 支付流程概述
使用過app支付的用戶都知道,在支付環節,如果選擇【支付寶支付】,我們的app會拉起支付寶app,並且將支付信息傳入到
支付寶app中顯示出來, 用戶確認付款完成付款操作, 有朋友就會問,那我們的應用邏輯應該放在哪個步驟呢?
步驟1: 應用程序拉起支付寶app前,會按照支付寶的規則傳遞相應的數據給支付寶,所以啟動支付寶前,我們需要構建數據, 基本數據包括商品描述,商品價錢等,但為了讓支付寶區別是哪個應用程序傳過來的數據,還需要AppId(應用程序在支付寶平台上對應的序號); 另外為了保障數據不被篡改,我們傳遞的數據需要進行RSA加密
步驟2: 對數據的加密工作是放在后台進行的,那為什么不放在前台呢,熟悉RSA加密的朋友都知道,RSA是非對稱加密,加密方與解密方 需要一對密鑰(公鑰和私鑰), 這些公鑰與私鑰是敏感信息,放在android端容易被盜取,所以將公鑰與私鑰放在后台,后台對數據加密完成后再傳給前台
步驟3: 前台將加密完成后的數據傳送給支付寶,由支付寶完成支付
步驟4: 支付完成后app端會得到相應的事件通知,前台根據相應的事件來判斷前台的業務走向
步驟5: 支付寶官方要求,使用支付寶完成支付后,支付寶后台 會調用 應用服務的后台用來告之交易支付是否真正成功
由於本地服務無法向外界提供服務, 所以支付寶回調本地測試的服務器接口無法連通,至於連通的方法,可以看一下
ngrok的配置, 這個工具可以穿透內網
2. Android端
項目運行后如下圖

2.1 下載導入項目到android studio中,將build.gradle中的storeFile的路徑設置正確

2.2 android端的通信使用 retrofit rxjava技術,所以訪問后台的請求url需要配置正確, 如果使用本文提供的后台,則可以不必修改,如果使用別人的后台服務,需要進行更改


3.3 android端訪問后台獲取加密數據,然后將加密數據傳遞給支付寶
注意:由於使用的支付寶的沙箱配置信息 ,必須加上 代碼 EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
如果使用正式環境的支付寶配置信息, 不能加上上面沙箱代碼
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
private
void
aliPay(){
BizContent bizContent =
new
BizContent();
bizContent.setOut_trade_no(outTradeNum);
bizContent.setTotal_amount(totalPrice);
bizContent.setSubject(
"某個項目"
+
"-支付訂單"
);
String strBizContent =
new
Gson().toJson(bizContent);
//generateOrderInfo去后台獲取加密的信息
payService
.generateOrderInfo(strBizContent)
.subscribe(
new
Action1<String>() {
@Override
public
void
call(String s) {
//orderInfo為加密的信息
final
String orderInfo = s;
Runnable payRunnable =
new
Runnable() {
@Override
public
void
run() {
//沙箱環境開啟沙箱功能
//正式環境不需要下面這行代碼
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
PayTask alipay =
new
PayTask(MainActivity.
this
);
Map<String, String> result = alipay.payV2(orderInfo,
true
);
Log.i(
"msp"
, result.toString());
Message msg =
new
Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
Thread payThread =
new
Thread(payRunnable);
payThread.start();
}
},
new
Action1<Throwable>() {
@Override
public
void
call(Throwable throwable) {
showToast(throwable.getMessage());
}
});
}
|
3.4 支付寶支付完成后執行 handleMessage
在handleMessage判斷支付情況, 如果 payResult.getResultStatus() == "9000" 則支付成功, 否則失敗
具體的失敗信息可以查看支付寶的官方文檔
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressWarnings("unused")
public void handleMessage(Message msg) {
switch (msg.what) {
case SDK_PAY_FLAG: {
@SuppressWarnings("unchecked")
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
/**
對於支付結果,請商戶依賴服務端的異步通知結果。同步通知結果,僅作為支付結束的通知。
*/
String resultInfo = payResult.getResult();// 同步返回需要驗證的信息
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為9000則代表支付成功
if (TextUtils.equals(resultStatus, "9000")) {
outTradeNum = "";
showToast("支付成功");
} else {
outTradeNum = "";
// 該筆訂單真實的支付結果,需要依賴服務端的異步通知。
Toast.makeText(MainActivity.this, "支付失敗", Toast.LENGTH_SHORT).show();
}
break;
}
case SDK_AUTH_FLAG: {
@SuppressWarnings("unchecked")
AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true);
String resultStatus = authResult.getResultStatus();
// 判斷resultStatus 為“9000”且result_code
// 為“200”則代表授權成功,具體狀態碼代表含義可參考授權接口文檔
if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) {
// 獲取alipay_open_id,調支付時作為參數extern_token 的value
// 傳入,則支付賬戶為該授權賬戶
Toast.makeText(MainActivity.this,
"授權成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT)
.show();
} else {
// 其他狀態值則為授權失敗
Toast.makeText(MainActivity.this,
"授權失敗" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show();
}
break;
}
default:
break;
}
};
};
3. Java后台
3.1 修改支付寶配置信息, 實例中使用的是本人支付寶中申請的沙箱配置信息
請將Config.properties中的信息改成自己的

3.2 generateOrderInfo對android傳遞的支付信息進行加密, 加密信息回傳給android端
@RequestMapping("/generateOrderInfo.htm")
@ResponseBody
public Result generateOrderInfo(String bizContent)
{
Result result = new Result();
AlipaySignature sing= new AlipaySignature();
if(StringUtils.isEmpty(bizContent)){
result.setData("");
result.setStateCode(Constant.RESULT_FAILURE);
result.setDesc("待加簽字符串不能為空");
}else
{
String appId = ConfigManager.getInstance().getConfigItem("APPID");
Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(appId, bizContent, true);
String orderParam = OrderInfoUtil2_0.buildOrderParam(params);
String privateKey = ConfigManager.getInstance().getConfigItem("ProjectPrivateKey");
String sign = OrderInfoUtil2_0.getSign(params, privateKey, true);
final String orderInfo = orderParam + "&" + sign;
result.setDesc("加簽成功");
result.setStateCode(Constant.RESULT_SUCCESS);
result.setData(orderInfo);
}
return result;
}
3.3 支付成功后,支付寶服務端回調后台應用程序接口,用來告之支付是否成功
/**
* 支付寶的回調接口
* @param request
*/
@RequestMapping("/notifyOrderInfo.htm")
public void notifyOrderInfo(HttpServletRequest request){
if ("TRADE_SUCCESS".equals(request.getParameter("trade_status"))) {
Enumeration<?> pNames = request.getParameterNames();
Map<String, String> param = new HashMap<String, String>();
try {
while (pNames.hasMoreElements()) {
String pName = (String) pNames.nextElement();
param.put(pName, request.getParameter(pName));
}
boolean signVerified = AlipaySignature.rsaCheckV1(param, ConfigManager.getInstance().getConfigItem("AlipayPublicKey"),
AlipayConstants.CHARSET_UTF8); // 校驗簽名是否正確
if (signVerified) {
// TODO 驗簽成功后
// 按照支付結果異步通知中的描述,對支付結果中的業務內容進行1\2\3\4二次校驗,校驗成功后在response中返回success,校驗失敗返回failure
System.out.println("訂單支付成功:" + JSON.toJSONString(param));
} else {
// TODO 驗簽失敗則記錄異常日志,並在response中返回failure.
}
} catch (Exception e) {
e.printStackTrace();
}
}
return;
}
關於支付寶的其它接口的更加詳細信息,請參照 【服務端詳解】
DEMO下載:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=844

