Android学习系列(28)--App集成支付宝[已过期]

手机的在线支付,被认为是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).执行


1

openssl genrsa  -out rsa_private_key.pem 1024

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


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

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=" "\""

                "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(015);

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

时间: 2024-10-11 07:48:00

Android学习系列(28)--App集成支付宝[已过期]的相关文章

Android学习系列(17)--App列表之圆角ListView(续)

http://www.cnblogs.com/qianxudetianxia/archive/2011/09/19/2068760.html 本来这篇文章想并到上篇Android学习系列(16)--App列表之圆角ListView中的,但是若是如此就让大家错过一篇新的好的文章,着实可惜.上篇中我们使用shape,corners,gradient实现了一个渐变的圆角效果,但是在完文之后的实践中,我发现有时效果不甚满意,选中和放手的事件监听没有去正确的判断,然后渐变效果也比较单一,性能也觉得不是很快

Android学习系列(7)--App轮询服务器消息

这篇文章是android开发人员的必备知识. 1.轮询服务器     一般的应用,定时通知消息可以采用轮询的方法从服务器拿取消息,当然实时消息通知的话,建议采用推送服务.    其中需要注意轮询的频率设置,要在需求和性能中平衡. 2.独立进程     无论程序是否正在运行,我们都要能通知到客户,我们需要一个独立进程的后台服务.     我们需要一个独立进程的后台服务.     在AndroidManifest.xml中注册Service时,有一个android:process属性,如果这个属性以

Android学习系列(15)--App列表之游标ListView(索引ListView)

游标ListView,提供索引标签,使用户能够快速定位列表项.      也可以叫索引ListView,有的人称也为Tweaked ListView,可能更形象些吧.      一看图啥都懂了: 1.游标(Fast scroll thumb)      就是右边的那个拖动的方块,这个非常的简单: 1 2 3 4 5 <ListView     android:id="@+id/tweaked_list"     android:layout_width="fill_pa

Android学习系列(23)--App主界面实现

在上篇文章<Android学习系列(22)--App主界面比较>中我们浅略的分析了几个主界面布局,选了一个最大众化的经典布局.今天我们就这个经典布局,用代码具体的实现它. 1.预览图先看下最终的界面设计图:    上面顶部是一个9patch背景图片+标题文字:下面底部是5个tab标签,表示应用的5大模块.中间内容部分则是各个模块的具体内容,可以再分类,或者直接显示内容. 2.准备素材按照上篇文章的界面,我们需要事先提供两大方面的素材:顶部+底部.顶部的素材非常简单,最重要的是背景(9patch

[转] Android学习系列(29)--App调试的几个命令实践

在Android的应用开发中,我们会用到各种代码调试:其实在Android的开发之后,我们可能会碰到一些随机的问题,如cpu过高,内存泄露等,我们无法简单的进行代码调试,我们需要一个系统日志等等,下面我把握工作中碰到的几个常用命令和方法给大家演示实践一下. 1.logcat命令这个命令最简单常用,可查看帮助,我不多说,如果需要打印时间,加参数-v time 1 adb logcat -v time 2.bugreport命令这个命令也非常简单,但是在实际应用中非常有用,会有从开机之后详细的dum

Android学习系列(20)--App数据格式之解析Json

JSON数据格式,在Android中被广泛运用于客户端和网络(或者说服务器)通信,非常有必要系统的了解学习.     恰逢本人最近对json做了一个简单的学习,特此总结一下,以飨各位.     为了文章简明清晰,尽量多列点,少废话.     参考文档:http://www.ietf.org/rfc/rfc4627.txt?number=4627 1.JSON解析     (1).解析Object之一: 1 {"url":"http://www.cnblogs.com/qian

Android学习系列--App工程结构搭建

本文算是一篇漫谈,谈一谈关于android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角度,看到整齐的代码,优美的分层总是一种舒服的享受的.      从艺术的角度看,其实我们是在追求一种美. 本文先分析几个当今比较流行的android软件包,最后我们汲取其中觉得优秀的部分,搭建我们自己的通用android工程模板.      1. 微盘      2. 久忆日记      3.网易新

Android App集成支付宝

原地址:http://blog.csdn.net/wenbingoon/article/details/7933078 手机的在线支付,被认为是2012年最看好的功能,我个人认为这也是移动互联网较传统互联网将会大放光彩的一个功能.人人有手机,人人携带手机,花钱买东西,不再需要取钱付现,不再需要回家上网银,想买什么,扫描一下,或者搜索一下,然后下单,不找零,直接送到你家,这将是手机支付给我们带来的全新交易体验.谷歌刚推出了谷歌钱包,这必是我们后面要使用的主要手段,但是鉴于当前国情,我觉得有必要介绍

【转】Android学习系列(1)--为App签名(为apk签名)

原文网址:http://www.cnblogs.com/qianxudetianxia/archive/2011/04/09/2010468.html Android学习系列(1)--为App签名(为apk签名) 写博客是一种快乐,前提是你有所写,与人分享,是另一种快乐,前提是你有舞台展示,博客园就是这样的舞台.这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用. 1.签名的意义 为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Pack