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(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.小結
支付寶的集成比我想象的要複雜一些,比較麻煩,首先需要審覈,然後代碼需要提取,所以寫出來與大家分享。 
在做集成配置的時候,一定要仔細認真,一個地方出錯,可能要導致後面查錯查很長時間。
因爲本人是先集成成功後才寫的這篇文章,難免會漏掉一些重要的細節或者步驟,如有不對,請留言指正。



發佈了2 篇原創文章 · 獲贊 3 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章