App集成支付宝

手机的在线支付,被认为是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="  + "\""
                 + "http://notify.java.jpxx.org/index.jsp"  + "\"" ;
 
         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.小结支付宝的集成比我想象的要复杂一些,比较麻烦,首先须要审核,而后代码须要提取,因此写出来与你们分享。 在作集成配置的时候,必定要仔细认真,一个地方出错,可能要致使后面查错查很长时间。由于本人是先集成成功后才写的这篇文章,不免会漏掉一些重要的细节或者步骤,若有不对,请留言指正。

相关文章
相关标签/搜索