@virex-tech/paywallo-sdk 2.5.1 → 2.5.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 +23 -2
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/paywallo/sdk/PaywalloStoreKitModule.kt +14 -2
- package/dist/index.d.mts +40 -29
- package/dist/index.d.ts +40 -29
- package/dist/index.js +456 -303
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +443 -290
- package/dist/index.mjs.map +1 -1
- package/ios/PaywalloAPNSProxy.m +138 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -233,6 +233,27 @@ const onStateChange = usePaywalloScreenTracking();
|
|
|
233
233
|
|
|
234
234
|
Works with React Navigation v6/v7 (no extra peer dependency).
|
|
235
235
|
|
|
236
|
+
## Identify users
|
|
237
|
+
|
|
238
|
+
Call `identify` after login (and on every logged-in app launch) to link the device to your user:
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
await PaywalloClient.identify({
|
|
242
|
+
email: user.email, // optional — improves ad-platform matching (CAPI)
|
|
243
|
+
firstName: user.firstName, // optional
|
|
244
|
+
properties: {
|
|
245
|
+
userId: user.id, // REQUIRED for per-user features — your backend's user id
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Key rules:
|
|
251
|
+
|
|
252
|
+
- **`properties.userId` is what links the user.** It becomes `external_user_id` server-side and is the value you pass as `recipients` in `POST /v1/notifications/trigger`. Calling `identify({ email })` alone does **not** link the user — push tokens stay anonymous and per-user sends silently target zero devices (`skippedReason: "no_active_tokens"`).
|
|
253
|
+
- **Order doesn't matter.** The link is retroactive: tokens registered before `identify` (e.g. during onboarding, pre-login) are linked when `identify` runs.
|
|
254
|
+
- **Call it on every logged-in launch, not just once.** `identify` is idempotent and queued durably (offline-safe), so repeating it is cheap — and it self-heals devices that missed a previous attempt.
|
|
255
|
+
- **`reset()` unlinks the device.** It regenerates the anonymous id (use it on logout/account switch). After the next login, call `identify` again or new tokens stay anonymous.
|
|
256
|
+
|
|
236
257
|
## Imperative API
|
|
237
258
|
|
|
238
259
|
For use outside React components:
|
|
@@ -240,9 +261,9 @@ For use outside React components:
|
|
|
240
261
|
```tsx
|
|
241
262
|
import { PaywalloClient } from '@virex-tech/paywallo-sdk';
|
|
242
263
|
|
|
243
|
-
// Identify
|
|
264
|
+
// Identify user (see "Identify users" above — properties.userId is what links the user).
|
|
244
265
|
// Model fields: email, firstName, lastName, dateOfBirth, gender, phone, properties.
|
|
245
|
-
await PaywalloClient.identify({ email: 'user@example.com',
|
|
266
|
+
await PaywalloClient.identify({ email: 'user@example.com', properties: { userId: 'user-123' } });
|
|
246
267
|
|
|
247
268
|
// Custom events
|
|
248
269
|
await PaywalloClient.track('my_event', { properties: { plan: 'premium' } });
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
-->
|
|
9
9
|
<uses-permission android:name="com.android.vending.BILLING" />
|
|
10
10
|
|
|
11
|
+
<!--
|
|
12
|
+
POST_NOTIFICATIONS (Android 13+) — required to display push notifications.
|
|
13
|
+
PaywalloNotificationsModule requests it at runtime, but the request only
|
|
14
|
+
surfaces a system prompt if the permission is declared in the merged
|
|
15
|
+
manifest. Declaring it here means the host app gets it for free.
|
|
16
|
+
-->
|
|
17
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
18
|
+
|
|
11
19
|
<application>
|
|
12
20
|
<!--
|
|
13
21
|
PaywalloFirebaseMessagingService handles FCM token refresh and message delivery.
|
|
@@ -233,7 +233,11 @@ class PaywalloStoreKitModule(private val reactContext: ReactApplicationContext)
|
|
|
233
233
|
listOf(productDetailsParams.build())
|
|
234
234
|
)
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
// Google Play rejects obfuscated account IDs longer than 64 chars
|
|
237
|
+
// with an IllegalArgumentException that aborts launchBillingFlow.
|
|
238
|
+
// A UUID is well under the limit; guard so an oversized id degrades
|
|
239
|
+
// to "no account id" instead of failing the whole purchase.
|
|
240
|
+
if (!appAccountToken.isNullOrBlank() && appAccountToken.length <= 64) {
|
|
237
241
|
flowParamsBuilder.setObfuscatedAccountId(appAccountToken)
|
|
238
242
|
}
|
|
239
243
|
|
|
@@ -391,9 +395,17 @@ class PaywalloStoreKitModule(private val reactContext: ReactApplicationContext)
|
|
|
391
395
|
.setProductType(BillingClient.ProductType.SUBS)
|
|
392
396
|
.build()
|
|
393
397
|
)
|
|
398
|
+
// INAPP covers non-consumables (lifetime unlocks). iOS restores
|
|
399
|
+
// these via Transaction.currentEntitlements, so querying only SUBS
|
|
400
|
+
// here would silently drop non-consumables from restore on Android.
|
|
401
|
+
val inappResult = client.queryPurchasesAsync(
|
|
402
|
+
QueryPurchasesParams.newBuilder()
|
|
403
|
+
.setProductType(BillingClient.ProductType.INAPP)
|
|
404
|
+
.build()
|
|
405
|
+
)
|
|
394
406
|
|
|
395
407
|
val seenTokens = mutableSetOf<String>()
|
|
396
|
-
for (purchase in subsResult.purchasesList) {
|
|
408
|
+
for (purchase in subsResult.purchasesList + inappResult.purchasesList) {
|
|
397
409
|
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
|
|
398
410
|
seenTokens.add(purchase.purchaseToken)
|
|
399
411
|
val map = WritableNativeMap()
|
package/dist/index.d.mts
CHANGED
|
@@ -922,6 +922,8 @@ interface AttributionCapture {
|
|
|
922
922
|
readonly capturedAt: string;
|
|
923
923
|
}
|
|
924
924
|
type AttributionInput = Omit<AttributionCapture, "capturedAt">;
|
|
925
|
+
/** Listener notified when `capture()` performs its first real write. */
|
|
926
|
+
type AttributionCaptureListener = (capture: AttributionCapture) => void;
|
|
925
927
|
/**
|
|
926
928
|
* Attribution module — first-write-wins persistence of UTM / click-ID data.
|
|
927
929
|
*
|
|
@@ -935,6 +937,7 @@ type AttributionInput = Omit<AttributionCapture, "capturedAt">;
|
|
|
935
937
|
declare class AttributionTracker {
|
|
936
938
|
private cache;
|
|
937
939
|
private captureInProgress;
|
|
940
|
+
private readonly listeners;
|
|
938
941
|
private readonly storage;
|
|
939
942
|
constructor(debug?: boolean);
|
|
940
943
|
/**
|
|
@@ -949,6 +952,14 @@ declare class AttributionTracker {
|
|
|
949
952
|
* @param data - Attribution fields without `capturedAt` (generated internally).
|
|
950
953
|
*/
|
|
951
954
|
capture(data: AttributionInput): Promise<void>;
|
|
955
|
+
/**
|
|
956
|
+
* Subscribes to the first real attribution write (first-write-wins — no-op
|
|
957
|
+
* captures never fire). Useful for late enrichment consumers (e.g. re-push
|
|
958
|
+
* of Superwall attributes when a warm deep link / deferred match lands after
|
|
959
|
+
* init). Returns an unsubscribe function.
|
|
960
|
+
*/
|
|
961
|
+
onCapture(listener: AttributionCaptureListener): () => void;
|
|
962
|
+
private notifyListeners;
|
|
952
963
|
/**
|
|
953
964
|
* Returns the persisted attribution capture, or `null` if none exists.
|
|
954
965
|
* Synchronous — reads from in-memory cache (populated via `loadFromStorage()`).
|
|
@@ -1877,6 +1888,35 @@ interface UsePlanPurchaseResult {
|
|
|
1877
1888
|
}
|
|
1878
1889
|
declare function usePlanPurchase(): UsePlanPurchaseResult;
|
|
1879
1890
|
|
|
1891
|
+
/**
|
|
1892
|
+
* Extracts the name of the currently active leaf route from a react-navigation
|
|
1893
|
+
* NavigationState. Descends into nested navigators recursively.
|
|
1894
|
+
*
|
|
1895
|
+
* Does NOT import react-navigation — accepts `state: unknown` and uses
|
|
1896
|
+
* structural duck-typing, so it works with v6, v7, and future versions alike.
|
|
1897
|
+
*
|
|
1898
|
+
* @returns the active route name string, or null if the shape is unexpected.
|
|
1899
|
+
*/
|
|
1900
|
+
declare function getActiveRouteName(state: unknown): string | null;
|
|
1901
|
+
/**
|
|
1902
|
+
* Returns a stable `onStateChange` callback ready to be passed directly to
|
|
1903
|
+
* `<NavigationContainer onStateChange={onStateChange}>`.
|
|
1904
|
+
*
|
|
1905
|
+
* Emits a `screen_view` event via the PaywalloClient pipeline each time the
|
|
1906
|
+
* active route changes. Consecutive calls with the same screen are no-ops
|
|
1907
|
+
* (deduplication by name). If the SDK is not yet initialized, calls are
|
|
1908
|
+
* silently ignored.
|
|
1909
|
+
*
|
|
1910
|
+
* @example
|
|
1911
|
+
* ```tsx
|
|
1912
|
+
* const onStateChange = usePaywalloScreenTracking();
|
|
1913
|
+
* <NavigationContainer onStateChange={onStateChange}>
|
|
1914
|
+
* {children}
|
|
1915
|
+
* </NavigationContainer>
|
|
1916
|
+
* ```
|
|
1917
|
+
*/
|
|
1918
|
+
declare function usePaywalloScreenTracking(): (state: unknown) => void;
|
|
1919
|
+
|
|
1880
1920
|
interface UsePlansResult {
|
|
1881
1921
|
currentPlan: Plan | null;
|
|
1882
1922
|
allPlans: Plan[];
|
|
@@ -1934,35 +1974,6 @@ interface UseSubscriptionResult {
|
|
|
1934
1974
|
}
|
|
1935
1975
|
declare function useSubscription(): UseSubscriptionResult;
|
|
1936
1976
|
|
|
1937
|
-
/**
|
|
1938
|
-
* Extracts the name of the currently active leaf route from a react-navigation
|
|
1939
|
-
* NavigationState. Descends into nested navigators recursively.
|
|
1940
|
-
*
|
|
1941
|
-
* Does NOT import react-navigation — accepts `state: unknown` and uses
|
|
1942
|
-
* structural duck-typing, so it works with v6, v7, and future versions alike.
|
|
1943
|
-
*
|
|
1944
|
-
* @returns the active route name string, or null if the shape is unexpected.
|
|
1945
|
-
*/
|
|
1946
|
-
declare function getActiveRouteName(state: unknown): string | null;
|
|
1947
|
-
/**
|
|
1948
|
-
* Returns a stable `onStateChange` callback ready to be passed directly to
|
|
1949
|
-
* `<NavigationContainer onStateChange={onStateChange}>`.
|
|
1950
|
-
*
|
|
1951
|
-
* Emits a `screen_view` event via the PaywalloClient pipeline each time the
|
|
1952
|
-
* active route changes. Consecutive calls with the same screen are no-ops
|
|
1953
|
-
* (deduplication by name). If the SDK is not yet initialized, calls are
|
|
1954
|
-
* silently ignored.
|
|
1955
|
-
*
|
|
1956
|
-
* @example
|
|
1957
|
-
* ```tsx
|
|
1958
|
-
* const onStateChange = usePaywalloScreenTracking();
|
|
1959
|
-
* <NavigationContainer onStateChange={onStateChange}>
|
|
1960
|
-
* {children}
|
|
1961
|
-
* </NavigationContainer>
|
|
1962
|
-
* ```
|
|
1963
|
-
*/
|
|
1964
|
-
declare function usePaywalloScreenTracking(): (state: unknown) => void;
|
|
1965
|
-
|
|
1966
1977
|
interface PaywalloProviderProps {
|
|
1967
1978
|
children: React__default.ReactNode;
|
|
1968
1979
|
config: PaywalloConfig;
|
package/dist/index.d.ts
CHANGED
|
@@ -922,6 +922,8 @@ interface AttributionCapture {
|
|
|
922
922
|
readonly capturedAt: string;
|
|
923
923
|
}
|
|
924
924
|
type AttributionInput = Omit<AttributionCapture, "capturedAt">;
|
|
925
|
+
/** Listener notified when `capture()` performs its first real write. */
|
|
926
|
+
type AttributionCaptureListener = (capture: AttributionCapture) => void;
|
|
925
927
|
/**
|
|
926
928
|
* Attribution module — first-write-wins persistence of UTM / click-ID data.
|
|
927
929
|
*
|
|
@@ -935,6 +937,7 @@ type AttributionInput = Omit<AttributionCapture, "capturedAt">;
|
|
|
935
937
|
declare class AttributionTracker {
|
|
936
938
|
private cache;
|
|
937
939
|
private captureInProgress;
|
|
940
|
+
private readonly listeners;
|
|
938
941
|
private readonly storage;
|
|
939
942
|
constructor(debug?: boolean);
|
|
940
943
|
/**
|
|
@@ -949,6 +952,14 @@ declare class AttributionTracker {
|
|
|
949
952
|
* @param data - Attribution fields without `capturedAt` (generated internally).
|
|
950
953
|
*/
|
|
951
954
|
capture(data: AttributionInput): Promise<void>;
|
|
955
|
+
/**
|
|
956
|
+
* Subscribes to the first real attribution write (first-write-wins — no-op
|
|
957
|
+
* captures never fire). Useful for late enrichment consumers (e.g. re-push
|
|
958
|
+
* of Superwall attributes when a warm deep link / deferred match lands after
|
|
959
|
+
* init). Returns an unsubscribe function.
|
|
960
|
+
*/
|
|
961
|
+
onCapture(listener: AttributionCaptureListener): () => void;
|
|
962
|
+
private notifyListeners;
|
|
952
963
|
/**
|
|
953
964
|
* Returns the persisted attribution capture, or `null` if none exists.
|
|
954
965
|
* Synchronous — reads from in-memory cache (populated via `loadFromStorage()`).
|
|
@@ -1877,6 +1888,35 @@ interface UsePlanPurchaseResult {
|
|
|
1877
1888
|
}
|
|
1878
1889
|
declare function usePlanPurchase(): UsePlanPurchaseResult;
|
|
1879
1890
|
|
|
1891
|
+
/**
|
|
1892
|
+
* Extracts the name of the currently active leaf route from a react-navigation
|
|
1893
|
+
* NavigationState. Descends into nested navigators recursively.
|
|
1894
|
+
*
|
|
1895
|
+
* Does NOT import react-navigation — accepts `state: unknown` and uses
|
|
1896
|
+
* structural duck-typing, so it works with v6, v7, and future versions alike.
|
|
1897
|
+
*
|
|
1898
|
+
* @returns the active route name string, or null if the shape is unexpected.
|
|
1899
|
+
*/
|
|
1900
|
+
declare function getActiveRouteName(state: unknown): string | null;
|
|
1901
|
+
/**
|
|
1902
|
+
* Returns a stable `onStateChange` callback ready to be passed directly to
|
|
1903
|
+
* `<NavigationContainer onStateChange={onStateChange}>`.
|
|
1904
|
+
*
|
|
1905
|
+
* Emits a `screen_view` event via the PaywalloClient pipeline each time the
|
|
1906
|
+
* active route changes. Consecutive calls with the same screen are no-ops
|
|
1907
|
+
* (deduplication by name). If the SDK is not yet initialized, calls are
|
|
1908
|
+
* silently ignored.
|
|
1909
|
+
*
|
|
1910
|
+
* @example
|
|
1911
|
+
* ```tsx
|
|
1912
|
+
* const onStateChange = usePaywalloScreenTracking();
|
|
1913
|
+
* <NavigationContainer onStateChange={onStateChange}>
|
|
1914
|
+
* {children}
|
|
1915
|
+
* </NavigationContainer>
|
|
1916
|
+
* ```
|
|
1917
|
+
*/
|
|
1918
|
+
declare function usePaywalloScreenTracking(): (state: unknown) => void;
|
|
1919
|
+
|
|
1880
1920
|
interface UsePlansResult {
|
|
1881
1921
|
currentPlan: Plan | null;
|
|
1882
1922
|
allPlans: Plan[];
|
|
@@ -1934,35 +1974,6 @@ interface UseSubscriptionResult {
|
|
|
1934
1974
|
}
|
|
1935
1975
|
declare function useSubscription(): UseSubscriptionResult;
|
|
1936
1976
|
|
|
1937
|
-
/**
|
|
1938
|
-
* Extracts the name of the currently active leaf route from a react-navigation
|
|
1939
|
-
* NavigationState. Descends into nested navigators recursively.
|
|
1940
|
-
*
|
|
1941
|
-
* Does NOT import react-navigation — accepts `state: unknown` and uses
|
|
1942
|
-
* structural duck-typing, so it works with v6, v7, and future versions alike.
|
|
1943
|
-
*
|
|
1944
|
-
* @returns the active route name string, or null if the shape is unexpected.
|
|
1945
|
-
*/
|
|
1946
|
-
declare function getActiveRouteName(state: unknown): string | null;
|
|
1947
|
-
/**
|
|
1948
|
-
* Returns a stable `onStateChange` callback ready to be passed directly to
|
|
1949
|
-
* `<NavigationContainer onStateChange={onStateChange}>`.
|
|
1950
|
-
*
|
|
1951
|
-
* Emits a `screen_view` event via the PaywalloClient pipeline each time the
|
|
1952
|
-
* active route changes. Consecutive calls with the same screen are no-ops
|
|
1953
|
-
* (deduplication by name). If the SDK is not yet initialized, calls are
|
|
1954
|
-
* silently ignored.
|
|
1955
|
-
*
|
|
1956
|
-
* @example
|
|
1957
|
-
* ```tsx
|
|
1958
|
-
* const onStateChange = usePaywalloScreenTracking();
|
|
1959
|
-
* <NavigationContainer onStateChange={onStateChange}>
|
|
1960
|
-
* {children}
|
|
1961
|
-
* </NavigationContainer>
|
|
1962
|
-
* ```
|
|
1963
|
-
*/
|
|
1964
|
-
declare function usePaywalloScreenTracking(): (state: unknown) => void;
|
|
1965
|
-
|
|
1966
1977
|
interface PaywalloProviderProps {
|
|
1967
1978
|
children: React__default.ReactNode;
|
|
1968
1979
|
config: PaywalloConfig;
|