本篇主要講解APP接入支付寶支付完整流程,包含服務(wù)端,內(nèi)容稍長
要接入支付寶支付杯巨,需要將APP在支付寶平臺創(chuàng)建應(yīng)用,提交審核旦棉,并進(jìn)行商戶簽約以獲得支付能力
詳細(xì)參閱官方文檔https://docs.open.alipay.com/399/106917/
下面主要從技術(shù)角度去講解下流程
開發(fā)設(shè)置中一定要配置好APP相關(guān)信息齿风,支付寶網(wǎng)關(guān)地址是固定的,授權(quán)回調(diào)地址即是你成功支付后的回調(diào)地址绑洛,對于支付結(jié)果聂宾,請商戶依賴服務(wù)端的異步通知結(jié)果 ,同步通知結(jié)果诊笤,僅作為支付結(jié)束的通知系谐,支付寶網(wǎng)關(guān)地址如果不配置,會造成即使你的授權(quán)回調(diào)地址正確讨跟,支付服務(wù)端也收不到任何的支付通知的信息(之前沒注意這個問題纪他,郁悶了倆小時)
支付寶平臺為開發(fā)者們提供個很好的沙箱測試工具,即使應(yīng)用沒有提交上線晾匠,也可以在沙箱環(huán)境中模擬一個APP去接入支付茶袒,并配置相關(guān)參數(shù),如密鑰凉馆,應(yīng)用網(wǎng)關(guān)薪寓,回調(diào)地址等等,這樣既能很好的測試程序是否能夠正常支付澜共,同時減少提交上線審核后出現(xiàn)的一些問題
主要內(nèi)容:首先是客戶端SDK請求商戶服務(wù)端母谎,獲取簽名后的訂單信息,再去調(diào)用支付接口京革,發(fā)起支付請求(提示:如果是支付APP沙箱測試奇唤,需要在onCreate方法中加入EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX); )
先上圖再上代碼
try {
String url = "http://xxx.com/AliPay?user_id=" + account + "&totalFee=" + total_fee;
byte[] buf = Util.httpGet(url);
if (buf != null && buf.length > 0) {
orderInfo = new String(buf);
} else {
Log.d("PAY_GET", "服務(wù)器請求錯誤");
Toast.makeText(MainActivity.this, "服務(wù)器請求錯誤", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Log.e("PAY_GET", "異常:" + e.getMessage());
}
Runnable payRunnable = new Runnable() {
@Override
public void run() {
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();
}
完成支付后返回通知支付結(jié)果
private Handler mHandler = new Handler() {
@Override
@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);
/**
* 對于支付結(jié)果,請商戶依賴服務(wù)端的異步通知結(jié)果匹摇。同步通知結(jié)果咬扇,僅作為支付結(jié)束的通知。
*/
String resultInfo = payResult.getResult();// 同步返回需要驗證的信息
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為9000則代表支付成功
if (TextUtils.equals(resultStatus, "9000")) {
// 該筆訂單是否真實支付成功廊勃,需要依賴服務(wù)端的異步通知懈贺。
Toast.makeText(MainActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
UnityPlayer.UnitySendMessage("PayMar", "IpayCallback",Integer.toString(MainActivity.number));
} else {
// 該筆訂單真實的支付結(jié)果,需要依賴服務(wù)端的異步通知供搀。
Toast.makeText(MainActivity.this, "支付失敗", Toast.LENGTH_SHORT).show();
}
break;
}
default:
break;
}
};
至此隅居,APP端的主要內(nèi)容完畢
下面介紹商戶服務(wù)端钠至,首先要配置相關(guān)參數(shù)葛虐,注意公鑰和RSA2或者RSA要一一對應(yīng)
服務(wù)器端接口支付APP端發(fā)來的請求并返回訂單信息(先上圖,再上代碼)
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
? ? System.err.println(request.getParameter("totalFee"));
? ? String? totalFee = request.getParameter("totalFee");
// 實例化客戶端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID,
APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
// 實例化具體API對應(yīng)的request類,類名稱和接口名稱對應(yīng),當(dāng)前調(diào)用接口名稱:alipay.trade.app.pay
AlipayTradeAppPayRequest alipayrequest = new AlipayTradeAppPayRequest();
// SDK已經(jīng)封裝掉了公共參數(shù)棉钧,這里只需要傳入業(yè)務(wù)參數(shù)屿脐。以下方法為sdk的model入?yún)⒎绞?model和biz_content同時存在的情況下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody("商品描述");//商品描述
model.setSubject("商品描述");//商品描述
model.setOutTradeNo(getOutTradeNo());//訂單號
model.setTimeoutExpress("30m");//超時時間
model.setTotalAmount(totalFee);//金額
model.setProductCode("QUICK_MSECURITY_PAY");
alipayrequest.setBizModel(model);
alipayrequest.setNotifyUrl("http://xxx.com/AliPayNotify"); //異步通知地址
try {
// 這里和普通的接口調(diào)用不同,使用的是sdkExecute
AlipayTradeAppPayResponse alipayresponse = alipayClient.sdkExecute(alipayrequest);
System.out.println(alipayresponse.getBody());// 就是orderString
// 可以直接給客戶端請求的诵,無需再做處理万栅。
response.getWriter().append(alipayresponse.getBody()).append(request.getContextPath());
} catch (AlipayApiException e) {
e.printStackTrace();
}
}
/**
* 要求外部訂單號必須唯一。
*
* @return
*/
private static String getOutTradeNo() {
SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
Date date = new Date();
String key = format.format(date);
Random r = new Random();
key = key + r.nextInt();
key = key.substring(0, 15);
return key;
}
當(dāng)APP端支付成功后將會異步通知到服務(wù)端回調(diào)的接口
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 支付寶支付結(jié)束回調(diào)接口
String result = "failure";
// 獲取支付寶POST過來反饋信息
Map<String, String> params = new HashMap<String, String>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// 亂碼解決,這段代碼在出現(xiàn)亂碼時使用代赁。
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
// 切記alipaypublickey是支付寶的公鑰扰她,請去open.alipay.com對應(yīng)應(yīng)用下查看。
try {
boolean flag = AlipaySignature.rsaCheckV1(params, alipaypublicKey, charset, "RSA2");
if (flag) {
// TODO 驗簽成功后
System.out.println("=============================驗簽成功==============================");
result = "success";
response.getOutputStream().write(result.getBytes("utf-8"));// 指定編碼為utf-8
} else {
System.out.println("=============================驗簽失敗==============================");
response.getOutputStream().write(result.getBytes("utf-8"));// 指定編碼為utf-8
}
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
最后再次提醒各位開發(fā)者容易出錯的幾個點:
1.沙箱測試時通過芭碍,而在項目APP里卻不通過徒役,很有可能是APP沒有提交上線簽約的緣故,或者是密鑰和公鑰未更改(或RSA窖壕,RSA2加密與公鑰不對照的問題)忧勿,以及初始化時候沙箱環(huán)境未取消
2.服務(wù)器訂單驗簽時候服務(wù)端提示驗簽成功,但是官方提供的驗簽工具卻提示失敗瞻讽,這個我也不太清楚原因鸳吸,好多開發(fā)者朋友都反映過,建議大家以服務(wù)器端的驗簽結(jié)果為準(zhǔn)
3.APP設(shè)置詳情里支付寶的網(wǎng)關(guān)地址默認(rèn)是空白的速勇,這時候要改成https://openapi.alipay.com/gateway.do层释,否則會遇到即使客戶端支付成功了,服務(wù)端也收不到異步通知的情況