手机的在线支付,被认为是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。
签约的时候还要向需要提供实名认证和上传应用,所以我建议先把应用做好了,最后再集成支付宝。


我大概等了1-2天审核,审核是失败的,回复是应用类型啥的应该是"虚拟货币",我改成那个马上自动就审核通过了。

3.密钥配置
解压openssl-0.9.8k_WIN32(RSA密钥生成工具).zip,打开cmd,命令行进入openssl-0.9.8k_WIN32(RSA密钥生成工具)\bin目录下,
(1).执行

openssl genrsa  -out rsa_private_key.pem 1024

生成rsa_private_key.pem文件。
(2).再执行

openssl rsa  -in rsa_private_key.pem  -pubout -out rsa_public_key.pem

生成rsa_public_key.pem 文件。
(3).在执行

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,因为这一段之前没有截图,现在弄好了又不好截图,如果有不明白的地方请大家参考官方文档。

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代码如下:

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的核心代码:

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=" + "\""
+ "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方法就是支付的方法,最简单的不设置的话,调用方法如下:

AlixPay alixPay = new AlixPay(SettingTabActivity.this);
alixPay.pay();

如果没有安装支付宝,它会提示你安装,如果已经安装,它直接让你选择付款:

这说明已经配置成功了。
然后可以删掉那些示例java文件了: AlixDemo.java,ProductListAdapter.java,Products.java。 
你也可以通过调整参数来修改订单信息,如主题,价格等。
另外在BaseHelper的94行:

dialog.setOnCancelListener( new AlixDemo.AlixOnCancelListener( (Activity)context ) );

需要修改为:

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);

需要修改为:

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.小结
支付宝的集成比我想象的要复杂一些,比较麻烦,首先需要审核,然后代码需要提取,所以写出来与大家分享。 
在做集成配置的时候,一定要仔细认真,一个地方出错,可能要导致后面查错查很长时间。
因为本人是先集成成功后才写的这篇文章,难免会漏掉一些重要的细节或者步骤,如有不对,请留言指正。

Android集成支付宝接口 实现在线支付的更多相关文章

  1. android开发支付宝接口开发流程(密钥篇)

    参考博客:http://blog.it985.com/12276.html 官方下载地址:http://download.alipay.com/public/api/base/WS_MOBILE_PA ...

  2. wemall app商城源码Android之支付宝接口公用函数

    wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之  ...

  3. Android应用集成支付宝接口的简化

    拿到支付宝接口的andriod demo后有点无语,集成一个支付服务而已,要在十几个java类之间引用来引用去,这样不仅容易导致应用本身代码结构的复杂化,调试起来也很累,于是操刀改造之: 该删的删,该 ...

  4. Android 集成支付宝支付详解

    一说到支付宝,相信没有人不知道,生活中付款,转账都会用到. 今天来详细介绍下在Android中如何集成支付宝支付到自己的APP中去.让APP能够拥有方便,快捷的支付功能. 准备工作: 商户在b.ali ...

  5. android 集成支付宝app支付(原生态)-包括android前端与java后台

    本文讲解了 android开发的原生态app集成了支付宝支付, 还提供了java后台服务器处理支付宝支付的加密代码, app前端与java后台服务器使用json数据格式交互信息,java后台服务主要用 ...

  6. Android集成支付宝的坑

    Android在集成支付宝sdk的时候, 如果有安装支付宝,则启动支付宝app进行支付: 如果没有安装,则启动 H5PayActivity 进行支付. 记得在AndroidManifest里面配置: ...

  7. wemall app商城系统Android之支付宝接口RSA函数

    wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享支付宝接口RSA函数,RSA签名.验签.解密等 ...

  8. Android 集成支付宝第三方登录

    前言: 在集成支付宝支付的时候遇到一点小麻烦,先在此记录供大家参考 1.授权 支付宝第三方登录需要在后台进行授权,在查看授权的时候我们一定要看清楚时候真的已经获得了权限(我在没有获取权限的情况下集成的 ...

  9. Node.js集成支付宝接口注意事项

    目录 签名 发送请求表单 验签 总结 签名 使用node.js自带的加密模块crypto和字符编码模块iconv-lite 根据支付宝接口文档参数格式得到签名之前的字符串beforeSignStr,然 ...

随机推荐

  1. bundle install rake-10.4.2

    这个是由于被墙了的原因,提供一个不用FQ解决的方法 淘宝做了一个gem镜像,地址是http://ruby.taobao.org/  为什么有这个? 由于国内网络原因(你懂的),导致 rubygems. ...

  2. Java WEB —— Java提升

    Java 5.0新特性: 枚举: enum Grade{ --> 相当于类 A("80~100"),B("70~80"),C("60~70&qu ...

  3. 桶排序-C-结构体排序

    struct TS { int index; ]; }; ] = {{,,,,,"s8"}}; ]; int i; int length = sizeof(a) / sizeof ...

  4. Fedora 14 x64 试用手记

    欢迎大家给我投票: http://2010blog.51cto.com/350944 刊登在: http://os.51cto.com/art/201011/235506.htm FC14桌面使用体验 ...

  5. js冲突

    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(//"~/Scripts/modernizr-2.6.2 ...

  6. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  7. HDU 2042 不容易系列之二 [补6.24] 分类: ACM 2015-06-26 20:40 9人阅读 评论(0) 收藏

    不容易系列之二 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  8. C#应用Newtonsoft.Json操作json[2]-反序列化不定类型

    在读json时,有时不知道对方的数据类型是什么样的,本文用Newtonsoft,把json反序列化为List>,在某种情况下还是有用的. private static List<Dicti ...

  9. LightOJ 1236 Pairs Forming LCM (LCM 唯一分解定理 + 素数筛选)

    http://lightoj.com/volume_showproblem.php?problem=1236 Pairs Forming LCM Time Limit:2000MS     Memor ...

  10. NSString 截取字符串

    NSString字符串常用方法2010-09-06 14:18/******************************************************************** ...