Android就業面試技巧系列-技術篇(支付相關)
關於支付,相信很多同學都不陌生,而且這也是找工作面試官問的高頻問題,這一篇文章主要就是向大家詳細介紹一下支付的一些流程。
支付難不難?
> 不難,都是屬於第三方的東西.難度不大
支付安不安全?
> 肯定是安全的.因爲這些都是大公司的產品.都有自己的安全策略;
做一個支付需要多久?
項目評估時間和實際開發有出入的,一般是兩倍關係
比如你一天能搞完,那你至少評估2天
前期準備工作可能就是 一週左右
程序員實際集成時間
* 項目評估
* 支付寶:大概5分鐘.
* 銀聯支付:大概5分鐘.
* 微信支付:大概10分鐘.
支付流程_從生活出發
1. 選擇商品
2. 選擇支付方式
3. 處理支付結果
1. 成功
2. 失敗
3. 取消
支付流程_從app開發角度(保證可以先完成功能)
1. 拼接支付信息,post到服務器;-->request
1. 支付信息包含支付方式
2. 服務器:***是我們自己的服務器***
3. 支付協議:`http://mobileif.maizuo.com/version3/orderform/order?version=2`
2. 服務器返回`支付串碼`;--->reponse
請求的url(支付協議/確認訂單協議): http://mobileif.maizuo.com/version3/orderform/order?version=2
請求方式:post
post參數形式:jsonString
輸入參數:
> 不難,都是屬於第三方的東西.難度不大
支付安不安全?
> 肯定是安全的.因爲這些都是大公司的產品.都有自己的安全策略;
做一個支付需要多久?
項目評估時間和實際開發有出入的,一般是兩倍關係
比如你一天能搞完,那你至少評估2天
前期準備工作可能就是 一週左右
程序員實際集成時間
* 項目評估
* 支付寶:大概5分鐘.
* 銀聯支付:大概5分鐘.
* 微信支付:大概10分鐘.
支付流程_從生活出發
1. 選擇商品
2. 選擇支付方式
3. 處理支付結果
1. 成功
2. 失敗
3. 取消
支付流程_從app開發角度(保證可以先完成功能)
1. 拼接支付信息,post到服務器;-->request
1. 支付信息包含支付方式
2. 服務器:***是我們自己的服務器***
3. 支付協議:`http://mobileif.maizuo.com/version3/orderform/order?version=2`
2. 服務器返回`支付串碼`;--->reponse
請求的url(支付協議/確認訂單協議): http://mobileif.maizuo.com/version3/orderform/order?version=2
請求方式:post
post參數形式:jsonString
輸入參數:
[Java] 純文本查看 複製代碼
1
2
|
{ "goodInfos" :[{ "goodCounts" : "1" , "goodExtInfo" :{}, "goodIDs" : "361" , "goodType" : "1" }], "loginFlag" : "0" , "mobile" : "18682036558" , "orderId" : "0" , "otherInfo" :{ "agentID" : "0-maizuo" , "channelID" : "31" , "clientID" : "31" }, "payDatas" :{ "discountInfo" :{ "activeID" : "0" , "discountID" : "0" , "discountPrice" : "" }, "payInfo" :[{ "bankType" : "7" , "payCount" : "3200" , "payTicketCount" : "1" , "payType" : "1" }], "payPass" : "" , "returnUrl" : "" , "totalPrice" : "3200" }, "processPath" : "1" , "sessionKey" : "mqneaadqapkpkqshxvdj" , "userID" : "200394160" } 輸出結果{ "result" : "ok" , "payExtInfo" :{ "alipayVerifyKey" : "_input_charset=\"UTF-8\"&body=\"賣座網電子影票\"&it_b_pay=\"1h\"¬ify_url=\"http%3A%2F%2Fpay.maizuo.com%2FmobileBack.htm\"&out_trade_no=\"201507238712113008\"&partner=\"2088411628331920\"&payment_type=\"1\"&seller_id=\"2088411628331920\"&service=\"mobile.securitypay.pay\"&subject=\"深圳金逸影城沙井店(2D通兌票1張)\"&total_fee=\"32.00\"&sign=\"M0O0Ej5J13A25SAWupc5a6vAGmJnblx2CvuWF2dwFGxMZ%2BxlRWmp%2F6ZDfI8Y%2FFJbjiEqE99MAsKh%0AfIBQqP4Y1TyNkbY0XixQFPgAAqsqwGqYJSDtqUFWRgje%2B8pI1KuxfPE3UcDZs4hxDZoP%2Bdof%2Bldf%0AKQmximUyqT5Crtwj1Ag%3D\"&sign_type=\"RSA\"" }, "bankType" : "" , "userId" : "200394160" , "resource" :{ "rel" : "view" , "link" : "/orderform/200394160/201507238712113008" }, "payType" : "7" , "payUrl" : "" , "orderId" : "201507238712113008" , "uniqueKey" : "D5585vO8624915A11z" , "timeNow" : "1437622208552" } |
3. 拿着支付串碼,調用第三方服務,完成支付-->5分鐘 (調用支付寶服務)
下載文檔和 demo 來處理
4. 處理支付結果
1. 同步返回:支付後通知我們自己的apk
2. 異步通知:支付後通知我們的server
什麼是支付串碼
這個是自己定義的一個概念.其實就是第三步調用起第三方支付平臺`核心支付方法所需要的參數`
集成支付寶流程
1. 我們自己要和支付寶簽約(商戶簽約).-->運營去籤合同
2. 祕鑰配置-->協助運營完成祕鑰的配置(公鑰互換),可能程序員會參與
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
|
//
合作身份者ID,以2088開頭由16位純數字組成的字符串 public
static
final
String ALIPAY_PartnerID = "" ; //
商戶簽約支付寶賬號 public
static
final
String ALIPAY_SellerID = "" ; //
商戶的私鑰(MD5) public
static
final
String ALIPAY_MD5_KEY = "" ; //
商戶私鑰(RSA),用戶自動生成 public
static
final
String ALIPAY_PartnerPrivKey = "" ; //
支付寶公鑰,無需修改該值 demo裏面有的 public
static
final
String ALIPAY_PubKey = "" ; |
3. 集成支付寶-->必須是程序員去做.
1. 下載sdk/demo/文檔
2. demo嘗試的去運行
1. 出現了錯誤:因爲缺少運行必須的,DEFAULT_PARTNER,DEFAULT_SELLER,如果1,2步完成的話,我們在這個時候就可以有`DEFAULT_PARTNER,DEFAULT_SELLER`
3. 開始集成-->`參照移動快捷支付應用集成接入包支付接口接入與使用規則.pdf`
1. 添加jar,alipay.jar
2. 添加lib,alipay_lib
3. 添加了一個activity
4. 添加了一些權限
5. 調用支付的核心代碼
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
|
//
3.調用第三方服務,完成支付 //獲取Alipay對象,構造參數爲當前Activity和Handler實例對象 AliPay
alipay = new
AliPay(MainActivity. this ,
mHandler); //調用pay方法,將訂單信息傳入 //同步返回
取消操作 支付成功 支付失敗(網絡異常) String
payResult = alipay.pay(alipayVerifyKey); |
6. 處理支付結果--->支付寶處理支付結果用的handler機制
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
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
|
/**---------------demo裏面處理支付結果的代碼---------------**/ /*Result
result = new Result((String) msg.obj); switch
(msg.what) { case
RQF_PAY: case
RQF_LOGIN: { Toast.makeText(ExternalPartner.this,
result.getResult(), Toast.LENGTH_SHORT).show(); } break; default: break; } */ /**---------------老師實際開發處理支付結果的代碼---------------**/ /*Result.sResult
= (String) msg.obj; Logger.i(TAG,
"strRet:" + Result.sResult); try
{ String
errorMsg = Result.getPayResult(); Logger.i(TAG,
"errorMsg:" + errorMsg); if
(!"".equals(errorMsg)) { Toast.makeText(getApplicationContext(),
errorMsg, 0).show(); if
("操作成功".equals(errorMsg)) { payOk();//支付成功可以調到訂單界面 }
else { payFail();//可以彈出對話框進行重複支付 } } }
catch (Exception e) { e.printStackTrace(); if
(!PayActivity.this.isFinishing()) { BaseHelper.showDialog(PayActivity.this,
"提示", Result.sResult + ",如有疑問請聯繫賣座客服:4001808400", R.drawable.infoicon); } } } };*/ |
支付寶demo分析
* 查看demo發現他的`支付串碼`,是在客戶端生成的.這個和老師所講的.支付串碼由服務器生成.有出入;
* 爲了不暴露我們privatekey,我們應該把生成支付串碼的過程交給我們的服務器
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
/**---------------生成支付串碼的過程
begin---------------**/ Log.i( "ExternalPartner" ,
"onItemClick" ); String
info = getNewOrderInfo(position); //其實下面的操作應該是server端去完成.不然我們會暴露privatekey---begin---add_by_billy //所有我們會把info的具體內容用jsonString的形式.post給server進行簽名.然後返回簽名後的結果---add_by_billy String
sign = Rsa.sign(info, Keys.PRIVATE); sign
= URLEncoder.encode(sign); info
+= "&sign=\""
+ sign + "\"&"
+ getSignType(); Log.i( "ExternalPartner" ,
"start
pay" ); //
start the pay. Log.i(TAG,
"info
= "
+ info); //其實下面的操作應該是server端去完成.不然我們會暴露privatekey---end---add_by_billy final
String orderInfo = info; /**---------------生成支付串碼的過程
end---------------**/ |
支付串碼_自己定義的一個概念
> 支付串碼就是調用第三方應用的時候需要的一些核心的參數 按照第三方支付的規範碼
支付串碼在支付寶就相當於訂單信息
支付串碼在銀聯就相當於交易流水號
支付串碼在微信就相當於訂單信息
銀聯支付
* 銀聯支付,就`只需要一個交易流水號`就可以,而且看代碼比較簡單,重點是,`銀聯強制要求生產支付串碼的過程必須交給我們的服務端`
* 集成形式
* 內嵌apk形式:就是把一個apk放到我們的assets目錄下面-->老的方式
* 新版本so庫形式:在libs下面就有很多的so庫.已經不需要在assets目錄下面放置apk
* 模式:
* 測試模式:銀聯會給我們提供一個測試環境+提供了一個銀聯的賬號/密碼
* 正式模式:就必須使用真實的賬號/密碼
* 運行demo看效果:
* 集成步驟:
* 1.添加libs裏面相關的東西;
* 2.添加activity配置
[XML] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
|
< activity android:name = "com.unionpay.uppay.PayActivityEx" android:configChanges = "orientation|keyboardHidden|screenSize" android:excludeFromRecents = "true" android:label = "@string/app_name" android:screenOrientation = "portrait" android:windowSoftInputMode = "adjustResize"
/> < activity android:name = "com.unionpay.uppay.PayActivity" android:configChanges = "orientation|keyboardHidden|screenSize" android:excludeFromRecents = "true" android:screenOrientation = "portrait"
/> |
* 3.添加權限
[XML] 純文本查看 複製代碼
1
2
3
4
|
<!--
uupay --> < uses-permission
android:name = "android.permission.CHANGE_NETWORK_STATE"
/> < uses-permission
android:name = "android.permission.WRITE_EXTERNAL_STORAGE"
/> < uses-permission
android:name = "android.permission.READ_PHONE_STATE"
/> |
* 4.調用核心的支付方法
[Java] 純文本查看 複製代碼
1
2
3
4
5
6
7
8
|
//拿着支付串碼 System.out.println( "alipayVerifyKey:"
+ alipayVerifyKey); //
3.調用第三方服務,完成支付 //
“00” – 銀聯正式環境 //
“01” – 銀聯測試環境,該環境中不發生真實交易 //tn
交易流水號 UPPayAssistEx.startPayByJAR(MainActivity. this ,
PayActivity. class ,
null ,
null , alipayVerifyKey,
"00" ); |
* 5.處理支付結果
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
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
|
/**---------------銀聯處理支付結果---------------**/ protected
void
onActivityResult( int
requestCode, int
resultCode, Intent data) { /************************************************* *
*
步驟4:處理銀聯手機支付控件返回的支付結果 *
************************************************/ if
(data == null )
{ return ; } String
msg = "" ; /* *
支付控件返回字符串:success、fail、cancel 分別代表支付成功,支付失敗,支付取消 */ String
str = data.getExtras().getString( "pay_result" ); if
(str.equalsIgnoreCase( "success" ))
{ msg
= "支付成功!" ; }
else
if
(str.equalsIgnoreCase( "fail" ))
{ msg
= "支付失敗!" ; }
else
if
(str.equalsIgnoreCase( "cancel" ))
{ msg
= "用戶取消了支付" ; } AlertDialog.Builder
builder = new
AlertDialog.Builder( this ); builder.setTitle( "支付結果通知" ); builder.setMessage(msg); builder.setInverseBackgroundForced( true ); //
builder.setCustomTitle(); builder.setNegativeButton( "確定" ,
new
DialogInterface.OnClickListener() { @Override public
void
onClick(DialogInterface dialog, int
which) { dialog.dismiss(); } }); builder.create().show(); } |
微信支付
* 直接運行demo,發現最後提示錯誤,發現工程下面有一個debug.keystore
* 想要運行.可以如下操作
* 自定義keystore
* 或者直接導出的時候用工程下面的debug.keystore去簽名
window-----Android----Build----Custom debug keystore:微信debug.keystore的路徑
如果上面的apk用微信支付還報錯的話,那麼就需要把apk簽名(debug.keystore)打包生成的apk
先將該app註冊到微信,然後再調用支付界面支付就可以了
* 微信支付的安全策略之一:必須包名和keystore簽名需要一致 即利用包名和keystore(sha1值)生成安全碼
* demo裏面定義的步驟
* 一、獲取 access_token
* 二、生成預支付訂單,需要用到第一步的access_token,得到的是prepayId
* 三、調起微信支付
* 四、處理支付結果
* 這個時候一看.感覺微信支付很難.但是我們看到demo裏面這樣的一句話`注意:不能hardcode在客戶端,建議genPackage這個過程由服務器端完成`,所以,其實我們的微信支付,一、二步驟爲了安全起見應該交給Server
* 實際開發,只需關心3,4步就可以
* 三、調起微信支付
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/** *
@author Administrator *
支付的時候真正關心的數據 *
這個對象是我自己封裝的.和微信支付的sdk沒有關係 */ public
class
WXPayData { private
String sign_method; private
String timestamp; private
String noncestr; private
String partnerid; private
String app_signature; private
String prepayid; private
String package1; private
String appid; } //核心支付方法 private
void
sendPayReq(WXPayData info) { api
= WXAPIFactory.createWXAPI( this ,
info.getAppid()); PayReq
req = new
PayReq(); req.appId
= info.getAppid(); req.partnerId
= info.getPartnerid(); req.prepayId
= info.getPrepayid(); //預支付id req.nonceStr
= info.getNoncestr(); //32位內的隨機串,防重發 req.timeStamp
= String.valueOf(info.getTimestamp()); //時間戳,爲
1970 年 1 月 1 日 00:00 到請求發起時間的秒數 req.packageValue
= info.getPackage1(); req.sign
= info.getApp_signature(); //
在支付之前,如果應用沒有註冊到微信,應該先調用IWXMsg.registerApp將應用註冊到微信 api.sendReq(req); } |
* 四、處理支付結果:微信支付支付的回調是在`net.sourceforge.simcpux.wxapi.WXPayEntryActivity.class`裏面
[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
|
@Override public
void
onResp(BaseResp resp) { Log.d(TAG,
"onPayFinish,
errCode = "
+ resp.errCode); if
(resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { AlertDialog.Builder
builder = new
AlertDialog.Builder( this ); builder.setTitle(R.string.app_tip); builder.setMessage(getString(R.string.pay_result_callback_msg,
String.valueOf(resp.errCode))); builder.show(); } } |
安全碼策略
> 安全碼的組成規則爲:Android簽名證書的sha1值+“;”+packagename(即:數字簽名+分號+包名)
* 作用:確定apk的唯一性