@virex-tech/paywallo-sdk 2.6.0 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,6 +25,17 @@ npm install expo-tracking-transparency
25
25
 
26
26
  This enables the ATT prompt ("Allow this app to track your activity?"). Without it, the SDK skips IDFA collection — everything else works normally.
27
27
 
28
+ ### Optional: Facebook `_fbp` on Android
29
+
30
+ For `fb_anon_id` (the `_fbp` cookie proxy used by Meta attribution) to work correctly on Android, the host app must declare the Facebook Android SDK as a runtime dependency:
31
+
32
+ ```gradle
33
+ // android/app/build.gradle
34
+ implementation 'com.facebook.android:facebook-android-sdk:latest.release'
35
+ ```
36
+
37
+ The SDK references the Facebook Android SDK as `compileOnly` (to avoid forcing it on all apps). If the host does **not** include it as `implementation`, `metaBridge.getAnonymousID()` returns `null` and the SDK falls back to a locally-generated `PW_<uuid>`. This local ID is **not recognised by Meta** as a `_fbp` value, so Facebook attribution will not benefit from cookie matching on Android. When debug mode is enabled, the SDK logs a warning when this fallback is triggered.
38
+
28
39
  ## Quick start
29
40
 
30
41
  ### 1. Wrap your app
@@ -49,7 +49,7 @@ repositories {
49
49
  dependencies {
50
50
  implementation "com.facebook.react:react-native:+"
51
51
  implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '2.0.21')}"
52
- implementation "com.android.billingclient:billing-ktx:7.0.0"
52
+ implementation "com.android.billingclient:billing-ktx:8.0.0"
53
53
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
54
54
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
55
55
  compileOnly "com.facebook.android:facebook-android-sdk:17.+"
@@ -57,4 +57,5 @@ dependencies {
57
57
  // 1.1.0+ required for MasterKey.Builder API used in PaywalloStorageModule.
58
58
  // 1.0.0 only ships the deprecated MasterKeys.getOrCreate(...) static helper.
59
59
  implementation "androidx.security:security-crypto:1.1.0"
60
+ implementation "com.google.android.gms:play-services-ads-identifier:18.0.1"
60
61
  }
@@ -16,6 +16,13 @@
16
16
  -->
17
17
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
18
18
 
19
+ <!--
20
+ AD_ID (Android 13+) — sem essa permissão o Google Play Services devolve
21
+ o GAID como all-zeros, quebrando o madid do Meta CAPI. O merge de manifest
22
+ propaga pro app host automaticamente.
23
+ -->
24
+ <uses-permission android:name="com.google.android.gms.permission.AD_ID" />
25
+
19
26
  <application>
20
27
  <!--
21
28
  PaywalloFirebaseMessagingService handles FCM token refresh and message delivery.
