From 6239fb5fba506294182f13a4c2aeeb05ae7e8ee9 Mon Sep 17 00:00:00 2001 From: Samwel Date: Thu, 4 Jul 2024 22:10:50 +0300 Subject: [PATCH] code optimization --- app/build.gradle | 2 + app/google-services.json | 826 ++++++++++++++++++ .../MPESAInstanceIDService.java | 25 +- .../MPESAMessagingService.java | 115 ++- .../mpesaintegrationandroid/MainActivity.java | 130 ++- .../res/drawable/ic_stat_ic_notification.png | Bin 0 -> 924 bytes build.gradle | 1 + gradle/libs.versions.toml | 6 +- .../mpesa/Api/ApiUtils.java | 4 - .../mpesa/Api/AuthenticationAPI.java | 4 - .../Interceptors/AccessTokenInterceptor.java | 4 - .../Api/Interceptors/AuthInterceptor.java | 4 - .../mpesa/Api/Models/AccessToken.java | 4 - .../mpesa/Api/Models/B2CPaymentRequest.java | 4 - .../mpesa/Api/Models/B2CPaymentResponse.java | 4 - .../mpesa/Api/Models/C2BPaymentRequest.java | 4 - .../mpesa/Api/Models/C2BPaymentResponse.java | 4 - .../mpesa/Api/MpesaApi.java | 4 - .../mpesa/Api/Network/NetworkClient.java | 4 - .../mpesa/Api/Network/TLSSocketFactory.java | 4 - .../mpesa/Api/Uris.java | 4 - .../mpesaandroidintegration/mpesa/Mode.java | 6 + .../mpesaandroidintegration/mpesa/Mpesa.java | 174 ++++ .../mpesa/MpesaLibrary.java | 4 - .../mpesa/Utils/CommonUtils.java | 4 - .../mpesa/Utils/Enumerations.java | 4 - .../mpesa/interfaces/AuthListener.java | 9 + .../mpesa/interfaces/MpesaListener.java | 9 + .../mpesa/models/STKPush.java | 139 +++ .../mpesa/network/NetworkHandler.java | 121 +++ .../mpesa/utils/Config.java | 12 + .../mpesa/utils/Pair.java | 10 + .../mpesa/utils/Preferences.java | 35 + .../mpesa/utils/TimeUtil.java | 12 + .../mpesa/utils/Utils.java | 36 + 35 files changed, 1664 insertions(+), 68 deletions(-) create mode 100644 app/google-services.json create mode 100644 app/src/main/res/drawable/ic_stat_ic_notification.png delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/ApiUtils.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/AuthenticationAPI.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Interceptors/AccessTokenInterceptor.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Interceptors/AuthInterceptor.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Models/AccessToken.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Models/B2CPaymentRequest.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Models/B2CPaymentResponse.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Models/C2BPaymentRequest.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Models/C2BPaymentResponse.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/MpesaApi.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Network/NetworkClient.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Network/TLSSocketFactory.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Api/Uris.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Mode.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/MpesaLibrary.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/CommonUtils.java delete mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/Enumerations.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/AuthListener.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/MpesaListener.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/models/STKPush.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/network/NetworkHandler.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Config.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Pair.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Preferences.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/TimeUtil.java create mode 100644 mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Utils.java diff --git a/app/build.gradle b/app/build.gradle index 48ad0b5..07fcd04 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.application' + alias(libs.plugins.google.gms.google.services) } android { @@ -31,6 +32,7 @@ android { dependencies { //butter knife implementation libs.butterknife + implementation libs.firebase.messaging annotationProcessor libs.butterknife.compiler //Networking implementation libs.retrofit diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..fbefbb8 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,826 @@ +{ + "project_info": { + "project_number": "95503285654", + "firebase_url": "https://buzz-fc665-default-rtdb.firebaseio.com", + "project_id": "buzz-fc665", + "storage_bucket": "buzz-fc665.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:60539aaf8559ee315571b7", + "android_client_info": { + "package_name": "cn.e_cart.e_cart" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-1dkb8ogt355u6fu1tl7a92b6n338b3f6.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "cn.e_cart.e_cart", + "certificate_hash": "de763318084d28179f9c03605ded3a7e5190deec" + } + }, + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:4b8f056120fe012e5571b7", + "android_client_info": { + "package_name": "cn.e_cart.rider" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-prmmrgof90aephie182jkdath26t5j6c.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "cn.e_cart.rider", + "certificate_hash": "de763318084d28179f9c03605ded3a7e5190deec" + } + }, + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:8ad508f7e4f2ecd15571b7", + "android_client_info": { + "package_name": "cn.e_cart.vendor" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-1p3d79fabjjcuiqvt02jtirn7e3bde38.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "cn.e_cart.vendor", + "certificate_hash": "de763318084d28179f9c03605ded3a7e5190deec" + } + }, + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:bc12b8a276dfd5dc5571b7", + "android_client_info": { + "package_name": "cn.samvpn.samvpn" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:dc9a194d4c19ccaa5571b7", + "android_client_info": { + "package_name": "cn.workoutmanager.workoutmanager" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:bc09491f2563e4315571b7", + "android_client_info": { + "package_name": "com.bloggernewsapp.bloggernewsapp" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:06fb612dae3c76675571b7", + "android_client_info": { + "package_name": "com.bloggingapp.blog" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:a54badf436306f2f5571b7", + "android_client_info": { + "package_name": "com.chatting.wchat" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7gnbp00polem7m8mb7k2m6hs8afg1pfq.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.chatting.wchat", + "certificate_hash": "de763318084d28179f9c03605ded3a7e5190deec" + } + }, + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:9fe47334e8266d425571b7", + "android_client_info": { + "package_name": "com.dairyproductscustomerapp.dairyproductscustomerapp" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:1731f65403b2b5425571b7", + "android_client_info": { + "package_name": "com.eazyybudget.eazzybudget" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:acbfcb8bf2e5cc0d5571b7", + "android_client_info": { + "package_name": "com.firebaseauth.firebaseauth" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:9172c85a7912104a5571b7", + "android_client_info": { + "package_name": "com.kheuattendance.khua" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:fabed5b412b5afba5571b7", + "android_client_info": { + "package_name": "com.ludoclassic.ludoclassic" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:0fcbae99dca424355571b7", + "android_client_info": { + "package_name": "com.mpesaandroidintegration.mpesaintegrationandroid" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:8857d05a467cdbb95571b7", + "android_client_info": { + "package_name": "com.myfriend.myfriend" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:f715a4452c3ee66c5571b7", + "android_client_info": { + "package_name": "com.oddjob.dialapadobgyn" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:28cc28e42083a0d45571b7", + "android_client_info": { + "package_name": "com.onlinequiz.onlinequiz" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:0379236fbb383ca45571b7", + "android_client_info": { + "package_name": "com.pdfreader.pdfreader" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:d14a7d224feb70c05571b7", + "android_client_info": { + "package_name": "com.pharmafast.deliveryboy" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:d6b55ce492e49f065571b7", + "android_client_info": { + "package_name": "com.pharmafast.pharmafast" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:4e7beb1b637a68725571b7", + "android_client_info": { + "package_name": "com.primevideoflix.primevideoflix" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:70681b39ca80285a5571b7", + "android_client_info": { + "package_name": "com.slimpdf.slimpdfapp" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:36d1dfb91e01b2275571b7", + "android_client_info": { + "package_name": "com.smartschool.smartschool" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:379eb95127f262f95571b7", + "android_client_info": { + "package_name": "com.stickersapp.stickers_app" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:2d8e68f49fc6952c5571b7", + "android_client_info": { + "package_name": "com.tclpay.tclpay" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:dc2659ce56e14a305571b7", + "android_client_info": { + "package_name": "com.whatsup.whatsup" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:95503285654:android:0eafffa123d28af85571b7", + "android_client_info": { + "package_name": "dev.samwelnyandoro.messenger" + } + }, + "oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBgKRUWRUCzMUSCzGbKcWgDApX2k1OGYWg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "95503285654-7rttfnr15cu0nu9uoshqg0ao3o8s2k9g.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAInstanceIDService.java b/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAInstanceIDService.java index ad2726a..26e8f2c 100644 --- a/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAInstanceIDService.java +++ b/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAInstanceIDService.java @@ -1,4 +1,27 @@ package com.mpesaandroidintegration.mpesaintegrationandroid; -public class MPESAInstanceIDService { +import android.content.SharedPreferences; +import android.util.Log; +import com.google.firebase.messaging.FirebaseMessagingService; + +public class MPESAInstanceIDService extends FirebaseMessagingService { + private static final String TAG = "MPESAInstanceIDService"; + @Override + public void onNewToken(String token) { + Log.d(TAG, "Refreshed token: " + token); + + // Save token to shared preferences + SharedPreferences sharedPreferences = getSharedPreferences(MainActivity.SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString("InstanceID", token); + editor.apply(); + + // Optionally, send token to your app server. + sendRegistrationToServer(token); + } + + private void sendRegistrationToServer(String token) { + // Implement this method to send token to your app server. + } } + diff --git a/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAMessagingService.java b/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAMessagingService.java index 1fa9197..bbfc5a9 100644 --- a/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAMessagingService.java +++ b/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MPESAMessagingService.java @@ -1,4 +1,117 @@ package com.mpesaandroidintegration.mpesaintegrationandroid; -public class MPESAMessagingService { +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.RingtoneManager; +import android.net.Uri; +import android.util.Log; + +import androidx.core.app.NotificationCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +import org.json.JSONObject; + +/** + * Created by miles on 20/11/2017. + */ + +public class MPESAMessagingService extends FirebaseMessagingService { + + private static final String TAG = "MPESAMessagingService"; + + /** + * Called when message is received. + * + * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. + */ + // [START receive_message] + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + // [START_EXCLUDE] + // There are two types of messages data messages and notification messages. Data messages are handled + // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type + // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app + // is in the foreground. When the app is in the background an automatically generated notification is displayed. + // When the user taps on the notification they are returned to the app. Messages containing both notification + // and data payloads are treated as notification messages. The Firebase console always sends notification + // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options + // [END_EXCLUDE] + + // TODO(developer): Handle FCM messages here. + // Not getting messages here? See why this may be: https://goo.gl/39bRNJ + //Log.d(TAG, "From: " + remoteMessage.getFrom()); + + // Check if message contains a data payload. + if (remoteMessage.getData().size() > 0) { + Log.d(TAG, "Message data payload: " + remoteMessage.getData()); + + handleNow(remoteMessage.getData().toString()); + + } + + // Check if message contains a notification payload. + if (remoteMessage.getNotification() != null) { + Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); + } + + // Also if you intend on generating your own notifications as a result of a received FCM + // message, here is where that should be initiated. See sendNotification method below. + } + // [END receive_message] + + + /** + * Handle time allotted to BroadcastReceivers. + */ + private void handleNow(String result) { + try { + JSONObject json = new JSONObject(result); + JSONObject data = json.getJSONObject("data"); + String title = data.getString("title"); + String message = data.getString("message"); + int code = data.getInt("code"); + //TODO read more attributes here + sendNotification(title, message); + + Intent pushNotification = new Intent(MainActivity.NOTIFICATION); + pushNotification.putExtra("message", message); + pushNotification.putExtra("title", title); + pushNotification.putExtra("code", code); + LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification); + + } catch (Exception e) { + Log.e(TAG, "Exception: " + e.getMessage()); + } + } + + /** + * Create and show a simple notification containing the received FCM message. + * + */ + private void sendNotification(String title, String message) { + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, + PendingIntent.FLAG_ONE_SHOT); + + Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + NotificationCompat.Builder notificationBuilder = + new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_stat_ic_notification) + .setContentTitle(title) + .setContentText(message) + .setAutoCancel(true) + .setSound(defaultSoundUri) + .setContentIntent(pendingIntent); + + NotificationManager notificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + notificationManager.notify(1 /* ID of notification */, notificationBuilder.build()); + } } diff --git a/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MainActivity.java b/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MainActivity.java index 7dc9974..d049fd7 100644 --- a/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MainActivity.java +++ b/app/src/main/java/com/mpesaandroidintegration/mpesaintegrationandroid/MainActivity.java @@ -4,11 +4,139 @@ import android.os.Bundle; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements AuthListener, MpesaListener { + //TODO: Replace these values from + public static final String BUSINESS_SHORT_CODE = "YOUR_SHORTCODE"; + public static final String PASSKEY = "YOUR_PASSKEY"; + public static final String CONSUMER_KEY = "YOUR_KEY"; + public static final String CONSUMER_SECRET = "YOUR_SECRET"; + public static final String CALLBACK_URL = "YOUR_CALLBACK_URL"; + + + public static final String NOTIFICATION = "PushNotification"; + public static final String SHARED_PREFERENCES = "com.bdhobare.mpesa_android_sdk"; + + Button pay; + ProgressDialog dialog; + EditText phone; + EditText amount; + + private BroadcastReceiver mRegistrationBroadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + pay = (Button)findViewById(R.id.pay); + phone = (EditText)findViewById(R.id.phone); + amount = (EditText)findViewById(R.id.amount); + Mpesa.with(this, CONSUMER_KEY, CONSUMER_SECRET); + dialog = new ProgressDialog(this); + dialog.setMessage("Processing"); + dialog.setIndeterminate(true); + + pay.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + String p = phone.getText().toString(); + int a = Integer.valueOf(amount.getText().toString()); + if (p.isEmpty()){ + phone.setError("Enter phone."); + return; + } + pay(p, a); + } + }); + + mRegistrationBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(NOTIFICATION)) { + String title = intent.getStringExtra("title"); + String message = intent.getStringExtra("message"); + int code = intent.getIntExtra("code", 0); + showDialog(title, message, code); + + } + } + }; + } + + @Override + public void onAuthError(Pair result) { + Log.e("Error", result.message); + } + + @Override + public void onAuthSuccess() { + + //TODO make payment + pay.setEnabled(true); + } + private void pay(String phone, int amount){ + dialog.show(); + STKPush.Builder builder = new Builder(BUSINESS_SHORT_CODE, PASSKEY, amount,BUSINESS_SHORT_CODE, phone); + + SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + String token = sharedPreferences.getString("InstanceID", ""); + + builder.setFirebaseRegID(token); + STKPush push = builder.build(); + + + + Mpesa.getInstance().pay(this, push); + + } + private void showDialog(String title, String message,int code){ + MaterialDialog dialog = new MaterialDialog.Builder(this) + .title(title) + .titleGravity(GravityEnum.CENTER) + .customView(R.layout.success_dialog, true) + .positiveText("OK") + .cancelable(false) + .widgetColorRes(R.color.colorPrimary) + .callback(new ButtonCallback() { + @Override + public void onPositive(MaterialDialog dialog) { + super.onPositive(dialog); + dialog.dismiss(); + finish(); + } + }) + .build(); + View view=dialog.getCustomView(); + TextView messageText = (TextView)view.findViewById(R.id.message); + ImageView imageView = (ImageView)view.findViewById(R.id.success); + if (code != 0){ + imageView.setVisibility(View.GONE); + } + messageText.setText(message); + dialog.show(); + } + + @Override + public void onMpesaError(Pair result) { + dialog.hide(); + Toast.makeText(this, result.message, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onMpesaSuccess(String MerchantRequestID, String CheckoutRequestID, String CustomerMessage) { + dialog.hide(); + Toast.makeText(this, CustomerMessage, Toast.LENGTH_SHORT).show(); + } + @Override + protected void onResume() { + super.onResume(); + + LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, + new IntentFilter(NOTIFICATION)); + + } + @Override + protected void onPause() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver); + super.onPause(); } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_stat_ic_notification.png b/app/src/main/res/drawable/ic_stat_ic_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..b32f64d17c4706d106b7dab2ba11a9bcb490c135 GIT binary patch literal 924 zcmV;N17rM&P)0P{Bng2z>LC;jdO%|%3?Ya@DvKIKLIg#LkwFm>NDt`&MWeFlF=$W; zN@etL)S##+dr%RDsDsL$K5lE#e`;~q=iGa4fe!u;e&nvb_PP7~d+)XPH6w!%LI@#* z5JCtcgb+dqA%qY@2q9#okVqunXkr;xh2IMv3;di4LWVVJ5 z?u+;QCUd{b0|%=hT_C`iMg zT#WIDXlg*t9IZ2sxUR5Q*joCW^P&OW5m5~KESiMB*Bv?Q%?zAKV+BpY%k_B0G>t7* zkNnr7)jDY@_N%blPyUwUb3+t^blHuewmqOnFXoK_uZ-|eVD zx-uR+q(!>WK7$_ML~<$S81F>UMx0u`#;jV$RiYGQXmwm|lohEOG-zr=8)hkn z&u6ebit+hMh(XUp8;sV8mgq}4zBkeq3#m;N_+DkZuYEt_b)uO@-nP3D#h^lbUW(2& zx^yx7%_jd^Iz{?l4ARNNixmX4mtdv5H}@tj2i=Kq^u4D?H7FY~tpU3nW(cf&m+8lEu>)!@PEI`>-svG@?nD~9m1GRwovu18 z=`61CabLU+M`WI(@U{D{0@DyX9KoP+^hu@(zFM?Hiz{yaFZAA~#o3p)34JS*jG+v9 yjr_L-A%qY@2qA> { + + @Override + protected Pair doInBackground(String... strings) { + HashMap headers = new HashMap<>(); + headers.put("Authorization", "Basic " + Preferences.getInstance().getAuthorization()); + return NetworkHandler.doGet(strings[0], headers); + } + + protected void onPostExecute(Pair result){ + if (result == null){ + Mpesa.getInstance().authListener.onAuthError(new Pair<>(418, Config.ERROR)); //User is a teapot :( + return; + } + if (result.code == 400){ + Mpesa.getInstance().authListener.onAuthError(new Pair<>(result.code, "Invalid credentials")); + return; + } + try { + JsonParser jsonParser = new JsonParser(); + JsonObject jo = (JsonObject) jsonParser.parse(result.message).getAsJsonObject(); + if (result.code / 100 != 2) { + //Error occurred + String message = jo.get("errorMessage").getAsString(); + Mpesa.getInstance().authListener.onAuthError(new Pair<>(result.code, message)); + return; + } + String access_token = jo.get("access_token").getAsString(); + Preferences.getInstance().setAccessToken(access_token); + Mpesa.getInstance().authListener.onAuthSuccess(); + return; + }catch (Exception e) { + String message = "Error completing fetching token.Please try again."; + Mpesa.getInstance().authListener.onAuthError(new Pair<>(result.code, message)); + } + } + } + static class PayService extends AsyncTask >{ + @Override + protected Pair doInBackground(String... strings) { + HashMap headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + Preferences.getInstance().getAccessToken()); + return NetworkHandler.doPost(strings[0],strings[1], headers); + } + + protected void onPostExecute(Pair result) { + if (result == null) { + Mpesa.getInstance().mpesaListener.onMpesaError(new Pair<>(418, Config.ERROR)); //User is a teapot :( + return; + } + try { + JsonParser jsonParser = new JsonParser(); + + JsonObject jo = (JsonObject) jsonParser.parse(result.message).getAsJsonObject(); + if (result.code / 100 != 2) { + //Error occurred + if (jo.has("errorMessage")) { + String message = jo.get("errorMessage").getAsString(); + Mpesa.getInstance().mpesaListener.onMpesaError(new Pair<>(result.code, message)); + return; + } + String message = "Error completing payment.Please try again."; + Mpesa.getInstance().mpesaListener.onMpesaError(new Pair<>(result.code, message)); + return; + } + Mpesa.getInstance().mpesaListener.onMpesaSuccess(jo.get("MerchantRequestID").getAsString(), jo.get("CheckoutRequestID").getAsString(), jo.get("CustomerMessage").getAsString()); + return; + } catch (Exception e) { + String message = "Error completing payment.Please try again."; + Mpesa.getInstance().mpesaListener.onMpesaError(new Pair<>(result.code, message)); + } + } + } + } diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/MpesaLibrary.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/MpesaLibrary.java deleted file mode 100644 index 9dcd5b4..0000000 --- a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/MpesaLibrary.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.mpesaandroidintegration.mpesa; - -public class MpesaLibrary { -} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/CommonUtils.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/CommonUtils.java deleted file mode 100644 index 05d2273..0000000 --- a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/CommonUtils.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.mpesaandroidintegration.mpesa.Utils; - -public class CommonUtils { -} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/Enumerations.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/Enumerations.java deleted file mode 100644 index aed08fe..0000000 --- a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/Utils/Enumerations.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.mpesaandroidintegration.mpesa.Utils; - -public class Enumerations { -} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/AuthListener.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/AuthListener.java new file mode 100644 index 0000000..1794c97 --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/AuthListener.java @@ -0,0 +1,9 @@ +package com.mpesaandroidintegration.mpesa.interfaces; + + +import com.mpesaandroidintegration.mpesa.utils.Pair; + +public interface AuthListener { + public void onAuthError(Pair result); + public void onAuthSuccess(); +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/MpesaListener.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/MpesaListener.java new file mode 100644 index 0000000..c67d5a0 --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/interfaces/MpesaListener.java @@ -0,0 +1,9 @@ +package com.mpesaandroidintegration.mpesa.interfaces; + + +import com.mpesaandroidintegration.mpesa.utils.Pair; + +public interface MpesaListener { + public void onMpesaError(Pair result); + public void onMpesaSuccess(String MerchantRequestID, String CheckoutRequestID, String CustomerMessage); +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/models/STKPush.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/models/STKPush.java new file mode 100644 index 0000000..82e750f --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/models/STKPush.java @@ -0,0 +1,139 @@ +package com.mpesaandroidintegration.mpesa.models; + +import android.util.Base64; + +import com.mpesaandroidintegration.mpesa.utils.Config; +import com.mpesaandroidintegration.mpesa.utils.TimeUtil; +import com.mpesaandroidintegration.mpesa.utils.Utils; + +public class STKPush { + + private String businessShortCode; + private String password; + private String timestamp; + private String transactionType; + private String amount; + private String partyA; + private String partyB; + private String phoneNumber; + private String callBackURL; + private String accountReference; + private String transactionDesc; + + private STKPush(Builder builder) { + this.businessShortCode = builder.businessShortCode; + this.timestamp = TimeUtil.getTimestamp(); + this.password = getPassword(builder.businessShortCode, builder.passkey, this.timestamp); + this.transactionType = builder.transactionType; + this.amount = String.valueOf(builder.amount); + this.partyA = builder.partyA; + this.partyB = builder.partyB; + this.phoneNumber = builder.phoneNumber; + this.accountReference = builder.accountReference; + this.transactionDesc = builder.transactionDesc; + this.callBackURL = builder.callBackURL; + if (!builder.firebaseRegID.isEmpty()){ + if (builder.callBackURL.endsWith("/")){ + this.callBackURL = builder.callBackURL + builder.firebaseRegID; + }else { + this.callBackURL = builder.callBackURL + "/" + builder.firebaseRegID; + } + } + + } + private String getPassword(String businessShortCode, String passkey, String timestamp){ + String str = businessShortCode + passkey + timestamp; + return Base64.encodeToString(str.getBytes(), Base64.NO_WRAP); + } + public static class Builder { + private String businessShortCode; + private String passkey; + private String transactionType = Config.DEFAULT_TRANSACTION_TYPE; + private int amount; + private String partyA; + private String partyB; + private String phoneNumber; + private String callBackURL = "https://mpesa.bdhobare.com/mpesa"; + private String accountReference; + private String transactionDesc; + private String firebaseRegID = ""; + public Builder(String businessShortCode,String passkey, int amount, String partyB, String phoneNumber){ + this.businessShortCode = businessShortCode; + this.passkey = passkey; + this.amount = amount; + this.partyA = Utils.sanitizePhoneNumber(phoneNumber); + this.partyB = partyB; + this.phoneNumber = Utils.sanitizePhoneNumber(phoneNumber); + this.accountReference = Utils.sanitizePhoneNumber(phoneNumber); + this.transactionDesc = Utils.sanitizePhoneNumber(phoneNumber); + } + public Builder setTransactionType(String transactionType){ + this.transactionType = transactionType; + return this; + } + public Builder setCallBackURL(String callBackURL){ + this.callBackURL = callBackURL; + return this; + } + public Builder setDescription(String description){ + this.transactionDesc = description; + return this; + } + public Builder setAccountReference(String accountReference){ + this.accountReference = accountReference; + return this; + } + public Builder setFirebaseRegID(String id){ + this.firebaseRegID = id; + return this; + } + public STKPush build(){ + return new STKPush(this); + } + + + } + public String getBusinessShortCode() { + return businessShortCode; + } + + public String getPassword() { + return password; + } + + public String getTimestamp() { + return timestamp; + } + + public String getTransactionType() { + return transactionType; + } + + public String getAmount() { + return amount; + } + + public String getPartyA() { + return partyA; + } + + public String getPartyB() { + return partyB; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public String getCallBackURL() { + return callBackURL; + } + + public String getAccountReference() { + return accountReference; + } + + public String getTransactionDesc() { + return transactionDesc; + } +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/network/NetworkHandler.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/network/NetworkHandler.java new file mode 100644 index 0000000..fc99918 --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/network/NetworkHandler.java @@ -0,0 +1,121 @@ +package com.mpesaandroidintegration.mpesa.network; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import com.mpesaandroidintegration.mpesa.utils.Pair; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +public class NetworkHandler { + public boolean isConnected(Context activity){ + ConnectivityManager connectivityManager=(ConnectivityManager)activity.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info=connectivityManager.getActiveNetworkInfo(); + if(info != null && info.isConnectedOrConnecting()){ + return true; + }else{ + return false; + } + } + + public static Pair doGet (String path, HashMap headers){ + HttpURLConnection connection=null; + try { + URL url=new URL(path); + connection =(HttpURLConnection)url.openConnection(); + connection.setRequestProperty("Accept", "application/json"); + connection.setRequestProperty("Content-Type", "application/json"); + for (Map.Entry entry: headers.entrySet()){ + String key = entry.getKey(); + String value = entry.getValue(); + connection.setRequestProperty(key, value); + } + InputStream is ; + if(connection.getResponseCode()/100 == 2){ + is=connection.getInputStream(); + }else{ + is=connection.getErrorStream(); + } + BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is)); + String line; + StringBuffer buffer=new StringBuffer(); + while ((line=bufferedReader.readLine())!= null){ + buffer.append(line); + buffer.append('\r'); + } + bufferedReader.close(); + return new Pair<>(connection.getResponseCode(), buffer.toString()); + } catch (Exception e) { + e.printStackTrace(); + return null; + }finally { + if (connection != null){ + connection.disconnect(); + } + } + } + + public static Pair doPost(String targetURL, String params, HashMap headers) { + URL url; + HttpURLConnection connection = null; + try { + //Create connection + url = new URL(targetURL); + connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("POST"); + connection.setUseCaches (false); + connection.setDoInput(true); + connection.setDoOutput(true); + + connection.setRequestProperty("Accept", "application/json"); + connection.setRequestProperty("Content-Type", "application/json"); + + for (Map.Entry entry: headers.entrySet()){ + String key = entry.getKey(); + String value = entry.getValue(); + connection.setRequestProperty(key, value); + } + //Send request + DataOutputStream wr = new DataOutputStream( + connection.getOutputStream()); + wr.writeBytes (params); + wr.flush (); + wr.close (); + //Get Response + InputStream is ; + if(connection.getResponseCode()/100 ==2){ + is=connection.getInputStream(); + }else{ + is=connection.getErrorStream(); + } + BufferedReader rd = new BufferedReader(new InputStreamReader(is)); + String line; + StringBuffer response = new StringBuffer(); + while((line = rd.readLine()) != null) { + response.append(line); + response.append('\r'); + } + rd.close(); + return new Pair<>(connection.getResponseCode(),response.toString()); + + } catch (Exception e) { + + e.printStackTrace(); + return null; + + } finally { + + if(connection != null) { + connection.disconnect(); + } + } + } +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Config.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Config.java new file mode 100644 index 0000000..e8a125c --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Config.java @@ -0,0 +1,12 @@ +package com.mpesaandroidintegration.mpesa.utils; + +public class Config { + public static final String BASE_URL = "https://sandbox.safaricom.co.ke/"; + public static String PRODUCTION_BASE_URL = "https://api.safaricom.co.ke/"; + + public static final String ACCESS_TOKEN_URL = "oauth/v1/generate?grant_type=client_credentials"; + public static final String PROCESS_REQUEST_URL ="mpesa/stkpush/v1/processrequest"; + + public static final String ERROR = "An error occurred while processing the request.Please check your internet connection and try again."; + public static final String DEFAULT_TRANSACTION_TYPE = "CustomerPayBillOnline"; +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Pair.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Pair.java new file mode 100644 index 0000000..175da0d --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Pair.java @@ -0,0 +1,10 @@ +package com.mpesaandroidintegration.mpesa.utils; + +public class Pair { + public X code; + public Y message; + public Pair(X x, Y y){ + this.code = x; + this.message = y; + } +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Preferences.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Preferences.java new file mode 100644 index 0000000..46186ab --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Preferences.java @@ -0,0 +1,35 @@ +package com.mpesaandroidintegration.mpesa.utils; + +import android.util.Base64; + +public class Preferences { + static Preferences instance; + private String access_token = ""; + private String key = "", secret = ""; + + private Preferences() {} + public static Preferences getInstance(){ + if(instance == null){ + instance = new Preferences(); + } + return instance; + } + public String getAccessToken() { + return access_token; + } + public String getAuthorization(){ + String keys = key + ":" + secret; + return Base64.encodeToString(keys.getBytes(), Base64.NO_WRAP); + } + + public void setAccessToken(String accessToken) { + this.access_token = accessToken; + } + + public void setKey(String key){ + this.key = key; + } + public void setSecret(String secret){ + this.secret = secret; + } +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/TimeUtil.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/TimeUtil.java new file mode 100644 index 0000000..dad437f --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/TimeUtil.java @@ -0,0 +1,12 @@ +package com.mpesaandroidintegration.mpesa.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class TimeUtil { + public static String getTimestamp(){ + String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date()); + return timeStamp; + } +} diff --git a/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Utils.java b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Utils.java new file mode 100644 index 0000000..d2327bb --- /dev/null +++ b/mpesa/src/main/java/com/mpesaandroidintegration/mpesa/utils/Utils.java @@ -0,0 +1,36 @@ +package com.mpesaandroidintegration.mpesa.utils; + +import android.net.Uri; + +import java.util.HashMap; +import java.util.Map; + +public class Utils { + + public static String addParamsToUrl(String url, HashMap params){ + if(!url.endsWith("?")) + url += "?"; + Uri.Builder builder = Uri.parse(url).buildUpon(); + for (Map.Entry entry : params.entrySet()) { + builder.appendQueryParameter(entry.getKey(), entry.getValue()); + } + + return builder.build().toString(); + } + public static String sanitizePhoneNumber(String phone) { + + if(phone.equals("")){ + return ""; + } + + if (phone.length() < 11 & phone.startsWith("0")) { + String p = phone.replaceFirst("^0", "254"); + return p; + } + if(phone.length() == 13 && phone.startsWith("+")){ + String p = phone.replaceFirst("^+", ""); + return p; + } + return phone; + } +}