react-native-iap 15.5.4 → 15.6.1
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 +163 -15
- package/android/src/main/java/com/margelo/nitro/iap/PendingEventBuffer.kt +86 -0
- package/android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt +13 -0
- package/ios/HybridRnIap.swift +31 -4
- package/ios/RnIapHelper.swift +3 -2
- package/ios/RnIapLog.swift +14 -0
- package/lib/module/hooks/useIAP.js +8 -2
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +105 -30
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.kepler.js +72 -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 +34 -22
- package/lib/module/vega-adapter.js.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts +16 -2
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +56 -12
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.kepler.d.ts +50 -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 +32 -6
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +119 -175
- 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++/JHybridRnIapSpec.cpp +16 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JNitroPurchase.hpp +5 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +7 -2
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +8 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +19 -0
- package/nitrogen/generated/ios/swift/NitroPurchase.swift +39 -2
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +1 -0
- 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 +22 -2
- package/src/index.kepler.ts +86 -8
- package/src/index.ts +139 -47
- package/src/kit-api.ts +136 -13
- package/src/specs/RnIap.nitro.ts +33 -6
- package/src/types.ts +130 -203
- 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 +50 -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(
|
|
@@ -771,6 +797,7 @@ function throwUnsupportedFeature(feature: string): never {
|
|
|
771
797
|
type VegaRnIapModule = Partial<RnIap> & {
|
|
772
798
|
acknowledgePurchaseAndroid(purchaseToken: string): Promise<boolean>;
|
|
773
799
|
consumePurchaseAndroid(purchaseToken: string): Promise<boolean>;
|
|
800
|
+
openRedeemOfferCodeAndroid(): Promise<boolean>;
|
|
774
801
|
restorePurchases(): Promise<void>;
|
|
775
802
|
};
|
|
776
803
|
|
|
@@ -1025,13 +1052,11 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1025
1052
|
options?: Parameters<RnIap['getAvailablePurchases']>[0],
|
|
1026
1053
|
): Promise<NitroPurchase[]> => {
|
|
1027
1054
|
const requestedType = options?.android?.type;
|
|
1028
|
-
const includeSuspended = Boolean(options?.android?.includeSuspended);
|
|
1029
1055
|
const receipts = await getPurchaseUpdateReceipts();
|
|
1030
1056
|
await hydrateProductTypesForReceipts(receipts);
|
|
1031
1057
|
return receipts
|
|
1032
1058
|
.filter((receipt) => {
|
|
1033
1059
|
if (receipt.isCancelled || receipt.cancelDate) return false;
|
|
1034
|
-
if (!includeSuspended && receipt.isDeferred) return false;
|
|
1035
1060
|
const openIapType = productTypeToOpenIap(
|
|
1036
1061
|
receipt.productType ??
|
|
1037
1062
|
getCachedProductType(receipt, productTypesBySku),
|
|
@@ -1113,7 +1138,7 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1113
1138
|
const requestedPurchases: NitroPurchase[] = [];
|
|
1114
1139
|
|
|
1115
1140
|
for (const receipt of receipts) {
|
|
1116
|
-
if (receipt.isCancelled || receipt.cancelDate
|
|
1141
|
+
if (receipt.isCancelled || receipt.cancelDate) {
|
|
1117
1142
|
continue;
|
|
1118
1143
|
}
|
|
1119
1144
|
|
|
@@ -1421,8 +1446,8 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1421
1446
|
): Promise<Awaited<ReturnType<RnIap['requestPurchase']>>> {
|
|
1422
1447
|
let sku: string | undefined;
|
|
1423
1448
|
try {
|
|
1424
|
-
|
|
1425
|
-
|
|
1449
|
+
const androidRequest = selectGooglePurchaseRequest(request);
|
|
1450
|
+
sku = getSkuFromRequest(androidRequest);
|
|
1426
1451
|
const fallbackProductType = hasSubscriptionRequestContext(
|
|
1427
1452
|
androidRequest?.subscriptionOffers,
|
|
1428
1453
|
)
|
|
@@ -1514,8 +1539,8 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1514
1539
|
purchaseToken: purchase.purchaseToken ?? null,
|
|
1515
1540
|
transactionDate: purchase.transactionDate,
|
|
1516
1541
|
autoRenewingAndroid: purchase.autoRenewingAndroid ?? true,
|
|
1517
|
-
basePlanIdAndroid: purchase.productId,
|
|
1518
|
-
currentPlanId: purchase.productId,
|
|
1542
|
+
basePlanIdAndroid: purchase.currentPlanId ?? purchase.productId,
|
|
1543
|
+
currentPlanId: purchase.currentPlanId ?? purchase.productId,
|
|
1519
1544
|
purchaseTokenAndroid: purchase.purchaseTokenAndroid ?? null,
|
|
1520
1545
|
}));
|
|
1521
1546
|
},
|
|
@@ -1539,9 +1564,7 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1539
1564
|
return true;
|
|
1540
1565
|
},
|
|
1541
1566
|
async restorePurchases(): Promise<void> {
|
|
1542
|
-
const purchases = await getAvailablePurchases(
|
|
1543
|
-
android: {includeSuspended: false},
|
|
1544
|
-
});
|
|
1567
|
+
const purchases = await getAvailablePurchases();
|
|
1545
1568
|
purchases.forEach(emitPurchaseUpdated);
|
|
1546
1569
|
},
|
|
1547
1570
|
addPurchaseUpdatedListener(listener): number {
|
|
@@ -1680,6 +1703,9 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1680
1703
|
async launchExternalLinkAndroid(): Promise<boolean> {
|
|
1681
1704
|
return throwUnsupportedFeature('launchExternalLinkAndroid');
|
|
1682
1705
|
},
|
|
1706
|
+
async openRedeemOfferCodeAndroid(): Promise<boolean> {
|
|
1707
|
+
return false;
|
|
1708
|
+
},
|
|
1683
1709
|
async canPresentExternalPurchaseNoticeIOS(): Promise<boolean> {
|
|
1684
1710
|
return throwUnsupportedFeature('canPresentExternalPurchaseNoticeIOS');
|
|
1685
1711
|
},
|
|
@@ -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"}
|