├── .gitignore
├── .idea
└── vcs.xml
├── README.md
├── android_pay_2022.3.13.1.aar
├── app
├── .gitignore
├── build.gradle
├── libs
│ ├── AliPaySDK.jar
│ ├── UPPayAssistEx.jar
│ ├── UPPaySDK.jar
│ └── WxPaySDK.jar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── android
│ │ └── pay
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── data.bin
│ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── pay
│ │ │ ├── alipay
│ │ │ ├── AliLogin.java
│ │ │ ├── AliPay.java
│ │ │ ├── AliUser.java
│ │ │ ├── Base64.java
│ │ │ ├── OnAliLoginListener.java
│ │ │ ├── OnAliPayListener.java
│ │ │ ├── OrderInfoUtil.java
│ │ │ ├── PayResult.java
│ │ │ └── SignUtils.java
│ │ │ ├── net
│ │ │ ├── Handler.java
│ │ │ ├── Http.java
│ │ │ ├── HttpsHostnameVerifier.java
│ │ │ ├── HttpsSSLSocketFactory.java
│ │ │ ├── HttpsX509TrustManager.java
│ │ │ ├── JSON.java
│ │ │ ├── OnHttpListener.java
│ │ │ ├── RequestParams.java
│ │ │ └── Response.java
│ │ │ ├── uupay
│ │ │ └── UUPay.java
│ │ │ ├── wechat
│ │ │ ├── OnWeChatLoginListener.java
│ │ │ ├── OnWeChatPayListener.java
│ │ │ ├── OnWeChatShareListener.java
│ │ │ ├── ShareHelper.java
│ │ │ ├── WeChatAccessToken.java
│ │ │ ├── WeChatConstants.java
│ │ │ ├── WeChatLogin.java
│ │ │ ├── WeChatPay.java
│ │ │ ├── WeChatShare.java
│ │ │ ├── WeChatUser.java
│ │ │ └── WeChatUtils.java
│ │ │ └── wxapi
│ │ │ ├── WeChatAuthActivity.java
│ │ │ └── WeChatPayActivity.java
│ └── res
│ │ ├── layout
│ │ └── android_aty_wx_pay.xml
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── android
│ └── pay
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidPay
2 | ## Fix-2022.3.13.1
3 | 1.微信SDK更新到6.8.0,支持Android 11吊起微信。
4 | 2.调整微信配置说明
5 | 3.调整gradle为7.0.2
6 | 4.调整JSON解析
7 | 5.handleIntent新增异常捕捉
8 | ## 功能介绍
9 | 1.微信支付、登录、分享功能
10 | 2.支付宝支付、授权登录(极简版+完整版本)功能
11 | 3.银联支付功能
12 | ## 软件架构
13 | Builder模式
14 | # 资源
15 | |名字|资源|
16 | |-|-|
17 | |GitHub-AAR|[android_pay.aar](https://github.com/RelinRan/AndroidPay/blob/master/android_pay_2022.3.13.1.aar)|
18 | |Gitee-AAR|[android_pay.aar](https://gitee.com/relin/AndroidPay/blob/master/android_pay_2022.3.13.1.aar)|
19 | |GitHub |[AndroidPay](https://github.com/RelinRan/AndroidPay)|
20 | |Gitee|[AndroidPay](https://gitee.com/relin/AndroidPay)|
21 | ## 使用说明
22 | ### Dependencies
23 | ./build.gradle | settings.gradle配置如下
24 | ```
25 | repositories {
26 | ...
27 | maven { url 'https://jitpack.io' }
28 | }
29 | ```
30 | ./app/build.gradle配置如下
31 | ```
32 | dependencies {
33 | implementation 'com.github.RelinRan:AndroidPay:2022.3.13.1'
34 | }
35 | ```
36 | #### ARR
37 | 下载之后放入libs文件夹里面,然后./app/build.gradle配置如下
38 | ```
39 | android {
40 | ....
41 | repositories {
42 | flatDir {
43 | dirs 'libs'
44 | }
45 | }
46 | }
47 | dependencies {
48 | implementation(name: 'android_pay_2022.3.13.1', ext: 'aar')
49 | }
50 | ```
51 | ## 微信支付
52 | 项目包名下新建wxapi文件夹,然后新建WXPayEntryActivity.java文件,继承WeChatPayActivity
53 | ```
54 | public class WXPayEntryActivity extends WeChatPayActivity {}
55 | ```
56 | AndroidManifest.xml配置
57 | ```
58 |
64 | ```
65 | manifest标签内(与权限同级)下配置
66 | ```
67 |
68 |
69 |
70 | ```
71 | 支付调用
72 | ```
73 | WeChatPay.Builder builder = new WeChatPay.Builder(this);
74 | builder.appId("xxxx");
75 | builder.partnerId("xxx");
76 | builder.prepayId("xxx");
77 | builder.nonceStr("xxxx");
78 | builder.timeStamp("xxxx");
79 | builder.packageValue("Sign=WXPay");
80 | builder.sign("xxxx");
81 | builder.listener(new OnWeChatPayListener() {
82 | @Override
83 | public void onWeChatPay(int code,String msg) {
84 | if(code==WeChatConstants.SUCCEED){//支付成功
85 |
86 | }
87 | if(code==WeChatConstants.CANCEL){//用户取消
88 |
89 | }
90 | if(code==WeChatConstants.FAILED){//支付失败
91 |
92 | }
93 | }
94 | });
95 | builder.extData(xxxxx);//支付提示文字
96 | builder.build();
97 | ```
98 |
99 | ## 支付宝支付
100 | AndroidManifest.xml配置,非必要配置(项目本身或者其他arr没有配置org.apache.http.legacy的情况之下需要)
101 | ```
102 |
105 | ```
106 | 支付调用
107 | ```
108 | AliPay.Builder builder = new AliPay.Builder(this);
109 | builder.orderInfo("xxxx");
110 | builder.listener(new OnAliPayListener() {
111 |
112 | /**
113 | * 参数解释
114 | *
115 | * @param status 是结果码(类型为字符串)。
116 | * 9000 订单支付成功
117 | * 8000 正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
118 | * 4000 订单支付失败
119 | * 5000 重复请求
120 | * 6001 用户中途取消
121 | * 6002 网络连接出错
122 | * 6004 支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
123 | * 其它 其它支付错误
124 | * @param json 是处理结果(类型为json结构字符串)
125 | * out_trade_no String 是 64 商户网站唯一订单号 70501111111S001111119
126 | * trade_no String 是 64 该交易在支付宝系统中的交易流水号。最长64位。 2014112400001000340011111118
127 | * app_id String 是 32 支付宝分配给开发者的应用Id。 2014072300007148
128 | * total_amount Price 是 9 该笔订单的资金总额,单位为RMB-Yuan。取值范围为[0.01,100000000.00],精确到小数点后两位。 9.00
129 | * seller_id String 是 16 收款支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字 2088111111116894
130 | * msg String 是 16 处理结果的描述,信息来自于code返回结果的描述 success
131 | * charset String 是 16 编码格式 utf-8
132 | * timestamp String 是 32 时间 2016-10-11 17:43:36
133 | * code String 是 16 结果码 具体见
134 | * @param description description是描述信息(类型为字符串)
135 | */
136 | @Override
137 | public void onAliPay(String status, String json, String description) {
138 | if(status.equals("9000")){//成功
139 |
140 | }
141 | else if(status.equals("6001")){//用户取消
142 |
143 | }
144 | else{//支付失败
145 |
146 | }
147 | }
148 | });
149 | builder.loading(true);
150 | builder.build();
151 | ```
152 | ## 银联支付
153 | ```
154 | UUPay uuPay = new UUPay(this);
155 | uuPay.pay(tn,UUPay.PayMode.FORM);
156 | ```
157 |
158 | ## 微信登录
159 | 项目包名下新建wxapi文件夹,然后新建WXEntryActivity.java文件,继承WeChatAuthActivity
160 | ```
161 | public class WXEntryActivity extends WeChatAuthActivity {}
162 | ```
163 | AndroidManifest.xml配置
164 | ```
165 |
172 |
173 | ```
174 | manifest标签内(与权限同级)下配置
175 | ```
176 |
177 |
178 |
179 | ```
180 | 微信登录
181 | ```
182 | WeChatLogin.Builder builder = new WeChatLogin.Builder(context);
183 | builder.appId("xxx");
184 | builder.appSecret("xxx");
185 | builder.listener(new OnWXLoginListener() {
186 | @Override
187 | public void onWeChatLogin(int code, String msg, WeChatUser user) {
188 | if (code==WeChatConstants.LOADING){//登录中
189 |
190 | }
191 | if (code==WeChatConstants.SUCCEED){//登录成功
192 |
193 | }
194 | if (code==WeChatConstants.CANCEL){//用户取消登录
195 |
196 | }
197 | if (code==WeChatConstants.AUTH_DENIED){//授权取消
198 |
199 | }
200 | }
201 | });
202 | builder.build();
203 | ```
204 | ## 支付宝登录([官方文档](https://docs.open.alipay.com/218/))
205 | 授权登录回调onAliLogin(int code, String memo, AliUser aliUser)回调返回code值如下:
206 | AliLogin.OK = 9000 (调用成功)
207 | AliLogin.Duplex = 5000 (3s内快速发起了多次支付 / 授权调用。稍后重试即可。)
208 | AliLogin.NOT_INSTALLED = 4001(用户未安装支付宝 App。)
209 | AliLogin.SYS_ERR = 4000(其它错误,如参数传递错误。)
210 | AliLogin.CANCEL = 6001(用户取消)
211 | AliLogin.NET_ERROR = 6002(网络连接出错)
212 | ### 极简版授权([官方文档](https://docs.open.alipay.com/218/sxc60m/))
213 | 在项目AndroidManifest.xml配置如下(注意:android:scheme="xxxxxxxxxx"这个需要自己配置,最好是自己应用包名)
214 | ```
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 | ```
224 | 支付宝登录代码
225 | ```
226 | AliLogin.Builder builder = new AliLogin.Builder(this);
227 | builder.appId("xxxxx");
228 | builder.scheme("xxxxxx");//必须跟AndroidManifest.xml配置一致
229 | builder.listener(new OnAliLoginListener() {
230 | @Override
231 | public void onAliLogin(int code, String memo, AliUser aliUser) {
232 | if (code == AliLogin.OK) {
233 | //处理你的逻辑,极简版本只有aliUser.getAuthCode()
234 | }
235 | }
236 | });
237 | builder.build();
238 | ```
239 | ### 完整版授权([官方文档](https://docs.open.alipay.com/218/105325/))
240 | 注意:authInfo需要后端提供,为了安全性。如果后端不提供就是调用OrderInfoUtil工具类如下方法获取
241 | ```
242 | /**
243 | * 构建授权信息
244 | *
245 | * @param privateKey 私钥(https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1)
246 | * @param pid 签约的支付宝账号对应的支付宝唯一用户号,以2088开头的16位纯数字组成
247 | * @param app_id支付宝分配给开发者的应用ID
248 | * @param target_id 商户标识该次用户授权请求的ID,该值在商户端应保持唯一
249 | * @param rsa2 签名类型是否是RSA2,否:RSA
250 | * @return
251 | */
252 | public static String buildAuthInfo(String privateKey, String pid, String app_id, String target_id, boolean rsa2)
253 | ```
254 | 授权AndroidManifest.xml配置
255 | ```
256 |
260 |
261 |
267 |
268 | ```
269 | 授权调用代码
270 | ```
271 | AliLogin.Builder builder = new AliLogin.Builder(this);
272 | builder.authInfo("xxxxx");
273 | builder.listener(new OnAliLoginListener() {
274 | @Override
275 | public void onAliLogin(int code, String memo, AliUser aliUser) {
276 | if (code == AliLogin.OK) {
277 | //处理你的逻辑,完整版本有aliUser.getUserId()和aliUser.getAliPayOpenId()
278 | }
279 | }
280 | });
281 | builder.build();
282 | ```
283 | ## 微信分享
284 | 项目包名下新建wxapi文件夹,然后新建WXEntryActivity.java文件,继承WeChatAuthActivity
285 | ```
286 | public class WXEntryActivity extends WeChatAuthActivity {}
287 | ```
288 | 因为根据官方文档集成,其中参数名字也跟官方文档一致,目前只是加了一个thumUrl和imageUrl不跟官方文档一致,为了方便缩略图和图片分享使用网络图片; 其他的参数参考[官方文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Share_and_Favorites/Android.html)
289 | ### 图片分享代码
290 | ```
291 | WeChatShare.Builder builder = new WeChatShare.Builder(getContext());
292 | builder.type(WeChatShare.TYPE_IMAGE);
293 | builder.appId(Constants.WE_CHAT_APP_ID);
294 | builder.scene(WeChatShare.SCENE_SESSION);
295 | builder.imagePath("本地图片地址");//或者builder.imageUrl("http://xxxxxx");
296 | builder.listener(new OnWeChatShareListener() {
297 | @Override
298 | public void onWeChatShare(int code, String msg) {
299 | //分享回调,官方目前取消了回调,不管是否正确分享都会进入。
300 | }
301 | });
302 | builder.build();
303 | ```
304 |
305 | ### 视频分享代码
306 | ```
307 | WeChatShare.Builder builder = new WeChatShare.Builder(getContext());
308 | builder.type(WeChatShare.TYPE_VIDEO);
309 | builder.appId(Constants.WE_CHAT_APP_ID);
310 | builder.scene(WeChatShare.SCENE_SESSION);
311 | builder.title("标题");
312 | builder.description("描述信息");
313 | builder.thumbImage(bitmap);//或 builder.thumbUrl("http://xxxxxx"); 或builder.thumbData(byte[]);
314 | builder.videoUrl("视频网络地址");
315 | builder.listener(new OnWeChatShareListener() {
316 | @Override
317 | public void onWeChatShare(int code, String msg) {
318 | //分享回调,官方目前取消了回调,不管是否正确分享都会进入。
319 | if (code==WeChatConstants.SUCCEED){//成功
320 |
321 | }
322 | if (code==WeChatConstants.CANCEL){//取消
323 |
324 | }
325 | }
326 | });
327 | builder.build();
328 | ```
329 |
330 | ### 网页分享代码
331 | ```
332 | WeChatShare.Builder builder = new WeChatShare.Builder(getContext());
333 | builder.type(WeChatShare.TYPE_WEB);
334 | builder.appId(Constants.WE_CHAT_APP_ID);
335 | builder.scene(WeChatShare.SCENE_SESSION);
336 | builder.title("标题");
337 | builder.description("描述信息");
338 | //缩略图设置
339 | builder.thumbImage(bitmap);//或 builder.thumbUrl("http://xxxxxx"); 或builder.thumbData(byte[]);
340 | builder.webpageUrl("网络地址");
341 | builder.listener(new OnWeChatShareListener() {
342 | @Override
343 | public void onWeChatShare(int code, String msg) {
344 | //分享回调,官方目前取消了回调,不管是否正确分享都会进入。
345 | if (code==WeChatConstants.SUCCEED){//成功
346 |
347 | }
348 | if (code==WeChatConstants.CANCEL){//取消
349 |
350 | }
351 | }
352 | });
353 | builder.build();
354 | ```
355 | ### 音乐分享代码
356 | ```
357 | WeChatShare.Builder builder = new WeChatShare.Builder(getContext());
358 | builder.type(WeChatShare.TYPE_MUSIC);
359 | builder.appId(Constants.WE_CHAT_APP_ID);
360 | builder.scene(WeChatShare.SCENE_SESSION);
361 | builder.title("标题");
362 | builder.description("描述信息");
363 | //缩略图设置
364 | builder.thumbImage(bitmap);//或 builder.thumbUrl("http://xxxxxx"); 或builder.thumbData(byte[]);
365 | builder.musicUrl("网络地址");
366 | builder.listener(new OnWeChatShareListener() {
367 | @Override
368 | public void onWeChatShare(int code, String msg) {
369 | //分享回调,官方目前取消了回调,不管是否正确分享都会进入。
370 | if (code==WeChatConstants.SUCCEED){//成功
371 |
372 | }
373 | if (code==WeChatConstants.CANCEL){//取消
374 |
375 | }
376 | }
377 | });
378 | builder.build();
379 | ```
380 | ### 小程序分享代码
381 | ```
382 | WeChatShare.Builder builder = new WeChatShare.Builder(getContext());
383 | builder.type(WeChatShare.TYPE_MIN_PROGRAM);
384 | builder.appId(Constants.WE_CHAT_APP_ID);
385 | builder.scene(WeChatShare.SCENE_SESSION);
386 | //兼容低版本的网页链接
387 | builder.webpageUrl("http://www.qq.com");
388 | //正式版:0,测试版:1,体验版:2
389 | builder.miniProgramType(WXMiniProgramObject.MINIPTOGRAM_TYPE_RELEASE);
390 | //小程序原始id
391 | builder.miniProgramUserName("gh_d43f693ca31f");
392 | //小程序页面路径
393 | builder.miniProgramPath("/pages/media");
394 | builder.title("标题");
395 | builder.description("描述信息");
396 | //缩略图设置
397 | builder.thumbImage(bitmap);//或 builder.thumbUrl("http://xxxxxx"); 或builder.thumbData(byte[]);
398 | builder.listener(new OnWeChatShareListener() {
399 | @Override
400 | public void onWeChatShare(int code, String msg) {
401 | //分享回调,官方目前取消了回调,不管是否正确分享都会进入。
402 | if (code==WeChatConstants.SUCCEED){//成功
403 |
404 | }
405 | if (code==WeChatConstants.CANCEL){//取消
406 |
407 | }
408 | }
409 | });
410 | builder.build();
411 | ```
--------------------------------------------------------------------------------
/android_pay_2022.3.13.1.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/android_pay_2022.3.13.1.aar
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | }
4 | android {
5 | compileSdkVersion 30
6 | buildToolsVersion "30.0.3"
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 30
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation 'androidx.appcompat:appcompat:1.3.0'
28 | implementation 'com.google.android.material:material:1.4.0'
29 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
30 | testImplementation 'junit:junit:4.13.2'
31 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
32 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
33 | implementation files('libs/UPPayAssistEx.jar')
34 | implementation files('libs/UPPaySDK.jar')
35 | implementation files('libs/WxPaySDK.jar')
36 | implementation files('libs/AliPaySDK.jar')
37 | }
38 |
--------------------------------------------------------------------------------
/app/libs/AliPaySDK.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/app/libs/AliPaySDK.jar
--------------------------------------------------------------------------------
/app/libs/UPPayAssistEx.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/app/libs/UPPayAssistEx.jar
--------------------------------------------------------------------------------
/app/libs/UPPaySDK.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/app/libs/UPPaySDK.jar
--------------------------------------------------------------------------------
/app/libs/WxPaySDK.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/app/libs/WxPaySDK.jar
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/android/pay/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.android.pay;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.android.pay", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/assets/data.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/app/src/main/assets/data.bin
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/AliLogin.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.os.Message;
7 | import android.util.Log;
8 |
9 | import com.alipay.sdk.app.AuthTask;
10 | import com.alipay.sdk.app.OpenAuthTask;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | public class AliLogin implements OpenAuthTask.Callback {
16 |
17 | private final int AUTH_V2 = 0x002;
18 |
19 | /**
20 | * 调用成功
21 | */
22 | public static final int OK = OpenAuthTask.OK;
23 |
24 | /**
25 | * 3s 内快速发起了多次支付 / 授权调用。稍后重试即可。
26 | */
27 | public static final int DUPLEX = OpenAuthTask.Duplex;
28 |
29 | /**
30 | * 用户未安装支付宝 App
31 | */
32 | public static final int NOT_INSTALLED = OpenAuthTask.NOT_INSTALLED;
33 |
34 | /**
35 | * 其它错误,如参数传递错误
36 | */
37 | public static final int SYS_ERR = OpenAuthTask.SYS_ERR;
38 |
39 | /**
40 | * 用户取消
41 | */
42 | public static final int CANCEL = 6001;
43 |
44 | /**
45 | * 网络连接出错
46 | */
47 | public static final int NET_ERROR = 6002;
48 |
49 | /**
50 | * 登录页面
51 | */
52 | public final Activity activity;
53 |
54 | /**
55 | * 支付宝平台APP_ID
56 | */
57 | public final String appId;
58 |
59 | /**
60 | * 支付宝回跳到你的应用时使用的 Intent Scheme。请设置为不和其它应用冲突的值,需要在你的 App 的 AndroidManifest.xml 中添加这一项
61 | */
62 | public final String scheme;
63 |
64 | /**
65 | * 授权信息
66 | */
67 | private final String authInfo;
68 |
69 | /**
70 | * 是否显示loading
71 | */
72 | private final boolean isShowLoading;
73 |
74 | /**
75 | * 登录回调监听
76 | */
77 | public final OnAliLoginListener listener;
78 |
79 | /**
80 | * 登录构造函数
81 | *
82 | * @param builder
83 | */
84 | public AliLogin(Builder builder) {
85 | this.activity = builder.activity;
86 | this.scheme = builder.scheme;
87 | this.appId = builder.appId;
88 | this.authInfo = builder.authInfo;
89 | this.isShowLoading = builder.isShowLoading;
90 | this.listener = builder.listener;
91 | auth();
92 | authV2(authInfo, isShowLoading);
93 | }
94 |
95 |
96 | public static class Builder {
97 |
98 | /**
99 | * 登录构建者
100 | *
101 | * @param activity 登录页面
102 | */
103 | public Builder(Activity activity) {
104 | this.activity = activity;
105 | }
106 |
107 | /**
108 | * 登录页面
109 | */
110 | private Activity activity;
111 |
112 | /**
113 | * 支付宝平台ID
114 | */
115 | private String appId;
116 |
117 | /**
118 | * 支付宝回跳到你的应用时使用的 Intent Scheme。请设置为不和其它应用冲突的值。
119 | */
120 | private String scheme;
121 |
122 | /**
123 | * 授权信息
124 | */
125 | private String authInfo;
126 |
127 | /**
128 | * 是否显示Loading
129 | */
130 | private boolean isShowLoading;
131 |
132 | /**
133 | * 登录监听
134 | */
135 | private OnAliLoginListener listener;
136 |
137 | /**
138 | * 获取当前登录页面
139 | *
140 | * @return
141 | */
142 | public Activity activity() {
143 | return activity;
144 | }
145 |
146 | /**
147 | * 平台ID
148 | *
149 | * @return
150 | */
151 | public String appId() {
152 | return appId;
153 | }
154 |
155 | /**
156 | * 设置平台ID
157 | *
158 | * @param appId
159 | * @return
160 | */
161 | public Builder appId(String appId) {
162 | this.appId = appId;
163 | return this;
164 | }
165 |
166 | /**
167 | * 支付宝回跳到你的应用时使用的 Intent Scheme
168 | *
169 | * @return
170 | */
171 | public String scheme() {
172 | return appId;
173 | }
174 |
175 | /**
176 | * 设置支付宝回跳到你的应用时使用的 Intent Scheme,需要在你的 App 的 AndroidManifest.xml 中添加这一项
177 | *
178 | * @param scheme
179 | * @return
180 | */
181 | public Builder scheme(String scheme) {
182 | this.scheme = scheme;
183 | return this;
184 | }
185 |
186 | /**
187 | * 设置授权信息(完成版使用)
188 | *
189 | * @param authInfo
190 | * @return
191 | */
192 | public Builder authInfo(String authInfo) {
193 | this.authInfo = authInfo;
194 | return this;
195 | }
196 |
197 | /**
198 | * 是否显示Loading(完成版使用)
199 | *
200 | * @param isShowLoading
201 | * @return
202 | */
203 | public Builder showLoading(boolean isShowLoading) {
204 | this.isShowLoading = isShowLoading;
205 | return this;
206 | }
207 |
208 | /**
209 | * 获取登录监听
210 | *
211 | * @return
212 | */
213 | public OnAliLoginListener listener() {
214 | return listener;
215 | }
216 |
217 | /**
218 | * 设置登录监听
219 | *
220 | * @param listener
221 | * @return
222 | */
223 | public Builder listener(OnAliLoginListener listener) {
224 | this.listener = listener;
225 | return this;
226 | }
227 |
228 | /**
229 | * 构建登录对象
230 | *
231 | * @return
232 | */
233 | public AliLogin build() {
234 | return new AliLogin(this);
235 | }
236 | }
237 |
238 | /**
239 | * 授权版本一(极简版 )
240 | */
241 | private void auth() {
242 | if (appId == null || scheme == null) {
243 | return;
244 | }
245 | // 传递给支付宝应用的业务参数
246 | Map bizParams = new HashMap<>();
247 | bizParams.put("url", "https://authweb.alipay.com/auth?auth_type=PURE_OAUTH_SDK&app_id=" + appId + "&scope=auth_user&state=init");
248 | // 唤起授权业务
249 | OpenAuthTask task = new OpenAuthTask(activity);
250 | task.execute(scheme, OpenAuthTask.BizType.AccountAuth, bizParams, this, true);
251 | }
252 |
253 | /**
254 | * 授权版本二(完整版)
255 | */
256 | private void authV2(final String authInfo, final boolean isShowLoading) {
257 | if (authInfo == null) {
258 | return;
259 | }
260 | Log.i(this.getClass().getSimpleName(), "->authV2 authInfo:" + authInfo);
261 | Runnable authRunnable = new Runnable() {
262 | @Override
263 | public void run() {
264 | // 构造AuthTask 对象
265 | AuthTask authTask = new AuthTask(activity);
266 | // 获取授权结果。
267 | Map result = authTask.authV2(authInfo, isShowLoading);
268 | Log.i(this.getClass().getSimpleName(), "->authV2 result:" + result.toString());
269 | Message message = handler.obtainMessage();
270 | message.what = AUTH_V2;
271 | message.obj = result;
272 | handler.sendMessage(message);
273 | }
274 | };
275 | Thread authThread = new Thread(authRunnable);
276 | authThread.start();
277 | }
278 |
279 |
280 | private Handler handler = new Handler() {
281 | @Override
282 | public void handleMessage(Message msg) {
283 | super.handleMessage(msg);
284 | if (msg.what == AUTH_V2) {
285 | Map info = (Map) msg.obj;
286 | String result = info.get("result");
287 | AliUser user = new AliUser();
288 | if (result.contains("&")) {
289 | String results[] = result.split("&");
290 | for (int i = 0; i < results.length; i++) {
291 | String item = results[i];
292 | String keyValues[] = item.split("=");
293 | info.put(keyValues[0], keyValues[1]);
294 | }
295 | }
296 | user.setResultStatus(info.get("resultStatus"));
297 | user.setAuthCode(info.get("auth_code"));
298 | user.setResultCode(info.get("result_code"));
299 | user.setAliPayOpenId(info.get("alipay_open_id"));
300 | user.setUserId(info.get("user_id"));
301 | Log.i(this.getClass().getSimpleName(), "-> auth v2 result:" + user.toString());
302 | if (listener != null) {
303 | int code = user.getResultCode().equals("200") && user.getResultStatus().equals(OK + "") ? OK : Integer.parseInt(user.getResultCode());
304 | listener.onAliLogin(code, info.get("memo"), user);
305 | }
306 | }
307 | }
308 | };
309 |
310 | /**
311 | * 登录结果
312 | *
313 | * @param resultCode
314 | * @param memo
315 | * @param bundle
316 | */
317 | @Override
318 | public void onResult(int resultCode, String memo, Bundle bundle) {
319 | if (listener != null) {
320 | AliUser user = new AliUser();
321 | user.setAppId(bundle.getString("app_id"));
322 | user.setResultCode(bundle.getString("result_code"));
323 | user.setScope(bundle.getString("scope"));
324 | user.setState(bundle.getString("state"));
325 | user.setAuthCode(bundle.getString("auth_code"));
326 | Log.i(this.getClass().getSimpleName(), "-> auth v1 result:" + user.toString());
327 | listener.onAliLogin(resultCode, memo, user);
328 | }
329 | }
330 |
331 | }
332 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/AliPay.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 | import android.os.Message;
8 | import android.util.Log;
9 |
10 | import com.alipay.sdk.app.H5PayActivity;
11 | import com.alipay.sdk.app.PayTask;
12 |
13 | import java.util.Map;
14 |
15 | /**
16 | * Created by Ice on 2016/2/17.
17 | * 支付宝支付类
18 | */
19 | public class AliPay {
20 |
21 | /**
22 | * 支付标志
23 | */
24 | private static final int SDK_PAY_FLAG = 1;
25 |
26 |
27 | /**
28 | * app支付请求参数字符串,主要包含商户的订单信息,key=value形式,以&连接。
29 | */
30 | public final String orderInfo;
31 |
32 | /**
33 | * 支付页面的Activity
34 | */
35 | public final Activity activity;
36 |
37 | /**
38 | * 支付回调函数
39 | */
40 | public final OnAliPayListener listener;
41 |
42 | /**
43 | * 用户在商户app内部点击付款,是否需要一个loading做为在钱包唤起之前的过渡,这个值设置为true,
44 | * 将会在调用pay接口的时候直接唤起一个loading,直到唤起H5支付页面或者唤起外部的钱包付款页面loading才消失。
45 | * (建议将该值设置为true,优化点击付款到支付唤起支付页面的过渡过程。)
46 | */
47 | public final boolean loading;
48 |
49 | /**
50 | * H5网页支付地址
51 | */
52 | public final String h5Url;
53 |
54 |
55 | public AliPay(Builder builder) {
56 | this.activity = builder.activity;
57 | this.orderInfo = builder.orderInfo;
58 | this.h5Url = builder.h5Url;
59 | this.listener = builder.listener;
60 | this.loading = builder.loading;
61 | pay();
62 | }
63 |
64 |
65 | public static class Builder {
66 |
67 | private String orderInfo;
68 | private String h5Url;
69 | private Activity activity;
70 | private OnAliPayListener listener;
71 | private boolean loading = true;
72 |
73 | public Builder(Activity activity) {
74 | this.activity = activity;
75 | }
76 |
77 | public String orderInfo() {
78 | return orderInfo;
79 | }
80 |
81 | public Builder orderInfo(String orderInfo) {
82 | this.orderInfo = orderInfo;
83 | return this;
84 | }
85 |
86 | public String h5Url() {
87 | return h5Url;
88 | }
89 |
90 | public Builder h5Url(String h5Url) {
91 | this.h5Url = h5Url;
92 | return this;
93 | }
94 |
95 | public Activity activity() {
96 | return activity;
97 | }
98 |
99 | public OnAliPayListener listener() {
100 | return listener;
101 | }
102 |
103 | public Builder listener(OnAliPayListener listener) {
104 | this.listener = listener;
105 | return this;
106 | }
107 |
108 | public boolean isLoading() {
109 | return loading;
110 | }
111 |
112 | public Builder loading(boolean loading) {
113 | this.loading = loading;
114 | return this;
115 | }
116 |
117 | public AliPay build() {
118 | return new AliPay(this);
119 | }
120 | }
121 |
122 | /**
123 | * 支付
124 | */
125 | public void pay() {
126 | if (orderInfo != null) {
127 | Runnable payRunnable = new Runnable() {
128 | @Override
129 | public void run() {
130 | PayTask task = new PayTask(activity);
131 | Map result = task.payV2(orderInfo, loading);
132 | Message msg = new Message();
133 | msg.what = SDK_PAY_FLAG;
134 | msg.obj = result;
135 | mHandler.sendMessage(msg);
136 | }
137 | };
138 | Thread payThread = new Thread(payRunnable);
139 | payThread.start();
140 | }
141 | if (h5Url != null) {
142 | h5Pay(h5Url);
143 | }
144 | }
145 |
146 | /**
147 | * 原生的H5(手机网页版支付切natvie支付) 【对应页面网页支付按钮】
148 | * 需要声明H5PayActivity
149 | *
150 | * @param url url可以是一号店或者美团等第三方的购物wap站点,在该网站的支付过程中,支付宝sdk完成拦截支付
151 | * url是测试的网站,在app内部打开页面是基于webview打开的,demo中的webview是H5PayDemoActivity,
152 | * demo中拦截url进行支付的逻辑是在H5PayDemoActivity中shouldOverrideUrlLoading方法实现,
153 | * 商户可以根据自己的需求来实现
154 | */
155 | public void h5Pay(String url) {
156 | getSDKVersion();
157 | Intent intent = new Intent(activity, H5PayActivity.class);
158 | Bundle extras = new Bundle();
159 | extras.putString("url", h5Url);
160 | intent.putExtras(extras);
161 | activity.startActivity(intent);
162 | }
163 |
164 | /**
165 | * get the sdk version. 获取SDK版本号
166 | */
167 | public void getSDKVersion() {
168 | PayTask payTask = new PayTask(activity);
169 | String version = payTask.getVersion();
170 | Log.i(this.getClass().getSimpleName(), "getSDKVersion is " + version);
171 | }
172 |
173 |
174 | /**
175 | * 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
176 | */
177 | private Handler mHandler = new Handler() {
178 | public void handleMessage(Message msg) {
179 | switch (msg.what) {
180 | case SDK_PAY_FLAG: {
181 | PayResult payResult = new PayResult((Map) msg.obj);
182 |
183 | if (listener != null) {
184 | listener.onAliPay(payResult.getResultStatus(), payResult.getResult(), payResult.getMemo());
185 | }
186 | break;
187 | }
188 | default:
189 | break;
190 | }
191 | }
192 | };
193 | }
194 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/AliUser.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | public class AliUser {
4 |
5 | /**
6 | * 支付宝分配给开发者的应用 ID
7 | */
8 | private String appId = "";
9 | /**
10 | * 结果码
11 | * 200:业务处理成功;
12 | * 1005:账户已冻结,如有疑问,请联系支付宝技术支持
13 | * 202:系统异常,请稍后再试或联系支付宝技术支持
14 | */
15 | private String resultCode = "0";
16 | /**
17 | * 极简版 SDK 固定参数,传其他参数无效。auth_base 为用户基础授权,仅用于静默获取用户支付宝 UID;auth_user 获取用户信息,网站支付宝登录。
18 | */
19 | private String scope = "";
20 | /**
21 | * OAuth 2 协议参数,可设置为随机字符串的 base64 编码(100位以内)
22 | */
23 | private String state = "";
24 | /**
25 | * 授权码
26 | */
27 | private String authCode = "";
28 | /**
29 | * 本次操作的状态(完整授权版本会返回)
30 | * 仅当resultStatus为“9000”且resultCode为“200”时,代表授权成功。
31 | */
32 | private String resultStatus = "";
33 | /**
34 | * 支付宝ID(完整授权版本会返回)
35 | */
36 | private String aliPayOpenId = "";
37 | /**
38 | * 用户ID(完整授权版本会返回)
39 | */
40 | private String userId = "";
41 |
42 | public String getAppId() {
43 | return appId;
44 | }
45 |
46 | public void setAppId(String appId) {
47 | this.appId = appId==null?"":appId;
48 | }
49 |
50 | public String getResultCode() {
51 | return resultCode;
52 | }
53 |
54 | public void setResultCode(String resultCode) {
55 | this.resultCode = resultCode==null?"0":resultCode;
56 | }
57 |
58 | public String getScope() {
59 | return scope;
60 | }
61 |
62 | public void setScope(String scope) {
63 | this.scope = scope==null?"":scope;
64 | }
65 |
66 | public String getState() {
67 | return state;
68 | }
69 |
70 | public void setState(String state) {
71 | this.state = state==null?"":state;
72 | }
73 |
74 | public String getAuthCode() {
75 | return authCode ;
76 | }
77 |
78 | public void setAuthCode(String authCode) {
79 | this.authCode = authCode==null?"":authCode;
80 | }
81 |
82 | public String getResultStatus() {
83 | return resultStatus;
84 | }
85 |
86 | public void setResultStatus(String resultStatus) {
87 | this.resultStatus = resultStatus==null?"0":resultStatus;
88 | }
89 |
90 | public String getAliPayOpenId() {
91 | return aliPayOpenId;
92 | }
93 |
94 | public void setAliPayOpenId(String aliPayOpenId) {
95 | this.aliPayOpenId = aliPayOpenId==null?"":aliPayOpenId;
96 | }
97 |
98 | public String getUserId() {
99 | return userId;
100 | }
101 |
102 | public void setUserId(String userId) {
103 | this.userId = userId==null?"":userId;
104 | }
105 |
106 | @Override
107 | public String toString() {
108 | return "AliUser{" +
109 | "appId='" + appId + '\'' +
110 | ", resultCode='" + resultCode + '\'' +
111 | ", scope='" + scope + '\'' +
112 | ", state='" + state + '\'' +
113 | ", authCode='" + authCode + '\'' +
114 | ", resultStatus='" + resultStatus + '\'' +
115 | ", aliPayOpenId='" + aliPayOpenId + '\'' +
116 | ", userId='" + userId + '\'' +
117 | '}';
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/Base64.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | public final class Base64 {
4 |
5 | private static final int BASELENGTH = 128;
6 | private static final int LOOKUPLENGTH = 64;
7 | private static final int TWENTYFOURBITGROUP = 24;
8 | private static final int EIGHTBIT = 8;
9 | private static final int SIXTEENBIT = 16;
10 | private static final int FOURBYTE = 4;
11 | private static final int SIGN = -128;
12 | private static char PAD = '=';
13 | private static byte[] base64Alphabet = new byte[BASELENGTH];
14 | private static char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
15 |
16 | static {
17 | for (int i = 0; i < BASELENGTH; ++i) {
18 | base64Alphabet[i] = -1;
19 | }
20 | for (int i = 'Z'; i >= 'A'; i--) {
21 | base64Alphabet[i] = (byte) (i - 'A');
22 | }
23 | for (int i = 'z'; i >= 'a'; i--) {
24 | base64Alphabet[i] = (byte) (i - 'a' + 26);
25 | }
26 |
27 | for (int i = '9'; i >= '0'; i--) {
28 | base64Alphabet[i] = (byte) (i - '0' + 52);
29 | }
30 |
31 | base64Alphabet['+'] = 62;
32 | base64Alphabet['/'] = 63;
33 |
34 | for (int i = 0; i <= 25; i++) {
35 | lookUpBase64Alphabet[i] = (char) ('A' + i);
36 | }
37 |
38 | for (int i = 26, j = 0; i <= 51; i++, j++) {
39 | lookUpBase64Alphabet[i] = (char) ('a' + j);
40 | }
41 |
42 | for (int i = 52, j = 0; i <= 61; i++, j++) {
43 | lookUpBase64Alphabet[i] = (char) ('0' + j);
44 | }
45 | lookUpBase64Alphabet[62] = (char) '+';
46 | lookUpBase64Alphabet[63] = (char) '/';
47 |
48 | }
49 |
50 | private static boolean isWhiteSpace(char octect) {
51 | return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
52 | }
53 |
54 | private static boolean isPad(char octect) {
55 | return (octect == PAD);
56 | }
57 |
58 | private static boolean isData(char octect) {
59 | return (octect < BASELENGTH && base64Alphabet[octect] != -1);
60 | }
61 |
62 | /**
63 | * Encodes hex octects into Base64
64 | *
65 | * @param binaryData
66 | * Array containing binaryData
67 | * @return Encoded Base64 array
68 | */
69 | public static String encode(byte[] binaryData) {
70 |
71 | if (binaryData == null) {
72 | return null;
73 | }
74 |
75 | int lengthDataBits = binaryData.length * EIGHTBIT;
76 | if (lengthDataBits == 0) {
77 | return "";
78 | }
79 |
80 | int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
81 | int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
82 | int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1
83 | : numberTriplets;
84 | char encodedData[] = null;
85 |
86 | encodedData = new char[numberQuartet * 4];
87 |
88 | byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
89 |
90 | int encodedIndex = 0;
91 | int dataIndex = 0;
92 |
93 | for (int i = 0; i < numberTriplets; i++) {
94 | b1 = binaryData[dataIndex++];
95 | b2 = binaryData[dataIndex++];
96 | b3 = binaryData[dataIndex++];
97 |
98 | l = (byte) (b2 & 0x0f);
99 | k = (byte) (b1 & 0x03);
100 |
101 | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
102 | : (byte) ((b1) >> 2 ^ 0xc0);
103 | byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
104 | : (byte) ((b2) >> 4 ^ 0xf0);
105 | byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
106 | : (byte) ((b3) >> 6 ^ 0xfc);
107 |
108 | encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
109 | encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
110 | encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
111 | encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
112 | }
113 |
114 | // form integral number of 6-bit groups
115 | if (fewerThan24bits == EIGHTBIT) {
116 | b1 = binaryData[dataIndex];
117 | k = (byte) (b1 & 0x03);
118 |
119 | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
120 | : (byte) ((b1) >> 2 ^ 0xc0);
121 | encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
122 | encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
123 | encodedData[encodedIndex++] = PAD;
124 | encodedData[encodedIndex++] = PAD;
125 | } else if (fewerThan24bits == SIXTEENBIT) {
126 | b1 = binaryData[dataIndex];
127 | b2 = binaryData[dataIndex + 1];
128 | l = (byte) (b2 & 0x0f);
129 | k = (byte) (b1 & 0x03);
130 |
131 | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
132 | : (byte) ((b1) >> 2 ^ 0xc0);
133 | byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
134 | : (byte) ((b2) >> 4 ^ 0xf0);
135 |
136 | encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
137 | encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
138 | encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
139 | encodedData[encodedIndex++] = PAD;
140 | }
141 |
142 | return new String(encodedData);
143 | }
144 |
145 | /**
146 | * Decodes Base64 data into octects
147 | *
148 | * @param encoded
149 | * string containing Base64 data
150 | * @return Array containind decoded data.
151 | */
152 | public static byte[] decode(String encoded) {
153 |
154 | if (encoded == null) {
155 | return null;
156 | }
157 |
158 | char[] base64Data = encoded.toCharArray();
159 | // remove white spaces
160 | int len = removeWhiteSpace(base64Data);
161 |
162 | if (len % FOURBYTE != 0) {
163 | return null;// should be divisible by four
164 | }
165 |
166 | int numberQuadruple = (len / FOURBYTE);
167 |
168 | if (numberQuadruple == 0) {
169 | return new byte[0];
170 | }
171 |
172 | byte decodedData[] = null;
173 | byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
174 | char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
175 |
176 | int i = 0;
177 | int encodedIndex = 0;
178 | int dataIndex = 0;
179 | decodedData = new byte[(numberQuadruple) * 3];
180 |
181 | for (; i < numberQuadruple - 1; i++) {
182 |
183 | if (!isData((d1 = base64Data[dataIndex++]))
184 | || !isData((d2 = base64Data[dataIndex++]))
185 | || !isData((d3 = base64Data[dataIndex++]))
186 | || !isData((d4 = base64Data[dataIndex++]))) {
187 | return null;
188 | }// if found "no data" just return null
189 |
190 | b1 = base64Alphabet[d1];
191 | b2 = base64Alphabet[d2];
192 | b3 = base64Alphabet[d3];
193 | b4 = base64Alphabet[d4];
194 |
195 | decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
196 | decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
197 | decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
198 | }
199 |
200 | if (!isData((d1 = base64Data[dataIndex++]))
201 | || !isData((d2 = base64Data[dataIndex++]))) {
202 | return null;// if found "no data" just return null
203 | }
204 |
205 | b1 = base64Alphabet[d1];
206 | b2 = base64Alphabet[d2];
207 |
208 | d3 = base64Data[dataIndex++];
209 | d4 = base64Data[dataIndex++];
210 | if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
211 | if (isPad(d3) && isPad(d4)) {
212 | if ((b2 & 0xf) != 0)// last 4 bits should be zero
213 | {
214 | return null;
215 | }
216 | byte[] tmp = new byte[i * 3 + 1];
217 | System.arraycopy(decodedData, 0, tmp, 0, i * 3);
218 | tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
219 | return tmp;
220 | } else if (!isPad(d3) && isPad(d4)) {
221 | b3 = base64Alphabet[d3];
222 | if ((b3 & 0x3) != 0)// last 2 bits should be zero
223 | {
224 | return null;
225 | }
226 | byte[] tmp = new byte[i * 3 + 2];
227 | System.arraycopy(decodedData, 0, tmp, 0, i * 3);
228 | tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
229 | tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
230 | return tmp;
231 | } else {
232 | return null;
233 | }
234 | } else { // No PAD e.g 3cQl
235 | b3 = base64Alphabet[d3];
236 | b4 = base64Alphabet[d4];
237 | decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
238 | decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
239 | decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
240 |
241 | }
242 |
243 | return decodedData;
244 | }
245 |
246 | /**
247 | * remove WhiteSpace from MIME containing encoded Base64 data.
248 | *
249 | * @param data
250 | * the byte array of base64 data (with WS)
251 | * @return the new length
252 | */
253 | private static int removeWhiteSpace(char[] data) {
254 | if (data == null) {
255 | return 0;
256 | }
257 |
258 | // count characters that's not whitespace
259 | int newSize = 0;
260 | int len = data.length;
261 | for (int i = 0; i < len; i++) {
262 | if (!isWhiteSpace(data[i])) {
263 | data[newSize++] = data[i];
264 | }
265 | }
266 | return newSize;
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/OnAliLoginListener.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | public interface OnAliLoginListener {
4 |
5 | /**
6 | * 支付宝登录回调
7 | *
8 | * @param code 例如{@link AliLogin#OK}判断用户登录成功
9 | * @param memo 提示信息
10 | * @param user 用户信息
11 | */
12 | void onAliLogin(int code, String memo, AliUser user);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/OnAliPayListener.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | /**
4 | * Created by Ice on 2016/2/18.
5 | * 支付宝支付回调
6 | */
7 | public interface OnAliPayListener {
8 |
9 | /**
10 | * 支付成功
11 | *
12 | * @param status 是结果码(类型为字符串)。
13 | * 9000 订单支付成功
14 | * 8000 正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
15 | * 4000 订单支付失败
16 | * 5000 重复请求
17 | * 6001 用户中途取消
18 | * 6002 网络连接出错
19 | * 6004 支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
20 | * 其它 其它支付错误
21 | * @param json 是处理结果(类型为json结构字符串)
22 | * out_trade_no String 是 64 商户网站唯一订单号 70501111111S001111119
23 | * trade_no String 是 64 该交易在支付宝系统中的交易流水号。最长64位。 2014112400001000340011111118
24 | * app_id String 是 32 支付宝分配给开发者的应用Id。 2014072300007148
25 | * total_amount Price 是 9 该笔订单的资金总额,单位为RMB-Yuan。取值范围为[0.01,100000000.00],精确到小数点后两位。 9.00
26 | * seller_id String 是 16 收款支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字 2088111111116894
27 | * msg String 是 16 处理结果的描述,信息来自于code返回结果的描述 success
28 | * charset String 是 16 编码格式 utf-8
29 | * timestamp String 是 32 时间 2016-10-11 17:43:36
30 | * code String 是 16 结果码 具体见
31 | * @param description description是描述信息(类型为字符串)
32 | */
33 | public void onAliPay(String status, String json, String description);
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/OrderInfoUtil.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.net.URLEncoder;
5 | import java.text.SimpleDateFormat;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.Date;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Locale;
12 | import java.util.Map;
13 | import java.util.Random;
14 |
15 | /**
16 | * 2.0 订单串本地签名逻辑
17 | * 注意:本 Demo 仅作为展示用途,实际项目中不能将 RSA_PRIVATE 和签名逻辑放在客户端进行!
18 | */
19 |
20 | public class OrderInfoUtil {
21 |
22 | /**
23 | * 构建授权信息
24 | *
25 | * @param privateKey 私钥(https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1)
26 | * @param pid 签约的支付宝账号对应的支付宝唯一用户号,以2088开头的16位纯数字组成
27 | * @param app_id 支付宝分配给开发者的应用ID
28 | * @param target_id 商户标识该次用户授权请求的ID,该值在商户端应保持唯一
29 | * @param rsa2 签名类型是否是RSA2,否:RSA
30 | * @return
31 | */
32 | public static String buildAuthInfo(String privateKey, String pid, String app_id, String target_id, boolean rsa2) {
33 | Map authInfoMap = buildAuthInfoMap(pid, app_id, target_id, rsa2);
34 | String info = buildOrderParam(authInfoMap);
35 | String sign = getSign(authInfoMap, privateKey, rsa2);
36 | return info + "&" + sign;
37 | }
38 |
39 | /**
40 | * 构造授权参数列表
41 | *
42 | * @param pid
43 | * @param app_id
44 | * @param target_id
45 | * @param rsa2
46 | * @return
47 | */
48 | public static Map buildAuthInfoMap(String pid, String app_id, String target_id, boolean rsa2) {
49 | Map keyValues = new HashMap();
50 | // 商户签约拿到的app_id,如:2013081700024223
51 | keyValues.put("app_id", app_id);
52 | // 商户签约拿到的pid,如:2088102123816631
53 | keyValues.put("pid", pid);
54 | // 服务接口名称, 固定值
55 | keyValues.put("apiname", "com.alipay.account.auth");
56 | // 服务接口名称, 固定值
57 | keyValues.put("methodname", "alipay.open.auth.sdk.code.get");
58 | // 商户类型标识, 固定值
59 | keyValues.put("app_name", "mc");
60 | // 业务类型, 固定值
61 | keyValues.put("biz_type", "openservice");
62 | // 产品码, 固定值
63 | keyValues.put("product_id", "APP_FAST_LOGIN");
64 | // 授权范围, 固定值
65 | keyValues.put("scope", "kuaijie");
66 | // 商户唯一标识,如:kkkkk091125
67 | keyValues.put("target_id", target_id);
68 | // 授权类型, 固定值
69 | keyValues.put("auth_type", "AUTHACCOUNT");
70 | // 签名类型
71 | keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
72 | return keyValues;
73 | }
74 |
75 | /**
76 | * 构造支付订单参数列表
77 | */
78 | public static Map buildOrderParamMap(String app_id, boolean rsa2) {
79 | Map keyValues = new HashMap();
80 | keyValues.put("app_id", app_id);
81 | keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\"0.01\",\"subject\":\"1\",\"body\":\"我是测试数据\",\"out_trade_no\":\"" + getOutTradeNo() + "\"}");
82 | keyValues.put("charset", "utf-8");
83 | keyValues.put("method", "alipay.trade.app.pay");
84 | keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
85 | keyValues.put("timestamp", "2016-07-29 16:55:53");
86 | keyValues.put("version", "1.0");
87 | return keyValues;
88 | }
89 |
90 | /**
91 | * 构造支付订单参数信息
92 | *
93 | * @param map 支付订单参数
94 | * @return
95 | */
96 | public static String buildOrderParam(Map map) {
97 | List keys = new ArrayList(map.keySet());
98 | StringBuilder sb = new StringBuilder();
99 | for (int i = 0; i < keys.size() - 1; i++) {
100 | String key = keys.get(i);
101 | String value = map.get(key);
102 | sb.append(buildKeyValue(key, value, true));
103 | sb.append("&");
104 | }
105 | String tailKey = keys.get(keys.size() - 1);
106 | String tailValue = map.get(tailKey);
107 | sb.append(buildKeyValue(tailKey, tailValue, true));
108 | return sb.toString();
109 | }
110 |
111 | /**
112 | * 拼接键值对
113 | *
114 | * @param key
115 | * @param value
116 | * @param isEncode
117 | * @return
118 | */
119 | private static String buildKeyValue(String key, String value, boolean isEncode) {
120 | StringBuilder sb = new StringBuilder();
121 | sb.append(key);
122 | sb.append("=");
123 | if (isEncode) {
124 | try {
125 | sb.append(URLEncoder.encode(value, "UTF-8"));
126 | } catch (UnsupportedEncodingException e) {
127 | sb.append(value);
128 | }
129 | } else {
130 | sb.append(value);
131 | }
132 | return sb.toString();
133 | }
134 |
135 | /**
136 | * 对支付参数信息进行签名
137 | *
138 | * @param map 待签名授权信息
139 | * @return
140 | */
141 | public static String getSign(Map map, String rsaKey, boolean rsa2) {
142 | List keys = new ArrayList(map.keySet());
143 | // key排序
144 | Collections.sort(keys);
145 | StringBuilder authInfo = new StringBuilder();
146 | for (int i = 0; i < keys.size() - 1; i++) {
147 | String key = keys.get(i);
148 | String value = map.get(key);
149 | authInfo.append(buildKeyValue(key, value, false));
150 | authInfo.append("&");
151 | }
152 | String tailKey = keys.get(keys.size() - 1);
153 | String tailValue = map.get(tailKey);
154 | authInfo.append(buildKeyValue(tailKey, tailValue, false));
155 | String oriSign = SignUtils.sign(authInfo.toString(), rsaKey, rsa2);
156 | String encodedSign = "";
157 |
158 | try {
159 | encodedSign = URLEncoder.encode(oriSign, "UTF-8");
160 | } catch (UnsupportedEncodingException e) {
161 | e.printStackTrace();
162 | }
163 | return "sign=" + encodedSign;
164 | }
165 |
166 | /**
167 | * 要求外部订单号必须唯一。
168 | *
169 | * @return
170 | */
171 | private static String getOutTradeNo() {
172 | SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
173 | Date date = new Date();
174 | String key = format.format(date);
175 | Random r = new Random();
176 | key = key + r.nextInt();
177 | key = key.substring(0, 15);
178 | return key;
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/PayResult.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
import android.text.TextUtils;
import java.util.Map;
public class PayResult {
private String resultStatus;
private String result;
private String memo;
public PayResult(String rawResult) {
if (TextUtils.isEmpty(rawResult))
return;
String[] resultParams = rawResult.split(";");
for (String resultParam : resultParams) {
if (resultParam.startsWith("resultStatus")) {
resultStatus = gatValue(resultParam, "resultStatus");
}
if (resultParam.startsWith("result")) {
result = gatValue(resultParam, "result");
}
if (resultParam.startsWith("memo")) {
memo = gatValue(resultParam, "memo");
}
}
}
public PayResult(Map rawResult) {
if (rawResult == null) {
return;
}
for (String key : rawResult.keySet()) {
if (TextUtils.equals(key, "resultStatus")) {
resultStatus = rawResult.get(key);
} else if (TextUtils.equals(key, "result")) {
result = rawResult.get(key);
} else if (TextUtils.equals(key, "memo")) {
memo = rawResult.get(key);
}
}
}
@Override
public String toString() {
return "resultStatus={" + resultStatus + "};memo={" + memo
+ "};result={" + result + "}";
}
private String gatValue(String content, String key) {
String prefix = key + "={";
return content.substring(content.indexOf(prefix) + prefix.length(),
content.lastIndexOf("}"));
}
/**
* @return the resultStatus
*/
public String getResultStatus() {
return resultStatus;
}
/**
* @return the memo
*/
public String getMemo() {
return memo;
}
/**
* @return the result
*/
public String getResult() {
return result;
}
}
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/alipay/SignUtils.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.alipay;
2 |
3 | import java.security.KeyFactory;
4 | import java.security.PrivateKey;
5 | import java.security.spec.PKCS8EncodedKeySpec;
6 |
7 | public class SignUtils {
8 |
9 | private static final String ALGORITHM = "RSA";
10 | private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
11 | private static final String DEFAULT_CHARSET = "UTF-8";
12 | private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";
13 |
14 | private static String getAlgorithms(boolean rsa2) {
15 | return rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS;
16 | }
17 |
18 | public static String sign(String content, String privateKey, boolean rsa2) {
19 | try {
20 | PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
21 | KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
22 | PrivateKey priKey = keyf.generatePrivate(priPKCS8);
23 | java.security.Signature signature = java.security.Signature.getInstance(getAlgorithms(rsa2));
24 | signature.initSign(priKey);
25 | signature.update(content.getBytes(DEFAULT_CHARSET));
26 | byte[] signed = signature.sign();
27 | return Base64.encode(signed);
28 | } catch (Exception e) {
29 | e.printStackTrace();
30 | }
31 | return null;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/net/Handler.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.net;
2 |
3 | import android.os.Message;
4 | import android.util.Log;
5 |
6 | /**
7 | * Created by Relin
8 | * on 2018-09-10.
9 | * Http异步处理类
10 | */
11 | public class Handler extends android.os.Handler {
12 |
13 | /**
14 | * 日志标识
15 | */
16 | public static final String TAG = "Http";
17 |
18 | /**
19 | * 网络请求失败的what
20 | */
21 | public static final int WHAT_ON_FAILURE = 0xa01;
22 |
23 | /**
24 | * 网络请求成功的what
25 | */
26 | public static final int WHAT_ON_SUCCEED = 0xb02;
27 |
28 | /**
29 | * 请求数据无返回的异常
30 | */
31 | public static final String HTTP_NO_RESPONSE = "The server request is unresponsive code = ";
32 |
33 | @Override
34 | public void handleMessage(Message msg) {
35 | super.handleMessage(msg);
36 | Response response = (Response) msg.obj;
37 | OnHttpListener listener = response.listener();
38 | printLog(response);
39 | switch (msg.what) {
40 | case WHAT_ON_FAILURE:
41 | if (listener != null && response != null && response.body() != null) {
42 | listener.onHttpFailure(response);
43 | }
44 | break;
45 | case WHAT_ON_SUCCEED:
46 | if (listener != null && response != null && response.body() != null) {
47 | listener.onHttpSucceed(response);
48 | }
49 | break;
50 | }
51 | }
52 |
53 | /**
54 | * 发送异常消息
55 | *
56 | * @param requestParams 请求参数
57 | * @param url 请求地址
58 | * @param code 请求结果代码
59 | * @param e 异常
60 | * @param listener 网络请求监听
61 | */
62 | public void sendExceptionMsg(RequestParams requestParams, String url, int code, Exception e, OnHttpListener listener) {
63 | Message msg = obtainMessage();
64 | msg.what = Handler.WHAT_ON_FAILURE;
65 | Response response = new Response();
66 | response.requestParams(requestParams);
67 | response.url(url);
68 | response.exception(e);
69 | response.code(code);
70 | response.listener(listener);
71 | msg.obj = response;
72 | sendMessage(msg);
73 | }
74 |
75 | /**
76 | * 发送成功信息
77 | *
78 | * @param requestParams 请求参数
79 | * @param url 请求地址
80 | * @param code 请求结果代码
81 | * @param result 请求结果
82 | * @param listener 网络请求监听
83 | */
84 | public void sendSuccessfulMsg(RequestParams requestParams, String url, int code, String result, OnHttpListener listener) {
85 | Response response = new Response();
86 | response.body(result);
87 | response.url(url);
88 | response.requestParams(requestParams);
89 | response.code(code);
90 | response.listener(listener);
91 | Message msg = this.obtainMessage();
92 | msg.what = Handler.WHAT_ON_SUCCEED;
93 | msg.obj = response;
94 | sendMessage(msg);
95 | }
96 |
97 | /**
98 | * 打印调试日志
99 | *
100 | * @param httpResult
101 | */
102 | private void printLog(Response httpResult) {
103 | StringBuffer logBuffer = new StringBuffer("Program interface debug mode");
104 | logBuffer.append("\n");
105 | logBuffer.append("┌──────────────────────────────────────");
106 | logBuffer.append("\n");
107 | logBuffer.append("│" + httpResult.url());
108 | logBuffer.append("\n");
109 | logBuffer.append("├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄");
110 | StringBuffer paramsBuffer = new StringBuffer("");
111 | if (httpResult.requestParams().getStringParams() != null) {
112 | for (String key : httpResult.requestParams().getStringParams().keySet()) {
113 | paramsBuffer.append("│\"" + key + "\":" + "\"" + httpResult.requestParams().getStringParams().get(key) + "\"");
114 | paramsBuffer.append("\n");
115 | }
116 | }
117 | if (httpResult.requestParams().getStringBody() != null) {
118 | paramsBuffer.append(httpResult.requestParams().getStringBody());
119 | paramsBuffer.append(",");
120 | paramsBuffer.append("│\"" + httpResult.requestParams().getStringBody() + "\"");
121 | paramsBuffer.append("\n");
122 | }
123 | if (paramsBuffer.toString().length() != 0) {
124 | logBuffer.append("\n");
125 | logBuffer.append(paramsBuffer);
126 | logBuffer.append("├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄");
127 | }
128 | if (httpResult != null) {
129 | logBuffer.append("\n");
130 | logBuffer.append("│\"" + "code:" + "\"" + httpResult.code() + "\"");
131 | logBuffer.append("\n");
132 | if (httpResult.exception() != null) {
133 | logBuffer.append("│ \"" + "exception:" + "\"" + httpResult.exception() + "\"");
134 | logBuffer.append("\n");
135 | }
136 | }
137 | logBuffer.append("│\"" + "body:" + httpResult.body());
138 | logBuffer.append("\n");
139 | logBuffer.append("└──────────────────────────────────────");
140 | Log.i(TAG, logBuffer.toString());
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/net/Http.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.net;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.net.NetworkInfo;
6 | import android.util.Log;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.DataOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.InputStreamReader;
13 | import java.net.HttpURLConnection;
14 | import java.net.MalformedURLException;
15 | import java.net.ProtocolException;
16 | import java.net.URL;
17 | import java.util.Map;
18 | import java.util.UUID;
19 | import java.util.concurrent.ExecutorService;
20 | import java.util.concurrent.Executors;
21 |
22 | import javax.net.ssl.HttpsURLConnection;
23 |
24 | public class Http {
25 |
26 | /**
27 | * 编码格式
28 | */
29 | private static final String CHARSET = "utf-8";
30 |
31 | /**
32 | * 前缀
33 | */
34 | private static final String PREFIX = "--";
35 |
36 | /**
37 | * 换行
38 | */
39 | private static final String LINE_FEED = "\r\n";
40 |
41 | /**
42 | * 边界标识 随机生成
43 | */
44 | private static final String BOUNDARY = UUID.randomUUID().toString();
45 |
46 | /**
47 | * 网络接口Handler
48 | */
49 | private static Handler handler;
50 |
51 | /**
52 | * 接口线程池
53 | */
54 | private static ExecutorService executorService;
55 |
56 | static {
57 | handler = new Handler();
58 | executorService = Executors.newFixedThreadPool(10);
59 | }
60 |
61 | /**
62 | * 创建服务器连接
63 | *
64 | * @param url 地址
65 | * @param requestMethod 请求方式 GET / POST
66 | * @return
67 | */
68 | protected static HttpURLConnection createHttpURLConnection(String url, String requestMethod) {
69 | HttpURLConnection conn = null;
70 | try {
71 | URL httpUrl = new URL(url);
72 | boolean isHttps = url.startsWith("https");
73 | //https
74 | if (isHttps) {
75 | HttpsURLConnection httpsURLConnection = (HttpsURLConnection) httpUrl.openConnection();
76 | httpsURLConnection.setSSLSocketFactory(HttpsSSLSocketFactory.factory());
77 | httpsURLConnection.setHostnameVerifier(new HttpsHostnameVerifier());
78 | conn = httpsURLConnection;
79 | } else {
80 | conn = (HttpURLConnection) httpUrl.openConnection();
81 | }
82 | conn.setInstanceFollowRedirects(true);
83 | conn.setRequestMethod(requestMethod);
84 | conn.setReadTimeout(30 * 1000);
85 | conn.setConnectTimeout(30 * 1000);
86 | conn.setReadTimeout(30 * 1000);
87 | conn.setConnectTimeout(30 * 1000);
88 | conn.setDoInput(true);
89 | conn.setUseCaches(false);
90 | if (requestMethod.equals("POST")) {
91 | conn.setDoOutput(true);
92 | }
93 | //设置请求头参数
94 | conn.setRequestProperty("Connection", "Keep-Alive");
95 | conn.setRequestProperty("Charset", "UTF-8");
96 | conn.setRequestProperty("User-Agent", "Android");
97 | conn.setRequestProperty("Content-Type", "multipart/form-data" + ";boundary=" + BOUNDARY);
98 | conn.connect();
99 | } catch (MalformedURLException e) {
100 | e.printStackTrace();
101 | } catch (ProtocolException e) {
102 | e.printStackTrace();
103 | } catch (IOException e) {
104 | e.printStackTrace();
105 | }
106 | return conn;
107 | }
108 |
109 |
110 | /**
111 | * 创建Get请求url地址
112 | *
113 | * @param url 地址
114 | * @param params 请求参数
115 | * @return
116 | */
117 | protected static String createGetUrl(String url, RequestParams params) {
118 | StringBuffer requestUrl = new StringBuffer();
119 | requestUrl.append(url);
120 | requestUrl.append("?");
121 | if (params != null && params.getStringParams() != null) {
122 | Map stringParams = params.getStringParams();
123 | for (String key : stringParams.keySet()) {
124 | String value = stringParams.get(key);
125 | requestUrl.append(key);
126 | requestUrl.append("=");
127 | requestUrl.append(value);
128 | requestUrl.append("&");
129 | }
130 | }
131 | String combinationUrl = requestUrl.toString().substring(0, requestUrl.lastIndexOf("&"));
132 | return combinationUrl;
133 | }
134 |
135 | /**
136 | * 添加Post请求参数
137 | *
138 | * @param conn 服务器连接对象
139 | * @param params 参数
140 | */
141 | protected static void addPostParams(HttpURLConnection conn, RequestParams params) {
142 | DataOutputStream dos;
143 | try {
144 | dos = new DataOutputStream(conn.getOutputStream());
145 | //文字参数
146 | if (params != null) {
147 | //单个数据字符 --- 一般支付上面需要
148 | if (params.getStringParams() != null) {
149 | //表单数据
150 | Map map = params.getStringParams();
151 | StringBuilder sb = new StringBuilder();
152 | for (Map.Entry entry : map.entrySet()) {
153 | sb.append(buildPostStringParams(entry.getKey(), entry.getValue()));
154 | }
155 | dos.writeBytes(sb.toString());
156 | }
157 | //纯字符参数
158 | if (params.getStringBody() != null) {
159 | dos.writeBytes(params.getStringBody());
160 | }
161 | }
162 | //请求结束标志
163 | dos.writeBytes(PREFIX + BOUNDARY + PREFIX + LINE_FEED);
164 | dos.flush();
165 | dos.close();
166 | } catch (IOException e) {
167 | e.printStackTrace();
168 | }
169 | }
170 |
171 |
172 | /**
173 | * 创建Post请求文字参数
174 | *
175 | * @param key 名称
176 | * @param value 值
177 | * @return
178 | */
179 | protected static StringBuilder buildPostStringParams(String key, String value) {
180 | StringBuilder sb = new StringBuilder();
181 | sb.append(PREFIX);
182 | sb.append(BOUNDARY);
183 | sb.append(LINE_FEED);
184 | sb.append("Content-Disposition: form-data; name=\"" + key + "\"" + LINE_FEED);
185 | sb.append("Content-Type: text/plain; charset=" + CHARSET + LINE_FEED);
186 | sb.append("Content-Transfer-Encoding: 8bit" + LINE_FEED);
187 | sb.append(LINE_FEED);// 参数头设置完以后需要两个换行,然后才是参数内容
188 | sb.append(value);
189 | sb.append(LINE_FEED);
190 | return sb;
191 | }
192 |
193 |
194 | /**
195 | * 请求
196 | *
197 | * @param requestMethod 请求方法
198 | * @param url 请求地址
199 | * @param params 参数
200 | * @param listener 回调
201 | */
202 | protected static void request(final String requestMethod, final String url, final RequestParams params, final OnHttpListener listener) {
203 | executorService.submit(new Runnable() {
204 | @Override
205 | public void run() {
206 | HttpURLConnection conn = null;
207 | if (requestMethod.equals("GET")) {
208 | Log.i("RRL", "->GET Url:" + createGetUrl(url, params));
209 | conn = createHttpURLConnection(createGetUrl(url, params), requestMethod);
210 | }
211 | if (requestMethod.equals("POST")) {
212 | conn = createHttpURLConnection(url, requestMethod);
213 | addPostParams(conn, params);
214 | }
215 | int code = -1;
216 | try {
217 | code = conn.getResponseCode();
218 | Log.i("RRL", "->GET request code:" + code);
219 | if (code == 200) {
220 | InputStream inputStream = conn.getInputStream();
221 | //获取返回数据
222 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
223 | String line;
224 | StringBuffer sb = new StringBuffer();
225 | while ((line = bufferedReader.readLine()) != null) {
226 | sb.append(line);
227 | }
228 | handler.sendSuccessfulMsg(params, url, code, sb.toString(), listener);
229 | } else {
230 | handler.sendExceptionMsg(params, url, code, new IOException(Handler.HTTP_NO_RESPONSE), listener);
231 | }
232 | } catch (IOException e) {
233 | e.printStackTrace();
234 | handler.sendExceptionMsg(params, url, code, e, listener);
235 | } finally {
236 | conn.disconnect();
237 | }
238 | }
239 | });
240 | }
241 |
242 | /**
243 | * Get请求参数
244 | *
245 | * @param url 请求地址
246 | * @param params 参数
247 | * @param listener 监听
248 | */
249 | public static void get(Context context, String url, RequestParams params, OnHttpListener listener) {
250 | if (!isAvailable(context)) {
251 | Response response = new Response();
252 | response.url(url);
253 | response.code(-11);
254 | response.requestParams(params);
255 | response.exception(new Exception("No network connection, unable to request data interface."));
256 | listener.onHttpFailure(response);
257 | return;
258 | }
259 | request("GET", url, params, listener);
260 | }
261 |
262 | /**
263 | * Post请求
264 | *
265 | * @param url 请求地址
266 | * @param params 参数
267 | * @param listener 监听
268 | */
269 | public static void post(Context context, String url, RequestParams params, OnHttpListener listener) {
270 | if (!isAvailable(context)) {
271 | Response response = new Response();
272 | response.url(url);
273 | response.code(-11);
274 | response.requestParams(params);
275 | response.exception(new Exception("No network connection, unable to request data interface."));
276 | listener.onHttpFailure(response);
277 | return;
278 | }
279 | request("POST", url, params, listener);
280 | }
281 |
282 |
283 | /**
284 | * 判断网络是否可用 [需要如下权限]
285 | *
286 | * @param context 上下文
287 | * @return
288 | */
289 | public static boolean isAvailable(Context context) {
290 | try {
291 | ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
292 | if (connectivity != null) {
293 | NetworkInfo info = connectivity.getActiveNetworkInfo();
294 | if (info != null && info.isConnected()) {
295 | if (info.getState() == NetworkInfo.State.CONNECTED) {
296 | return true;
297 | }
298 | }
299 | }
300 | } catch (Exception e) {
301 | return false;
302 | }
303 | return false;
304 | }
305 |
306 |
307 | }
308 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/net/HttpsHostnameVerifier.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.net;
2 |
3 | import javax.net.ssl.HostnameVerifier;
4 | import javax.net.ssl.SSLSession;
5 |
6 | /**
7 | * Created by Relin
8 | * on 2018-11-01.
9 | */
10 | public class HttpsHostnameVerifier implements HostnameVerifier {
11 | @Override
12 | public boolean verify(String hostname, SSLSession session) {
13 | return true;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/net/HttpsSSLSocketFactory.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.net;
2 |
3 | import java.security.KeyManagementException;
4 | import java.security.NoSuchAlgorithmException;
5 | import java.security.SecureRandom;
6 |
7 | import javax.net.ssl.SSLContext;
8 | import javax.net.ssl.SSLSocketFactory;
9 | import javax.net.ssl.TrustManager;
10 |
11 | /**
12 | * Created by Relin
13 | * on 2018-11-01.
14 | */
15 | public class HttpsSSLSocketFactory {
16 |
17 | public static SSLSocketFactory factory() {
18 | SSLContext sslContext = null;
19 | try {
20 | sslContext = SSLContext.getInstance("SSL");
21 | TrustManager[] tm = {new HttpsX509TrustManager()};
22 | sslContext.init(null, tm, new SecureRandom());
23 | } catch (NoSuchAlgorithmException e) {
24 | e.printStackTrace();
25 | } catch (KeyManagementException e) {
26 | e.printStackTrace();
27 | }
28 | return sslContext.getSocketFactory();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/net/HttpsX509TrustManager.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.net;
2 |
3 | import java.security.cert.CertificateException;
4 | import java.security.cert.X509Certificate;
5 |
6 | import javax.net.ssl.X509TrustManager;
7 |
8 | /**
9 | * Created by Relin
10 | * on 2018-11-01.
11 | */
12 | public class HttpsX509TrustManager implements X509TrustManager{
13 | @Override
14 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
15 |
16 | }
17 |
18 | @Override
19 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
20 |
21 | }
22 |
23 | @Override
24 | public X509Certificate[] getAcceptedIssuers() {
25 | return new X509Certificate[0];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/pay/net/JSON.java:
--------------------------------------------------------------------------------
1 | package com.android.pay.net;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONException;
5 | import org.json.JSONObject;
6 |
7 | import java.lang.reflect.Array;
8 | import java.lang.reflect.Constructor;
9 | import java.lang.reflect.Field;
10 | import java.lang.reflect.InvocationTargetException;
11 | import java.lang.reflect.ParameterizedType;
12 | import java.lang.reflect.Type;
13 | import java.util.ArrayList;
14 | import java.util.Collection;
15 | import java.util.HashMap;
16 | import java.util.Iterator;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 | /**
21 | * Json工具
22 | */
23 | public class JSON {
24 |
25 | /**
26 | * @param json 字符
27 | * @return 是否为空
28 | */
29 | public static boolean isNone(String json) {
30 | return json == null || json.length() == 0 || json.equals("null");
31 | }
32 |
33 | /**
34 | * @param json 字符
35 | * @return JSONObject对象
36 | */
37 | public static JSONObject toJSONObject(String json) {
38 | if (isNone(json)) {
39 | return null;
40 | }
41 | JSONObject jsonObject = null;
42 | try {
43 | jsonObject = new JSONObject(json);
44 | } catch (JSONException e) {
45 | e.printStackTrace();
46 | }
47 | return jsonObject;
48 | }
49 |
50 | /**
51 | * @param object 对象
52 | * @return Map数据对象
53 | */
54 | public static Map toMap(JSONObject object) {
55 | if (object == null) {
56 | return null;
57 | }
58 | Map map = new HashMap<>();
59 | Iterator iterator = object.keys();
60 | while (iterator.hasNext()) {
61 | String key = iterator.next();
62 | try {
63 | Object value = object.get(key);
64 | map.put(key, value);
65 | } catch (JSONException e) {
66 | e.printStackTrace();
67 | }
68 | }
69 | return map;
70 | }
71 |
72 | /**
73 | * @param json 字符串
74 | * @return 字符转Map对象
75 | */
76 | public static Map toMap(String json) {
77 | if (isNone(json)) {
78 | return null;
79 | }
80 | if (json.startsWith("{}")) {
81 | return null;
82 | }
83 | return toMap(toJSONObject(json));
84 | }
85 |
86 | /**
87 | * @param clazz 类
88 | * @param fieldName 字段名称
89 | * @return 是否是声明的字段
90 | */
91 | public static boolean isDeclaredField(Class clazz, String fieldName) {
92 | if (clazz == null) {
93 | return false;
94 | }
95 | if (fieldName == null || fieldName.length() == 0) {
96 | return false;
97 | }
98 | Field[] fields = findClassDeclaredFields(clazz);
99 | for (int i = 0; i < fields.length; i++) {
100 | String name = fields[i].getName();
101 | if (name == null) {
102 | return false;
103 | }
104 | if (fieldName.equals(name)) {
105 | return true;
106 | }
107 | }
108 | return false;
109 | }
110 |
111 | /**
112 | * @param componentType 组件类型
113 | * @param jsonArray json数组
114 | * @return 数组实例
115 | */
116 | public static Object newArrayInstance(Class componentType, JSONArray jsonArray) {
117 | Object arrayObj = Array.newInstance(componentType, jsonArray.length());
118 | for (int i = 0; i < jsonArray.length(); i++) {
119 | Object obj = null;
120 | try {
121 | obj = jsonArray.get(i);
122 | } catch (JSONException e) {
123 | e.printStackTrace();
124 | }
125 | if (componentType == String.class) {
126 | String[] array = (String[]) arrayObj;
127 | array[i] = (String) obj;
128 | }
129 | if (componentType == Character.class) {
130 | Character[] array = (Character[]) arrayObj;
131 | array[i] = (Character) obj;
132 | }
133 | if (componentType == CharSequence.class) {
134 | CharSequence[] array = (CharSequence[]) arrayObj;
135 | array[i] = (CharSequence) obj;
136 | }
137 | if (componentType == int.class) {
138 | int[] array = (int[]) arrayObj;
139 | array[i] = (int) obj;
140 | }
141 | if (componentType == long.class) {
142 | long[] array = (long[]) arrayObj;
143 | array[i] = (long) obj;
144 | }
145 | if (componentType == double.class) {
146 | double[] array = (double[]) arrayObj;
147 | array[i] = (double) obj;
148 | }
149 | if (componentType == float.class) {
150 | float[] array = (float[]) arrayObj;
151 | array[i] = (float) obj;
152 | }
153 | if (componentType == short.class) {
154 | short[] array = (short[]) arrayObj;
155 | array[i] = (short) obj;
156 | }
157 | if (componentType == boolean.class) {
158 | boolean[] array = (boolean[]) arrayObj;
159 | array[i] = (boolean) obj;
160 | }
161 | }
162 | return arrayObj;
163 | }
164 |
165 | /**
166 | * 设置字段值
167 | *
168 | * @param field 字段
169 | * @param bean 对象
170 | * @param value 值
171 | */
172 | public static void setFieldValue(Field field, Object bean, String value) {
173 | try {
174 | Class fieldType = field.getType();
175 | //字符
176 | if (fieldType == String.class || fieldType == Character.class || fieldType == CharSequence.class) {
177 | field.set(bean, value);
178 | }
179 | //Int类型
180 | if (fieldType == int.class || fieldType == Integer.class) {
181 | if (!value.contains(".")) {
182 | value = value.length() == 0 ? "0" : value;
183 | field.set(bean, Integer.parseInt(value));
184 | }
185 | }
186 | //Short类型
187 | if (fieldType == short.class || fieldType == Short.class) {
188 | if (!value.contains(".")) {
189 | value = value.length() == 0 ? "0" : value;
190 | field.set(bean, Short.parseShort(value));
191 | }
192 | }
193 | //Long类型
194 | if (fieldType == long.class || fieldType == Long.class) {
195 | if (!value.contains(".")) {
196 | value = value.length() == 0 ? "0" : value;
197 | field.set(bean, Long.parseLong(value));
198 | }
199 | }
200 | //Double类型
201 | if (field.getType() == double.class || fieldType == Double.class) {
202 | value = value.length() == 0 ? "0.00" : value;
203 | value = value.contains(".") ? value : value + ".00";
204 | field.set(bean, Double.parseDouble(value));
205 | }
206 | //Float类型
207 | if (fieldType == float.class || fieldType == Float.class) {
208 | value = value.length() == 0 ? "0.00" : value;
209 | value = value.contains(".") ? value : value + ".00";
210 | field.set(bean, Float.parseFloat(value));
211 | }
212 | //Boolean类型
213 | if (fieldType == boolean.class || fieldType == Boolean.class) {
214 | value = value.length() == 0 ? "false" : value;
215 | boolean booleanValue = false;
216 | if (value.equals("false") || value.equals("0")) {
217 | booleanValue = false;
218 | }
219 | if (value.equals("true") || value.equals("1")) {
220 | booleanValue = true;
221 | }
222 | field.set(bean, booleanValue);
223 | }
224 | } catch (IllegalAccessException e) {
225 | e.printStackTrace();
226 | }
227 | }
228 |
229 | /**
230 | * @param clazz 类
231 | * @param name 字段名称
232 | * @return 本类及其父类寻找是否有此类
233 | */
234 | public static Field findClassField(Class clazz, String name) {
235 | for (Field field : findClassDeclaredFields(clazz)) {
236 | field.setAccessible(true);
237 | if (field.getName().equals(name)) {
238 | return field;
239 | }
240 | }
241 | return null;
242 | }
243 |
244 |
245 | /**
246 | * @param object 对象
247 | * @param clazz 数据对象类
248 | * @return JsonObject转对象
249 | */
250 | public static T toObject(JSONObject object, Class clazz) {
251 | T bean = null;
252 | if (clazz == null || object == null) {
253 | return null;
254 | }
255 | try {
256 | Constructor>[] constructors = clazz.getDeclaredConstructors();
257 | if (constructors.length == 0) {
258 | bean = clazz.newInstance();
259 | } else {
260 | Constructor constructor = constructors[0];
261 | constructor.setAccessible(true);
262 | bean = (T) constructor.newInstance();
263 | }
264 | Iterator iterator = object.keys();
265 | while (iterator.hasNext()) {
266 | String key = iterator.next();
267 | if (isDeclaredField(clazz, key)) {
268 | setObjectValue(clazz, bean, object, key);
269 | }
270 | }
271 | } catch (InstantiationException e) {
272 | e.printStackTrace();
273 | } catch (IllegalAccessException e) {
274 | e.printStackTrace();
275 | } catch (InvocationTargetException e) {
276 | e.printStackTrace();
277 | }
278 | return bean;
279 | }
280 |
281 | /**
282 | * 设置对象值
283 | *
284 | * @param clazz 对象类
285 | * @param clazzBean 对象实例化
286 | * @param object json对象
287 | * @param fieldName 字段名称
288 | * @param 实体
289 | */
290 | public static void setObjectValue(Class clazz, T clazzBean, JSONObject object, String fieldName) {
291 | try {
292 | Field field = findClassField(clazz, fieldName);
293 | if (field != null) {
294 | field.setAccessible(true);
295 | Object value = object.get(fieldName);
296 | String valueString = String.valueOf(value);
297 | valueString = isNone(valueString) ? "" : valueString;
298 | Class fieldType = field.getType();
299 | if (isPrimitive(fieldType)) {
300 | //Primitive
301 | setFieldValue(field, clazzBean, valueString);
302 | } else {
303 | //Collection
304 | if (Collection.class.isAssignableFrom(fieldType)) {
305 | Type genericType = field.getGenericType();
306 | if (genericType instanceof ParameterizedType) {
307 | ParameterizedType parameterizedType = (ParameterizedType) genericType;
308 | Class argumentsClazz = (Class) parameterizedType.getActualTypeArguments()[0];
309 | if (isPrimitive(argumentsClazz)) {
310 | field.set(clazzBean, toCollection(valueString, argumentsClazz));
311 | } else {
312 | field.set(clazzBean, toCollection(field, argumentsClazz, valueString));
313 | }
314 | }
315 | } else if (fieldType.isArray()) {
316 | //Array
317 | JSONArray jsonArray = (JSONArray) value;
318 | Class componentType = fieldType.getComponentType();
319 | field.set(clazzBean, newArrayInstance(componentType, jsonArray));
320 | } else if (Map.class.isAssignableFrom(fieldType)) {
321 | //Map
322 | JSONObject jsonObject = (JSONObject) value;
323 | Map map = new HashMap<>();
324 | Iterator it = jsonObject.keys();
325 | while (it.hasNext()) {
326 | String name = (String) it.next();
327 | Object val = jsonObject.get(name);
328 | map.put(name, val);
329 | }
330 | field.set(clazzBean, map);
331 | } else if (JSONArray.class.isAssignableFrom(fieldType)){
332 | //JSONArray
333 | field.set(clazzBean, toCollection(valueString, fieldType));
334 | } else if (JSONObject.class.isAssignableFrom(fieldType)){
335 | //JSONObject
336 | field.set(clazzBean, toObject(valueString, fieldType));
337 | }else if (Object.class.isAssignableFrom(fieldType)){
338 | //Object
339 | field.set(clazzBean,toObject(valueString, fieldType));
340 | } else {
341 | field.set(clazzBean, toObject(valueString, fieldType));
342 | }
343 | }
344 | }
345 | } catch (JSONException e) {
346 | e.printStackTrace();
347 | } catch (IllegalAccessException e) {
348 | e.printStackTrace();
349 | }
350 | }
351 |
352 | /**
353 | * @param json 字符串
354 | * @param clazz 类
355 | * @return 数据对象
356 | */
357 | public static T toObject(String json, Class clazz) {
358 | if (isNone(json)) {
359 | return null;
360 | }
361 | return toObject(toJSONObject(json), clazz);
362 | }
363 |
364 | /**
365 | * @param json Json字符串转
366 | * @return JSONArray对象
367 | */
368 | public static JSONArray toJSONArray(String json) {
369 | if (json == null || json.length() == 0 || json.equals("null")) {
370 | return null;
371 | }
372 | if (!json.startsWith("[{") && !json.endsWith("}]")) {
373 | return null;
374 | }
375 | JSONArray jsonArray = null;
376 | try {
377 | jsonArray = new JSONArray(json);
378 | } catch (JSONException e) {
379 | e.printStackTrace();
380 | }
381 | return jsonArray;
382 | }
383 |
384 | /**
385 | * @param jsonArray JSONArray对象
386 | * @return Map对象的列表数据
387 | */
388 | public static List