@@ -0,0 +1,55 @@
1
+ package com.paywallo.sdk
2
+
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import java.util.concurrent.Executors
8
+
9
+ private val ALL_ZEROS = "00000000-0000-0000-0000-000000000000"
10
+
11
+ class PaywalloAdvertisingIdModule(private val reactContext: ReactApplicationContext) :
12
+ ReactContextBaseJavaModule(reactContext) {
13
+
14
+ private val executor = Executors.newSingleThreadExecutor()
15
+
16
+ override fun getName() = "PaywalloAdvertisingId"
17
+
18
+ // invalidate() is the post-TurboModule replacement for onCatalystInstanceDestroy.
19
+ // Shuts down the background executor to avoid thread leak on bridge reload.
20
+ override fun invalidate() {
21
+ executor.shutdown()
22
+ super.invalidate()
23
+ }
24
+
25
+ @ReactMethod
26
+ fun getAdvertisingId(promise: Promise) {
27
+ // AdvertisingIdClient.getAdvertisingIdInfo() is blocking and must NOT run
28
+ // on the main thread — run on a background executor instead.
29
+ executor.execute {
30
+ try {
31
+ val info = com.google.android.gms.ads.identifier.AdvertisingIdClient
32
+ .getAdvertisingIdInfo(reactApplicationContext)
33
+
34
+ if (info.isLimitAdTrackingEnabled) {
35
+ // User opted out of ad tracking — honour their choice.
36
+ promise.resolve(null)
37
+ return@execute
38
+ }
39
+
40
+ val id = info.id
41
+ if (id == null || id == ALL_ZEROS) {
42
+ promise.resolve(null)
43
+ } else {
44
+ promise.resolve(id)
45
+ }
46
+ } catch (t: Throwable) {
47
+ // Catches GooglePlayServicesNotAvailableException,
48
+ // GooglePlayServicesRepairableException, NoClassDefFoundError,
49
+ // IOException, and any other runtime failure.
50
+ // Never reject/crash — gracefully return null.
51
+ promise.resolve(null)
52
+ }
53
+ }
54
+ }
55
+ }
@@ -11,8 +11,9 @@ class PaywalloSdkPackage : ReactPackage {
11
11
  PaywalloStoreKitModule(reactContext),
12
12
  PaywalloFBBridgeModule(reactContext),
13
13
  PaywalloStorageModule(reactContext),
14
- PaywalloNotificationsModule(reactContext),
15
- PaywalloEnvModule(reactContext)
14
+ PaywalloNotificationsModule(reactContext),
15
+ PaywalloEnvModule(reactContext),
16
+ PaywalloAdvertisingIdModule(reactContext)
16
17
  )
17
18
  }
18
19
 
@@ -56,7 +56,12 @@ class PaywalloStoreKitModule(private val reactContext: ReactApplicationContext)
56
56
  if (billingClient == null || billingClient?.isReady != true) {
57
57
  billingClient = BillingClient.newBuilder(reactContext)
58
58
  .setListener(this)
59
- .enablePendingPurchases()
59
+ // A Billing 7 depreciou e a 8 REMOVEU o enablePendingPurchases() sem argumento.
60
+ // Um app que traz a billing 8 por outra dependência (ex.: Superwall) resolve a
61
+ // versão mais alta no Gradle e a chamada antiga vira NoSuchMethodError em runtime.
62
+ .enablePendingPurchases(
63
+ PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()
64
+ )
60
65
  .build()
61
66
  }
62
67
  return billingClient!!
@@ -173,10 +178,12 @@ class PaywalloStoreKitModule(private val reactContext: ReactApplicationContext)
173
178
  .build()
174
179
 
