├── .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> toMapCollection(JSONArray jsonArray) { 389 | if (jsonArray == null) { 390 | return null; 391 | } 392 | List> list = new ArrayList<>(); 393 | for (int i = 0; i < jsonArray.length(); i++) { 394 | try { 395 | String json = jsonArray.getString(i); 396 | if (json != null && json.length() != 0 && !json.equals("null")) { 397 | JSONObject jsonObject = (JSONObject) jsonArray.get(i); 398 | list.add(toMap(jsonObject)); 399 | } 400 | } catch (JSONException e) { 401 | e.printStackTrace(); 402 | } 403 | } 404 | return list; 405 | } 406 | 407 | /** 408 | * @param json Json字符串 409 | * @return Map对象的列表数据 410 | */ 411 | public static List> toMapCollection(String json) { 412 | if (isNone(json)) { 413 | return null; 414 | } 415 | if (json.equals("[]")) { 416 | return new ArrayList<>(); 417 | } 418 | JSONArray array = toJSONArray(json); 419 | if (array == null) { 420 | return null; 421 | } 422 | return toMapCollection(array); 423 | } 424 | 425 | /** 426 | * @param field 列表字段 427 | * @param clazz 列表字段的参数对象 428 | * @param json json字符串 429 | * @return JsonArray转List对象 430 | */ 431 | public static List toCollection(Field field, Class clazz, String json) { 432 | List list = null; 433 | try { 434 | if (field.getType() == List.class) { 435 | list = new ArrayList<>(); 436 | } else { 437 | list = (List) field.getType().newInstance(); 438 | } 439 | if (isJSONArray(json)) { 440 | JSONArray jsonArray = toJSONArray(json); 441 | int size = jsonArray == null ? 0 : jsonArray.length(); 442 | for (int i = 0; i < size; i++) { 443 | JSONObject jsonObject = (JSONObject) jsonArray.get(i); 444 | T t = toObject(jsonObject, clazz); 445 | list.add(t); 446 | } 447 | } 448 | } catch (InstantiationException e) { 449 | e.printStackTrace(); 450 | } catch (IllegalAccessException e) { 451 | e.printStackTrace(); 452 | } catch (JSONException e) { 453 | e.printStackTrace(); 454 | } 455 | return list; 456 | } 457 | 458 | /** 459 | * Json转List对象,此处只要是是实现了List接口的对象都可以 460 | * 461 | * @param array 数组 462 | * @param clazz 对象类 463 | * @return 列表数据 464 | */ 465 | public static List toCollection(JSONArray array, Class clazz) { 466 | List list = new ArrayList<>(); 467 | if (array == null) { 468 | return list; 469 | } 470 | for (int i = 0; i < array.length(); i++) { 471 | try { 472 | JSONObject jsonObject = (JSONObject) array.get(i); 473 | list.add(toObject(jsonObject, clazz)); 474 | } catch (JSONException e) { 475 | e.printStackTrace(); 476 | } 477 | } 478 | return list; 479 | } 480 | 481 | /** 482 | * JSONArray对象字符串转换为对象 483 | * 484 | * @param json 字符 485 | * @param clazz 对象类 486 | * @return 列表数据 487 | */ 488 | public static List toCollection(String json, Class clazz) { 489 | List list = new ArrayList<>(); 490 | try { 491 | JSONArray jsonArray = new JSONArray(json); 492 | for (int i = 0; i < jsonArray.length(); i++) { 493 | Object obj = jsonArray.get(i); 494 | if (isPrimitive(obj.getClass())) { 495 | list.add((T) obj); 496 | } 497 | if (obj.getClass().isAssignableFrom(JSONObject.class)) { 498 | list.add(toObject((JSONObject) obj, clazz)); 499 | } 500 | } 501 | } catch (JSONException e) { 502 | e.printStackTrace(); 503 | } 504 | return list; 505 | } 506 | 507 | /** 508 | * @param type 类型 509 | * @return 是否基础变量 510 | */ 511 | public static boolean isPrimitive(Class type) { 512 | return type.isPrimitive() 513 | || type.isAssignableFrom(String.class) 514 | || type.isAssignableFrom(Boolean.class) 515 | || type.isAssignableFrom(Character.class) 516 | || type.isAssignableFrom(Byte.class) 517 | || type.isAssignableFrom(Short.class) 518 | || type.isAssignableFrom(Integer.class) 519 | || type.isAssignableFrom(Long.class) 520 | || type.isAssignableFrom(Float.class) 521 | || type.isAssignableFrom(Double.class) 522 | || type.isAssignableFrom(Void.class); 523 | } 524 | 525 | /** 526 | * @param field 字段 527 | * @return 是否预定义字段 528 | */ 529 | public static boolean isPredefined(Field field) { 530 | if (!field.isAccessible()) { 531 | field.setAccessible(true); 532 | } 533 | String name = field.getName(); 534 | return name.equals("$change") 535 | || name.equals("serialVersionUID") 536 | || name.equals("NULL") 537 | || name.equals("NEGATIVE_ZERO"); 538 | } 539 | 540 | /** 541 | * @param json 字符 542 | * @return 是否集合|数组 543 | */ 544 | public static boolean isJSONArray(String json) { 545 | return json != null && json.startsWith("[") && json.endsWith("]"); 546 | } 547 | 548 | /** 549 | * @param json 字符 550 | * @return 是否对象 551 | */ 552 | public static boolean isJSONObject(String json) { 553 | return json != null && json.startsWith("{") && json.endsWith("}"); 554 | } 555 | 556 | /** 557 | * 添加到JSONObject 558 | * 559 | * @param jsonObject json对象 560 | * @param key 键 561 | * @param value 值 562 | */ 563 | public static void addJSONObjectKeyValue(JSONObject jsonObject, String key, Object value) { 564 | try { 565 | if (value != null) { 566 | if (isPrimitive(value.getClass())) { 567 | jsonObject.put(key, value); 568 | } else { 569 | String objValueJson = toJson(value); 570 | jsonObject.put(key, isJSONObject(objValueJson) ? new JSONObject(objValueJson) : new JSONArray(objValueJson)); 571 | } 572 | } 573 | } catch (JSONException e) { 574 | e.printStackTrace(); 575 | } 576 | } 577 | 578 | /** 579 | * @param clazz 类 580 | * @return 当前类及其父类类声明字段 581 | */ 582 | public static Field[] findClassDeclaredFields(Class clazz) { 583 | List fields = new ArrayList<>(); 584 | while (clazz != null) { 585 | for (Field field : clazz.getDeclaredFields()) { 586 | field.setAccessible(true); 587 | fields.add(field); 588 | } 589 | clazz = clazz.getSuperclass(); 590 | } 591 | return fields.toArray(new Field[fields.size()]); 592 | } 593 | 594 | /** 595 | * @param obj 对象 596 | * @return json字符 597 | */ 598 | public static String toJson(Object obj) { 599 | if (obj == null) { 600 | return ""; 601 | } 602 | if (Collection.class.isAssignableFrom(obj.getClass())) { 603 | //Collection 604 | JSONArray jsonArray = new JSONArray(); 605 | List list = (List) obj; 606 | int count = list == null ? 0 : list.size(); 607 | //基本类型集合 608 | List primitiveArray = new ArrayList<>(); 609 | for (int i = 0; i < count; i++) { 610 | Object item = list.get(i); 611 | try { 612 | if (isPrimitive(item.getClass())) { 613 | primitiveArray.add(item); 614 | } else { 615 | jsonArray.put(new JSONObject(toJson(item))); 616 | } 617 | } catch (JSONException e) { 618 | e.printStackTrace(); 619 | } 620 | } 621 | if (primitiveArray.size() > 0) { 622 | jsonArray = new JSONArray(primitiveArray); 623 | } 624 | return jsonArray.toString(); 625 | } else if (Map.class.isAssignableFrom(obj.getClass())) { 626 | //Map 627 | JSONObject jsonObject = new JSONObject(); 628 | Map objMap = (Map) obj; 629 | for (String key : objMap.keySet()) { 630 | Object objValue = objMap.get(key); 631 | addJSONObjectKeyValue(jsonObject, key, objValue); 632 | } 633 | return jsonObject.toString(); 634 | } else if (obj.getClass().isArray()) { 635 | //Array 636 | JSONArray jsonArray = new JSONArray(); 637 | for (int i = 0; i < Array.getLength(obj); i++) { 638 | jsonArray.put(Array.get(obj, i)); 639 | } 640 | return jsonArray.toString(); 641 | } else { 642 | //普通类 643 | JSONObject jsonObject = new JSONObject(); 644 | Field[] fields = findClassDeclaredFields(obj.getClass()); 645 | if (fields.length == 0) { 646 | return jsonObject.toString(); 647 | } 648 | for (Field field : fields) { 649 | field.setAccessible(true); 650 | Class type = field.getType(); 651 | String name = field.getName(); 652 | if (isPredefined(field)) { 653 | continue; 654 | } 655 | try { 656 | //普通类型 657 | Object value = field.get(obj); 658 | if (isPrimitive(type)) { 659 | jsonObject.put(name, value); 660 | } else { 661 | addJSONObjectKeyValue(jsonObject, name, value); 662 | } 663 | } catch (IllegalAccessException | JSONException e) { 664 | e.printStackTrace(); 665 | } 666 | } 667 | return jsonObject.toString(); 668 | } 669 | } 670 | 671 | /** 672 | * JSON格式化 673 | * 674 | * @param json 字符 675 | * @return 676 | */ 677 | public static String format(String json) { 678 | if (isNone(json)) { 679 | return ""; 680 | } 681 | if (isJSONObject(json)) { 682 | try { 683 | JSONObject object = new JSONObject(json); 684 | json = object.toString(2); 685 | } catch (JSONException e) { 686 | e.printStackTrace(); 687 | } 688 | } 689 | if (isJSONArray(json)) { 690 | try { 691 | JSONArray array = new JSONArray(json); 692 | json = array.toString(2); 693 | } catch (JSONException e) { 694 | e.printStackTrace(); 695 | } 696 | } 697 | return json; 698 | } 699 | 700 | } 701 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/net/OnHttpListener.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.net; 2 | 3 | /** 4 | * Created by Ice 5 | * OkHttp回调函数 6 | * on 2017/3/20. 7 | */ 8 | 9 | public interface OnHttpListener { 10 | 11 | /** 12 | * get data from http failure method callback 13 | * 14 | * @param result response succeed information 15 | */ 16 | void onHttpFailure(Response result); 17 | 18 | /** 19 | * get data from http succeed method callback 20 | * 21 | * @param result response succeed information 22 | */ 23 | void onHttpSucceed(Response result); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/net/RequestParams.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.net; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by Relin 8 | * 请求参数 9 | * on 2017/3/13. 10 | */ 11 | 12 | public class RequestParams { 13 | 14 | /** 15 | * 文字参数 16 | */ 17 | private Map stringParams; 18 | 19 | 20 | /** 21 | * 字符串参数 22 | */ 23 | private String body; 24 | 25 | 26 | public RequestParams() { 27 | 28 | } 29 | 30 | /** 31 | * 添加文字参数 32 | * Add String Params 33 | * 34 | * @param key 35 | * @param value 36 | */ 37 | public void add(String key, String value) { 38 | if (stringParams == null) { 39 | stringParams = new HashMap<>(); 40 | } 41 | stringParams.put(key, value == null ? "" : value); 42 | } 43 | 44 | 45 | /** 46 | * 添加字符串实例 47 | * 48 | * @param body 49 | */ 50 | public void addStringBody(String body) { 51 | this.body = body; 52 | } 53 | 54 | /** 55 | * 获取文字参数 56 | * 57 | * @return 58 | */ 59 | public Map getStringParams() { 60 | return stringParams; 61 | } 62 | 63 | /** 64 | * 返回字符串的Body实例 65 | * 66 | * @return 67 | */ 68 | public String getStringBody() { 69 | return body; 70 | } 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/net/Response.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.net; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by Ice on 2017/8/6. 7 | */ 8 | 9 | public class Response implements Serializable { 10 | 11 | //返回的数据 12 | private String body; 13 | //请求地址 14 | private String url; 15 | //请求的结果code 16 | private int code; 17 | //请求参数 18 | private RequestParams requestParams; 19 | //文件流异常 20 | private Exception exception; 21 | //回调接口 22 | private OnHttpListener httpListener; 23 | 24 | public String body() { 25 | return body; 26 | } 27 | 28 | public void body(String body) { 29 | this.body = body; 30 | } 31 | 32 | public String url() { 33 | return url; 34 | } 35 | 36 | public void url(String url) { 37 | this.url = url; 38 | } 39 | 40 | public Exception exception() { 41 | return exception; 42 | } 43 | 44 | public void exception(Exception exception) { 45 | this.exception = exception; 46 | } 47 | 48 | public OnHttpListener listener() { 49 | return httpListener; 50 | } 51 | 52 | public void listener(OnHttpListener httpListener) { 53 | this.httpListener = httpListener; 54 | } 55 | 56 | public RequestParams requestParams() { 57 | return requestParams; 58 | } 59 | 60 | public void requestParams(RequestParams requestParams) { 61 | this.requestParams = requestParams; 62 | } 63 | 64 | public int code() { 65 | return code; 66 | } 67 | 68 | public void code(int code) { 69 | this.code = code; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/uupay/UUPay.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.uupay; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import com.unionpay.UPPayAssistEx; 7 | import com.unionpay.uppay.PayActivity; 8 | 9 | /** 10 | * Created by Ice on 2016/2/23. 11 | * 银联支付 12 | */ 13 | public class UUPay { 14 | 15 | private Activity activity; 16 | 17 | public UUPay(Activity activity) { 18 | this.activity = activity; 19 | } 20 | 21 | /** 22 | * 银联支付 23 | * 24 | * @param tn 流水号 25 | * @param payMode 支付模式 FORM:正式 TEST:测试 26 | */ 27 | public void pay(String tn, Enum payMode) { 28 | if (tn != null) { 29 | UPPayAssistEx.startPayByJAR(activity, PayActivity.class, null, null, tn, payMode.equals(PayMode.TEST) ? "01" : "00"); 30 | }else{ 31 | Log.e(this.getClass().getSimpleName(),"tn is null please check you tn"); 32 | } 33 | } 34 | 35 | public enum PayMode { 36 | TEST, FORM; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/OnWeChatLoginListener.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | /** 4 | * 微信登录回调 5 | */ 6 | public interface OnWeChatLoginListener { 7 | 8 | 9 | /** 10 | * 微信登录回调信息 11 | * 12 | * @param code 13 | * @param msg 14 | * @param user 15 | */ 16 | void onWeChatLogin(int code, String msg, WeChatUser user); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/OnWeChatPayListener.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | public interface OnWeChatPayListener { 4 | 5 | void onWeChatPay(int code, String msg); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/OnWeChatShareListener.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | /** 4 | * 微信分享回调 5 | */ 6 | public interface OnWeChatShareListener { 7 | 8 | 9 | /** 10 | * 微信分享回调 11 | * 12 | * @param code 代码 13 | * @param msg 信息 14 | */ 15 | void onWeChatShare(int code, String msg); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/ShareHelper.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.net.HttpURLConnection; 13 | import java.net.MalformedURLException; 14 | import java.net.URL; 15 | 16 | public class ShareHelper { 17 | 18 | /** 19 | * File 转 byte[] 20 | * 21 | * @param path 文件路径 22 | * @return 23 | */ 24 | public static byte[] decodeFile(String path) { 25 | byte[] buffer = null; 26 | try { 27 | File file = new File(path); 28 | FileInputStream fis = new FileInputStream(file); 29 | ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); 30 | byte[] b = new byte[1024]; 31 | int n; 32 | while ((n = fis.read(b)) != -1) { 33 | bos.write(b, 0, n); 34 | } 35 | fis.close(); 36 | bos.close(); 37 | buffer = bos.toByteArray(); 38 | } catch (FileNotFoundException e) { 39 | e.printStackTrace(); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | return buffer; 44 | } 45 | 46 | /** 47 | * Bitmap转byte[] 48 | * 49 | * @param bitmap 文件位图 50 | * @return 51 | */ 52 | public static byte[] decodeBitmap(Bitmap bitmap) { 53 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 54 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); 55 | return bos.toByteArray(); 56 | } 57 | 58 | /** 59 | * byte[]转Bitmap 60 | * 61 | * @param bytes 文件二进制 62 | * @return 63 | */ 64 | public static Bitmap decodeByte(byte[] bytes) { 65 | if (bytes.length != 0) { 66 | return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 67 | } else { 68 | return null; 69 | } 70 | } 71 | 72 | /** 73 | * 图片URL解析到Bitmap 74 | * 75 | * @param urlPath 图片地址 76 | * @param width 宽度 77 | * @param height 高度 78 | * @param listener 回调 79 | */ 80 | public static void decodeUrl(final String urlPath, final int width, final int height, final OnUrlDecodeBitmapListener listener) { 81 | new Thread() { 82 | @Override 83 | public void run() { 84 | super.run(); 85 | URL url; 86 | try { 87 | url = new URL(urlPath); 88 | HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection(); 89 | httpUrl.connect(); 90 | InputStream inputStream = httpUrl.getInputStream(); 91 | Bitmap thumbImage = BitmapFactory.decodeStream(inputStream); 92 | Bitmap bitmap = Bitmap.createScaledBitmap(thumbImage, width, height, true); 93 | if (listener != null) { 94 | listener.onUrlDecode(bitmap); 95 | } 96 | inputStream.close(); 97 | } catch (MalformedURLException e) { 98 | e.printStackTrace(); 99 | } catch (IOException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | }.start(); 104 | } 105 | 106 | /** 107 | * URL解析到Bitmap 108 | */ 109 | public interface OnUrlDecodeBitmapListener { 110 | 111 | void onUrlDecode(Bitmap bitmap); 112 | 113 | } 114 | 115 | 116 | /*** 117 | * 图片URL转为byte数组 118 | * @param urlPath URL回调地址 119 | * @param listener 解析回调 120 | */ 121 | public static void decodeUrl(final String urlPath, final OnUrlDecodeByteListener listener) { 122 | new Thread() { 123 | @Override 124 | public void run() { 125 | super.run(); 126 | byte[] data; 127 | URL url; 128 | InputStream input; 129 | try { 130 | url = new URL(urlPath); 131 | HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection(); 132 | httpUrl.connect(); 133 | httpUrl.getInputStream(); 134 | input = httpUrl.getInputStream(); 135 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 136 | byte[] buf = new byte[1024]; 137 | int numBytesRead; 138 | while ((numBytesRead = input.read(buf)) != -1) { 139 | output.write(buf, 0, numBytesRead); 140 | } 141 | data = output.toByteArray(); 142 | if (listener != null) { 143 | listener.onUrlDecode(data); 144 | } 145 | output.close(); 146 | input.close(); 147 | } catch (MalformedURLException e) { 148 | e.printStackTrace(); 149 | } catch (IOException e) { 150 | e.printStackTrace(); 151 | } 152 | } 153 | }.start(); 154 | } 155 | 156 | /** 157 | * URL解析byte数据 158 | */ 159 | public interface OnUrlDecodeByteListener { 160 | 161 | void onUrlDecode(byte[] data); 162 | 163 | } 164 | 165 | 166 | } 167 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatAccessToken.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import java.io.Serializable; 4 | 5 | public class WeChatAccessToken implements Serializable { 6 | 7 | /** 8 | * 错误代码 9 | */ 10 | private int errcode = 0; 11 | /** 12 | * 错误信息 13 | */ 14 | private String errmsg; 15 | private String rid; 16 | /** 17 | * 接口调用凭证 18 | */ 19 | private String access_token; 20 | 21 | /** 22 | * 用户刷新 access_token 23 | */ 24 | private String refresh_token; 25 | 26 | /** 27 | * 当且仅当该移动应用已获得该用户的 userinfo 授权时,才会出现该字段 28 | */ 29 | private String unionid; 30 | 31 | /** 32 | * 应用唯一标识,在微信开放平台提交应用审核通过后获得 33 | */ 34 | private String openid; 35 | 36 | /** 37 | * 用户授权的作用域,使用逗号(,)分隔 38 | */ 39 | private String scope; 40 | 41 | /** 42 | * access_token 接口调用凭证超时时间,单位(秒) 43 | */ 44 | private String expires_in; 45 | 46 | public void setAccess_token(String access_token) { 47 | this.access_token = access_token; 48 | } 49 | 50 | public void setRefresh_token(String refresh_token) { 51 | this.refresh_token = refresh_token; 52 | } 53 | 54 | public void setUnionid(String unionid) { 55 | this.unionid = unionid; 56 | } 57 | 58 | public void setOpenid(String openid) { 59 | this.openid = openid; 60 | } 61 | 62 | public void setScope(String scope) { 63 | this.scope = scope; 64 | } 65 | 66 | public void setExpires_in(String expires_in) { 67 | this.expires_in = expires_in; 68 | } 69 | 70 | public String getAccess_token() { 71 | return access_token; 72 | } 73 | 74 | public String getRefresh_token() { 75 | return refresh_token; 76 | } 77 | 78 | public String getUnionid() { 79 | return unionid; 80 | } 81 | 82 | public String getOpenid() { 83 | return openid; 84 | } 85 | 86 | public String getScope() { 87 | return scope; 88 | } 89 | 90 | public String getExpires_in() { 91 | return expires_in; 92 | } 93 | 94 | public int getErrcode() { 95 | return errcode; 96 | } 97 | 98 | public void setErrcode(int errcode) { 99 | this.errcode = errcode; 100 | } 101 | 102 | public String getErrmsg() { 103 | return errmsg; 104 | } 105 | 106 | public void setErrmsg(String errmsg) { 107 | this.errmsg = errmsg; 108 | } 109 | 110 | public String getRid() { 111 | return rid; 112 | } 113 | 114 | public void setRid(String rid) { 115 | this.rid = rid; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatConstants.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import com.tencent.mm.opensdk.constants.ConstantsAPI; 4 | import com.tencent.mm.opensdk.modelbase.BaseResp; 5 | import com.tencent.mm.opensdk.modelmsg.WXMiniProgramObject; 6 | 7 | /** 8 | * 微信常量
9 | */ 10 | public class WeChatConstants { 11 | 12 | /** 13 | * 微信appId 14 | */ 15 | public static String APP_ID; 16 | /** 17 | * 微信secret 18 | */ 19 | public static String APP_SECRET; 20 | /** 21 | * 微信Token地址 22 | */ 23 | public static final String URL_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token"; 24 | /** 25 | * 微信用户信息地址 26 | */ 27 | public static final String URL_USER_INFO = "https://api.weixin.qq.com/sns/userinfo"; 28 | /** 29 | * 微信刷新Token 30 | */ 31 | public static final String URL_REFRESH_TOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token"; 32 | /** 33 | * 微信授权类型 34 | */ 35 | public static final String GRANT_TYPE = "authorization_code"; 36 | /** 37 | * 微信Action 38 | */ 39 | public static final String ACTION = "ACTION_COM_ANDROID_PAY_WX_ACTION"; 40 | /** 41 | * 微信授权失败 42 | */ 43 | public static final int AUTH_DENIED = BaseResp.ErrCode.ERR_AUTH_DENIED; 44 | /** 45 | * 微信用户取消登录 46 | */ 47 | public static final int CANCEL = BaseResp.ErrCode.ERR_USER_CANCEL; 48 | /** 49 | * 平台参数不一致 50 | */ 51 | public static final int ERR_BAN = BaseResp.ErrCode.ERR_BAN; 52 | /** 53 | * 微信用户正在登录 54 | */ 55 | public static final int LOADING = BaseResp.ErrCode.ERR_OK; 56 | /** 57 | * 微信打开小程序 58 | */ 59 | public static final int MINI_PROGRAM_LAUNCHER = ConstantsAPI.COMMAND_LAUNCH_WX_MINIPROGRAM; 60 | /** 61 | * 微信登录 62 | */ 63 | public static final int LOGIN = 1; 64 | /** 65 | * 微信分享 66 | */ 67 | public static final int SHARE = 2; 68 | /** 69 | * 成功 70 | */ 71 | public static final int SUCCEED = 1; 72 | /** 73 | * 失败 74 | */ 75 | public static final int FAILED = -1; 76 | /** 77 | * 代码 78 | */ 79 | public static final String CODE = "code"; 80 | /** 81 | * 消息 82 | */ 83 | public static final String MSG = "msg"; 84 | /** 85 | * 用户信息 86 | */ 87 | public static final String USER_INFO = "user_info"; 88 | /** 89 | * 授权信息 90 | */ 91 | public static final String ACCESS_TOKEN_INFO = "access_token_info"; 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatLogin.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | 9 | import com.tencent.mm.opensdk.modelmsg.SendAuth; 10 | import com.tencent.mm.opensdk.openapi.IWXAPI; 11 | import com.tencent.mm.opensdk.openapi.WXAPIFactory; 12 | 13 | public class WeChatLogin { 14 | 15 | /** 16 | * 微信appId 17 | */ 18 | public final String appId; 19 | 20 | /** 21 | * 上下文对象 22 | */ 23 | public final Context context; 24 | 25 | /** 26 | * 微信Secret 27 | */ 28 | public final String appSecret; 29 | 30 | /** 31 | * 登录接收器 32 | */ 33 | private WeChatReceiver receiver; 34 | 35 | /** 36 | * 登录监听 37 | */ 38 | public final OnWeChatLoginListener listener; 39 | 40 | /** 41 | * 微信登录构造函数 42 | * 43 | * @param builder 构造器 44 | */ 45 | public WeChatLogin(Builder builder) { 46 | this.context = builder.context; 47 | this.appId = builder.appId; 48 | this.appSecret = builder.appSecret; 49 | this.listener = builder.listener; 50 | WeChatConstants.APP_ID = appId; 51 | WeChatConstants.APP_SECRET = appSecret; 52 | if (context instanceof Activity){ 53 | Activity activity = (Activity) context; 54 | if (activity.isFinishing()){ 55 | return; 56 | } 57 | } 58 | if (listener != null && context != null && receiver == null) { 59 | receiver = new WeChatReceiver(); 60 | IntentFilter filter = new IntentFilter(WeChatConstants.ACTION); 61 | context.registerReceiver(receiver, filter); 62 | } 63 | login(appId); 64 | } 65 | 66 | public static class Builder { 67 | /** 68 | * 上下文对象 69 | */ 70 | private Context context; 71 | 72 | /** 73 | * 微信Secret 74 | */ 75 | private String appSecret; 76 | 77 | /** 78 | * 微信appId 79 | */ 80 | private String appId; 81 | 82 | private OnWeChatLoginListener listener; 83 | 84 | public Builder(Context context) { 85 | this.context = context; 86 | 87 | } 88 | 89 | /** 90 | * 获取上下文对象 91 | * 92 | * @return Context 93 | */ 94 | public Context context() { 95 | return context; 96 | } 97 | 98 | 99 | /** 100 | * 获取微信appSecret 101 | * 102 | * @return String 103 | */ 104 | public String appSecret() { 105 | return appSecret; 106 | } 107 | 108 | /** 109 | * 设置微信appSecret 110 | * 111 | * @return Builder 112 | */ 113 | public Builder appSecret(String appSecret) { 114 | this.appSecret = appSecret; 115 | return this; 116 | } 117 | 118 | /** 119 | * 获取微信AppId 120 | * 121 | * @return 122 | */ 123 | public String appId() { 124 | return appId; 125 | } 126 | 127 | /** 128 | * 设置微信AppId 129 | * 130 | * @param appId 131 | * @return 132 | */ 133 | public Builder appId(String appId) { 134 | this.appId = appId; 135 | return this; 136 | } 137 | 138 | /** 139 | * 登录监听 140 | * 141 | * @return 142 | */ 143 | public OnWeChatLoginListener listener() { 144 | return listener; 145 | } 146 | 147 | /** 148 | * 设置登录监听 149 | * 150 | * @param listener 151 | * @return 152 | */ 153 | public Builder listener(OnWeChatLoginListener listener) { 154 | this.listener = listener; 155 | return this; 156 | } 157 | 158 | /** 159 | * 构建登录对象 160 | * 161 | * @return WxLogin 162 | */ 163 | public WeChatLogin build() { 164 | return new WeChatLogin(this); 165 | } 166 | } 167 | 168 | /** 169 | * 登录 170 | * 171 | * @param appId 172 | */ 173 | private void login(String appId) { 174 | WeChatConstants.APP_ID = appId; 175 | IWXAPI wxAPI = WXAPIFactory.createWXAPI(context, appId, true); 176 | wxAPI.registerApp(appId); 177 | SendAuth.Req req = new SendAuth.Req(); 178 | req.scope = "snsapi_userinfo"; 179 | req.state = String.valueOf(System.currentTimeMillis()); 180 | wxAPI.sendReq(req); 181 | } 182 | 183 | /** 184 | * 登录接收器 185 | */ 186 | private class WeChatReceiver extends BroadcastReceiver { 187 | 188 | @Override 189 | public void onReceive(Context context, Intent intent) { 190 | String action = intent.getAction(); 191 | if (action.equals(WeChatConstants.ACTION)) { 192 | int code = intent.getIntExtra(WeChatConstants.CODE, -200); 193 | String msg = intent.getStringExtra(WeChatConstants.MSG); 194 | WeChatUser user = null; 195 | if (intent.getSerializableExtra(WeChatConstants.USER_INFO) != null) { 196 | user = (WeChatUser) intent.getSerializableExtra(WeChatConstants.USER_INFO); 197 | } 198 | if (listener != null) { 199 | listener.onWeChatLogin(code, msg, user); 200 | } 201 | if (context != null && receiver != null && (code == WeChatConstants.SUCCEED || code == WeChatConstants.CANCEL || code == WeChatConstants.AUTH_DENIED)) { 202 | context.unregisterReceiver(receiver); 203 | } 204 | } 205 | } 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatPay.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.util.Log; 8 | 9 | import com.tencent.mm.opensdk.modelpay.PayReq; 10 | import com.tencent.mm.opensdk.openapi.IWXAPI; 11 | import com.tencent.mm.opensdk.openapi.WXAPIFactory; 12 | 13 | 14 | /** 15 | * Created by Ice on 2016/2/18. 16 | * 微信支付 17 | * #WXPay 18 | * APP_ID 19 | * MCH_ID 20 | * API_KEY 21 | */ 22 | public class WeChatPay { 23 | 24 | 25 | 26 | private IWXAPI iwxapi; 27 | 28 | public final Context context; 29 | public final String appId; 30 | public final String partnerId; 31 | public final String prepayId; 32 | public final String nonceStr; 33 | public final String timeStamp; 34 | public final String packageValue; 35 | public final String sign; 36 | public final String extData; 37 | 38 | public final OnWeChatPayListener listener; 39 | 40 | private WXPayReceiver receiver; 41 | 42 | public static class Builder { 43 | 44 | private Context context; 45 | private String appId; 46 | private String partnerId; 47 | private String prepayId; 48 | private String nonceStr; 49 | private String timeStamp; 50 | private String packageValue = "Sign=WXPay"; 51 | private String sign; 52 | private String extData; 53 | 54 | private OnWeChatPayListener listener; 55 | 56 | public Builder(Context context) { 57 | this.context = context; 58 | } 59 | 60 | public String appId() { 61 | return appId; 62 | } 63 | 64 | public Builder appId(String appId) { 65 | this.appId = appId; 66 | return this; 67 | } 68 | 69 | public String partnerId() { 70 | return partnerId; 71 | } 72 | 73 | public Builder partnerId(String partnerId) { 74 | this.partnerId = partnerId; 75 | return this; 76 | } 77 | 78 | public String prepayId() { 79 | return prepayId; 80 | } 81 | 82 | public Builder prepayId(String prepayId) { 83 | this.prepayId = prepayId; 84 | return this; 85 | } 86 | 87 | public String nonceStr() { 88 | return nonceStr; 89 | } 90 | 91 | public Builder nonceStr(String nonceStr) { 92 | this.nonceStr = nonceStr; 93 | return this; 94 | } 95 | 96 | public String timeStamp() { 97 | return timeStamp; 98 | } 99 | 100 | public Builder timeStamp(String timeStamp) { 101 | this.timeStamp = timeStamp; 102 | return this; 103 | } 104 | 105 | public String packageValue() { 106 | return packageValue; 107 | } 108 | 109 | public Builder packageValue(String packageValue) { 110 | this.packageValue = packageValue; 111 | return this; 112 | } 113 | 114 | public String sign() { 115 | return sign; 116 | } 117 | 118 | public Builder sign(String sign) { 119 | this.sign = sign; 120 | return this; 121 | } 122 | 123 | public String extData() { 124 | return extData; 125 | } 126 | 127 | public Builder extData(String extData) { 128 | this.extData = extData; 129 | return this; 130 | } 131 | 132 | public OnWeChatPayListener listener() { 133 | return listener; 134 | } 135 | 136 | public Builder listener(OnWeChatPayListener listener) { 137 | this.listener = listener; 138 | return this; 139 | } 140 | 141 | public WeChatPay build() { 142 | return new WeChatPay(this); 143 | } 144 | 145 | } 146 | 147 | public WeChatPay(Builder builder) { 148 | this.context = builder.context; 149 | this.appId = builder.appId; 150 | this.partnerId = builder.partnerId; 151 | this.prepayId = builder.prepayId; 152 | this.nonceStr = builder.nonceStr; 153 | this.timeStamp = builder.timeStamp; 154 | this.packageValue = builder.packageValue; 155 | this.sign = builder.sign; 156 | this.extData = builder.extData; 157 | this.listener = builder.listener; 158 | if (listener != null && receiver == null) { 159 | IntentFilter filter = new IntentFilter(WeChatConstants.ACTION); 160 | receiver = new WXPayReceiver(); 161 | context.registerReceiver(receiver, filter); 162 | } 163 | WeChatConstants.APP_ID = appId; 164 | iwxapi = WXAPIFactory.createWXAPI(context, appId); 165 | pay(); 166 | } 167 | 168 | public void pay() { 169 | PayReq req = new PayReq(); 170 | req.appId = appId; 171 | req.partnerId = partnerId; 172 | req.prepayId = prepayId; 173 | req.nonceStr = nonceStr; 174 | req.timeStamp = timeStamp; 175 | req.packageValue = packageValue; 176 | req.sign = sign; 177 | req.extData = extData; 178 | Log.i(this.getClass().getSimpleName(), "-[pay]->" + "appId:" + appId + ",partnerId:" + partnerId + ",prepayId:" + prepayId + ",nonceStr:" + nonceStr + ",timeStamp:" + timeStamp + ",packageValue:" + packageValue + ",sign:" + sign + ",extData:" + extData); 179 | iwxapi.registerApp(appId); 180 | iwxapi.sendReq(req); 181 | } 182 | 183 | 184 | private class WXPayReceiver extends BroadcastReceiver { 185 | 186 | @Override 187 | public void onReceive(Context context, Intent intent) { 188 | String action = intent.getAction(); 189 | if (action.equals(WeChatConstants.ACTION)) { 190 | int code = intent.getIntExtra(WeChatConstants.CODE, -1); 191 | String msg = intent.getStringExtra(WeChatConstants.MSG); 192 | if (listener != null) { 193 | listener.onWeChatPay(code, msg); 194 | } 195 | if (context != null && receiver != null) { 196 | context.unregisterReceiver(receiver); 197 | } 198 | } 199 | } 200 | } 201 | 202 | 203 | } 204 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatShare.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | import android.text.TextUtils; 11 | 12 | import com.android.pay.R; 13 | import com.tencent.mm.opensdk.modelmsg.SendMessageToWX; 14 | import com.tencent.mm.opensdk.modelmsg.WXImageObject; 15 | import com.tencent.mm.opensdk.modelmsg.WXMediaMessage; 16 | import com.tencent.mm.opensdk.modelmsg.WXMiniProgramObject; 17 | import com.tencent.mm.opensdk.modelmsg.WXMusicObject; 18 | import com.tencent.mm.opensdk.modelmsg.WXTextObject; 19 | import com.tencent.mm.opensdk.modelmsg.WXVideoObject; 20 | import com.tencent.mm.opensdk.modelmsg.WXWebpageObject; 21 | import com.tencent.mm.opensdk.openapi.IWXAPI; 22 | import com.tencent.mm.opensdk.openapi.WXAPIFactory; 23 | 24 | /** 25 | * 微信分享 26 | */ 27 | public class WeChatShare { 28 | 29 | /** 30 | * 会话场景 31 | */ 32 | public static final int SCENE_SESSION = SendMessageToWX.Req.WXSceneSession; 33 | 34 | /** 35 | * 朋友圈场景 36 | */ 37 | public static final int SCENE_TIME_LINE = SendMessageToWX.Req.WXSceneTimeline; 38 | 39 | /** 40 | * 收藏场景 41 | */ 42 | public static final int SCENE_FAVORITE = SendMessageToWX.Req.WXSceneFavorite; 43 | 44 | /** 45 | * 纯文本 46 | */ 47 | public static final int TYPE_TEXT = 0x010; 48 | /** 49 | * 纯图片 50 | */ 51 | public static final int TYPE_IMAGE = 0x020; 52 | /** 53 | * 音乐 54 | */ 55 | public static final int TYPE_MUSIC = 0x030; 56 | /** 57 | * 视频 58 | */ 59 | public static final int TYPE_VIDEO = 0x040; 60 | /** 61 | * 网页 62 | */ 63 | public static final int TYPE_WEB = 0x050; 64 | /** 65 | * 小程序 66 | */ 67 | public static final int TYPE_MIN_PROGRAM = 0x060; 68 | 69 | /** 70 | * 微信api对象 71 | */ 72 | private IWXAPI api; 73 | 74 | /** 75 | * 上下文对象 76 | */ 77 | public final Context context; 78 | 79 | /** 80 | * 微信appId 81 | */ 82 | public final String appId; 83 | 84 | /** 85 | * 分享场景 86 | */ 87 | public final int scene; 88 | 89 | /** 90 | * 消息标题 91 | */ 92 | public final String title; 93 | 94 | /** 95 | * 消息描述 96 | */ 97 | public final String description; 98 | 99 | /** 100 | * 缩略图的二进制数据,内容大小不超过 10MB 101 | */ 102 | public final byte[] thumbData; 103 | 104 | /** 105 | * 缩略图位图数据,内容大小不超过 10MB 106 | */ 107 | public final Bitmap thumbImage; 108 | 109 | /** 110 | * 缩略图位图数据,内容大小不超过 10MB 111 | */ 112 | public final String thumbUrl; 113 | 114 | /** 115 | * 缩略图大小 116 | */ 117 | public final int thumbSize; 118 | 119 | /** 120 | * 文本 121 | */ 122 | public final String text; 123 | 124 | /** 125 | * 图片的二进制数据,内容大小不超过 10MB 126 | */ 127 | public final byte[] imageData; 128 | 129 | /** 130 | * 图片的本地路径 对应图片内容大小不超过 10MB 131 | */ 132 | public final String imagePath; 133 | 134 | /** 135 | * 图片的网络路径 对应图片内容大小不超过 10MB 136 | */ 137 | public final String imageUrl; 138 | 139 | 140 | /** 141 | * 音频网页的 URL 地址,限制长度不超过 10KB 142 | */ 143 | public final String musicUrl; 144 | 145 | /** 146 | * 供低带宽环境下使用的音频网页 URL 地址,限制长度不超过 10KB 147 | */ 148 | public final String musicLowBandUrl; 149 | 150 | /** 151 | * 音频数据的 URL 地址,限制长度不超过 10KB 152 | */ 153 | public final String musicDataUrl; 154 | 155 | /** 156 | * 供低带宽环境下使用的音频数据 URL 地址,限制长度不超过 10KB 157 | */ 158 | public final String musicLowBandDataUrl; 159 | 160 | 161 | /** 162 | * 视频链接,限制长度不超过 10KB 163 | */ 164 | public final String videoUrl; 165 | 166 | /** 167 | * 供低带宽的环境下使用的视频链接,限制长度不超过 10KB 168 | */ 169 | public final String videoLowBandUrl; 170 | 171 | /** 172 | * html 链接,限制长度不超过 10KB 173 | */ 174 | public final String webpageUrl; 175 | 176 | /** 177 | * 小程序,正式版:0,测试版:1,体验版:2 178 | */ 179 | public final int miniProgramType; 180 | /** 181 | * 小程序原始id 182 | */ 183 | public final String miniProgramUserName; 184 | /** 185 | * 小程序页面路径;对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar" 186 | */ 187 | public final String miniProgramPath; 188 | 189 | /** 190 | * 分享回调函数 191 | */ 192 | public final OnWeChatShareListener listener; 193 | 194 | /** 195 | * 分享类型 196 | */ 197 | public final int type; 198 | 199 | private WeChatReceiver receiver; 200 | 201 | /** 202 | * 分享构造函数 203 | * 204 | * @param builder 分享构建者 205 | */ 206 | public WeChatShare(Builder builder) { 207 | this.context = builder.context; 208 | this.appId = builder.appId; 209 | this.scene = builder.scene; 210 | this.title = builder.title; 211 | this.description = builder.description; 212 | this.thumbData = builder.thumbData; 213 | this.thumbImage = builder.thumbImage; 214 | this.thumbUrl = builder.thumbUrl; 215 | this.thumbSize = builder.thumbSize; 216 | this.text = builder.text; 217 | this.imageData = builder.imageData; 218 | this.imagePath = builder.imagePath; 219 | this.imageUrl = builder.imageUrl; 220 | this.musicUrl = builder.musicUrl; 221 | this.musicLowBandUrl = builder.musicLowBandUrl; 222 | this.musicDataUrl = builder.musicDataUrl; 223 | this.musicLowBandDataUrl = builder.musicLowBandDataUrl; 224 | this.videoUrl = builder.videoUrl; 225 | this.videoLowBandUrl = builder.videoLowBandUrl; 226 | this.webpageUrl = builder.webpageUrl; 227 | this.listener = builder.listener; 228 | this.miniProgramType = builder.miniProgramType; 229 | this.miniProgramUserName = builder.miniProgramUserName; 230 | this.miniProgramPath = builder.miniProgramPath; 231 | this.type = builder.type; 232 | share(); 233 | } 234 | 235 | /** 236 | * 构建者 237 | */ 238 | public static class Builder { 239 | 240 | /** 241 | * 上下文对象 242 | */ 243 | private Context context; 244 | 245 | /** 246 | * 微信appId 247 | */ 248 | private String appId; 249 | 250 | /** 251 | * 分享场景 252 | */ 253 | private int scene; 254 | 255 | /** 256 | * 消息标题 257 | */ 258 | private String title; 259 | 260 | /** 261 | * 消息描述 262 | */ 263 | private String description; 264 | 265 | /** 266 | * 缩略图的二进制数据,内容大小不超过 10MB 267 | */ 268 | private byte[] thumbData; 269 | 270 | /** 271 | * 缩略图位图数据,内容大小不超过 10MB 272 | */ 273 | private Bitmap thumbImage; 274 | 275 | /** 276 | * 缩略图位图数据,内容大小不超过 10MB 277 | */ 278 | private String thumbUrl; 279 | 280 | /** 281 | * 缩略图大小 282 | */ 283 | private int thumbSize = 120; 284 | 285 | /** 286 | * 文本 287 | */ 288 | private String text; 289 | 290 | /** 291 | * 图片的二进制数据,内容大小不超过 10MB 292 | */ 293 | private byte[] imageData; 294 | 295 | /** 296 | * 图片的本地路径 对应图片内容大小不超过 10MB 297 | */ 298 | private String imagePath; 299 | 300 | /** 301 | * 图片的网络路径 对应图片内容大小不超过 10MB 302 | */ 303 | private String imageUrl; 304 | 305 | /** 306 | * 音频网页的 URL 地址,限制长度不超过 10KB 307 | */ 308 | private String musicUrl; 309 | 310 | /** 311 | * 供低带宽环境下使用的音频网页 URL 地址,限制长度不超过 10KB 312 | */ 313 | private String musicLowBandUrl; 314 | 315 | /** 316 | * 音频数据的 URL 地址,限制长度不超过 10KB 317 | */ 318 | private String musicDataUrl; 319 | 320 | /** 321 | * 供低带宽环境下使用的音频数据 URL 地址,限制长度不超过 10KB 322 | */ 323 | private String musicLowBandDataUrl; 324 | 325 | /** 326 | * 视频链接,限制长度不超过 10KB 327 | */ 328 | private String videoUrl; 329 | 330 | /** 331 | * 供低带宽的环境下使用的视频链接,限制长度不超过 10KB 332 | */ 333 | private String videoLowBandUrl; 334 | 335 | /** 336 | * html链接,限制长度不超过 10KB 337 | */ 338 | private String webpageUrl; 339 | 340 | /** 341 | * 分享回调 342 | */ 343 | private OnWeChatShareListener listener; 344 | 345 | /** 346 | * 分享类型(默认Web) 347 | */ 348 | private int type = TYPE_WEB; 349 | 350 | /** 351 | * 小程序,正式版:0,测试版:1,体验版:2 352 | */ 353 | public int miniProgramType = 1; 354 | /** 355 | * 小程序原始id 356 | */ 357 | public String miniProgramUserName; 358 | /** 359 | * 小程序页面路径;对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar" 360 | */ 361 | public String miniProgramPath ; 362 | 363 | /** 364 | * 分享构建者 365 | * 366 | * @param context 367 | */ 368 | public Builder(Context context) { 369 | this.context = context; 370 | } 371 | 372 | /** 373 | * 上下文对象 374 | * 375 | * @return 376 | */ 377 | public Context context() { 378 | return context; 379 | } 380 | 381 | /** 382 | * 微信appId 383 | * 384 | * @return 385 | */ 386 | public String appId() { 387 | return appId; 388 | } 389 | 390 | /** 391 | * 设置微信appId 392 | * 393 | * @param appId 394 | * @return 395 | */ 396 | public Builder appId(String appId) { 397 | this.appId = appId; 398 | return this; 399 | } 400 | 401 | /** 402 | * 会话场景 403 | * 404 | * @return 405 | */ 406 | public int scene() { 407 | return scene; 408 | } 409 | 410 | /** 411 | * 设置会话场景 412 | * 413 | * @param scene 414 | * @return 415 | */ 416 | public Builder scene(int scene) { 417 | this.scene = scene; 418 | return this; 419 | } 420 | 421 | /** 422 | * 消息标题 423 | * 424 | * @return 425 | */ 426 | public String title() { 427 | return title; 428 | } 429 | 430 | /** 431 | * 设置消息标题 432 | * 433 | * @param title 434 | */ 435 | public void title(String title) { 436 | this.title = title; 437 | } 438 | 439 | /** 440 | * 描述信息 441 | * 442 | * @return 443 | */ 444 | public String description() { 445 | return description; 446 | } 447 | 448 | /** 449 | * 描述信息 450 | * 451 | * @param description 452 | */ 453 | public void description(String description) { 454 | this.description = description; 455 | } 456 | 457 | /** 458 | * 缩略图的二进制数据,内容大小不超过 10MB 459 | * 460 | * @return 461 | */ 462 | public byte[] thumbData() { 463 | return thumbData; 464 | } 465 | 466 | /** 467 | * 缩略图的二进制数据,内容大小不超过 10MB 468 | * 469 | * @param thumbData 470 | */ 471 | public void thumbData(byte[] thumbData) { 472 | this.thumbData = thumbData; 473 | } 474 | 475 | /** 476 | * 缩略图的网络地址,内容大小不超过 10MB 477 | * 478 | * @return 479 | */ 480 | public String thumbUrl() { 481 | return thumbUrl; 482 | } 483 | 484 | /** 485 | * 缩略图的网络地址,内容大小不超过 10MB 486 | * 487 | * @param thumbUrl 488 | * @return 489 | */ 490 | public Builder thumbUrl(String thumbUrl) { 491 | this.thumbUrl = thumbUrl; 492 | return this; 493 | } 494 | 495 | /** 496 | * 缩略图大小 497 | * 498 | * @return 499 | */ 500 | public int thumbSize() { 501 | return thumbSize; 502 | } 503 | 504 | /** 505 | * 缩略图大小 506 | * 507 | * @param thumbSize 508 | */ 509 | public void thumbSize(int thumbSize) { 510 | this.thumbSize = thumbSize; 511 | } 512 | 513 | /** 514 | * 分享图片的缩略图,内容大小不超过 10MB 515 | * 516 | * @return 517 | */ 518 | public Bitmap thumbImage() { 519 | return thumbImage; 520 | } 521 | 522 | /** 523 | * 分享图片的缩略图,内容大小不超过 10MB 524 | * 525 | * @return 526 | */ 527 | public void thumbImage(Bitmap thumbImage) { 528 | this.thumbImage = thumbImage; 529 | } 530 | 531 | /** 532 | * 文本内容 533 | * 534 | * @return 535 | */ 536 | public String text() { 537 | return text; 538 | } 539 | 540 | /** 541 | * 设置文本内容 542 | * 543 | * @param text 544 | */ 545 | public void text(String text) { 546 | this.text = text; 547 | } 548 | 549 | /** 550 | * 图片的二进制数据 551 | * 552 | * @return 553 | */ 554 | public byte[] imageData() { 555 | return imageData; 556 | } 557 | 558 | /** 559 | * 图片的二进制数据,内容大小不超过 10MB 560 | * 561 | * @param imageData 562 | */ 563 | public void imageData(byte[] imageData) { 564 | this.imageData = imageData; 565 | } 566 | 567 | /** 568 | * 图片的本地路径,对应图片内容大小不超过 10MB 569 | * 570 | * @return 571 | */ 572 | public String imagePath() { 573 | return imagePath; 574 | } 575 | 576 | /** 577 | * 图片的本地路径,对应图片内容大小不超过 10MB 578 | * 579 | * @param imagePath 580 | */ 581 | public void imagePath(String imagePath) { 582 | this.imagePath = imagePath; 583 | } 584 | 585 | /** 586 | * 图片的网络路径,对应图片内容大小不超过 10MB 587 | * 588 | * @return 589 | */ 590 | public String imageUrl() { 591 | return imageUrl; 592 | } 593 | 594 | /** 595 | * 图片的网络路径,对应图片内容大小不超过 10MB 596 | * 597 | * @return 598 | */ 599 | public void imageUrl(String imageUrl) { 600 | this.imageUrl = imageUrl; 601 | } 602 | 603 | /** 604 | * 音频网页的 URL 地址,限制长度不超过 10KB 605 | * 606 | * @return 607 | */ 608 | public String musicUrl() { 609 | return musicUrl; 610 | } 611 | 612 | /** 613 | * 音频网页的 URL 地址,限制长度不超过 10KB 614 | * 615 | * @param musicUrl 616 | */ 617 | public Builder musicUrl(String musicUrl) { 618 | this.musicUrl = musicUrl; 619 | return this; 620 | } 621 | 622 | /** 623 | * 供低带宽环境下使用的音频网页 URL 地址,限制长度不超过 10KB 624 | * 625 | * @return 626 | */ 627 | public String musicLowBandUrl() { 628 | return musicLowBandUrl; 629 | } 630 | 631 | /** 632 | * 供低带宽环境下使用的音频网页 URL 地址,限制长度不超过 10KB 633 | * 634 | * @return 635 | */ 636 | public Builder musicLowBandUrl(String musicLowBandUrl) { 637 | this.musicLowBandUrl = musicLowBandUrl; 638 | return this; 639 | } 640 | 641 | /** 642 | * 音频数据的URL地址,限制长度不超过 10KB 643 | * 644 | * @return 645 | */ 646 | public String musicDataUrl() { 647 | return musicDataUrl; 648 | } 649 | 650 | /** 651 | * 音频数据的 URL 地址,限制长度不超过 10KB 652 | * 653 | * @param musicDataUrl 654 | */ 655 | public Builder musicDataUrl(String musicDataUrl) { 656 | this.musicDataUrl = musicDataUrl; 657 | return this; 658 | } 659 | 660 | /** 661 | * 供低带宽环境下使用的音频数据 URL 地址,限制长度不超过 10KB 662 | * 663 | * @return 664 | */ 665 | public String musicLowBandDataUrl() { 666 | return musicLowBandDataUrl; 667 | } 668 | 669 | /** 670 | * 供低带宽环境下使用的音频数据 URL 地址,限制长度不超过 10KB 671 | * 672 | * @param musicLowBandDataUrl 673 | */ 674 | public Builder musicLowBandDataUrl(String musicLowBandDataUrl) { 675 | this.musicLowBandDataUrl = musicLowBandDataUrl; 676 | return this; 677 | } 678 | 679 | /** 680 | * 视频链接,限制长度不超过10KB 681 | * 682 | * @return 683 | */ 684 | public String videoUrl() { 685 | return videoUrl; 686 | } 687 | 688 | /** 689 | * 视频链接,限制长度不超过10KB 690 | * 691 | * @param videoUrl 692 | */ 693 | public Builder videoUrl(String videoUrl) { 694 | this.videoUrl = videoUrl; 695 | return this; 696 | } 697 | 698 | /** 699 | * 供低带宽的环境下使用的视频链接,限制长度不超过10KB 700 | * 701 | * @return 702 | */ 703 | public String videoLowBandUrl() { 704 | return videoLowBandUrl; 705 | } 706 | 707 | /** 708 | * 供低带宽的环境下使用的视频链接,限制长度不超过10KB 709 | * 710 | * @return 711 | */ 712 | public Builder videoLowBandUrl(String videoLowBandUrl) { 713 | this.videoLowBandUrl = videoLowBandUrl; 714 | return this; 715 | } 716 | 717 | /** 718 | * html 链接,限制长度不超过 10KB 719 | * 720 | * @return 721 | */ 722 | public String webageUrl() { 723 | return webpageUrl; 724 | } 725 | 726 | /** 727 | * html 链接,限制长度不超过10KB 728 | * 729 | * @param webpageUrl 730 | */ 731 | public void webpageUrl(String webpageUrl) { 732 | this.webpageUrl = webpageUrl; 733 | } 734 | 735 | /** 736 | * 分享回调函数 737 | * 738 | * @return 739 | */ 740 | public OnWeChatShareListener listener() { 741 | return listener; 742 | } 743 | 744 | /** 745 | * 分享回调 746 | * 747 | * @param listener 748 | */ 749 | public void listener(OnWeChatShareListener listener) { 750 | this.listener = listener; 751 | } 752 | 753 | /** 754 | * 分享类型 755 | * 756 | * @return 757 | */ 758 | public int type() { 759 | return type; 760 | } 761 | 762 | /** 763 | * 分享类型 764 | * 765 | * @param type 766 | * @return 767 | */ 768 | public Builder type(int type) { 769 | this.type = type; 770 | return this; 771 | } 772 | 773 | /** 774 | * @return 小程序:正式版:0,测试版:1,体验版:2 775 | */ 776 | public int getMiniProgramType() { 777 | return type; 778 | } 779 | 780 | /** 781 | * 小程序:正式版:0,测试版:1,体验版:2 782 | * 783 | * @param miniProgramType 正式版:0,测试版:1,体验版:2 784 | * @return 785 | */ 786 | public Builder miniProgramType(int miniProgramType) { 787 | this.miniProgramType = miniProgramType; 788 | return this; 789 | } 790 | 791 | /** 792 | * @return 小程序原始id 793 | */ 794 | public String getMiniProgramUserName() { 795 | return miniProgramUserName; 796 | } 797 | 798 | /** 799 | * 小程序,小程序原始id 800 | * 801 | * @param miniProgramUserName 小程序原始id 802 | * @return 803 | */ 804 | public Builder miniProgramUserName(String miniProgramUserName) { 805 | this.miniProgramUserName = miniProgramUserName; 806 | return this; 807 | } 808 | 809 | /** 810 | * @return 小程序页面路径;对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar" 811 | */ 812 | public String getMiniProgramPath() { 813 | return miniProgramPath; 814 | } 815 | 816 | /** 817 | * 小程序页面路径;对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar" 818 | * 819 | * @param miniProgramPath 小程序原始id 820 | * @return 821 | */ 822 | public Builder miniProgramPath(String miniProgramPath) { 823 | this.miniProgramPath = miniProgramPath; 824 | return this; 825 | } 826 | 827 | /** 828 | * 构建分享对象进行分享 829 | * 830 | * @return 831 | */ 832 | public WeChatShare build() { 833 | return new WeChatShare(this); 834 | } 835 | 836 | } 837 | 838 | /** 839 | * 分享 840 | */ 841 | private void share() { 842 | if (context instanceof Activity) { 843 | Activity activity = (Activity) context; 844 | if (activity.isFinishing()) { 845 | return; 846 | } 847 | } 848 | if (receiver != null) { 849 | context.unregisterReceiver(receiver); 850 | receiver = null; 851 | } 852 | if (listener != null && context != null && receiver == null) { 853 | receiver = new WeChatReceiver(); 854 | IntentFilter filter = new IntentFilter(WeChatConstants.ACTION); 855 | context.registerReceiver(receiver, filter); 856 | } 857 | api = WXAPIFactory.createWXAPI(context, WeChatConstants.APP_ID, true); 858 | api.registerApp(appId); 859 | //分享类型 860 | if (type == TYPE_TEXT) { 861 | shareText(); 862 | } 863 | if (type == TYPE_IMAGE) { 864 | if (imageUrl != null) { 865 | ShareHelper.decodeUrl(imageUrl, new ShareHelper.OnUrlDecodeByteListener() { 866 | @Override 867 | public void onUrlDecode(byte[] data) { 868 | shareImage(data); 869 | } 870 | }); 871 | } else { 872 | shareImage(imageData); 873 | } 874 | } 875 | if (type == TYPE_MUSIC) { 876 | shareMusic(); 877 | } 878 | if (type == TYPE_VIDEO) { 879 | shareVideo(); 880 | } 881 | if (type == TYPE_WEB) { 882 | shareWebPage(); 883 | } 884 | if (type == TYPE_MIN_PROGRAM) { 885 | shareMiniProgram(); 886 | } 887 | } 888 | 889 | /** 890 | * 分享纯文本 891 | */ 892 | private void shareText() { 893 | if (text == null) { 894 | return; 895 | } 896 | WXTextObject textObj = new WXTextObject(); 897 | textObj.text = text; 898 | WXMediaMessage msg = new WXMediaMessage(); 899 | msg.mediaObject = textObj; 900 | msg.description = text; 901 | shareMessage("text" + System.currentTimeMillis(), msg, scene, ""); 902 | } 903 | 904 | /** 905 | * 分享本地图片 906 | */ 907 | private void shareImage(byte[] imageData) { 908 | //图片的二进制数据 和 图片的本地路径都为空 909 | if (imageData == null && imagePath == null) { 910 | return; 911 | } 912 | WXImageObject imgObj = new WXImageObject(); 913 | if (imageData != null) { 914 | imgObj.imageData = imageData; 915 | } 916 | if (imagePath != null) { 917 | imgObj.imagePath = imagePath; 918 | } 919 | WXMediaMessage msg = new WXMediaMessage(); 920 | msg.mediaObject = imgObj; 921 | if (!TextUtils.isEmpty(title)) { 922 | msg.title = title; 923 | } 924 | if (!TextUtils.isEmpty(description)) { 925 | msg.description = description; 926 | } 927 | if (!TextUtils.isEmpty(imagePath)) { 928 | Bitmap bitmap = BitmapFactory.decodeFile(imagePath); 929 | Bitmap thumbBmp = Bitmap.createScaledBitmap(bitmap, thumbSize, thumbSize, true); 930 | msg.thumbData = ShareHelper.decodeBitmap(thumbBmp); 931 | } 932 | if (thumbImage != null) { 933 | Bitmap bitmap = Bitmap.createScaledBitmap(thumbImage, thumbSize, thumbSize, true); 934 | msg.thumbData = ShareHelper.decodeBitmap(bitmap); 935 | } 936 | if (thumbData != null && thumbData.length != 0) { 937 | msg.thumbData = thumbData; 938 | } 939 | shareMessage("image" + System.currentTimeMillis(), msg, scene, ""); 940 | } 941 | 942 | /** 943 | * 分享音乐 944 | */ 945 | private void shareMusic() { 946 | if (musicUrl == null && musicLowBandDataUrl == null && musicDataUrl == null && musicLowBandUrl == null) { 947 | return; 948 | } 949 | WXMusicObject music = new WXMusicObject(); 950 | if (!TextUtils.isEmpty(musicUrl)) { 951 | music.musicUrl = musicUrl; 952 | } 953 | if (!TextUtils.isEmpty(musicLowBandDataUrl)) { 954 | music.musicLowBandDataUrl = musicLowBandDataUrl; 955 | } 956 | if (!TextUtils.isEmpty(musicDataUrl)) { 957 | music.musicDataUrl = musicDataUrl; 958 | } 959 | if (!TextUtils.isEmpty(musicLowBandUrl)) { 960 | music.musicLowBandUrl = musicLowBandUrl; 961 | } 962 | WXMediaMessage msg = new WXMediaMessage(); 963 | msg.mediaObject = music; 964 | if (!TextUtils.isEmpty(title)) { 965 | msg.title = title; 966 | } 967 | if (!TextUtils.isEmpty(description)) { 968 | msg.description = description; 969 | } 970 | if (thumbImage != null) { 971 | Bitmap bitmap = Bitmap.createScaledBitmap(thumbImage, thumbSize, thumbSize, true); 972 | msg.thumbData = ShareHelper.decodeBitmap(bitmap); 973 | } 974 | if (thumbData != null && thumbData.length != 0) { 975 | msg.thumbData = thumbData; 976 | } 977 | shareMessage("music" + System.currentTimeMillis(), msg, scene, ""); 978 | } 979 | 980 | /** 981 | * 分享视频 982 | */ 983 | private void shareVideo() { 984 | if (videoUrl == null && videoLowBandUrl == null) { 985 | return; 986 | } 987 | WXVideoObject video = new WXVideoObject(); 988 | if (!TextUtils.isEmpty(videoUrl)) { 989 | video.videoUrl = videoUrl; 990 | } 991 | if (!TextUtils.isEmpty(videoLowBandUrl)) { 992 | video.videoLowBandUrl = videoLowBandUrl; 993 | } 994 | WXMediaMessage msg = new WXMediaMessage(video); 995 | msg.title = title; 996 | msg.description = description; 997 | if (thumbImage != null) { 998 | Bitmap bitmap = Bitmap.createScaledBitmap(thumbImage, thumbSize, thumbSize, true); 999 | msg.thumbData = ShareHelper.decodeBitmap(bitmap); 1000 | } 1001 | if (thumbData != null && thumbData.length != 0) { 1002 | msg.thumbData = thumbData; 1003 | } 1004 | shareMessage("video" + System.currentTimeMillis(), msg, scene, ""); 1005 | } 1006 | 1007 | /** 1008 | * 分享网页 1009 | */ 1010 | private void shareWebPage() { 1011 | if (webpageUrl == null) { 1012 | return; 1013 | } 1014 | WXWebpageObject webPageObject = new WXWebpageObject(); 1015 | webPageObject.webpageUrl = webpageUrl; 1016 | WXMediaMessage msg = new WXMediaMessage(webPageObject); 1017 | msg.title = title; 1018 | msg.description = description; 1019 | if (thumbImage != null) { 1020 | Bitmap bitmap = Bitmap.createScaledBitmap(thumbImage, thumbSize, thumbSize, true); 1021 | msg.thumbData = ShareHelper.decodeBitmap(bitmap); 1022 | } 1023 | if (thumbData != null && thumbData.length != 0) { 1024 | msg.thumbData = thumbData; 1025 | } 1026 | shareMessage("webpage" + System.currentTimeMillis(), msg, scene, ""); 1027 | } 1028 | 1029 | /** 1030 | * 分享小程序 1031 | */ 1032 | private void shareMiniProgram() { 1033 | if (webpageUrl == null) { 1034 | return; 1035 | } 1036 | WXMiniProgramObject miniProgramObject = new WXMiniProgramObject(); 1037 | miniProgramObject.webpageUrl = webpageUrl; 1038 | miniProgramObject.miniprogramType = miniProgramType; 1039 | miniProgramObject.userName = miniProgramUserName; 1040 | miniProgramObject.path = miniProgramPath; 1041 | WXMediaMessage msg = new WXMediaMessage(miniProgramObject); 1042 | msg.title = title; 1043 | msg.description = description; 1044 | if (thumbImage != null) { 1045 | Bitmap bitmap = Bitmap.createScaledBitmap(thumbImage, thumbSize, thumbSize, true); 1046 | msg.thumbData = ShareHelper.decodeBitmap(bitmap); 1047 | } 1048 | if (thumbData != null && thumbData.length != 0) { 1049 | msg.thumbData = thumbData; 1050 | } 1051 | shareMessage("miniProgram" + System.currentTimeMillis(), msg, scene, ""); 1052 | } 1053 | 1054 | /** 1055 | * 调用api接口,发送数据到微信 1056 | * 1057 | * @param transaction 对应该请求的事务 ID,通常由 Req 发起,回复 Resp 时应填入对应事务 ID 1058 | * @param message 微信消息 1059 | * @param scene 场景 1060 | * @param openId 授权获取的openId 1061 | */ 1062 | public void shareMessage(final String transaction, final WXMediaMessage message, final int scene, final String openId) { 1063 | if (thumbUrl != null) { 1064 | ShareHelper.decodeUrl(thumbUrl, thumbSize, thumbSize, new ShareHelper.OnUrlDecodeBitmapListener() { 1065 | @Override 1066 | public void onUrlDecode(Bitmap srcBitmap) { 1067 | Bitmap bitmap = Bitmap.createScaledBitmap(srcBitmap, thumbSize, thumbSize, true); 1068 | message.thumbData = ShareHelper.decodeBitmap(bitmap); 1069 | SendMessageToWX.Req req = new SendMessageToWX.Req(); 1070 | req.transaction = transaction; 1071 | req.message = message; 1072 | req.openId = openId; 1073 | req.scene = scene; 1074 | api.sendReq(req); 1075 | } 1076 | }); 1077 | } else { 1078 | SendMessageToWX.Req req = new SendMessageToWX.Req(); 1079 | req.transaction = transaction; 1080 | req.message = message; 1081 | req.openId = openId; 1082 | req.scene = scene; 1083 | api.sendReq(req); 1084 | } 1085 | } 1086 | 1087 | /** 1088 | * 登录接收器 1089 | */ 1090 | private class WeChatReceiver extends BroadcastReceiver { 1091 | 1092 | @Override 1093 | public void onReceive(Context context, Intent intent) { 1094 | String action = intent.getAction(); 1095 | if (action.equals(WeChatConstants.ACTION)) { 1096 | int code = intent.getIntExtra(WeChatConstants.CODE, -200); 1097 | String msg = intent.getStringExtra(WeChatConstants.MSG); 1098 | if (listener != null) { 1099 | listener.onWeChatShare(code, msg); 1100 | } 1101 | if (context != null && receiver != null && (code == WeChatConstants.SUCCEED || code == WeChatConstants.CANCEL || code == WeChatConstants.AUTH_DENIED)) { 1102 | context.unregisterReceiver(receiver); 1103 | receiver = null; 1104 | } 1105 | } 1106 | } 1107 | } 1108 | 1109 | 1110 | } 1111 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatUser.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import java.io.Serializable; 4 | 5 | public class WeChatUser implements Serializable { 6 | 7 | /** 8 | * 国家,如中国为 CN 9 | */ 10 | private String country; 11 | 12 | /** 13 | * 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的 unionid 是唯一的。 14 | */ 15 | private String unionid; 16 | 17 | /** 18 | * 普通用户个人资料填写的省份 19 | */ 20 | private String province; 21 | 22 | /** 23 | * 普通用户个人资料填写的城市 24 | */ 25 | private String city; 26 | 27 | /** 28 | * 普通用户的标识,对当前开发者帐号唯一 29 | */ 30 | private String openid; 31 | 32 | /** 33 | * 普通用户性别,1 为男性,2 为女性 34 | */ 35 | private String sex; 36 | 37 | /** 38 | * 普通用户昵称 39 | */ 40 | private String nickname; 41 | 42 | /** 43 | * 用户头像,最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640*640 正方形头像),用户没有头像时该项为空 44 | */ 45 | private String headimgurl; 46 | 47 | /** 48 | * 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom) 49 | */ 50 | private String privilege; 51 | 52 | public void setCountry(String country) { 53 | this.country = country; 54 | } 55 | 56 | public void setUnionid(String unionid) { 57 | this.unionid = unionid; 58 | } 59 | 60 | public void setProvince(String province) { 61 | this.province = province; 62 | } 63 | 64 | public void setCity(String city) { 65 | this.city = city; 66 | } 67 | 68 | public void setOpenid(String openid) { 69 | this.openid = openid; 70 | } 71 | 72 | public void setSex(String sex) { 73 | this.sex = sex; 74 | } 75 | 76 | public void setNickname(String nickname) { 77 | this.nickname = nickname; 78 | } 79 | 80 | public void setHeadimgurl(String headimgurl) { 81 | this.headimgurl = headimgurl; 82 | } 83 | 84 | public void setPrivilege(String privilege) { 85 | this.privilege = privilege; 86 | } 87 | 88 | public String getCountry() { 89 | return country; 90 | } 91 | 92 | public String getUnionid() { 93 | return unionid; 94 | } 95 | 96 | public String getProvince() { 97 | return province; 98 | } 99 | 100 | public String getCity() { 101 | return city; 102 | } 103 | 104 | public String getOpenid() { 105 | return openid; 106 | } 107 | 108 | public String getSex() { 109 | return sex; 110 | } 111 | 112 | public String getNickname() { 113 | return nickname; 114 | } 115 | 116 | public String getHeadimgurl() { 117 | return headimgurl; 118 | } 119 | 120 | public String getPrivilege() { 121 | return privilege; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wechat/WeChatUtils.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wechat; 2 | 3 | import android.content.Context; 4 | import android.net.wifi.WifiInfo; 5 | import android.net.wifi.WifiManager; 6 | import android.util.Log; 7 | import android.util.Xml; 8 | 9 | import org.xmlpull.v1.XmlPullParser; 10 | 11 | import java.io.StringReader; 12 | import java.net.Inet4Address; 13 | import java.net.InetAddress; 14 | import java.net.NetworkInterface; 15 | import java.net.SocketException; 16 | import java.security.MessageDigest; 17 | import java.util.Comparator; 18 | import java.util.Enumeration; 19 | import java.util.HashMap; 20 | import java.util.Iterator; 21 | import java.util.Map; 22 | import java.util.Random; 23 | import java.util.TreeMap; 24 | 25 | /** 26 | * Created by Ice on 2016/2/19. 27 | * 微信支付工具 28 | */ 29 | public class WeChatUtils { 30 | 31 | /** 32 | * 根包签名 33 | * 34 | * @param params 签名的参数 35 | * @return 返回签名 36 | */ 37 | public final static String getSign(Context context, String apiKey, Map params) { 38 | StringBuilder sb = new StringBuilder(); 39 | Map map = sortMapByKey(params); 40 | Iterator> iterator = map.entrySet().iterator(); 41 | while (iterator.hasNext()) { 42 | Map.Entry entry = iterator.next(); 43 | sb.append(entry.getKey()); 44 | sb.append('='); 45 | sb.append(entry.getValue()); 46 | sb.append('&'); 47 | } 48 | sb.append("key="); 49 | sb.append(apiKey); 50 | String packageSign = getMessageDigest(sb.toString().getBytes()).toUpperCase(); 51 | return packageSign; 52 | } 53 | 54 | /** 55 | * 利用参数生成包签名 56 | * 57 | * @param buffer 58 | * @return 59 | */ 60 | private static final String getMessageDigest(byte[] buffer) { 61 | char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 62 | try { 63 | MessageDigest mdTemp = MessageDigest.getInstance("MD5"); 64 | mdTemp.update(buffer); 65 | byte[] md = mdTemp.digest(); 66 | int j = md.length; 67 | char str[] = new char[j * 2]; 68 | int k = 0; 69 | for (int i = 0; i < j; i++) { 70 | byte byte0 = md[i]; 71 | str[k++] = hexDigits[byte0 >>> 4 & 0xf]; 72 | str[k++] = hexDigits[byte0 & 0xf]; 73 | } 74 | return new String(str); 75 | } catch (Exception e) { 76 | return null; 77 | } 78 | } 79 | 80 | /** 81 | * 把参数组成XML字符串 82 | * 83 | * @param map 84 | * @return 85 | */ 86 | public static String paramsToXml(TreeMap map) { 87 | StringBuilder sb = new StringBuilder(); 88 | sb.append(""); 89 | Iterator> iterator = map.entrySet().iterator(); 90 | while (iterator.hasNext()) { 91 | Map.Entry entry = iterator.next(); 92 | sb.append("<" + entry.getKey() + ">"); 93 | sb.append(entry.getValue()); 94 | sb.append(""); 95 | } 96 | sb.append(""); 97 | return sb.toString(); 98 | } 99 | 100 | /** 101 | * 解析返回的XML字符串 102 | * 103 | * @param content 104 | * @return 105 | */ 106 | public static final Map decodeXml(String content) { 107 | try { 108 | Map xml = new HashMap(); 109 | XmlPullParser parser = Xml.newPullParser(); 110 | parser.setInput(new StringReader(content)); 111 | int event = parser.getEventType(); 112 | while (event != XmlPullParser.END_DOCUMENT) { 113 | String nodeName = parser.getName(); 114 | switch (event) { 115 | case XmlPullParser.START_DOCUMENT: 116 | break; 117 | case XmlPullParser.START_TAG: 118 | if ("xml".equals(nodeName) == false) { 119 | // 实例化student对象 120 | xml.put(nodeName, parser.nextText()); 121 | } 122 | break; 123 | case XmlPullParser.END_TAG: 124 | break; 125 | } 126 | event = parser.next(); 127 | } 128 | return xml; 129 | } catch (Exception e) { 130 | Log.e("WxUtils", e.toString()); 131 | } 132 | return null; 133 | } 134 | 135 | public static final String getNonceStr() { 136 | Random random = new Random(); 137 | return getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); 138 | } 139 | 140 | public static final String getOutTradNo() { 141 | Random random = new Random(); 142 | return getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); 143 | } 144 | 145 | public static final long getTimeStamp() { 146 | return System.currentTimeMillis() / 1000; 147 | } 148 | 149 | 150 | /** 151 | * 得到手机IP地址 152 | * 153 | * @return 154 | */ 155 | public static final String getLocalIpAddress(Context context) { 156 | //获取wifi服务 157 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 158 | //判断wifi是否开启 159 | if (wifiManager.isWifiEnabled()) { 160 | WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 161 | int ipAddress = wifiInfo.getIpAddress(); 162 | return intToIp(ipAddress); 163 | } else { 164 | try { 165 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { 166 | NetworkInterface intf = en.nextElement(); 167 | for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { 168 | InetAddress inetAddress = enumIpAddr.nextElement(); 169 | if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) { 170 | return inetAddress.getHostAddress().toString(); 171 | } 172 | } 173 | } 174 | } catch (SocketException ex) { 175 | 176 | } 177 | } 178 | return null; 179 | } 180 | 181 | private static String intToIp(int i) { 182 | return (i & 0xFF) + "." + 183 | ((i >> 8) & 0xFF) + "." + 184 | ((i >> 16) & 0xFF) + "." + 185 | (i >> 24 & 0xFF); 186 | } 187 | 188 | /** 189 | * 元转分 190 | * 191 | * @param money 192 | * @return 193 | */ 194 | public static String parseMoneyToFen(String money) { 195 | return String.valueOf((int) (Double.parseDouble(money) * 100)); 196 | } 197 | 198 | /** 199 | * 使用 Map按key进行排序 200 | * 201 | * @param map 202 | * @return 203 | */ 204 | public static Map sortMapByKey(Map map) { 205 | if (map == null || map.isEmpty()) { 206 | return null; 207 | } 208 | Map sortMap = new TreeMap<>(new MapKeyComparator()); 209 | sortMap.putAll(map); 210 | return sortMap; 211 | } 212 | 213 | public static class MapKeyComparator implements Comparator { 214 | @Override 215 | public int compare(String str1, String str2) { 216 | return str1.compareTo(str2); 217 | } 218 | } 219 | 220 | 221 | } 222 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wxapi/WeChatAuthActivity.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wxapi; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | 8 | import com.android.pay.net.Http; 9 | import com.android.pay.net.JSON; 10 | import com.android.pay.net.OnHttpListener; 11 | import com.android.pay.net.RequestParams; 12 | import com.android.pay.net.Response; 13 | import com.android.pay.wechat.WeChatAccessToken; 14 | import com.android.pay.wechat.WeChatConstants; 15 | import com.android.pay.wechat.WeChatUser; 16 | import com.tencent.mm.opensdk.modelbase.BaseReq; 17 | import com.tencent.mm.opensdk.modelbase.BaseResp; 18 | import com.tencent.mm.opensdk.modelmsg.SendAuth; 19 | import com.tencent.mm.opensdk.openapi.IWXAPI; 20 | import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler; 21 | import com.tencent.mm.opensdk.openapi.WXAPIFactory; 22 | 23 | /** 24 | * 微信授权页面 25 | */ 26 | public class WeChatAuthActivity extends Activity implements IWXAPIEventHandler, OnHttpListener { 27 | 28 | private IWXAPI api; 29 | private String TAG = "WeChatAuthActivity"; 30 | private int type; 31 | private WeChatUser userInfo; 32 | private WeChatAccessToken accessToken; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | Log.i(TAG, "[onCreate]"); 38 | api = WXAPIFactory.createWXAPI(this, WeChatConstants.APP_ID, false); 39 | api.registerApp(WeChatConstants.APP_ID); 40 | try { 41 | api.handleIntent(getIntent(), this); 42 | } catch (Exception e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | @Override 48 | protected void onNewIntent(Intent intent) { 49 | super.onNewIntent(intent); 50 | setIntent(intent); 51 | api.handleIntent(getIntent(), this); 52 | } 53 | 54 | @Override 55 | public void onReq(BaseReq arg0) { 56 | Log.i(TAG, "[onReq] -> arg0:" + arg0); 57 | } 58 | 59 | @Override 60 | public void onResp(BaseResp resp) { 61 | type = resp.getType(); 62 | Bundle bundle = new Bundle(); 63 | resp.toBundle(bundle); 64 | Log.i(TAG, "-[onResp]->type:" + type + ",code:" + resp.errCode + ",openId:" + resp.openId + ",bundle:" + bundle.toString()); 65 | switch (resp.errCode) { 66 | case BaseResp.ErrCode.ERR_BAN: 67 | Log.i(TAG, "-[onResp]-> 运行参数与平台配置参数不一致"); 68 | sendMessage(WeChatConstants.FAILED, "参数与平台配置参数不一致"); 69 | break; 70 | case BaseResp.ErrCode.ERR_AUTH_DENIED: 71 | Log.i(TAG, "-[onResp]-> 拒绝授权微信登录"); 72 | sendMessage(WeChatConstants.AUTH_DENIED, "已拒绝授权微信登录"); 73 | break; 74 | case BaseResp.ErrCode.ERR_USER_CANCEL: 75 | String message = ""; 76 | if (type == WeChatConstants.LOGIN) { 77 | message = "登录取消"; 78 | } 79 | if (type == WeChatConstants.SHARE) { 80 | message = "分享取消"; 81 | } 82 | Log.i(TAG, "-[onResp]-> " + message); 83 | sendMessage(WeChatConstants.CANCEL, message); 84 | break; 85 | case BaseResp.ErrCode.ERR_OK: 86 | if (type == WeChatConstants.LOGIN) { 87 | Log.i(TAG, "-[onResp]-> 用户开始微信授权"); 88 | String code = ((SendAuth.Resp) resp).code; 89 | sendMessage(WeChatConstants.LOADING, "用户开始微信授权"); 90 | reqAccessToken(code, WeChatConstants.APP_ID, WeChatConstants.APP_SECRET, WeChatConstants.GRANT_TYPE); 91 | } 92 | if (type == WeChatConstants.SHARE) { 93 | Log.i(TAG, "-[onResp]-> 用户分享结束"); 94 | sendMessage(WeChatConstants.SUCCEED, "分享成功"); 95 | } 96 | if (type == WeChatConstants.MINI_PROGRAM_LAUNCHER) { 97 | Log.i(TAG, "-[onResp]-> 微信小程序打开APP"); 98 | sendMessage(WeChatConstants.SUCCEED, "微信小程序打开APP"); 99 | } 100 | break; 101 | } 102 | finish(); 103 | } 104 | 105 | /** 106 | * 通过code获取access_token 107 | * 108 | * @param code 填写第一步获取的 code 参数 109 | * @param appid 应用唯一标识,在微信开放平台提交应用审核通过后获得 110 | * @param secret 应用密钥 AppSecret,在微信开放平台提交应用审核通过后获得 111 | * @param grant_type 112 | */ 113 | private void reqAccessToken(String code, String appid, String secret, String grant_type) { 114 | RequestParams params = new RequestParams(); 115 | params.add("code", code); 116 | params.add("appid", appid); 117 | params.add("secret", secret); 118 | params.add("grant_type", grant_type); 119 | Http.get(this, WeChatConstants.URL_ACCESS_TOKEN, params, this); 120 | } 121 | 122 | /** 123 | * 获取用户个人信息(UnionID 机制) 124 | * 125 | * @param access_token 调用凭证 126 | * @param openid 普通用户的标识,对当前开发者帐号唯一 127 | * @param lang 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语,默认为 zh-CN 128 | */ 129 | private void reqUserInfo(String access_token, String openid, String lang) { 130 | RequestParams params = new RequestParams(); 131 | params.add("openid", openid); 132 | params.add("access_token", access_token); 133 | params.add("lang", lang); 134 | Http.get(this, WeChatConstants.URL_USER_INFO, params, this); 135 | } 136 | 137 | /** 138 | * 刷新或续期 access_token 使用 139 | * 140 | * @param appid 应用唯一标识 141 | * @param refresh_token 填写通过 access_token 获取到的 refresh_token 参数 142 | */ 143 | private void reqRefreshToken(String appid, String refresh_token) { 144 | RequestParams params = new RequestParams(); 145 | params.add("appid", appid); 146 | params.add("grant_type", "refresh_token"); 147 | params.add("refresh_token", refresh_token); 148 | Http.get(this, WeChatConstants.URL_REFRESH_TOKEN, params, this); 149 | } 150 | 151 | @Override 152 | public void onHttpSucceed(Response result) { 153 | if (result.url().contains(WeChatConstants.URL_ACCESS_TOKEN)) { 154 | accessToken = JSON.toObject(result.body(), WeChatAccessToken.class); 155 | if (accessToken.getErrcode() == BaseResp.ErrCode.ERR_OK) { 156 | reqRefreshToken(WeChatConstants.APP_ID, accessToken.getRefresh_token()); 157 | } else { 158 | Log.i(TAG, "-[ACCESS_TOKEN]-> errorCode:" + accessToken.getErrcode()+",errorMsg:"+accessToken.getErrmsg()); 159 | sendMessage(WeChatConstants.FAILED,accessToken.getErrmsg()); 160 | } 161 | } 162 | if (result.url().contains(WeChatConstants.URL_REFRESH_TOKEN)) { 163 | accessToken = JSON.toObject(result.body(), WeChatAccessToken.class); 164 | if (accessToken.getErrcode() == BaseResp.ErrCode.ERR_OK) { 165 | reqUserInfo(accessToken.getAccess_token(), accessToken.getOpenid(), "zh-CN"); 166 | } else { 167 | Log.i(TAG, "-[REFRESH_TOKEN]-> errorCode:" + accessToken.getErrcode()+",errorMsg:"+accessToken.getErrmsg()); 168 | sendMessage(WeChatConstants.FAILED,accessToken.getErrmsg()); 169 | } 170 | } 171 | if (result.url().contains(WeChatConstants.URL_USER_INFO)) { 172 | userInfo = JSON.toObject(result.body(), WeChatUser.class); 173 | Intent intent = new Intent(WeChatConstants.ACTION); 174 | intent.putExtra(WeChatConstants.ACCESS_TOKEN_INFO, accessToken); 175 | intent.putExtra(WeChatConstants.USER_INFO, userInfo); 176 | intent.putExtra(WeChatConstants.CODE, WeChatConstants.SUCCEED); 177 | intent.putExtra(WeChatConstants.MSG, "登录成功"); 178 | sendBroadcast(intent); 179 | } 180 | } 181 | 182 | @Override 183 | public void onHttpFailure(Response response) { 184 | Log.i(TAG, "-[onHttpFailure]-> msg:" + response.exception().toString()); 185 | } 186 | 187 | /** 188 | * 发送信息 189 | * 190 | * @param code 代码类别 191 | * @param msg 信息 192 | */ 193 | private void sendMessage(int code, String msg) { 194 | Intent intent = new Intent(WeChatConstants.ACTION); 195 | intent.putExtra(WeChatConstants.CODE, code); 196 | intent.putExtra(WeChatConstants.MSG, msg); 197 | sendBroadcast(intent); 198 | } 199 | 200 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/pay/wxapi/WeChatPayActivity.java: -------------------------------------------------------------------------------- 1 | package com.android.pay.wxapi; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | 8 | import com.android.pay.R; 9 | import com.android.pay.wechat.WeChatConstants; 10 | import com.android.pay.wechat.WeChatPay; 11 | import com.tencent.mm.opensdk.constants.ConstantsAPI; 12 | import com.tencent.mm.opensdk.modelbase.BaseReq; 13 | import com.tencent.mm.opensdk.modelbase.BaseResp; 14 | import com.tencent.mm.opensdk.openapi.IWXAPI; 15 | import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler; 16 | import com.tencent.mm.opensdk.openapi.WXAPIFactory; 17 | 18 | 19 | public class WeChatPayActivity extends Activity implements IWXAPIEventHandler { 20 | 21 | private IWXAPI api; 22 | private final String TAG = "WXPayEntryActivity"; 23 | 24 | @Override 25 | public void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.android_aty_wx_pay); 28 | api = WXAPIFactory.createWXAPI(this, WeChatConstants.APP_ID); 29 | api.handleIntent(getIntent(), this); 30 | } 31 | 32 | @Override 33 | protected void onNewIntent(Intent intent) { 34 | super.onNewIntent(intent); 35 | setIntent(intent); 36 | api.handleIntent(intent, this); 37 | } 38 | 39 | @Override 40 | public void onResp(BaseResp baseResp) { 41 | //错误参数 https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2 42 | //errorCode:[0:success,-1:fail,-2:cancel] 43 | String msg = ""; 44 | int code = baseResp.errCode; 45 | if (baseResp.errCode == 0) { 46 | msg = "支付成功"; 47 | code = WeChatConstants.SUCCEED; 48 | Log.i(TAG, "-[onResp]-> 微信支付成功,展示成功页面。"); 49 | } 50 | if (baseResp.errCode == -1) { 51 | msg = "支付失败"; 52 | code = WeChatConstants.FAILED; 53 | Log.e(TAG, "-[onResp]-> 微信支付调用失败,可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。"); 54 | } 55 | if (baseResp.errCode == -2) { 56 | msg = "取消支付"; 57 | code = WeChatConstants.CANCEL; 58 | Log.i(TAG, "-[onResp]-> 微信支付用户取消,无需处理。发生场景:用户不支付了,点击取消,返回APP。"); 59 | } 60 | if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { 61 | Intent intent = new Intent(WeChatConstants.ACTION); 62 | intent.putExtra(WeChatConstants.CODE, code); 63 | intent.putExtra(WeChatConstants.MSG, msg); 64 | sendBroadcast(intent); 65 | } 66 | finish(); 67 | } 68 | 69 | @Override 70 | public void onReq(BaseReq baseReq) { 71 | 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/android_aty_wx_pay.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidPay 3 | 4 | WX_APP_ID 5 | 6 | -------------------------------------------------------------------------------- /app/src/test/java/com/android/pay/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.android.pay; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.0.2" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | jcenter() // Warning: this repository is going to shut down soon 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RelinRan/AndroidPay/cabecb790be506d2e443c8ba359bba3d87724703/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 22 12:16:16 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "AndroidPay" 2 | include ':app' 3 | --------------------------------------------------------------------------------