react-native-iap 15.6.0 → 15.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/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +28 -4
- package/android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt +13 -0
- package/ios/HybridRnIap.swift +25 -4
- package/ios/RnIapHelper.swift +3 -2
- package/ios/RnIapLog.swift +14 -0
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +72 -30
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.kepler.js +71 -5
- package/lib/module/index.kepler.js.map +1 -1
- package/lib/module/kit-api.js +78 -3
- package/lib/module/kit-api.js.map +1 -1
- package/lib/module/types.js +29 -25
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/deprecation.js +15 -0
- package/lib/module/utils/deprecation.js.map +1 -0
- package/lib/module/utils/platform-request.js +28 -0
- package/lib/module/utils/platform-request.js.map +1 -0
- package/lib/module/utils/type-bridge.js +9 -4
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/module/vega-adapter.js +31 -22
- package/lib/module/vega-adapter.js.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts +14 -2
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +37 -12
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.kepler.d.ts +49 -4
- package/lib/typescript/src/index.kepler.d.ts.map +1 -1
- package/lib/typescript/src/kit-api.d.ts +18 -8
- package/lib/typescript/src/kit-api.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +24 -6
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +105 -176
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/deprecation.d.ts +3 -0
- package/lib/typescript/src/utils/deprecation.d.ts.map +1 -0
- package/lib/typescript/src/utils/platform-request.d.ts +12 -0
- package/lib/typescript/src/utils/platform-request.d.ts.map +1 -0
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/lib/typescript/src/vega-adapter.d.ts +1 -0
- package/lib/typescript/src/vega-adapter.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JNitroPurchase.hpp +5 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +7 -2
- package/nitrogen/generated/ios/swift/NitroPurchase.swift +39 -2
- package/nitrogen/generated/shared/c++/NitroPurchase.hpp +5 -1
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/src/hooks/useIAP.ts +14 -2
- package/src/index.kepler.ts +82 -8
- package/src/index.ts +104 -47
- package/src/kit-api.ts +136 -13
- package/src/specs/RnIap.nitro.ts +24 -6
- package/src/types.ts +116 -204
- package/src/utils/deprecation.ts +16 -0
- package/src/utils/platform-request.ts +37 -0
- package/src/utils/type-bridge.ts +20 -4
- package/src/vega-adapter.ts +46 -24
- package/lib/module/hooks/useWebhookEvents.js +0 -113
- package/lib/module/hooks/useWebhookEvents.js.map +0 -1
- package/lib/module/webhook-client.js +0 -164
- package/lib/module/webhook-client.js.map +0 -1
- package/lib/typescript/src/hooks/useWebhookEvents.d.ts +0 -55
- package/lib/typescript/src/hooks/useWebhookEvents.d.ts.map +0 -1
- package/lib/typescript/src/webhook-client.d.ts +0 -82
- package/lib/typescript/src/webhook-client.d.ts.map +0 -1
- package/src/hooks/useWebhookEvents.ts +0 -180
- package/src/webhook-client.ts +0 -312
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {RnIapConsole} from './debug';
|
|
2
|
+
|
|
3
|
+
const emittedLegacyWarnings = new Set<string>();
|
|
4
|
+
|
|
5
|
+
export const warnLegacyOnce = (key: string, message: string): void => {
|
|
6
|
+
if (emittedLegacyWarnings.has(key)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
emittedLegacyWarnings.add(key);
|
|
11
|
+
RnIapConsole.warn(message);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const resetLegacyWarningsForTesting = (): void => {
|
|
15
|
+
emittedLegacyWarnings.clear();
|
|
16
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface PlatformRequestSelection<T> {
|
|
2
|
+
usesLegacyKey: boolean;
|
|
3
|
+
value: T | null | undefined;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Selects a canonical platform request by key presence, not value.
|
|
8
|
+
*
|
|
9
|
+
* An explicitly supplied canonical `null` or `undefined` is authoritative and
|
|
10
|
+
* must not fall back to the deprecated alias.
|
|
11
|
+
*/
|
|
12
|
+
export const selectCanonicalPlatformRequest = <T>(
|
|
13
|
+
request: object,
|
|
14
|
+
canonicalKey: PropertyKey,
|
|
15
|
+
legacyKey: PropertyKey,
|
|
16
|
+
): PlatformRequestSelection<T> => {
|
|
17
|
+
const record = request as Record<PropertyKey, T | null | undefined>;
|
|
18
|
+
|
|
19
|
+
if (Object.prototype.hasOwnProperty.call(record, canonicalKey)) {
|
|
20
|
+
return {
|
|
21
|
+
usesLegacyKey: false,
|
|
22
|
+
value: record[canonicalKey],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (Object.prototype.hasOwnProperty.call(record, legacyKey)) {
|
|
27
|
+
return {
|
|
28
|
+
usesLegacyKey: true,
|
|
29
|
+
value: record[legacyKey],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
usesLegacyKey: false,
|
|
35
|
+
value: undefined,
|
|
36
|
+
};
|
|
37
|
+
};
|
package/src/utils/type-bridge.ts
CHANGED
|
@@ -470,8 +470,9 @@ export function convertNitroPurchaseToPurchase(
|
|
|
470
470
|
isAutoRenewing: Boolean(nitroPurchase.isAutoRenewing),
|
|
471
471
|
currentPlanId: toNullableString(nitroPurchase.currentPlanId),
|
|
472
472
|
ids: nitroPurchase.ids ?? null,
|
|
473
|
-
// PurchaseIOS requires
|
|
474
|
-
transactionId:
|
|
473
|
+
// PurchaseIOS requires a transaction ID; legacy native payloads used id.
|
|
474
|
+
transactionId:
|
|
475
|
+
toNullableString(nitroPurchase.transactionId) ?? nitroPurchase.id,
|
|
475
476
|
advancedCommerceInfoIOS: nitroPurchase.advancedCommerceInfoIOS ?? null,
|
|
476
477
|
billingPlanTypeIOS: nitroPurchase.billingPlanTypeIOS ?? null,
|
|
477
478
|
commitmentInfoIOS: nitroPurchase.commitmentInfoIOS ?? null,
|
|
@@ -525,6 +526,20 @@ export function convertNitroPurchaseToPurchase(
|
|
|
525
526
|
return iosPurchase;
|
|
526
527
|
}
|
|
527
528
|
|
|
529
|
+
const explicitAndroidTransactionId = toNullableString(
|
|
530
|
+
nitroPurchase.transactionId,
|
|
531
|
+
);
|
|
532
|
+
const legacyAndroidId = toNullableString(nitroPurchase.id);
|
|
533
|
+
const androidPurchaseToken = toNullableString(
|
|
534
|
+
nitroPurchase.purchaseToken ?? nitroPurchase.purchaseTokenAndroid,
|
|
535
|
+
);
|
|
536
|
+
const androidTransactionId =
|
|
537
|
+
explicitAndroidTransactionId ??
|
|
538
|
+
(legacyAndroidId != null &&
|
|
539
|
+
(store !== STORE_GOOGLE || legacyAndroidId !== androidPurchaseToken)
|
|
540
|
+
? legacyAndroidId
|
|
541
|
+
: null);
|
|
542
|
+
|
|
528
543
|
const androidPurchase: PurchaseAndroid = {
|
|
529
544
|
id: nitroPurchase.id,
|
|
530
545
|
productId: nitroPurchase.productId,
|
|
@@ -538,8 +553,9 @@ export function convertNitroPurchaseToPurchase(
|
|
|
538
553
|
isAutoRenewing: Boolean(nitroPurchase.isAutoRenewing),
|
|
539
554
|
currentPlanId: toNullableString(nitroPurchase.currentPlanId),
|
|
540
555
|
ids: nitroPurchase.ids ?? null,
|
|
541
|
-
//
|
|
542
|
-
transactionId
|
|
556
|
+
// Android id falls back to purchaseToken when Play has no orderId, so do
|
|
557
|
+
// not synthesize a transactionId from it.
|
|
558
|
+
transactionId: androidTransactionId,
|
|
543
559
|
autoRenewingAndroid: toNullableBoolean(
|
|
544
560
|
nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing,
|
|
545
561
|
),
|
package/src/vega-adapter.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
} from './specs/RnIap.nitro';
|
|
12
12
|
import {ErrorCode} from './types';
|
|
13
13
|
import type {SubResponseCodeAndroid} from './types';
|
|
14
|
+
import {selectCanonicalPlatformRequest} from './utils/platform-request';
|
|
14
15
|
|
|
15
16
|
type ResponseOperation =
|
|
16
17
|
| 'product-data'
|
|
@@ -53,6 +54,7 @@ interface VegaProduct {
|
|
|
53
54
|
interface VegaReceipt {
|
|
54
55
|
cancelDate?: Date | number | string | null;
|
|
55
56
|
deferredDate?: Date | number | string | null;
|
|
57
|
+
deferredSku?: string | null;
|
|
56
58
|
isCancelled?: boolean | null;
|
|
57
59
|
isDeferred?: boolean | null;
|
|
58
60
|
productType?: unknown;
|
|
@@ -126,7 +128,6 @@ const FULFILLMENT_RESULT_FULFILLED = 1;
|
|
|
126
128
|
const RESPONSE_SUCCESS = 1;
|
|
127
129
|
const PURCHASE_RESPONSE_SUCCESS = 0;
|
|
128
130
|
const PURCHASE_STATE_PURCHASED = 1;
|
|
129
|
-
const PURCHASE_STATE_PENDING = 2;
|
|
130
131
|
const IAPKIT_DEFAULT_BASE_URL = 'https://kit.openiap.dev';
|
|
131
132
|
const IAPKIT_VERIFY_PATH = '/v1/purchase/verify';
|
|
132
133
|
const VEGA_PARSER_ERROR_MESSAGES = [
|
|
@@ -574,8 +575,13 @@ function getSubscriptionPeriod(product: VegaProduct): string {
|
|
|
574
575
|
return '';
|
|
575
576
|
}
|
|
576
577
|
|
|
578
|
+
function nonBlankString(value: unknown): string | null {
|
|
579
|
+
if (typeof value !== 'string') return null;
|
|
580
|
+
return value.trim().length > 0 ? value : null;
|
|
581
|
+
}
|
|
582
|
+
|
|
577
583
|
function getReceiptSku(receipt: VegaReceipt): string {
|
|
578
|
-
return receipt.sku ?? receipt.termSku ?? '';
|
|
584
|
+
return nonBlankString(receipt.sku) ?? nonBlankString(receipt.termSku) ?? '';
|
|
579
585
|
}
|
|
580
586
|
|
|
581
587
|
function getCachedProductType(
|
|
@@ -711,38 +717,58 @@ function mapReceipt(
|
|
|
711
717
|
const receiptId = receipt.receiptId ?? '';
|
|
712
718
|
const productId = productIdOverride ?? getReceiptSku(receipt);
|
|
713
719
|
const type = productTypeToOpenIap(receipt.productType ?? fallbackProductType);
|
|
714
|
-
const isPending = Boolean(receipt.isDeferred);
|
|
715
720
|
const isCanceled = Boolean(receipt.isCancelled || receipt.cancelDate);
|
|
716
|
-
const isActive = !isCanceled
|
|
721
|
+
const isActive = !isCanceled;
|
|
722
|
+
const deferredSku = nonBlankString(receipt.deferredSku);
|
|
717
723
|
|
|
718
724
|
return {
|
|
719
725
|
id: receiptId,
|
|
726
|
+
transactionId: receiptId,
|
|
720
727
|
productId,
|
|
721
728
|
transactionDate: toTimestamp(receipt.purchaseDate),
|
|
722
729
|
purchaseToken: receiptId,
|
|
723
|
-
currentPlanId:
|
|
730
|
+
currentPlanId:
|
|
731
|
+
type === 'subs' ? (nonBlankString(receipt.termSku) ?? productId) : null,
|
|
724
732
|
ids: productId ? [productId] : [],
|
|
725
733
|
platform: 'android',
|
|
726
734
|
store: 'amazon',
|
|
727
735
|
quantity: 1,
|
|
728
|
-
purchaseState:
|
|
736
|
+
purchaseState: isActive ? 'purchased' : 'unknown',
|
|
729
737
|
isAutoRenewing: type === 'subs' && isActive,
|
|
730
738
|
purchaseTokenAndroid: receiptId,
|
|
731
739
|
dataAndroid: stringifyJson(receipt),
|
|
732
740
|
signatureAndroid: null,
|
|
733
741
|
autoRenewingAndroid: type === 'subs' && isActive,
|
|
734
|
-
purchaseStateAndroid:
|
|
735
|
-
? PURCHASE_STATE_PENDING
|
|
736
|
-
: isActive
|
|
737
|
-
? PURCHASE_STATE_PURCHASED
|
|
738
|
-
: 0,
|
|
742
|
+
purchaseStateAndroid: isActive ? PURCHASE_STATE_PURCHASED : 0,
|
|
739
743
|
isAcknowledgedAndroid: false,
|
|
740
|
-
|
|
744
|
+
packageNameAndroid: null,
|
|
745
|
+
obfuscatedAccountIdAndroid: null,
|
|
746
|
+
obfuscatedProfileIdAndroid: null,
|
|
747
|
+
developerPayloadAndroid: null,
|
|
748
|
+
isSuspendedAndroid: false,
|
|
749
|
+
pendingPurchaseUpdateAndroid:
|
|
750
|
+
receipt.isDeferred && deferredSku
|
|
751
|
+
? {products: [deferredSku], purchaseToken: receiptId}
|
|
752
|
+
: null,
|
|
741
753
|
};
|
|
742
754
|
}
|
|
743
755
|
|
|
744
|
-
|
|
745
|
-
|
|
756
|
+
type VegaPurchaseRequest = Parameters<RnIap['requestPurchase']>[0];
|
|
757
|
+
type VegaAndroidPurchaseRequest = NonNullable<VegaPurchaseRequest['google']>;
|
|
758
|
+
|
|
759
|
+
function selectGooglePurchaseRequest(
|
|
760
|
+
request: VegaPurchaseRequest,
|
|
761
|
+
): VegaAndroidPurchaseRequest | null | undefined {
|
|
762
|
+
return selectCanonicalPlatformRequest<VegaAndroidPurchaseRequest>(
|
|
763
|
+
request,
|
|
764
|
+
'google',
|
|
765
|
+
'android',
|
|
766
|
+
).value;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function getSkuFromRequest(
|
|
770
|
+
androidRequest: VegaAndroidPurchaseRequest | null | undefined,
|
|
771
|
+
) {
|
|
746
772
|
const skus = androidRequest?.skus ?? [];
|
|
747
773
|
if (skus.length !== 1) {
|
|
748
774
|
throw createVegaError(
|
|
@@ -1026,13 +1052,11 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1026
1052
|
options?: Parameters<RnIap['getAvailablePurchases']>[0],
|
|
1027
1053
|
): Promise<NitroPurchase[]> => {
|
|
1028
1054
|
const requestedType = options?.android?.type;
|
|
1029
|
-
const includeSuspended = Boolean(options?.android?.includeSuspended);
|
|
1030
1055
|
const receipts = await getPurchaseUpdateReceipts();
|
|
1031
1056
|
await hydrateProductTypesForReceipts(receipts);
|
|
1032
1057
|
return receipts
|
|
1033
1058
|
.filter((receipt) => {
|
|
1034
1059
|
if (receipt.isCancelled || receipt.cancelDate) return false;
|
|
1035
|
-
if (!includeSuspended && receipt.isDeferred) return false;
|
|
1036
1060
|
const openIapType = productTypeToOpenIap(
|
|
1037
1061
|
receipt.productType ??
|
|
1038
1062
|
getCachedProductType(receipt, productTypesBySku),
|
|
@@ -1114,7 +1138,7 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1114
1138
|
const requestedPurchases: NitroPurchase[] = [];
|
|
1115
1139
|
|
|
1116
1140
|
for (const receipt of receipts) {
|
|
1117
|
-
if (receipt.isCancelled || receipt.cancelDate
|
|
1141
|
+
if (receipt.isCancelled || receipt.cancelDate) {
|
|
1118
1142
|
continue;
|
|
1119
1143
|
}
|
|
1120
1144
|
|
|
@@ -1422,8 +1446,8 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1422
1446
|
): Promise<Awaited<ReturnType<RnIap['requestPurchase']>>> {
|
|
1423
1447
|
let sku: string | undefined;
|
|
1424
1448
|
try {
|
|
1425
|
-
|
|
1426
|
-
|
|
1449
|
+
const androidRequest = selectGooglePurchaseRequest(request);
|
|
1450
|
+
sku = getSkuFromRequest(androidRequest);
|
|
1427
1451
|
const fallbackProductType = hasSubscriptionRequestContext(
|
|
1428
1452
|
androidRequest?.subscriptionOffers,
|
|
1429
1453
|
)
|
|
@@ -1515,8 +1539,8 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1515
1539
|
purchaseToken: purchase.purchaseToken ?? null,
|
|
1516
1540
|
transactionDate: purchase.transactionDate,
|
|
1517
1541
|
autoRenewingAndroid: purchase.autoRenewingAndroid ?? true,
|
|
1518
|
-
basePlanIdAndroid: purchase.productId,
|
|
1519
|
-
currentPlanId: purchase.productId,
|
|
1542
|
+
basePlanIdAndroid: purchase.currentPlanId ?? purchase.productId,
|
|
1543
|
+
currentPlanId: purchase.currentPlanId ?? purchase.productId,
|
|
1520
1544
|
purchaseTokenAndroid: purchase.purchaseTokenAndroid ?? null,
|
|
1521
1545
|
}));
|
|
1522
1546
|
},
|
|
@@ -1540,9 +1564,7 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1540
1564
|
return true;
|
|
1541
1565
|
},
|
|
1542
1566
|
async restorePurchases(): Promise<void> {
|
|
1543
|
-
const purchases = await getAvailablePurchases(
|
|
1544
|
-
android: {includeSuspended: false},
|
|
1545
|
-
});
|
|
1567
|
+
const purchases = await getAvailablePurchases();
|
|
1546
1568
|
purchases.forEach(emitPurchaseUpdated);
|
|
1547
1569
|
},
|
|
1548
1570
|
addPurchaseUpdatedListener(listener): number {
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { connectWebhookStream } from "../webhook-client.js";
|
|
5
|
-
// React hook wrapping the SSE webhook stream. Lifecycle:
|
|
6
|
-
// - opens on mount (once `apiKey` is non-empty),
|
|
7
|
-
// - closes on unmount,
|
|
8
|
-
// - reconnects automatically when EventSource raises a transport
|
|
9
|
-
// error (the underlying client auto-reconnects via the EventSource
|
|
10
|
-
// spec; this hook just surfaces the error and re-renders).
|
|
11
|
-
//
|
|
12
|
-
// Why a hook: openiap's UX guidance is that consumers consume webhook
|
|
13
|
-
// events from React state (granting entitlement, refreshing the
|
|
14
|
-
// subscription view) rather than via an imperative listener. The
|
|
15
|
-
// hook's `events` buffer + `onEvent` callback cover both styles.
|
|
16
|
-
export function useWebhookEvents({
|
|
17
|
-
apiKey,
|
|
18
|
-
baseUrl,
|
|
19
|
-
eventSourceFactory,
|
|
20
|
-
bufferSize = 50,
|
|
21
|
-
onEvent,
|
|
22
|
-
onError
|
|
23
|
-
}) {
|
|
24
|
-
const [events, setEvents] = useState([]);
|
|
25
|
-
const [lastError, setLastError] = useState(null);
|
|
26
|
-
const [isConnected, setIsConnected] = useState(false);
|
|
27
|
-
|
|
28
|
-
// Stash callbacks in refs so reconnects don't fire on every render.
|
|
29
|
-
// The underlying SSE connection should only restart when `apiKey` /
|
|
30
|
-
// `baseUrl` change. `eventSourceFactory` is held in a ref too so
|
|
31
|
-
// anonymous-function callers don't tear down the connection every
|
|
32
|
-
// render (a common React pitfall — was previously documented as a
|
|
33
|
-
// caller-side constraint, now enforced by the hook). `bufferSize`
|
|
34
|
-
// is also a ref so adjusting the buffer cap from the host component
|
|
35
|
-
// doesn't tear down the stream and lose in-flight events.
|
|
36
|
-
const onEventRef = useRef(onEvent);
|
|
37
|
-
const onErrorRef = useRef(onError);
|
|
38
|
-
const eventSourceFactoryRef = useRef(eventSourceFactory);
|
|
39
|
-
const bufferSizeRef = useRef(bufferSize);
|
|
40
|
-
onEventRef.current = onEvent;
|
|
41
|
-
onErrorRef.current = onError;
|
|
42
|
-
eventSourceFactoryRef.current = eventSourceFactory;
|
|
43
|
-
bufferSizeRef.current = bufferSize;
|
|
44
|
-
|
|
45
|
-
// Trim the visible buffer immediately when bufferSize is lowered
|
|
46
|
-
// mid-stream. The ref-based update would otherwise only take
|
|
47
|
-
// effect on the next event.
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
setEvents(prev => bufferSize > 0 ? prev.slice(0, bufferSize) : []);
|
|
50
|
-
}, [bufferSize]);
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
// Fresh stream → fresh state. Resetting events + lastError on
|
|
53
|
-
// (re)connect prevents a stale payload from the previous
|
|
54
|
-
// apiKey/baseUrl from briefly leaking into the new context.
|
|
55
|
-
setEvents([]);
|
|
56
|
-
setLastError(null);
|
|
57
|
-
if (!apiKey) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
let listener = null;
|
|
61
|
-
let mounted = true;
|
|
62
|
-
try {
|
|
63
|
-
listener = connectWebhookStream({
|
|
64
|
-
apiKey,
|
|
65
|
-
baseUrl,
|
|
66
|
-
eventSourceFactory: eventSourceFactoryRef.current,
|
|
67
|
-
onEvent: event => {
|
|
68
|
-
if (!mounted) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
setIsConnected(true);
|
|
72
|
-
const cap = bufferSizeRef.current;
|
|
73
|
-
if (cap > 0) {
|
|
74
|
-
setEvents(prev => [event, ...prev].slice(0, cap));
|
|
75
|
-
}
|
|
76
|
-
onEventRef.current?.(event);
|
|
77
|
-
},
|
|
78
|
-
onError: error => {
|
|
79
|
-
if (!mounted) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
setLastError(error);
|
|
83
|
-
onErrorRef.current?.(error);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
} catch (error) {
|
|
87
|
-
const wrapped = {
|
|
88
|
-
code: 'TRANSPORT_ERROR',
|
|
89
|
-
message: error instanceof Error ? error.message : 'Failed to open webhook stream',
|
|
90
|
-
cause: error
|
|
91
|
-
};
|
|
92
|
-
setLastError(wrapped);
|
|
93
|
-
onErrorRef.current?.(wrapped);
|
|
94
|
-
}
|
|
95
|
-
return () => {
|
|
96
|
-
mounted = false;
|
|
97
|
-
listener?.close();
|
|
98
|
-
setIsConnected(false);
|
|
99
|
-
};
|
|
100
|
-
// `eventSourceFactory` deliberately omitted from deps — held in a
|
|
101
|
-
// ref above so anonymous-function callers don't trigger reconnects
|
|
102
|
-
// on every render. The connection is only re-opened when apiKey or
|
|
103
|
-
// baseUrl changes; a runtime factory swap is picked up on that
|
|
104
|
-
// next reconnect via the ref.
|
|
105
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
-
}, [apiKey, baseUrl]);
|
|
107
|
-
return {
|
|
108
|
-
events,
|
|
109
|
-
lastError,
|
|
110
|
-
isConnected
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=useWebhookEvents.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["useEffect","useRef","useState","connectWebhookStream","useWebhookEvents","apiKey","baseUrl","eventSourceFactory","bufferSize","onEvent","onError","events","setEvents","lastError","setLastError","isConnected","setIsConnected","onEventRef","onErrorRef","eventSourceFactoryRef","bufferSizeRef","current","prev","slice","listener","mounted","event","cap","error","wrapped","code","message","Error","cause","close"],"sourceRoot":"../../../src","sources":["hooks/useWebhookEvents.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Transport-agnostic webhook client for the openiap kit SSE stream
|
|
4
|
-
// (`GET /v1/webhooks/stream/{apiKey}`). Used by the JavaScript / TS
|
|
5
|
-
// wrappers (react-native-iap, expo-iap) but written without React or
|
|
6
|
-
// React-Native imports so it can also run in plain Node, browser, or
|
|
7
|
-
// any other JS runtime.
|
|
8
|
-
//
|
|
9
|
-
// The wire format is documented in `packages/kit/server/api/v1/webhooks.ts`
|
|
10
|
-
// and matches the GraphQL `WebhookEvent` shape from `webhook.graphql`.
|
|
11
|
-
//
|
|
12
|
-
// Parser logic is split out from the connection so it can be unit-
|
|
13
|
-
// tested without a live server. See `webhook-client.test.ts`.
|
|
14
|
-
|
|
15
|
-
export const WEBHOOK_EVENT_TYPES = ["SubscriptionStarted", "SubscriptionRenewed", "SubscriptionExpired", "SubscriptionInGracePeriod", "SubscriptionInBillingRetry", "SubscriptionRecovered", "SubscriptionCanceled", "SubscriptionUncanceled", "SubscriptionRevoked", "SubscriptionPriceChange", "SubscriptionProductChanged", "SubscriptionPaused", "SubscriptionResumed", "PurchaseRefunded", "PurchaseConsumptionRequest", "TestNotification"];
|
|
16
|
-
const DEFAULT_BASE_URL = "https://kit.openiap.dev";
|
|
17
|
-
export function connectWebhookStream(options) {
|
|
18
|
-
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
19
|
-
const url = `${trimTrailingSlash(baseUrl)}/v1/webhooks/stream/${encodeURIComponent(options.apiKey)}`;
|
|
20
|
-
const factory = options.eventSourceFactory ?? defaultEventSourceFactory;
|
|
21
|
-
let stream;
|
|
22
|
-
try {
|
|
23
|
-
stream = factory(url, {});
|
|
24
|
-
} catch (error) {
|
|
25
|
-
options.onError?.({
|
|
26
|
-
code: "NO_EVENTSOURCE",
|
|
27
|
-
message: error instanceof Error ? error.message : "EventSource constructor unavailable in this runtime",
|
|
28
|
-
cause: error
|
|
29
|
-
});
|
|
30
|
-
return {
|
|
31
|
-
close: () => {}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
const seenIds = new Set();
|
|
35
|
-
const seenOrder = [];
|
|
36
|
-
const markSeen = id => {
|
|
37
|
-
if (seenIds.has(id)) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
seenIds.add(id);
|
|
41
|
-
seenOrder.push(id);
|
|
42
|
-
if (seenOrder.length > 1024) {
|
|
43
|
-
const evicted = seenOrder.shift();
|
|
44
|
-
if (evicted !== undefined) {
|
|
45
|
-
seenIds.delete(evicted);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
};
|
|
50
|
-
const handleData = raw => {
|
|
51
|
-
const parsed = parseWebhookEventData(raw);
|
|
52
|
-
if (parsed.kind === "error") {
|
|
53
|
-
options.onError?.({
|
|
54
|
-
code: "PARSE_ERROR",
|
|
55
|
-
message: parsed.message
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (parsed.kind === "skip") {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (markSeen(parsed.event.id)) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
options.onEvent(parsed.event);
|
|
66
|
-
};
|
|
67
|
-
if (typeof stream.addEventListener === "function") {
|
|
68
|
-
stream.addEventListener("message", event => handleData(event.data));
|
|
69
|
-
// WHATWG EventSource dispatches frames with `event: Foo` only to
|
|
70
|
-
// listeners registered for `Foo`, not to `message` / `onmessage`.
|
|
71
|
-
// Kit emits webhook frames as typed SSE events, so subscribe to
|
|
72
|
-
// every known webhook type and keep `message` for older servers or
|
|
73
|
-
// polyfills that collapse typed frames into the generic channel.
|
|
74
|
-
for (const eventType of WEBHOOK_EVENT_TYPES) {
|
|
75
|
-
stream.addEventListener(eventType, event => handleData(event.data));
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
stream.onmessage = event => handleData(event.data);
|
|
79
|
-
}
|
|
80
|
-
stream.onerror = error => {
|
|
81
|
-
options.onError?.({
|
|
82
|
-
code: "TRANSPORT_ERROR",
|
|
83
|
-
message: "SSE transport error (auto-reconnecting)",
|
|
84
|
-
cause: error
|
|
85
|
-
});
|
|
86
|
-
};
|
|
87
|
-
return {
|
|
88
|
-
close: () => {
|
|
89
|
-
try {
|
|
90
|
-
stream.close();
|
|
91
|
-
} catch {
|
|
92
|
-
// Closing an already-closed EventSource is a no-op in browsers
|
|
93
|
-
// but throws in some polyfills.
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// Pure helpers (exported for testing).
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
export function parseWebhookEventData(raw) {
|
|
104
|
-
if (!raw) {
|
|
105
|
-
return {
|
|
106
|
-
kind: "skip",
|
|
107
|
-
reason: "heartbeat"
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
let parsed;
|
|
111
|
-
try {
|
|
112
|
-
parsed = JSON.parse(raw);
|
|
113
|
-
} catch (error) {
|
|
114
|
-
return {
|
|
115
|
-
kind: "error",
|
|
116
|
-
message: error instanceof Error ? `Failed to parse SSE payload: ${error.message}` : "Failed to parse SSE payload"
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
if (typeof parsed !== "object" || parsed === null || !("type" in parsed) || typeof parsed.type !== "string") {
|
|
120
|
-
// Stream-control messages (the `ready`/`stream-error` envelopes
|
|
121
|
-
// emitted by the kit server) have no `type` and are surfaced as
|
|
122
|
-
// skips so consumers don't see them as events.
|
|
123
|
-
return {
|
|
124
|
-
kind: "skip",
|
|
125
|
-
reason: "stream-control"
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
const event = parsed;
|
|
129
|
-
if (typeof event.id !== "string" || typeof event.occurredAt !== "number" || typeof event.receivedAt !== "number") {
|
|
130
|
-
return {
|
|
131
|
-
kind: "error",
|
|
132
|
-
message: `WebhookEvent missing required fields (id/occurredAt/receivedAt)`
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
// purchaseToken is required for every event type *except*
|
|
136
|
-
// TestNotification — Apple ASN v2 / Google RTDN test payloads
|
|
137
|
-
// carry no transaction. Hard-rejecting here would surface valid
|
|
138
|
-
// test webhooks as MALFORMED_EVENT and never reach listeners.
|
|
139
|
-
if (event.type !== "TestNotification" && typeof event.purchaseToken !== "string") {
|
|
140
|
-
return {
|
|
141
|
-
kind: "error",
|
|
142
|
-
message: `WebhookEvent missing required field purchaseToken`
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
kind: "ok",
|
|
147
|
-
event
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function trimTrailingSlash(url) {
|
|
151
|
-
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
152
|
-
}
|
|
153
|
-
function defaultEventSourceFactory(url, _headers) {
|
|
154
|
-
// EventSource is part of the WHATWG spec and available in all
|
|
155
|
-
// browser environments and most JS runtimes (Bun, Node 22+, Deno).
|
|
156
|
-
// RN does not ship it natively — consumers must pass
|
|
157
|
-
// `eventSourceFactory` from `react-native-sse` or similar.
|
|
158
|
-
const ctor = globalThis.EventSource;
|
|
159
|
-
if (!ctor) {
|
|
160
|
-
throw new Error("EventSource is not defined. Pass `eventSourceFactory` for runtimes without a built-in EventSource.");
|
|
161
|
-
}
|
|
162
|
-
return new ctor(url);
|
|
163
|
-
}
|
|
164
|
-
//# sourceMappingURL=webhook-client.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["WEBHOOK_EVENT_TYPES","DEFAULT_BASE_URL","connectWebhookStream","options","baseUrl","url","trimTrailingSlash","encodeURIComponent","apiKey","factory","eventSourceFactory","defaultEventSourceFactory","stream","error","onError","code","message","Error","cause","close","seenIds","Set","seenOrder","markSeen","id","has","add","push","length","evicted","shift","undefined","delete","handleData","raw","parsed","parseWebhookEventData","kind","event","onEvent","addEventListener","data","eventType","onmessage","onerror","reason","JSON","parse","type","occurredAt","receivedAt","purchaseToken","endsWith","slice","_headers","ctor","globalThis","EventSource"],"sourceRoot":"../../src","sources":["webhook-client.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { type WebhookEventPayload, type WebhookEventStream, type WebhookListenerError } from '../webhook-client';
|
|
2
|
-
export type UseWebhookEventsOptions = {
|
|
3
|
-
/**
|
|
4
|
-
* kit project API key — same value used for receipt verification.
|
|
5
|
-
* Must be non-empty to start the stream; pass `null`/`undefined` to
|
|
6
|
-
* disable the listener (e.g. before the user is logged in).
|
|
7
|
-
*/
|
|
8
|
-
apiKey: string | null | undefined;
|
|
9
|
-
/**
|
|
10
|
-
* Override the kit base URL. Defaults to https://kit.openiap.dev.
|
|
11
|
-
*/
|
|
12
|
-
baseUrl?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Optional EventSource factory. Required on React Native because RN
|
|
15
|
-
* does not ship a global EventSource — pass an instance from
|
|
16
|
-
* `react-native-sse` (or any compatible polyfill).
|
|
17
|
-
*/
|
|
18
|
-
eventSourceFactory?: (url: string, headers: Record<string, string>) => WebhookEventStream;
|
|
19
|
-
/**
|
|
20
|
-
* Maximum number of events to retain in the in-memory ring buffer
|
|
21
|
-
* surfaced as `events`. Older entries are discarded. Defaults to 50.
|
|
22
|
-
* Set 0 to opt out of the buffer entirely (consume only via
|
|
23
|
-
* `onEvent`).
|
|
24
|
-
*/
|
|
25
|
-
bufferSize?: number;
|
|
26
|
-
/**
|
|
27
|
-
* Called for every received event in addition to being appended to
|
|
28
|
-
* the buffer. Useful for side effects (toast, analytics, granting
|
|
29
|
-
* entitlement). Called with the latest stable callback identity.
|
|
30
|
-
*/
|
|
31
|
-
onEvent?: (event: WebhookEventPayload) => void;
|
|
32
|
-
/**
|
|
33
|
-
* Called when the stream surfaces a transport / parse error.
|
|
34
|
-
* EventSource auto-reconnects regardless of this hook — this is
|
|
35
|
-
* primarily for telemetry + UI surfacing.
|
|
36
|
-
*/
|
|
37
|
-
onError?: (error: WebhookListenerError) => void;
|
|
38
|
-
};
|
|
39
|
-
export type UseWebhookEventsResult = {
|
|
40
|
-
/** Most recent N events (most-recent-first). Capped at bufferSize. */
|
|
41
|
-
events: WebhookEventPayload[];
|
|
42
|
-
/** Last error reported by the underlying stream. Null when healthy. */
|
|
43
|
-
lastError: WebhookListenerError | null;
|
|
44
|
-
/**
|
|
45
|
-
* True once the first webhook event has been received from the
|
|
46
|
-
* stream. Remains false if the connection is open but idle (the
|
|
47
|
-
* underlying SSE bridge doesn't surface a "stream opened"
|
|
48
|
-
* lifecycle event we can hook into; isConnected is therefore an
|
|
49
|
-
* activity indicator, not a raw socket-state flag). Reset to
|
|
50
|
-
* false on cleanup / apiKey change.
|
|
51
|
-
*/
|
|
52
|
-
isConnected: boolean;
|
|
53
|
-
};
|
|
54
|
-
export declare function useWebhookEvents({ apiKey, baseUrl, eventSourceFactory, bufferSize, onEvent, onError, }: UseWebhookEventsOptions): UseWebhookEventsResult;
|
|
55
|
-
//# sourceMappingURL=useWebhookEvents.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useWebhookEvents.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useWebhookEvents.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,KAAK,oBAAoB,EAC1B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC5B,kBAAkB,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,sEAAsE;IACtE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,uEAAuE;IACvE,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACvC;;;;;;;OAOG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAaF,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,kBAAkB,EAClB,UAAe,EACf,OAAO,EACP,OAAO,GACR,EAAE,uBAAuB,GAAG,sBAAsB,CA8FlD"}
|