175
180
  return suspendCancellableCoroutine { cont ->
176
- client.queryProductDetailsAsync(params) { result, detailsList ->
181
+ // Na Billing 8 o callback passou a receber QueryProductDetailsResult em vez da
182
+ // lista direta — a assinatura antiga não existe mais em runtime.
183
+ client.queryProductDetailsAsync(params) { result, detailsResult ->
177
184
  if (cont.isActive) {
178
185
  if (result.responseCode == BillingClient.BillingResponseCode.OK) {
179
- cont.resume(detailsList ?: emptyList()) {}
186
+ cont.resume(detailsResult.productDetailsList) {}
180
187
  } else {
181
188
  cont.resume(emptyList()) {}
182
189
  }
package/dist/index.d.mts CHANGED
@@ -346,6 +346,7 @@ interface IdentifyOptions {
346
346
  lastName?: string;
347
347
  dateOfBirth?: string;
348
348
  gender?: "m" | "f";
349
+ zipCode?: string;
349
350
  }
350
351
  interface TrackEventOptions {
351
352
  properties?: UserProperties;
@@ -407,9 +408,12 @@ interface EventContext {
407
408
  /**
408
409
  * Event priority controls flush semantics:
409
410
  *
410
- * - `"critical"` → flushed as a batch-of-1 on the next microtask (via
411
- * `queueMicrotask`). Used for events where loss is unacceptable:
412
- * `transaction`, `identify`, `lifecycle {type: "install"}`,
411
+ * - `"critical"` → posted as a batch-of-1 immediately, and `track()` awaits
412
+ * the post before resolving. `PostFn` (`ApiClient.postWithQueue`) performs
413
+ * a write-ahead journal to `OfflineQueue` for critical events before any
414
+ * network attempt, so by the time `track()` resolves the event is durable
415
+ * on disk — not just handed off to a microtask. Used for events where loss
416
+ * is unacceptable: `transaction`, `identify`, `lifecycle {type: "install"}`,
413
417
  * `subscription_*`, `refund`, `checkout_started`.
414
418
  * - `"normal"` → buffered and flushed on a rolling window (whichever fires
415
419
  * first): `BATCH_MAX_SIZE` events OR `BATCH_FLUSH_MS`. Used for events
@@ -535,6 +539,7 @@ declare class ApiClient {
535
539
  lastName?: string;
536
540
  dateOfBirth?: string;
537
541
  gender?: string;
542
+ zipCode?: string;
538
543
  }): Promise<void>;
539
544
  getVariant(flagKey: string, distinctId: string): Promise<FlagVariant>;
540
545
  getPaywall(placement: string): Promise<PaywallConfig | null>;
@@ -559,6 +564,7 @@ interface IdentityState {
559
564
  lastName: string | null;
560
565
  dateOfBirth: string | null;
561
566
  gender: string | null;
567
+ zipCode: string | null;
562
568
  }
563
569
  declare class IdentityManager {
564
570
  private deviceId;
@@ -570,6 +576,7 @@ declare class IdentityManager {
570
576
  private lastName;
571
577
  private dateOfBirth;
572
578
  private gender;
579
+ private zipCode;
573
580
  private apiClient;
574
581
  private secureStorage;
575
582
  private debug;
package/dist/index.d.ts CHANGED
@@ -346,6 +346,7 @@ interface IdentifyOptions {
346
346
  lastName?: string;
347
347
  dateOfBirth?: string;
348
348
  gender?: "m" | "f";
349
+ zipCode?: string;
349
350
  }
350
351
  interface TrackEventOptions {
351
352
  properties?: UserProperties;
@@ -407,9 +408,12 @@ interface EventContext {
407
408
  /**
408
409
  * Event priority controls flush semantics:
409
410
  *
410
- * - `"critical"` → flushed as a batch-of-1 on the next microtask (via
411
- * `queueMicrotask`). Used for events where loss is unacceptable:
412
- * `transaction`, `identify`, `lifecycle {type: "install"}`,
411
+ * - `"critical"` → posted as a batch-of-1 immediately, and `track()` awaits
412
+ * the post before resolving. `PostFn` (`ApiClient.postWithQueue`) performs
413
+ * a write-ahead journal to `OfflineQueue` for critical events before any
414
+ * network attempt, so by the time `track()` resolves the event is durable
415
+ * on disk — not just handed off to a microtask. Used for events where loss
416
+ * is unacceptable: `transaction`, `identify`, `lifecycle {type: "install"}`,
413
417
  * `subscription_*`, `refund`, `checkout_started`.
414
418
  * - `"normal"` → buffered and flushed on a rolling window (whichever fires
415
419
  * first): `BATCH_MAX_SIZE` events OR `BATCH_FLUSH_MS`. Used for events
@@ -535,6 +539,7 @@ declare class ApiClient {
535
539
  lastName?: string;
536
540
  dateOfBirth?: string;
537
541
  gender?: string;
542
+ zipCode?: string;
538
543
  }): Promise<void>;
539
544
  getVariant(flagKey: string, distinctId: string): Promise<FlagVariant>;
540
545
  getPaywall(placement: string): Promise<PaywallConfig | null>;
@@ -559,6 +564,7 @@ interface IdentityState {
559
564
  lastName: string | null;
560
565
  dateOfBirth: string | null;
561
566
  gender: string | null;
567
+ zipCode: string | null;
562
568
  }
563
569
  declare class IdentityManager {
564
570
  private deviceId;
@@ -570,6 +576,7 @@ declare class IdentityManager {
570
576
  private lastName;
571
577
  private dateOfBirth;
572
578
  private gender;
579
+ private zipCode;
573
580
  private apiClient;
574
581
  private secureStorage;
575
582
  private debug;