From 9f764ecd711ec56dadb0bb6f605251454afda4b2 Mon Sep 17 00:00:00 2001 From: Hessi Pard Date: Tue, 29 Sep 2020 16:02:36 +0330 Subject: [PATCH] + Removed Useless files. + Updated Gradle Version. + Updated version to 2.0.0. --- .gitattributes | 0 .gitignore | 33 +- android/app/BUCK | 65 --- android/app/build.gradle | 25 -- android/app/proguard-rules.pro | 66 --- android/app/src/main/AndroidManifest.xml | 6 - .../reactnative/RNCustomBillingModule.java | 407 ------------------ .../reactnative/RNCustomBillingPackage.java | 33 -- android/build.gradle | 31 +- android/settings.gradle | 1 - .../vending/billing/IInAppBillingService.aidl | 0 .../reactnative/RNCustomBillingModule.java | 407 +++++++++++++++++- .../reactnative/RNCustomBillingPackage.java | 13 +- .../src/main/java/com/util/Base64.java | 0 .../java/com/util/Base64DecoderException.java | 0 .../src/main/java/com/util/IabException.java | 0 .../src/main/java/com/util/IabHelper.java | 0 .../src/main/java/com/util/IabResult.java | 0 .../src/main/java/com/util/Inventory.java | 0 .../src/main/java/com/util/Purchase.java | 0 .../src/main/java/com/util/Security.java | 0 .../src/main/java/com/util/SkuDetails.java | 0 package.json | 2 +- 23 files changed, 435 insertions(+), 654 deletions(-) delete mode 100644 .gitattributes delete mode 100644 android/app/BUCK delete mode 100644 android/app/build.gradle delete mode 100644 android/app/proguard-rules.pro delete mode 100644 android/app/src/main/AndroidManifest.xml delete mode 100644 android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java delete mode 100644 android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java delete mode 100644 android/settings.gradle rename android/{app => }/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl (100%) rename android/{app => }/src/main/java/com/util/Base64.java (100%) rename android/{app => }/src/main/java/com/util/Base64DecoderException.java (100%) rename android/{app => }/src/main/java/com/util/IabException.java (100%) rename android/{app => }/src/main/java/com/util/IabHelper.java (100%) rename android/{app => }/src/main/java/com/util/IabResult.java (100%) rename android/{app => }/src/main/java/com/util/Inventory.java (100%) rename android/{app => }/src/main/java/com/util/Purchase.java (100%) rename android/{app => }/src/main/java/com/util/Security.java (100%) rename android/{app => }/src/main/java/com/util/SkuDetails.java (100%) diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e69de29..0000000 diff --git a/.gitignore b/.gitignore index 57b75f4..056e9dc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,37 +5,8 @@ # .DS_Store -# --------------------------------------- Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -project.xcworkspace -ios/TapsellSDKv3.framework -# ---------------------------------------- ios/Pods -ios/Pods/ -ios/Podfile.lock - # --------------------------------------- Android/IntelliJ # -android/app/src/main/assets/index.android.bundle -android/app/src/main/assets/index.android.bundle.js -android/app/src/main/assets/index.android.bundle.meta -android/app/src/main/assets/index.android.bundle.js.meta -build/ .project org.eclipse.* .classpath @@ -80,6 +51,4 @@ fastlane/screenshots # coverage/* jest/* - -# react-native-config codegen -ios/tmp.xcconfig +.git diff --git a/android/app/BUCK b/android/app/BUCK deleted file mode 100644 index 46b61d2..0000000 --- a/android/app/BUCK +++ /dev/null @@ -1,65 +0,0 @@ -# To learn about Buck see [Docs](https://buckbuild.com/). -# To run your application with Buck: -# - install Buck -# - `npm start` - to start the packager -# - `cd android` -# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` -# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck -# - `buck install -r android/app` - compile, install and run application -# - -lib_deps = [] - -for jarfile in glob(['libs/*.jar']): - name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] - lib_deps.append(':' + name) - prebuilt_jar( - name = name, - binary_jar = jarfile, - ) - -for aarfile in glob(['libs/*.aar']): - name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] - lib_deps.append(':' + name) - android_prebuilt_aar( - name = name, - aar = aarfile, - ) - -android_library( - name = "all-libs", - exported_deps = lib_deps, -) - -android_library( - name = "app-code", - srcs = glob([ - "src/main/java/**/*.java", - ]), - deps = [ - ":all-libs", - ":build_config", - ":res", - ], -) - -android_build_config( - name = "build_config", - package = "com.cafebazaar", -) - -android_resource( - name = "res", - package = "com.cafebazaar", - res = "src/main/res", -) - -android_binary( - name = "app", - keystore = "//android/keystores:debug", - manifest = "src/main/AndroidManifest.xml", - package_type = "debug", - deps = [ - ":app-code", - ], -) diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index f953070..0000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -apply plugin: "com.android.library" - -android { - compileSdkVersion 28 - buildToolsVersion "28.0.3" - - defaultConfig { - minSdkVersion 16 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } -} - -dependencies { - compile 'com.intellij:annotations:+@jar' - compile 'com.google.code.gson:gson:2.2.4' - compile "com.facebook.react:react-native:+" // From node_modules -} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro deleted file mode 100644 index 48361a9..0000000 --- a/android/app/proguard-rules.pro +++ /dev/null @@ -1,66 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Disabling obfuscation is useful if you collect stack traces from production crashes -# (unless you are using a system that supports de-obfuscate the stack traces). --dontobfuscate - -# React Native - -# Keep our interfaces so they can be used by other ProGuard rules. -# See http://sourceforge.net/p/proguard/bugs/466/ --keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip --keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters --keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip - -# Do not strip any method/class that is annotated with @DoNotStrip --keep @com.facebook.proguard.annotations.DoNotStrip class * --keep @com.facebook.common.internal.DoNotStrip class * --keepclassmembers class * { - @com.facebook.proguard.annotations.DoNotStrip *; - @com.facebook.common.internal.DoNotStrip *; -} - --keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { - void set*(***); - *** get*(); -} - --keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } --keep class * extends com.facebook.react.bridge.NativeModule { *; } --keepclassmembers,includedescriptorclasses class * { native ; } --keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } --keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } --keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } - --dontwarn com.facebook.react.** - -# okhttp - --keepattributes Signature --keepattributes *Annotation* --keep class okhttp3.** { *; } --keep interface okhttp3.** { *; } --dontwarn okhttp3.** - -# okio - --keep class sun.misc.Unsafe { *; } --dontwarn java.nio.file.* --dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement --dontwarn okio.** diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 5489d5c..0000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java b/android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java deleted file mode 100644 index da570ae..0000000 --- a/android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java +++ /dev/null @@ -1,407 +0,0 @@ - -package com.customBilling.reactnative; - -import android.app.Activity; -import android.util.Log; -import android.content.Intent; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.lang.Object; - -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableNativeArray; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.modules.core.DeviceEventManagerModule; -import com.facebook.react.bridge.ActivityEventListener; - -import com.util.IabHelper; -import com.util.IabResult; -import com.util.Inventory; -import com.util.Purchase; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import com.google.gson.Gson; -import org.jetbrains.annotations.Nullable; - -public class RNCustomBillingModule extends ReactContextBaseJavaModule implements ActivityEventListener { - - private final ReactApplicationContext _reactContext; - private String LICENSE_KEY = null; - private String VENDOR_INTENT = null; - private String VENDOR_PACKAGE = null; - private IabHelper mHelper; - private final Gson gson = new Gson(); - private static final String E_SETUP_ERROR = "E_SETUP_ERROR"; - private static final String E_SETUP_DISCONNECT = "E_SETUP_DISCONNECT"; - private static final String E_LOAD_ITEMS_FAILURE = "E_LOAD_ITEMS_FAILURE"; - private static final String E_LAYOUT_ERROR = "E_LAYOUT_ERROR"; - private static final String E_PURCHASE_DISCONNECT = "E_PURCHASE_DISCONNECT"; - private static final String E_PURCHASE_FAILURE = "E_PURCHASE_FAILURE"; - private static final String E_PURCHASE_PAYLOAD_VERIFY = "E_PURCHASE_PAYLOAD_VERIFY"; - private static final String E_PURCHASE_ERROR = "E_PURCHASE_ERROR"; - private static final String E_CONSUME_FAILURE = "E_CONSUME_FAILURE"; - private static final String E_CONSUME_ERROR = "E_CONSUME_ERROR"; - private static final String E_CONSUME_INITIAL = "E_CONSUME_INITIAL"; - private final String TAG; - private Inventory userInvo; - - public RNCustomBillingModule(ReactApplicationContext reactContext) { - super(reactContext); - _reactContext = reactContext; - - int vendorResourceId = _reactContext - .getResources() - .getIdentifier("RNCB_VENDOR_NAME", "string", _reactContext.getPackageName()); - TAG = _reactContext.getString(vendorResourceId); - - int keyResourceId = _reactContext - .getResources() - .getIdentifier("RNCB_VENDOR_PUBLIC_KEY", "string", _reactContext.getPackageName()); - this.LICENSE_KEY = _reactContext.getString(keyResourceId); - - switch (TAG) { - case "google": - VENDOR_INTENT = "com.android.vending.billing.InAppBillingService.BIND"; - VENDOR_PACKAGE = "com.android.vending"; - break; - case "bazaar": - VENDOR_INTENT = "ir.cafebazaar.pardakht.InAppBillingService.BIND"; - VENDOR_PACKAGE = "com.farsitel.bazaar"; - break; - case "myket": - VENDOR_INTENT = "ir.mservices.market.InAppBillingService.BIND"; - VENDOR_PACKAGE = "ir.mservices.market"; - break; - case "iranapps": - VENDOR_INTENT = "ir.tgbs.iranapps.billing.InAppBillingService.BIND"; - VENDOR_PACKAGE = "ir.tgbs.android.iranapp"; - break; - default: - break; - } - - reactContext.addActivityEventListener(this); - } - - @Override - public String getName() { - return "RNCustomBilling"; - } - - @ReactMethod - public void open(final Promise promise) { - if (VENDOR_INTENT == null) { - // Oh noes, there was a problem. - promise.reject(E_SETUP_ERROR, "No Vendor Defined !"); - return; - } - mHelper = new IabHelper(_reactContext, LICENSE_KEY); - - // enable debug logging (for a production application, you should set this to - // false). - mHelper.enableDebugLogging(false); - - try { - // Start setup. This is asynchronous and the specified listener - // will be called once setup completes. - mHelper.startSetup(VENDOR_INTENT, VENDOR_PACKAGE, new IabHelper.OnIabSetupFinishedListener() { - public void onIabSetupFinished(IabResult result) { - - if (result.isSuccess()) { - promise.resolve(gson.toJson(result)); - } else { - // Have we been disposed of in the meantime? If so, quit. - if (mHelper == null) { - promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); - } else { - // Oh noes, there was a problem. - promise.reject(E_SETUP_ERROR, "There is a problem in setting up" + TAG); - } - } - } - }); - - } catch (Exception excep) { - promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); - Log.e(TAG + "OpenError", "Could not open " + TAG, excep); - } - } - - @ReactMethod - public void loadInventory(ReadableArray skuList, final Promise promise) { - ArrayList skus = new ArrayList<>(); - for (int i = 0; i < skuList.size(); i++) { - skus.add(skuList.getString(i)); - } - mHelper.queryInventoryAsync(true, skus, new IabHelper.QueryInventoryFinishedListener() { - public void onQueryInventoryFinished(IabResult result, Inventory inventory) { - // Have we been disposed of in the meantime? If so, quit. - if (mHelper == null) { - promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); - } - // Is it a failure? - else if (result.isFailure()) { - promise.reject(E_LAYOUT_ERROR, "Failed to query inventory: " + result.getMessage()); - } else { - promise.resolve(gson.toJson(inventory)); - } - } - }); - } - - @ReactMethod - public void loadOwnedItems(final Promise promise) { - mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() { - public void onQueryInventoryFinished(IabResult result, Inventory inventory) { - // Have we been disposed of in the meantime? If so, quit. - WritableMap params = Arguments.createMap(); - if (mHelper == null) { - promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); - } - // Is it a failure? - else if (result.isFailure()) { - promise.reject(E_LOAD_ITEMS_FAILURE, result.getMessage()); - } else { - userInvo = inventory; - promise.resolve(gson.toJson(inventory.getAllOwnedSkus())); - } - } - }); - } - - public void loadOwnedItemsWithEvent() { - mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() { - public void onQueryInventoryFinished(IabResult result, Inventory inventory) { - // Have we been disposed of in the meantime? If so, quit. - WritableMap params = Arguments.createMap(); - if (mHelper == null) { - params.putString("LoadOwnedItem", "Disposed!"); - sendEvent(TAG, params); - } - // Is it a failure? - if (result.isFailure()) { - params.putString("LoadOwnedItem", result.getMessage()); - sendEvent(TAG, params); - } - userInvo = inventory; - params.putString("LoadOwnedItem", gson.toJson(inventory.getAllOwnedSkus())); - sendEvent(TAG, params); - } - }); - } - - @ReactMethod - public void purchaseWithEvent(String sku, String payload, int rcRequest) { - try { - mHelper.launchPurchaseFlow(getCurrentActivity(), sku, rcRequest, - new IabHelper.OnIabPurchaseFinishedListener() { - public void onIabPurchaseFinished(IabResult result, Purchase purchase) { - // if we were disposed of in the meantime, quit. - WritableMap params = Arguments.createMap(); - - if (mHelper == null) { - params.putString("PurchaseResult", "Connection Error!"); - sendEvent(TAG, params); - } else { - if (result.isFailure()) { - params.putString("Error", result.getMessage()); - sendEvent(TAG, params); - } else { - if (!verifyDeveloperPayload(purchase)) { - params.putString("Error", "could not verify developer payload"); - sendEvent(TAG, params); - } else { - params.putString("Details", gson.toJson(purchase)); - sendEvent(TAG, params); - } - } - } - } - }, payload); - } catch (Exception ex) { - WritableMap params = Arguments.createMap(); - params.putString("Error", ex.getMessage()); - sendEvent(TAG, params); - } - } - - @ReactMethod - public void purchase(String sku, String payload, int rcRequest, final Promise promise) { - try { - mHelper.launchPurchaseFlow(getCurrentActivity(), sku, rcRequest, - new IabHelper.OnIabPurchaseFinishedListener() { - public void onIabPurchaseFinished(IabResult result, Purchase purchase) { - // if we were disposed of in the meantime, quit. - WritableMap params = Arguments.createMap(); - - if (mHelper == null) { - promise.reject(E_PURCHASE_DISCONNECT, "Connection Error!"); - } else { - if (result.isFailure()) { - promise.reject(E_PURCHASE_FAILURE, result.getMessage()); - } else { - if (!verifyDeveloperPayload(purchase)) { - promise.reject(E_PURCHASE_PAYLOAD_VERIFY, "could not verify developer payload"); - } else { - promise.resolve(gson.toJson(purchase)); - } - } - } - } - }, payload); - } catch (Exception ex) { - promise.reject(E_PURCHASE_ERROR, ex.getMessage()); - } - } - - /** Verifies the developer payload of a purchase. */ - boolean verifyDeveloperPayload(Purchase p) { - String payload = p.getDeveloperPayload(); - - /* - * TODO: verify that the developer payload of the purchase is correct. It will - * be the same one that you sent when initiating the purchase. - * - * WARNING: Locally generating a random string when starting a purchase and - * verifying it here might seem like a good approach, but this will fail in the - * case where the user purchases an item on one device and then uses your app on - * a different device, because on the other device you will not have access to - * the random string you originally generated. - * - * So a good developer payload has these characteristics: - * - * 1. If two different users purchase an item, the payload is different between - * them, so that one user's purchase can't be replayed to another user. - * - * 2. The payload must be such that you can verify it even when the app wasn't - * the one who initiated the purchase flow (so that items purchased by the user - * on one device work on other devices owned by the user). - * - * Using your own server to store and verify developer payloads across app - * installations is recommended. - */ - - return true; - } - - private void sendEvent(String eventName, @Nullable WritableMap params) { - _reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); - } - - @ReactMethod - public void consume(String sku, final Promise promise) { - if (userInvo != null) { - if (userInvo.hasPurchase(sku)) { - mHelper.consumeAsync(userInvo.getPurchase(sku), new IabHelper.OnConsumeFinishedListener() { - public void onConsumeFinished(Purchase purchase, IabResult result) { - WritableMap params = Arguments.createMap(); - if (result.isSuccess()) { - // provision the in-app purchase to the user - promise.resolve(gson.toJson(purchase)); - } else { - // handle error - promise.reject(E_CONSUME_FAILURE, result.getMessage()); - } - } - }); - } else { - promise.reject(E_CONSUME_ERROR, "user did not purchase item"); - } - } else { - promise.reject(E_CONSUME_INITIAL, "inventory not loaded!"); - } - } - - @ReactMethod - public void consumeWithEvent(String sku) { - if (userInvo != null) { - if (userInvo.hasPurchase(sku)) { - mHelper.consumeAsync(userInvo.getPurchase(sku), new IabHelper.OnConsumeFinishedListener() { - public void onConsumeFinished(Purchase purchase, IabResult result) { - WritableMap params = Arguments.createMap(); - if (result.isSuccess()) { - // provision the in-app purchase to the user - // (for example, credit 50 gold coins to player's character) - params.putString("SuccessfulConsume", gson.toJson(purchase)); - sendEvent(TAG, params); - } else { - // handle error - params.putString("Error", result.getMessage()); - sendEvent(TAG, params); - } - } - }); - } else { - WritableMap params = Arguments.createMap(); - params.putString("Error", "user did not purchase item"); - sendEvent(TAG, params); - } - } else { - WritableMap params = Arguments.createMap(); - params.putString("Error", "inventory not loaded!"); - sendEvent(TAG, params); - } - } - - @Deprecated - public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) { - if (mHelper == null) - return; - - // Pass on the activity result to the helper for handling - if (!mHelper.handleActivityResult(requestCode, resultCode, intent)) { - // not handled, so handle it ourselves (here's where you'd - // perform any handling of activity results not related to in-app - // billing... - WritableMap params = Arguments.createMap(); - params.putString("Warning", "you need to use your activity to perfom billing"); - sendEvent(TAG, params); - } - } - - public void onActivityResult(final Activity activity, final int requestCode, final int resultCode, - final Intent intent) { - if (mHelper == null) - return; - - // Pass on the activity result to the helper for handling - if (!mHelper.handleActivityResult(requestCode, resultCode, intent)) { - // not handled, so handle it ourselves (here's where you'd - // perform any handling of activity results not related to in-app - // billing... - WritableMap params = Arguments.createMap(); - params.putString("Warning", "you need to use your activity to perfom billing"); - sendEvent(TAG, params); - } - } - - @Override - public void onNewIntent(Intent intent) { - - } - - @ReactMethod - public void close(final Promise promise) { - // very important: - if (mHelper != null) { - mHelper.dispose(); - mHelper = null; - } - promise.resolve(true); - } -} \ No newline at end of file diff --git a/android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java b/android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java deleted file mode 100644 index f0160f6..0000000 --- a/android/app/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java +++ /dev/null @@ -1,33 +0,0 @@ - -package com.customBilling.reactnative; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; -import com.facebook.react.bridge.JavaScriptModule; - -public class RNCustomBillingPackage implements ReactPackage { - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - List modules = new ArrayList<>(); - - modules.add(new RNCustomBillingModule(reactContext)); - - return modules; - } - - // Deprecated from RN 0.47 - public List> createJSModules() { - return Collections.emptyList(); - } - - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Collections.emptyList(); - } -} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index cb03ff3..2631200 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,11 +1,30 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' + classpath 'com.android.tools.build:gradle:3.6.4' + } +} + +apply plugin: "com.android.library" + +android { + compileSdkVersion 28 + buildToolsVersion "28.0.3" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + versionCode 2 + versionName "2.0.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } } } @@ -19,3 +38,9 @@ allprojects { } } } + +dependencies { + compile 'com.intellij:annotations:+@jar' + compile 'com.google.code.gson:gson:2.2.4' + compile "com.facebook.react:react-native:+" // From node_modules +} diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index e7b4def..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/android/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl b/android/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl similarity index 100% rename from android/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl rename to android/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl diff --git a/android/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java b/android/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java index 6e4c775..da570ae 100644 --- a/android/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java +++ b/android/src/main/java/com/customBilling/reactnative/RNCustomBillingModule.java @@ -1,22 +1,407 @@ package com.customBilling.reactnative; +import android.app.Activity; +import android.util.Log; +import android.content.Intent; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.lang.Object; + +import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.bridge.ActivityEventListener; + +import com.util.IabHelper; +import com.util.IabResult; +import com.util.Inventory; +import com.util.Purchase; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import com.google.gson.Gson; +import org.jetbrains.annotations.Nullable; + +public class RNCustomBillingModule extends ReactContextBaseJavaModule implements ActivityEventListener { + + private final ReactApplicationContext _reactContext; + private String LICENSE_KEY = null; + private String VENDOR_INTENT = null; + private String VENDOR_PACKAGE = null; + private IabHelper mHelper; + private final Gson gson = new Gson(); + private static final String E_SETUP_ERROR = "E_SETUP_ERROR"; + private static final String E_SETUP_DISCONNECT = "E_SETUP_DISCONNECT"; + private static final String E_LOAD_ITEMS_FAILURE = "E_LOAD_ITEMS_FAILURE"; + private static final String E_LAYOUT_ERROR = "E_LAYOUT_ERROR"; + private static final String E_PURCHASE_DISCONNECT = "E_PURCHASE_DISCONNECT"; + private static final String E_PURCHASE_FAILURE = "E_PURCHASE_FAILURE"; + private static final String E_PURCHASE_PAYLOAD_VERIFY = "E_PURCHASE_PAYLOAD_VERIFY"; + private static final String E_PURCHASE_ERROR = "E_PURCHASE_ERROR"; + private static final String E_CONSUME_FAILURE = "E_CONSUME_FAILURE"; + private static final String E_CONSUME_ERROR = "E_CONSUME_ERROR"; + private static final String E_CONSUME_INITIAL = "E_CONSUME_INITIAL"; + private final String TAG; + private Inventory userInvo; + + public RNCustomBillingModule(ReactApplicationContext reactContext) { + super(reactContext); + _reactContext = reactContext; + + int vendorResourceId = _reactContext + .getResources() + .getIdentifier("RNCB_VENDOR_NAME", "string", _reactContext.getPackageName()); + TAG = _reactContext.getString(vendorResourceId); + + int keyResourceId = _reactContext + .getResources() + .getIdentifier("RNCB_VENDOR_PUBLIC_KEY", "string", _reactContext.getPackageName()); + this.LICENSE_KEY = _reactContext.getString(keyResourceId); + + switch (TAG) { + case "google": + VENDOR_INTENT = "com.android.vending.billing.InAppBillingService.BIND"; + VENDOR_PACKAGE = "com.android.vending"; + break; + case "bazaar": + VENDOR_INTENT = "ir.cafebazaar.pardakht.InAppBillingService.BIND"; + VENDOR_PACKAGE = "com.farsitel.bazaar"; + break; + case "myket": + VENDOR_INTENT = "ir.mservices.market.InAppBillingService.BIND"; + VENDOR_PACKAGE = "ir.mservices.market"; + break; + case "iranapps": + VENDOR_INTENT = "ir.tgbs.iranapps.billing.InAppBillingService.BIND"; + VENDOR_PACKAGE = "ir.tgbs.android.iranapp"; + break; + default: + break; + } + + reactContext.addActivityEventListener(this); + } + + @Override + public String getName() { + return "RNCustomBilling"; + } + + @ReactMethod + public void open(final Promise promise) { + if (VENDOR_INTENT == null) { + // Oh noes, there was a problem. + promise.reject(E_SETUP_ERROR, "No Vendor Defined !"); + return; + } + mHelper = new IabHelper(_reactContext, LICENSE_KEY); + + // enable debug logging (for a production application, you should set this to + // false). + mHelper.enableDebugLogging(false); + + try { + // Start setup. This is asynchronous and the specified listener + // will be called once setup completes. + mHelper.startSetup(VENDOR_INTENT, VENDOR_PACKAGE, new IabHelper.OnIabSetupFinishedListener() { + public void onIabSetupFinished(IabResult result) { + + if (result.isSuccess()) { + promise.resolve(gson.toJson(result)); + } else { + // Have we been disposed of in the meantime? If so, quit. + if (mHelper == null) { + promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); + } else { + // Oh noes, there was a problem. + promise.reject(E_SETUP_ERROR, "There is a problem in setting up" + TAG); + } + } + } + }); + + } catch (Exception excep) { + promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); + Log.e(TAG + "OpenError", "Could not open " + TAG, excep); + } + } + + @ReactMethod + public void loadInventory(ReadableArray skuList, final Promise promise) { + ArrayList skus = new ArrayList<>(); + for (int i = 0; i < skuList.size(); i++) { + skus.add(skuList.getString(i)); + } + mHelper.queryInventoryAsync(true, skus, new IabHelper.QueryInventoryFinishedListener() { + public void onQueryInventoryFinished(IabResult result, Inventory inventory) { + // Have we been disposed of in the meantime? If so, quit. + if (mHelper == null) { + promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); + } + // Is it a failure? + else if (result.isFailure()) { + promise.reject(E_LAYOUT_ERROR, "Failed to query inventory: " + result.getMessage()); + } else { + promise.resolve(gson.toJson(inventory)); + } + } + }); + } + + @ReactMethod + public void loadOwnedItems(final Promise promise) { + mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() { + public void onQueryInventoryFinished(IabResult result, Inventory inventory) { + // Have we been disposed of in the meantime? If so, quit. + WritableMap params = Arguments.createMap(); + if (mHelper == null) { + promise.reject(E_SETUP_DISCONNECT, "there is no connection to " + TAG); + } + // Is it a failure? + else if (result.isFailure()) { + promise.reject(E_LOAD_ITEMS_FAILURE, result.getMessage()); + } else { + userInvo = inventory; + promise.resolve(gson.toJson(inventory.getAllOwnedSkus())); + } + } + }); + } + + public void loadOwnedItemsWithEvent() { + mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() { + public void onQueryInventoryFinished(IabResult result, Inventory inventory) { + // Have we been disposed of in the meantime? If so, quit. + WritableMap params = Arguments.createMap(); + if (mHelper == null) { + params.putString("LoadOwnedItem", "Disposed!"); + sendEvent(TAG, params); + } + // Is it a failure? + if (result.isFailure()) { + params.putString("LoadOwnedItem", result.getMessage()); + sendEvent(TAG, params); + } + userInvo = inventory; + params.putString("LoadOwnedItem", gson.toJson(inventory.getAllOwnedSkus())); + sendEvent(TAG, params); + } + }); + } + + @ReactMethod + public void purchaseWithEvent(String sku, String payload, int rcRequest) { + try { + mHelper.launchPurchaseFlow(getCurrentActivity(), sku, rcRequest, + new IabHelper.OnIabPurchaseFinishedListener() { + public void onIabPurchaseFinished(IabResult result, Purchase purchase) { + // if we were disposed of in the meantime, quit. + WritableMap params = Arguments.createMap(); + + if (mHelper == null) { + params.putString("PurchaseResult", "Connection Error!"); + sendEvent(TAG, params); + } else { + if (result.isFailure()) { + params.putString("Error", result.getMessage()); + sendEvent(TAG, params); + } else { + if (!verifyDeveloperPayload(purchase)) { + params.putString("Error", "could not verify developer payload"); + sendEvent(TAG, params); + } else { + params.putString("Details", gson.toJson(purchase)); + sendEvent(TAG, params); + } + } + } + } + }, payload); + } catch (Exception ex) { + WritableMap params = Arguments.createMap(); + params.putString("Error", ex.getMessage()); + sendEvent(TAG, params); + } + } + + @ReactMethod + public void purchase(String sku, String payload, int rcRequest, final Promise promise) { + try { + mHelper.launchPurchaseFlow(getCurrentActivity(), sku, rcRequest, + new IabHelper.OnIabPurchaseFinishedListener() { + public void onIabPurchaseFinished(IabResult result, Purchase purchase) { + // if we were disposed of in the meantime, quit. + WritableMap params = Arguments.createMap(); + + if (mHelper == null) { + promise.reject(E_PURCHASE_DISCONNECT, "Connection Error!"); + } else { + if (result.isFailure()) { + promise.reject(E_PURCHASE_FAILURE, result.getMessage()); + } else { + if (!verifyDeveloperPayload(purchase)) { + promise.reject(E_PURCHASE_PAYLOAD_VERIFY, "could not verify developer payload"); + } else { + promise.resolve(gson.toJson(purchase)); + } + } + } + } + }, payload); + } catch (Exception ex) { + promise.reject(E_PURCHASE_ERROR, ex.getMessage()); + } + } + + /** Verifies the developer payload of a purchase. */ + boolean verifyDeveloperPayload(Purchase p) { + String payload = p.getDeveloperPayload(); + + /* + * TODO: verify that the developer payload of the purchase is correct. It will + * be the same one that you sent when initiating the purchase. + * + * WARNING: Locally generating a random string when starting a purchase and + * verifying it here might seem like a good approach, but this will fail in the + * case where the user purchases an item on one device and then uses your app on + * a different device, because on the other device you will not have access to + * the random string you originally generated. + * + * So a good developer payload has these characteristics: + * + * 1. If two different users purchase an item, the payload is different between + * them, so that one user's purchase can't be replayed to another user. + * + * 2. The payload must be such that you can verify it even when the app wasn't + * the one who initiated the purchase flow (so that items purchased by the user + * on one device work on other devices owned by the user). + * + * Using your own server to store and verify developer payloads across app + * installations is recommended. + */ + + return true; + } + + private void sendEvent(String eventName, @Nullable WritableMap params) { + _reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); + } + + @ReactMethod + public void consume(String sku, final Promise promise) { + if (userInvo != null) { + if (userInvo.hasPurchase(sku)) { + mHelper.consumeAsync(userInvo.getPurchase(sku), new IabHelper.OnConsumeFinishedListener() { + public void onConsumeFinished(Purchase purchase, IabResult result) { + WritableMap params = Arguments.createMap(); + if (result.isSuccess()) { + // provision the in-app purchase to the user + promise.resolve(gson.toJson(purchase)); + } else { + // handle error + promise.reject(E_CONSUME_FAILURE, result.getMessage()); + } + } + }); + } else { + promise.reject(E_CONSUME_ERROR, "user did not purchase item"); + } + } else { + promise.reject(E_CONSUME_INITIAL, "inventory not loaded!"); + } + } + + @ReactMethod + public void consumeWithEvent(String sku) { + if (userInvo != null) { + if (userInvo.hasPurchase(sku)) { + mHelper.consumeAsync(userInvo.getPurchase(sku), new IabHelper.OnConsumeFinishedListener() { + public void onConsumeFinished(Purchase purchase, IabResult result) { + WritableMap params = Arguments.createMap(); + if (result.isSuccess()) { + // provision the in-app purchase to the user + // (for example, credit 50 gold coins to player's character) + params.putString("SuccessfulConsume", gson.toJson(purchase)); + sendEvent(TAG, params); + } else { + // handle error + params.putString("Error", result.getMessage()); + sendEvent(TAG, params); + } + } + }); + } else { + WritableMap params = Arguments.createMap(); + params.putString("Error", "user did not purchase item"); + sendEvent(TAG, params); + } + } else { + WritableMap params = Arguments.createMap(); + params.putString("Error", "inventory not loaded!"); + sendEvent(TAG, params); + } + } + + @Deprecated + public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) { + if (mHelper == null) + return; + + // Pass on the activity result to the helper for handling + if (!mHelper.handleActivityResult(requestCode, resultCode, intent)) { + // not handled, so handle it ourselves (here's where you'd + // perform any handling of activity results not related to in-app + // billing... + WritableMap params = Arguments.createMap(); + params.putString("Warning", "you need to use your activity to perfom billing"); + sendEvent(TAG, params); + } + } + + public void onActivityResult(final Activity activity, final int requestCode, final int resultCode, + final Intent intent) { + if (mHelper == null) + return; -public class RNCustomBillingModule extends ReactContextBaseJavaModule { + // Pass on the activity result to the helper for handling + if (!mHelper.handleActivityResult(requestCode, resultCode, intent)) { + // not handled, so handle it ourselves (here's where you'd + // perform any handling of activity results not related to in-app + // billing... + WritableMap params = Arguments.createMap(); + params.putString("Warning", "you need to use your activity to perfom billing"); + sendEvent(TAG, params); + } + } - private final ReactApplicationContext reactContext; + @Override + public void onNewIntent(Intent intent) { - public RNCustomBillingModule(ReactApplicationContext reactContext) { - super(reactContext); - this.reactContext = reactContext; - } + } - @Override - public String getName() { - return "RNCustomBilling"; - } + @ReactMethod + public void close(final Promise promise) { + // very important: + if (mHelper != null) { + mHelper.dispose(); + mHelper = null; + } + promise.resolve(true); + } } \ No newline at end of file diff --git a/android/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java b/android/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java index de23a2b..f0160f6 100644 --- a/android/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java +++ b/android/src/main/java/com/customBilling/reactnative/RNCustomBillingPackage.java @@ -1,7 +1,7 @@ package com.customBilling.reactnative; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -10,19 +10,24 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import com.facebook.react.bridge.JavaScriptModule; + public class RNCustomBillingPackage implements ReactPackage { @Override public List createNativeModules(ReactApplicationContext reactContext) { - return Arrays.asList(new RNCustomBillingModule(reactContext)); + List modules = new ArrayList<>(); + + modules.add(new RNCustomBillingModule(reactContext)); + + return modules; } // Deprecated from RN 0.47 public List> createJSModules() { - return Collections.emptyList(); + return Collections.emptyList(); } @Override public List createViewManagers(ReactApplicationContext reactContext) { - return Collections.emptyList(); + return Collections.emptyList(); } } \ No newline at end of file diff --git a/android/app/src/main/java/com/util/Base64.java b/android/src/main/java/com/util/Base64.java similarity index 100% rename from android/app/src/main/java/com/util/Base64.java rename to android/src/main/java/com/util/Base64.java diff --git a/android/app/src/main/java/com/util/Base64DecoderException.java b/android/src/main/java/com/util/Base64DecoderException.java similarity index 100% rename from android/app/src/main/java/com/util/Base64DecoderException.java rename to android/src/main/java/com/util/Base64DecoderException.java diff --git a/android/app/src/main/java/com/util/IabException.java b/android/src/main/java/com/util/IabException.java similarity index 100% rename from android/app/src/main/java/com/util/IabException.java rename to android/src/main/java/com/util/IabException.java diff --git a/android/app/src/main/java/com/util/IabHelper.java b/android/src/main/java/com/util/IabHelper.java similarity index 100% rename from android/app/src/main/java/com/util/IabHelper.java rename to android/src/main/java/com/util/IabHelper.java diff --git a/android/app/src/main/java/com/util/IabResult.java b/android/src/main/java/com/util/IabResult.java similarity index 100% rename from android/app/src/main/java/com/util/IabResult.java rename to android/src/main/java/com/util/IabResult.java diff --git a/android/app/src/main/java/com/util/Inventory.java b/android/src/main/java/com/util/Inventory.java similarity index 100% rename from android/app/src/main/java/com/util/Inventory.java rename to android/src/main/java/com/util/Inventory.java diff --git a/android/app/src/main/java/com/util/Purchase.java b/android/src/main/java/com/util/Purchase.java similarity index 100% rename from android/app/src/main/java/com/util/Purchase.java rename to android/src/main/java/com/util/Purchase.java diff --git a/android/app/src/main/java/com/util/Security.java b/android/src/main/java/com/util/Security.java similarity index 100% rename from android/app/src/main/java/com/util/Security.java rename to android/src/main/java/com/util/Security.java diff --git a/android/app/src/main/java/com/util/SkuDetails.java b/android/src/main/java/com/util/SkuDetails.java similarity index 100% rename from android/app/src/main/java/com/util/SkuDetails.java rename to android/src/main/java/com/util/SkuDetails.java diff --git a/package.json b/package.json index 82a67a1..766cf63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-custom-billing", - "version": "1.5.0", + "version": "2.0.0", "description": "Customizable InAppBilling feature for react native application", "main": "index.js", "scripts": {