http://blog.csdn.net/wenzhibinbin_pt/article/details/7933078
手机的在线支付,被认为是2012年最看好的功能,我个人认为这也是移动互联网较传统互联网将会大放光彩的一个功能。
人人有手机,人人携带手机,花钱买东西,不再需要取钱付现,不再需要回家上网银,想买什么,扫描一下,或者搜索一下,然后下单,不找零,直接送到你家,这将是手机支付给我们带来的全新交易体验。
谷歌刚推出了谷歌钱包,这必是我们后面要使用的主要手段,但是鉴于当前国情,我觉得有必要介绍一下android手机集成支付宝功能。
1.下载官方架包和说明文档
其实官方已经提供了安装指南,下载地址:
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。
签约的时候还要向需要提供实名认证和上传应用,所以我建议先把应用做好了,最后再集成支付宝。
3.密钥配置
解压openssl-0.9.8k_WIN32(RSA密钥生成工具).zip,打开cmd,命令行进入openssl-0.9.8k_WIN32(RSA密钥生成工具)\bin目录下,
(1).执行
生成rsa_private_key.pem文件。
(2).再执行
生成rsa_public_key.pem 文件。
(3).在执行
将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,因为这一段之前没有截图,现在弄好了又不好截图,如果有不明白的地方请大家参考官方文档。
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 Activity mActivity; public AlixPay(Activity activity) { mActivity = activity; } 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( mActivity, "提示" , 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(mActivity, "提示" , 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(mActivity); boolean isMobile_spExist = mspHelper.detectMobile_sp(); if (!isMobile_spExist) return ; if (!checkInfo()) { BaseHelper.showDialog(mActivity, "提示" , "缺少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, mActivity); 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方法就是支付的方法,最简单的不设置的话,调用方法如下:
如果没有安装支付宝,它会提示你安装,如果已经安装,它直接让你选择付款:
这说明已经配置成功了。
然后可以删掉那些示例java文件了: AlixDemo.java,ProductListAdapter.java,Products.java。
你也可以通过调整参数来修改订单信息,如主题,价格等。
另外在BaseHelper的94行:
需要修改为:
7.注意
我在测试的时候,调用的activity是框在一个ActivityGroup里的(与tabhost类似,据说tabhost也有这个问题),导致MobileSecurePayer.java的pay方法中调用服务的两行代码:
需要修改为:
不然会报错java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.android.server.am.ActivityRecord$Token...
8.小结
支付宝的集成比我想象的要复杂一些,比较麻烦,首先需要审核,然后代码需要提取,所以写出来与大家分享。
在做集成配置的时候,一定要仔细认真,一个地方出错,可能要导致后面查错查很长时间。
因为本人是先集成成功后才写的这篇文章,难免会漏掉一些重要的细节或者步骤,如有不对,请留言指正。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
大家好,今天这讲,我们来学习下如何在Andrdoid平台上集成支付宝的支付接口。
现在跑步进入了移动互联网时代,移动支付必然是一个趋势,虽然目前还没普及,但是,时间问题,我相信,到了年末,甚至就会越来越多的人用手机支付。我同学有一个网店,我也打算用他的网店,利用淘宝无线的开源API,加上支付宝的无线支付,去开发一个小软件,当然只在计划中,最后会不会做成,技术不是问题,问题是因为校园招聘已经开始了,我马上要准备找工作了,压力比较大,做什么都没有激情了,定下工作的事是目前最大的事情。
好了,闲话少说,大家跟着我走,我只给大家列一个大体的框架,具体怎么做,其实文档说的很明白了,不过作为新手,想要尽快集成,还是挺麻烦的一个事。
第一步:你首先要有淘宝网和支付宝的账号,并且在支付宝是实名认证的用户,这个条件是必须的,为了安全。没完成的朋友,先去完成这步。
第二步:https://mobiless.alipay.com/index.htm
这个网址是支付宝无线平台的官网,不好找,所以贴出来给大家。
登陆之后,在我的产品那可以签约,因为我已经签约完毕,并且好像每个账号只能签约一种商品,所以我没法给大家截图,一点一点看。大家根据页面的提示和文字,很容易就可以明白的,我不多说。
这是我签约后的页面:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
为自己把过程要点给备忘一下。
## 准备工作
1. 要有一个支付宝账号
2. 去http://ms.alipay.com 登录和自助签约 “无线部-自助签约-手机安全支付”;
3. 完成后登录 ms.alipay.com 在“我的产品页” 会见到 账户信息像以下内容
账户名:abc@xxx.com
账户ID:208800*******993
合作商户ID:2088*******993
4. 这个时候先不管密钥管理,先去下载SDK包和文档,
下载地址 https://mobiless.alipay.com/product/product_down_load.htm?product_id=1
解压出以下东东备用
5. 按文档说明生成商家私钥、公钥对,再将私钥转换为 PKCS8 格式,备用~
6. 回到 ms.alipay.com 进入账号信息下的密钥管理,上传刚生成的商家公钥,获得 AliPay的对应公钥,备用~
(注意不要混淆 商家pub_key 和Ali_pub_key)
## 准备工作 part.2
呃,还得准备准备,刚才说了DEMO里面有很多好东西,可以直接拿来就用,省下不少爬文档和代码的时间省力又省心..有用的是
全部拿出来扔到我们自己的项目包内,然后逐一来看看
AlixId.java ——这个不需要动,只是一些静态的全局变量;
Base64.java ——编码算法,不动;
Rsa.java ——同上,不动;
BaseHelper.java ——一些调用 ProgressDialog AlertDialog 还有格式转换的现成的方法,不动;
MobileSecurePayer.java ——不用改动,里面的 pay((String, Handler, int, Activity) ,支付用它;
MobileSecurePayHelper.java ——这里要注意,捆绑安装支付控件的话,要把mobile_sp.apk 放到项目 assets 路径下,文件名可以改别的随便,但要注意与源码中52行 cachePath 文件名一致,还有 retrieveApkFromAssets()中的第二个文件名参数一致。若还打算提供在线下载安装控件的话还要更改 apkUrl ,url根据实际情况修改。其余的不用动,是一些检验、安装安全控件的方法;
NetworkManager.java ——不用动,上面如果启用了在线下载安装控件,就要用到这个包实现下载。
ResultChecker.java ——支付结果的解释和校验用,留意 Rsa.doCheck() 的第三个参数,这里要用的是 Ali_pub_key;
## 开工集成
public class Pay extends Activity {
Spinner spPrice; //下拉菜单,有几个不同金额的支付项目选择
Button btAlipayl; //支付按钮
ProgressDialog pd; //提示Loading信息用
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay);
//绑定实例
spPrice = (Spinner) findViewById(R.id.sp_pay);
btAlipay = (Button) findViewById(R.id.bt_pay_alipay);
pd = new ProgressDialog(this);
pd.setCancelable(false);
//点击支付按钮
btAlipay.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// check to see if the MobileSecurePay is already installed. 没有支付安全控件的话提示安装
MobileSecurePayHelper mspHelper = new MobileSecurePayHelper(Pay.this);
boolean isMobile_spExist = mspHelper.detectMobile_sp();
if( !isMobile_spExist )
return;
// start pay for this order.
try
{
// prepare the order info.
String orderInfo = getOrderInfo(spPrice.getSelectedItemPosition()); //组织要发去支付接口的数据,这里传递了一个下拉菜单的选中序号,我在Array.xml仲分别定义了下拉菜单各项对应的商品名称、详情、价格数组
String strsign = Rsa.sign(orderInfo,getString(R.string.privateKey_Rsa)); //rsa算法,文档说支持md5,我用出错不清楚原因,换回来了
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, Pay.this);
if( bRet )
{// 尝试收起ProgressDialog
try
{ if( pd != null ) pd.dismiss(); }
catch(Exception e)
{e.printStackTrace(); }
//然后投放支付中的提示
pd.setMessage("正在支付");
pd.show();
}
else ;
}
catch (Exception ex)
{showMsg(getString(R.string.remote_call_failed), false); }
} });
}
//Alipay 的函数族
// get the selected order info for pay. 组织交易数据
String getOrderInfo(int arg2)
{
//以下内容在string.xml 或Array.xml中定义,参考SDK文档
String strOrderInfo = "partner=" + "\"" + getString(R.string.partner) + "\"";
strOrderInfo += "&";
strOrderInfo += "seller=" + "\"" + getString(R.string.seller) + "\"";
strOrderInfo += "&";
strOrderInfo += "out_trade_no=" + "\"" + getOutTradeNo() + "\"";
strOrderInfo += "&";
strOrderInfo += "subject=" + "\"" + getString(R.string.subject) + "\"";
strOrderInfo += "&";
strOrderInfo += "body=" + "\"" + getResources().getStringArray(R.array.pay_name)[arg2] + "\"";
strOrderInfo += "&";
strOrderInfo += "total_fee=" + "\"" + getResources().getStringArray(R.array.pay_ap)[arg2] + "\"";
strOrderInfo += "&";
strOrderInfo += "notify_url=" + "\"" + getString(R.string.notify_url) + "\"";
return strOrderInfo;
}
// get the out_trade_no for an order.根据文档生成一个不重复的商家交易号ID
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. 定义签名的算法,这里用了RSA
String getSignType()
{
String getSignType = "sign_type=" + "\"" + "RSA" + "\"";
return getSignType;
}
// the handler use to receive the pay result. 接收响应支付结果的 Handler
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg)
{
try
{
String strRet = (String)msg.obj; //这个是交易结果返回的信息由几部分组成,如: resultStatus={};memo={};result={}
switch (msg.what)
{
case AlixId.RQF_PAY:
{
//如果支付中提示仍在,收起
try
{if( pd != null ) pd.dismiss();}
catch(Exception e)
{e.printStackTrace(); }
//把返回的结果拆包..解析成各部分信息,包括交易结果的状态码、错误信息等
try
{
String memo = "memo=";
int imemoStart = strRet.indexOf("memo=");
imemoStart += memo.length();
int imemoEnd = strRet.indexOf(";result=");
memo = strRet.substring(imemoStart, imemoEnd);
BaseHelper.showDialog(PayForm.this, "提示", memo, R.drawable.icon);
// handle result
ResultChecker resultChecker = new ResultChecker(strRet);
boolean isPayOk = resultChecker.isPayOk(); //这种是严谨的验证支付结果是否成功的方法,不严谨的可以只检测状态码是否9000详情参考文档说明
if(isPayOk){//支付成功了,以下该干啥干啥...
... ...
}else ; //支付不成功的处理...
}
catch(Exception e)
{ e.printStackTrace(); }
}
break;
}
super.handleMessage(msg);
}
catch(Exception e)
{e.printStackTrace();}
} };
}
联系客服