手機的在線支付,被認爲是2012年最看好的功能,我我的認爲這也是移動互聯網較傳統互聯網將會大放光彩的一個功能。
人人有手機,人人攜帶手機,花錢買東西,再也不須要取錢付現,再也不須要回家上網銀,想買什麼,掃描一下,或者搜索一下,而後下單,不找零,直接送到你家,這將是手機支付給咱們帶來的全新交易體驗。
谷歌剛推出了谷歌錢包,這必是咱們後面要使用的主要手段,可是鑑於當前國情,我以爲有必要介紹一下android手機集成支付寶功能。 java
1.下載官方架包和說明文檔
其實官方已經提供了安裝指南,下載地址:android
https://b.alipay.com/order/productDetail.htm?productId=2012120700377310&tabId=4#ps-tabinfo-hashapi
https://mobiless.alipay.com/product/product_down_load.htm?code=SECURITY_PAY
裏面有有個pdf,詳細說明了說用指南,寫的比較詳細,能夠重點參考。安全
下載下來,咱們主要是用到Android(20120104)目錄下的alipay_plugin.jar和AppDemo/assets下的alipay_plugin223_0309.apk,這兩個文件是咱們不能修改的支付寶api和安裝包。服務器
2. 商戶簽約
如今的安全機制,都是這樣,客戶端須要先和服務端請求驗證後才能進行進一步操做,oauth也是如此。
打開https://ms.alipay.com/,登錄支付寶,點擊簽約入口,選擇"應用類產品",填寫並等待審覈,獲取商戶ID和帳戶ID。
簽約的時候還要向須要提供實名認證和上傳應用,因此我建議先把應用作好了,最後再集成支付寶。app
我大概等了1-2天審覈,審覈是失敗的,回覆是應用類型啥的應該是"虛擬貨幣",我改爲那個立刻自動就審覈經過了。less
3.密鑰配置
解壓openssl-0.9.8k_WIN32(RSA密鑰生成工具).zip,打開cmd,命令行進入openssl-0.9.8k_WIN32(RSA密鑰生成工具)\bin目錄下,
(1).執行dom
1
|
openssl genrsa -out rsa_private_key.pem 1024
|
生成rsa_private_key.pem文件。
(2).再執行jsp
1
|
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
|
生成rsa_public_key.pem 文件。
(3).在執行工具
1
|
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
|
將RSA私鑰轉換成 PKCS8 格式,去掉begin和end那兩行,把裏面的內容拷貝出來,保存到某個txt中,如rsa_private_pkcs8_key.txt中(我好像沒用到這個)。
打開rsa_public_key.pem,即商戶的公鑰,複製到一個新的TXT中,刪除文件頭」-----BEGIN PUBLIC KEY-----「與文件尾」-----END PUBLIC KEY-----「還有空格、換行,變成一行字符串並保存該 TXT 文件,而後在網站的「個人商家服務」切換卡下的右邊點擊「密鑰管理」,而後有個"上傳商戶公鑰(RSA)"項,選擇上傳剛纔的TXT文件.
好了,服務器配置OK,由於這一段以前沒有截圖,如今弄好了又很差截圖,若是有不明白的地方請你們參考官方文檔。
錯誤提示 failure calling remote service
緣由: 私鑰沒轉PKCS8
4.引用jar和包含安裝包
(1).新建android工程;
(2).copy上面說的alipay_plugin.jar到工程的libs目錄下,並在java build path中經過Add External JARs找到並引用該jar;
(3).copy上面說的alipay_plugin223_0309.apk安裝包到assets目錄下,後面配置路徑用到。
若是libs和assets目錄沒有,手動創建者兩個目錄。
5.調用代碼整理
這裏咱們要嚴重的參考文檔中AppDemo,咱們建一個包com.tianxia.lib.baseworld.alipay,把AppDemo的com.alipay.android.appDemo4包下的源碼所有copy到剛纔咱們本身的包下,還有res目錄下的資源文件也合併到咱們工程res下。
其中AlixDemo.java,ProductListAdapter.java,Products.java是示例類,咱們借鑑完後能夠刪除。
PartnerConfig.java是配置類,配置商戶的一些配置參數。
其餘的類是嚴重參考類,直接留下使用。
PartnerConfig.java代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
PartnerConfig {
//合做商戶ID。用簽約支付寶帳號登陸ms.alipay.com後,在帳戶信息頁面獲取。
public
static
final
String PARTNER =
"xxx"
;
//帳戶ID。用簽約支付寶帳號登陸ms.alipay.com後,在帳戶信息頁面獲取。
public
static
final
String SELLER =
"xxx"
;
//商戶(RSA)私鑰 ,即rsa_private_key.pem中去掉首行,最後一行,空格和換行最後拼成一行的字符串
public
static
final
String RSA_PRIVATE =
"xxx"
;
//支付寶(RSA)公鑰 用簽約支付寶帳號登陸ms.alipay.com後,在密鑰管理頁面獲取。
public
static
final
String RSA_ALIPAY_PUBLIC =
"xxx"
;
//下面的配置告訴應用去assets目錄下找安裝包
public
static
final
String ALIPAY_PLUGIN_NAME =
"alipay_plugin223_0309.apk"
;
}
|
AlixDemo中代碼是最終的調用代碼在onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {}中,下面咱們提取其中的核心代碼。
6.提取核心調用代碼
在AlixDemo.java同目錄下新建AlixPay.java,來提取AlixDemo.java的核心代碼:
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
package
com.tianxia.lib.baseworld.alipay;
import
java.net.URLEncoder;
import
java.text.SimpleDateFormat;
import
java.util.Date;
import
com.tianxia.lib.baseworld.R;
import
android.app.Activity;
import
android.app.ProgressDialog;
import
android.content.DialogInterface;
import
android.os.Handler;
import
android.os.Message;
import
android.view.KeyEvent;
import
android.widget.Toast;
public
class
AlixPay {
static
String TAG =
"AlixPay"
;
private Context
mContext;
public
AlixPay(Context mContext) {
mContext
= mContext;
}
private
ProgressDialog mProgress =
null
;
// the handler use to receive the pay result.
private
Handler mHandler =
new
Handler() {
public
void
handleMessage(Message msg) {
try
{
String strRet = (String) msg.obj;
switch
(msg.what) {
case
AlixId.RQF_PAY: {
closeProgress();
BaseHelper.log(TAG, strRet);
try
{
String memo =
"memo="
;
int
imemoStart = strRet.indexOf(
"memo="
);
imemoStart += memo.length();
int
imemoEnd = strRet.indexOf(
";result="
);
memo = strRet.substring(imemoStart, imemoEnd);
ResultChecker resultChecker =
new
ResultChecker(strRet);
int
retVal = resultChecker.checkSign();
if
(retVal == ResultChecker.RESULT_CHECK_SIGN_FAILED) {
BaseHelper.showDialog(
mContext
,
"提示"
,
mActivity.getResources().getString(
R.string.check_sign_failed),
android.R.drawable.ic_dialog_alert);
}
else
{
BaseHelper.showDialog(mActivity,
"提示"
, memo,
R.drawable.infoicon);
}
}
catch
(Exception e) {
e.printStackTrace();
BaseHelper.showDialog(mContext,
"提示"
, strRet,
R.drawable.infoicon);
}
}
break
;
}
super
.handleMessage(msg);
}
catch
(Exception e) {
e.printStackTrace();
}
}
};
// close the progress bar
void
closeProgress() {
try
{
if
(mProgress !=
null
) {
mProgress.dismiss();
mProgress =
null
;
}
}
catch
(Exception e) {
e.printStackTrace();
}
}
public
void
pay() {
MobileSecurePayHelper mspHelper =
new
MobileSecurePayHelper(mContext);
boolean
isMobile_spExist = mspHelper.detectMobile_sp();
if
(!isMobile_spExist)
return
;
if
(!checkInfo()) {
BaseHelper.showDialog(mContext,
"提示"
,
"缺乏partner或者seller,"
, R.drawable.infoicon);
return
;
}
try
{
// prepare the order info.
String orderInfo = getOrderInfo();
String signType = getSignType();
String strsign = sign(signType, orderInfo);
strsign = URLEncoder.encode(strsign);
String info = orderInfo +
"&sign="
+
"\""
+ strsign +
"\""
+
"&"
+ getSignType();
// start the pay.
MobileSecurePayer msp =
new
MobileSecurePayer();
boolean
bRet = msp.pay(info, mHandler, AlixId.RQF_PAY, mContext);
if
(bRet) {
// show the progress bar to indicate that we have started paying.
closeProgress();
mProgress = BaseHelper.showProgress(mActivity,
null
,
"正在支付"
,
false
,
true
);
}
else
;
}
catch
(Exception ex) {
Toast.makeText(mActivity, R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
private
boolean
checkInfo() {
String partner = PartnerConfig.PARTNER;
String seller = PartnerConfig.SELLER;
if
(partner ==
null
|| partner.length() <=
0
|| seller ==
null
|| seller.length() <=
0
)
return
false
;
return
true
;
}
// get the selected order info for pay.
String getOrderInfo() {
String strOrderInfo =
"partner="
+
"\""
+ PartnerConfig.PARTNER +
"\""
;
strOrderInfo +=
"&"
;
strOrderInfo +=
"seller="
+
"\""
+ PartnerConfig.SELLER +
"\""
;
strOrderInfo +=
"&"
;
strOrderInfo +=
"out_trade_no="
+
"\""
+ getOutTradeNo() +
"\""
;
strOrderInfo +=
"&"
;
//這筆交易價錢
strOrderInfo +=
"subject="
+
"\""
+ mActivity.getString(R.string.donate_subject) +
"\""
;
strOrderInfo +=
"&"
;
//這筆交易內容
strOrderInfo +=
"body="
+
"\""
+ mActivity.getString(R.string.donate_body) +
"\""
;
strOrderInfo +=
"&"
;
//這筆交易價錢
strOrderInfo +=
"total_fee="
+
"\""
+
"10.00"
+
"\""
;
strOrderInfo +=
"&"
;
strOrderInfo +=
"notify_url="
+
"\""
return
strOrderInfo;
}
// get the out_trade_no for an order.
String getOutTradeNo() {
SimpleDateFormat format =
new
SimpleDateFormat(
"MMddHHmmss"
);
Date date =
new
Date();
String strKey = format.format(date);
java.util.Random r =
new
java.util.Random();
strKey = strKey + r.nextInt();
strKey = strKey.substring(
0
,
15
);
return
strKey;
}
// get the sign type we use.
String getSignType() {
String getSignType =
"sign_type="
+
"\""
+
"RSA"
+
"\""
;
return
getSignType;
}
// sign the order info.
String sign(String signType, String content) {
return
Rsa.sign(content, PartnerConfig.RSA_PRIVATE);
}
// the OnCancelListener for lephone platform.
static
class
AlixOnCancelListener
implements
DialogInterface.OnCancelListener {
Activity mcontext;
AlixOnCancelListener(Activity context) {
mcontext = context;
}
public
void
onCancel(DialogInterface dialog) {
mcontext.onKeyDown(KeyEvent.KEYCODE_BACK,
null
);
}
}
}
|
這個類的pay方法就是支付的方法,最簡單的不設置的話,調用方法以下:
1
2
|
AlixPay alixPay =
new
AlixPay(SettingTabActivity.
this
);
alixPay.pay();
|
若是沒有安裝支付寶,它會提示你安裝,若是已經安裝,它直接讓你選擇付款:
這說明已經配置成功了。
而後能夠刪掉那些示例java文件了: AlixDemo.java,ProductListAdapter.java,Products.java。
你也能夠經過調整參數來修改訂單信息,如主題,價格等。
另外在BaseHelper的94行:
1
|
dialog.setOnCancelListener(
new
AlixDemo.AlixOnCancelListener( (Activity)context ) );
|
須要修改成:
1
|
dialog.setOnCancelListener(
new
AlixPay.AlixOnCancelListener( (Activity)context ) );
|
7.注意
若是不是下面這種狀況不須要改,否則你按下面操做會卡在正在運行中
我在測試的時候,調用的activity是框在一個ActivityGroup裏的(與tabhost相似,聽說tabhost也有這個問題),致使MobileSecurePayer.java的pay方法中調用服務的兩行代碼:
mActivity.bindService(
new
Intent(IAlixPay.
class
.getName()), mAlixPayConnection, Context.BIND_AUTO_CREATE);
mActivity.unbindService(mAlixPayConnection);
|
須要修改成:
1
2
|
mActivity.getApplicationContext().bindService(
new
Intent(IAlixPay.
class
.getName()), mAlixPayConnection, Context.BIND_AUTO_CREATE);
mActivity.getApplicationContext().unbindService(mAlixPayConnection);
|
否則會報錯java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.android.server.am.ActivityRecord$Token...
8.小結支付寶的集成比我想象的要複雜一些,比較麻煩,首先須要審覈,而後代碼須要提取,因此寫出來與你們分享。 在作集成配置的時候,必定要仔細認真,一個地方出錯,可能要致使後面查錯查很長時間。由於本人是先集成成功後才寫的這篇文章,不免會漏掉一些重要的細節或者步驟,若有不對,請留言指正。