react-native-iap 16.2.4 → 16.3.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/README.md +2 -2
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +115 -15
- package/ios/HybridRnIap.swift +5 -1
- package/lib/module/index.js +57 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.kepler.js +9 -0
- package/lib/module/index.kepler.js.map +1 -1
- package/lib/module/kit-api.js +3 -1
- package/lib/module/kit-api.js.map +1 -1
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/vega-adapter.js +9 -0
- package/lib/module/vega-adapter.js.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.kepler.d.ts.map +1 -1
- package/lib/typescript/src/kit-api.d.ts +119 -2
- package/lib/typescript/src/kit-api.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +13 -3
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +44 -9
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/vega-adapter.d.ts.map +1 -1
- package/nitrogen/generated/android/NitroIap+autolinking.cmake +1 -1
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +8 -4
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JNitroPurchaseVerificationResultAndroid.hpp +5 -1
- package/nitrogen/generated/android/c++/JNitroPurchaseVerificationResultHorizon.hpp +69 -0
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitAmazonProps.hpp +5 -1
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitResult.hpp +5 -1
- package/nitrogen/generated/android/c++/JVariant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid_NitroPurchaseVerificationResultHorizon.cpp +30 -0
- package/nitrogen/generated/android/c++/{JVariant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid.hpp → JVariant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid_NitroPurchaseVerificationResultHorizon.hpp} +33 -16
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseVerificationResultAndroid.kt +8 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseVerificationResultHorizon.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitAmazonProps.kt +8 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitResult.kt +7 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/{Variant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid.kt → Variant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid_NitroPurchaseVerificationResultHorizon.kt} +21 -8
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.cpp +4 -4
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +43 -34
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +4 -1
- package/nitrogen/generated/ios/swift/{Func_void_std__variant_NitroPurchaseVerificationResultIOS__NitroPurchaseVerificationResultAndroid_.swift → Func_void_std__variant_NitroPurchaseVerificationResultIOS__NitroPurchaseVerificationResultAndroid__NitroPurchaseVerificationResultHorizon_.swift} +14 -11
- package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +11 -9
- package/nitrogen/generated/ios/swift/NitroPurchaseVerificationResultAndroid.swift +7 -2
- package/nitrogen/generated/ios/swift/NitroPurchaseVerificationResultHorizon.swift +71 -0
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitAmazonProps.swift +39 -2
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitResult.swift +38 -1
- package/nitrogen/generated/ios/swift/{Variant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid.swift → Variant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid_NitroPurchaseVerificationResultHorizon.swift} +6 -4
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +4 -1
- package/nitrogen/generated/shared/c++/NitroPurchaseVerificationResultAndroid.hpp +5 -1
- package/nitrogen/generated/shared/c++/NitroPurchaseVerificationResultHorizon.hpp +93 -0
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitAmazonProps.hpp +6 -2
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitResult.hpp +5 -1
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/src/index.kepler.ts +10 -2
- package/src/index.ts +94 -19
- package/src/kit-api.ts +125 -4
- package/src/specs/RnIap.nitro.ts +16 -2
- package/src/types.ts +45 -9
- package/src/vega-adapter.ts +10 -0
- package/nitrogen/generated/android/c++/JVariant_NitroPurchaseVerificationResultIOS_NitroPurchaseVerificationResultAndroid.cpp +0 -26
|
@@ -51,6 +51,7 @@ namespace margelo::nitro::iap {
|
|
|
51
51
|
struct NitroVerifyPurchaseWithIapkitResult final {
|
|
52
52
|
public:
|
|
53
53
|
std::optional<std::variant<nitro::NullType, NitroIapkitProductClientPayload>> clientPayload SWIFT_PRIVATE;
|
|
54
|
+
std::optional<std::variant<nitro::NullType, std::string>> environment SWIFT_PRIVATE;
|
|
54
55
|
bool isValid SWIFT_PRIVATE;
|
|
55
56
|
std::optional<std::variant<nitro::NullType, std::string>> productId SWIFT_PRIVATE;
|
|
56
57
|
IapkitPurchaseState state SWIFT_PRIVATE;
|
|
@@ -58,7 +59,7 @@ namespace margelo::nitro::iap {
|
|
|
58
59
|
|
|
59
60
|
public:
|
|
60
61
|
NitroVerifyPurchaseWithIapkitResult() = default;
|
|
61
|
-
explicit NitroVerifyPurchaseWithIapkitResult(std::optional<std::variant<nitro::NullType, NitroIapkitProductClientPayload>> clientPayload, bool isValid, std::optional<std::variant<nitro::NullType, std::string>> productId, IapkitPurchaseState state, IapStore store): clientPayload(clientPayload), isValid(isValid), productId(productId), state(state), store(store) {}
|
|
62
|
+
explicit NitroVerifyPurchaseWithIapkitResult(std::optional<std::variant<nitro::NullType, NitroIapkitProductClientPayload>> clientPayload, std::optional<std::variant<nitro::NullType, std::string>> environment, bool isValid, std::optional<std::variant<nitro::NullType, std::string>> productId, IapkitPurchaseState state, IapStore store): clientPayload(clientPayload), environment(environment), isValid(isValid), productId(productId), state(state), store(store) {}
|
|
62
63
|
|
|
63
64
|
public:
|
|
64
65
|
friend bool operator==(const NitroVerifyPurchaseWithIapkitResult& lhs, const NitroVerifyPurchaseWithIapkitResult& rhs) = default;
|
|
@@ -75,6 +76,7 @@ namespace margelo::nitro {
|
|
|
75
76
|
jsi::Object obj = arg.asObject(runtime);
|
|
76
77
|
return margelo::nitro::iap::NitroVerifyPurchaseWithIapkitResult(
|
|
77
78
|
JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroIapkitProductClientPayload>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "clientPayload"))),
|
|
79
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "environment"))),
|
|
78
80
|
JSIConverter<bool>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isValid"))),
|
|
79
81
|
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "productId"))),
|
|
80
82
|
JSIConverter<margelo::nitro::iap::IapkitPurchaseState>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "state"))),
|
|
@@ -84,6 +86,7 @@ namespace margelo::nitro {
|
|
|
84
86
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroVerifyPurchaseWithIapkitResult& arg) {
|
|
85
87
|
jsi::Object obj(runtime);
|
|
86
88
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "clientPayload"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroIapkitProductClientPayload>>>::toJSI(runtime, arg.clientPayload));
|
|
89
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "environment"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.environment));
|
|
87
90
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "isValid"), JSIConverter<bool>::toJSI(runtime, arg.isValid));
|
|
88
91
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "productId"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.productId));
|
|
89
92
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "state"), JSIConverter<margelo::nitro::iap::IapkitPurchaseState>::toJSI(runtime, arg.state));
|
|
@@ -99,6 +102,7 @@ namespace margelo::nitro {
|
|
|
99
102
|
return false;
|
|
100
103
|
}
|
|
101
104
|
if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroIapkitProductClientPayload>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "clientPayload")))) return false;
|
|
105
|
+
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "environment")))) return false;
|
|
102
106
|
if (!JSIConverter<bool>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isValid")))) return false;
|
|
103
107
|
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "productId")))) return false;
|
|
104
108
|
if (!JSIConverter<margelo::nitro::iap::IapkitPurchaseState>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "state")))) return false;
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
package/src/index.kepler.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {getVegaIapModule} from './vega';
|
|
2
|
+
import {ErrorCode} from './types';
|
|
2
3
|
import type {
|
|
3
4
|
MutationField,
|
|
4
5
|
Product,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
validateNitroPurchase,
|
|
28
29
|
} from './utils/type-bridge';
|
|
29
30
|
import {convertAndroidPurchasesOrThrow} from './utils/available-purchases';
|
|
31
|
+
import {createPurchaseError} from './utils/errorMapping';
|
|
30
32
|
|
|
31
33
|
export * from './types';
|
|
32
34
|
export * from './utils/error';
|
|
@@ -129,13 +131,19 @@ export const fetchProducts = async (
|
|
|
129
131
|
const normalizedType = normalizeProductQueryType(type);
|
|
130
132
|
const nitroProducts = await getModule().fetchProducts(skus, normalizedType);
|
|
131
133
|
return mapProducts(nitroProducts, normalizedType) as
|
|
132
|
-
|
|
|
133
|
-
| ProductSubscription[];
|
|
134
|
+
Product[] | ProductSubscription[];
|
|
134
135
|
};
|
|
135
136
|
|
|
136
137
|
export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
137
138
|
request,
|
|
138
139
|
) => {
|
|
140
|
+
const type = normalizeProductQueryType(request.type);
|
|
141
|
+
if (type === 'all') {
|
|
142
|
+
throw createPurchaseError({
|
|
143
|
+
code: ErrorCode.DeveloperError,
|
|
144
|
+
message: 'Product type all is only supported for product queries.',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
139
147
|
const perPlatformRequest = request.request as
|
|
140
148
|
| RequestPurchasePropsByPlatforms
|
|
141
149
|
| RequestSubscriptionPropsByPlatforms
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
NitroPurchaseVerificationParams,
|
|
10
10
|
NitroPurchaseVerificationResultIOS,
|
|
11
11
|
NitroPurchaseVerificationResultAndroid,
|
|
12
|
+
NitroPurchaseVerificationResultHorizon,
|
|
12
13
|
NitroPurchaseUpdatedListenerOptions,
|
|
13
14
|
NitroSubscriptionStatus,
|
|
14
15
|
RnIap,
|
|
@@ -41,6 +42,7 @@ import type {
|
|
|
41
42
|
PurchaseIOS,
|
|
42
43
|
QueryField,
|
|
43
44
|
VerifyPurchaseResultAndroid,
|
|
45
|
+
VerifyPurchaseResultHorizon,
|
|
44
46
|
VerifyPurchaseResultIOS,
|
|
45
47
|
RequestPurchaseAndroidProps,
|
|
46
48
|
RequestPurchaseIosProps,
|
|
@@ -884,7 +886,10 @@ export const fetchProducts: QueryField<'fetchProducts'> = async (request) => {
|
|
|
884
886
|
|
|
885
887
|
try {
|
|
886
888
|
if (!skus?.length) {
|
|
887
|
-
throw
|
|
889
|
+
throw createPurchaseError({
|
|
890
|
+
message: 'No SKUs provided',
|
|
891
|
+
code: ErrorCode.EmptySkuList,
|
|
892
|
+
});
|
|
888
893
|
}
|
|
889
894
|
|
|
890
895
|
const normalizedType = normalizeProductQueryType(type);
|
|
@@ -1673,6 +1678,12 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1673
1678
|
try {
|
|
1674
1679
|
const {request: platformRequest, type} = request;
|
|
1675
1680
|
const normalizedType = normalizeProductQueryType(type ?? 'in-app');
|
|
1681
|
+
if (normalizedType === 'all') {
|
|
1682
|
+
throw createPurchaseError({
|
|
1683
|
+
code: ErrorCode.DeveloperError,
|
|
1684
|
+
message: 'Product type all is only supported for product queries.',
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1676
1687
|
const isSubs = isSubscriptionQuery(normalizedType);
|
|
1677
1688
|
const perPlatformRequest = platformRequest as
|
|
1678
1689
|
| RequestPurchasePropsByPlatforms
|
|
@@ -1691,15 +1702,23 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1691
1702
|
|
|
1692
1703
|
if (Platform.OS === 'ios') {
|
|
1693
1704
|
if (!iosRequestSource?.sku) {
|
|
1694
|
-
throw
|
|
1695
|
-
'Invalid request for iOS. The `sku` property is required.',
|
|
1696
|
-
|
|
1705
|
+
throw createPurchaseError({
|
|
1706
|
+
message: 'Invalid request for iOS. The `sku` property is required.',
|
|
1707
|
+
code: ErrorCode.EmptySkuList,
|
|
1708
|
+
});
|
|
1697
1709
|
}
|
|
1698
1710
|
} else if (isAndroidStoreRuntime()) {
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1711
|
+
const skus = androidRequestSource?.skus as unknown;
|
|
1712
|
+
if (
|
|
1713
|
+
!Array.isArray(skus) ||
|
|
1714
|
+
skus.length === 0 ||
|
|
1715
|
+
skus.some((sku) => typeof sku !== 'string' || sku.trim() === '')
|
|
1716
|
+
) {
|
|
1717
|
+
throw createPurchaseError({
|
|
1718
|
+
message:
|
|
1719
|
+
'Invalid request for Android. The `skus` property must contain only non-empty strings.',
|
|
1720
|
+
code: ErrorCode.EmptySkuList,
|
|
1721
|
+
});
|
|
1703
1722
|
}
|
|
1704
1723
|
} else {
|
|
1705
1724
|
throw unsupportedPlatformError();
|
|
@@ -1756,6 +1775,29 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1756
1775
|
}
|
|
1757
1776
|
|
|
1758
1777
|
if (isAndroidStoreRuntime() && androidRequestSource) {
|
|
1778
|
+
const rawAndroidRequest = androidRequestSource as unknown as Record<
|
|
1779
|
+
string,
|
|
1780
|
+
unknown
|
|
1781
|
+
>;
|
|
1782
|
+
const subscriptionOnlyFields = [
|
|
1783
|
+
'subscriptionOffers',
|
|
1784
|
+
'subscriptionProductReplacementParams',
|
|
1785
|
+
'purchaseToken',
|
|
1786
|
+
'originalExternalTransactionId',
|
|
1787
|
+
];
|
|
1788
|
+
if (
|
|
1789
|
+
(!isSubs &&
|
|
1790
|
+
subscriptionOnlyFields.some(
|
|
1791
|
+
(field) => rawAndroidRequest[field] != null,
|
|
1792
|
+
)) ||
|
|
1793
|
+
(isSubs && rawAndroidRequest.offerToken != null)
|
|
1794
|
+
) {
|
|
1795
|
+
throw createPurchaseError({
|
|
1796
|
+
message:
|
|
1797
|
+
'Invalid request for Android. Purchase options must match the selected product type.',
|
|
1798
|
+
code: ErrorCode.DeveloperError,
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1759
1801
|
const androidRequest = isSubs
|
|
1760
1802
|
? (androidRequestSource as RequestSubscriptionAndroidProps)
|
|
1761
1803
|
: (androidRequestSource as RequestPurchaseAndroidProps);
|
|
@@ -1788,6 +1830,29 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1788
1830
|
|
|
1789
1831
|
if (isSubs) {
|
|
1790
1832
|
const subsRequest = androidRequest as RequestSubscriptionAndroidProps;
|
|
1833
|
+
const subscriptionOffers = subsRequest.subscriptionOffers as unknown;
|
|
1834
|
+
if (
|
|
1835
|
+
subscriptionOffers != null &&
|
|
1836
|
+
(!Array.isArray(subscriptionOffers) ||
|
|
1837
|
+
subscriptionOffers.some((offer) => {
|
|
1838
|
+
if (offer == null || typeof offer !== 'object') {
|
|
1839
|
+
return true;
|
|
1840
|
+
}
|
|
1841
|
+
const candidate = offer as Record<string, unknown>;
|
|
1842
|
+
return (
|
|
1843
|
+
typeof candidate.sku !== 'string' ||
|
|
1844
|
+
candidate.sku.trim() === '' ||
|
|
1845
|
+
typeof candidate.offerToken !== 'string' ||
|
|
1846
|
+
candidate.offerToken.trim() === ''
|
|
1847
|
+
);
|
|
1848
|
+
}))
|
|
1849
|
+
) {
|
|
1850
|
+
throw createPurchaseError({
|
|
1851
|
+
message:
|
|
1852
|
+
'Invalid request for Android. Every subscription offer must include non-empty `sku` and `offerToken` strings.',
|
|
1853
|
+
code: ErrorCode.DeveloperError,
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1791
1856
|
if (subsRequest.purchaseToken) {
|
|
1792
1857
|
androidPayload.purchaseToken = subsRequest.purchaseToken;
|
|
1793
1858
|
}
|
|
@@ -1800,15 +1865,11 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1800
1865
|
subsRequest.subscriptionProductReplacementParams;
|
|
1801
1866
|
}
|
|
1802
1867
|
androidPayload.subscriptionOffers = (
|
|
1803
|
-
|
|
1804
|
-
)
|
|
1805
|
-
.
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
.map((offer) => ({
|
|
1809
|
-
sku: offer.sku,
|
|
1810
|
-
offerToken: offer.offerToken,
|
|
1811
|
-
}));
|
|
1868
|
+
(subscriptionOffers ?? []) as AndroidSubscriptionOfferInput[]
|
|
1869
|
+
).map((offer) => ({
|
|
1870
|
+
sku: offer.sku,
|
|
1871
|
+
offerToken: offer.offerToken,
|
|
1872
|
+
}));
|
|
1812
1873
|
}
|
|
1813
1874
|
|
|
1814
1875
|
unifiedRequest.google = androidPayload;
|
|
@@ -2135,11 +2196,20 @@ export const verifyPurchase: MutationField<'verifyPurchase'> = async (
|
|
|
2135
2196
|
: undefined,
|
|
2136
2197
|
};
|
|
2137
2198
|
return result;
|
|
2199
|
+
} else if (params.horizon !== null) {
|
|
2200
|
+
const horizonResult =
|
|
2201
|
+
nitroResult as NitroPurchaseVerificationResultHorizon;
|
|
2202
|
+
const result: VerifyPurchaseResultHorizon = {
|
|
2203
|
+
isValid: horizonResult.isValid,
|
|
2204
|
+
grantTime: horizonResult.grantTime,
|
|
2205
|
+
success: horizonResult.success,
|
|
2206
|
+
};
|
|
2207
|
+
return result;
|
|
2138
2208
|
} else {
|
|
2139
|
-
// Android
|
|
2140
2209
|
const androidResult =
|
|
2141
2210
|
nitroResult as NitroPurchaseVerificationResultAndroid;
|
|
2142
2211
|
const result: VerifyPurchaseResultAndroid = {
|
|
2212
|
+
isValid: androidResult.isValid,
|
|
2143
2213
|
autoRenewing: androidResult.autoRenewing,
|
|
2144
2214
|
betaProduct: androidResult.betaProduct,
|
|
2145
2215
|
cancelDate: androidResult.cancelDate,
|
|
@@ -2194,9 +2264,11 @@ export const verifyPurchase: MutationField<'verifyPurchase'> = async (
|
|
|
2194
2264
|
* // apple: { jws: purchase.purchaseToken },
|
|
2195
2265
|
* // google: { purchaseToken: purchase.purchaseToken },
|
|
2196
2266
|
* amazon: {
|
|
2267
|
+
* expectedProductId: purchase.productId,
|
|
2197
2268
|
* userId: amazonUserId,
|
|
2198
2269
|
* receiptId: purchase.purchaseToken,
|
|
2199
|
-
*
|
|
2270
|
+
* // Enable only for App Tester after the IAPKit project opt-in.
|
|
2271
|
+
* sandbox: amazonSandboxEnabled,
|
|
2200
2272
|
* },
|
|
2201
2273
|
* },
|
|
2202
2274
|
* });
|
|
@@ -2226,6 +2298,9 @@ export const verifyPurchaseWithProvider: MutationField<
|
|
|
2226
2298
|
...(result.iapkit.clientPayload == null
|
|
2227
2299
|
? {}
|
|
2228
2300
|
: {clientPayload: result.iapkit.clientPayload}),
|
|
2301
|
+
...(result.iapkit.environment == null
|
|
2302
|
+
? {}
|
|
2303
|
+
: {environment: result.iapkit.environment}),
|
|
2229
2304
|
isValid: result.iapkit.isValid,
|
|
2230
2305
|
...(result.iapkit.productId == null
|
|
2231
2306
|
? {}
|
package/src/kit-api.ts
CHANGED
|
@@ -31,7 +31,15 @@ export type KitSubscription = {
|
|
|
31
31
|
id: string;
|
|
32
32
|
productId: string;
|
|
33
33
|
platform: "IOS" | "Android";
|
|
34
|
-
state:
|
|
34
|
+
state:
|
|
35
|
+
| "Active"
|
|
36
|
+
| "InGracePeriod"
|
|
37
|
+
| "InBillingRetry"
|
|
38
|
+
| "Expired"
|
|
39
|
+
| "Revoked"
|
|
40
|
+
| "Refunded"
|
|
41
|
+
| "Paused"
|
|
42
|
+
| "Unknown";
|
|
35
43
|
expiresAt?: number;
|
|
36
44
|
renewsAt?: number;
|
|
37
45
|
willRenew?: boolean;
|
|
@@ -41,6 +49,7 @@ export type KitSubscription = {
|
|
|
41
49
|
startedAt: number;
|
|
42
50
|
updatedAt: number;
|
|
43
51
|
purchaseToken: string;
|
|
52
|
+
originalTransactionId?: string;
|
|
44
53
|
userId?: string;
|
|
45
54
|
};
|
|
46
55
|
|
|
@@ -58,7 +67,8 @@ export type StatusResponse = {
|
|
|
58
67
|
export type KitProductPlatform = "IOS" | "Android";
|
|
59
68
|
|
|
60
69
|
export type KitProductClientPayload = {
|
|
61
|
-
|
|
70
|
+
/** Current values are toml, json, and text; preserve unknown values. */
|
|
71
|
+
format: string;
|
|
62
72
|
body: string;
|
|
63
73
|
version: number;
|
|
64
74
|
updatedAt: number;
|
|
@@ -84,6 +94,13 @@ export type KitProduct = {
|
|
|
84
94
|
type: "Subscription" | "NonConsumable" | "Consumable";
|
|
85
95
|
title: string;
|
|
86
96
|
description?: string;
|
|
97
|
+
baseLocale?: string;
|
|
98
|
+
localizations?: Array<{
|
|
99
|
+
locale: string;
|
|
100
|
+
title: string;
|
|
101
|
+
description?: string;
|
|
102
|
+
}>;
|
|
103
|
+
regions?: "all" | string[];
|
|
87
104
|
priceAmountMicros?: number;
|
|
88
105
|
currency?: string;
|
|
89
106
|
state: "Draft" | "Ready" | "Active" | "Removed";
|
|
@@ -113,6 +130,108 @@ export type KitProductsResponse = {
|
|
|
113
130
|
nextCursor?: string;
|
|
114
131
|
};
|
|
115
132
|
|
|
133
|
+
export type KitSubscriptionsResponse = {
|
|
134
|
+
items: KitSubscription[];
|
|
135
|
+
total: number;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export type KitMrrCurrencyEntry = {
|
|
139
|
+
currency: string;
|
|
140
|
+
mrrMicros: number;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export type KitMetricsResponse = {
|
|
144
|
+
activeSubs: number;
|
|
145
|
+
inGracePeriod: number;
|
|
146
|
+
inBillingRetry: number;
|
|
147
|
+
refunded30d: number;
|
|
148
|
+
canceled30d: number;
|
|
149
|
+
mrrMicros: number;
|
|
150
|
+
currency: string;
|
|
151
|
+
reportingCurrency: string;
|
|
152
|
+
mrrByCurrency: KitMrrCurrencyEntry[];
|
|
153
|
+
excludedMrrByCurrency: KitMrrCurrencyEntry[];
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export type KitRevenueMetricsResponse = {
|
|
157
|
+
days: Array<{
|
|
158
|
+
day: string;
|
|
159
|
+
currency: string;
|
|
160
|
+
productId: string;
|
|
161
|
+
platform: KitProductPlatform;
|
|
162
|
+
activeSubs: number;
|
|
163
|
+
newSubs: number;
|
|
164
|
+
renewals: number;
|
|
165
|
+
cancellations: number;
|
|
166
|
+
refunds: number;
|
|
167
|
+
revenueMicros: number;
|
|
168
|
+
}>;
|
|
169
|
+
currencies: string[];
|
|
170
|
+
productIds: string[];
|
|
171
|
+
platforms: KitProductPlatform[];
|
|
172
|
+
truncated: boolean;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export type KitProductUpsertResponse = { id: string; created: boolean };
|
|
176
|
+
export type KitProductStateResponse = {
|
|
177
|
+
id: string;
|
|
178
|
+
state: KitProduct["state"];
|
|
179
|
+
};
|
|
180
|
+
export type KitClientPayloadStateResponse = {
|
|
181
|
+
expectedVersion: number;
|
|
182
|
+
clientPayload?: KitProductClientPayload;
|
|
183
|
+
};
|
|
184
|
+
export type KitSetClientPayloadResponse = {
|
|
185
|
+
id: string;
|
|
186
|
+
created: boolean;
|
|
187
|
+
changed: boolean;
|
|
188
|
+
version: number;
|
|
189
|
+
updatedAt: number;
|
|
190
|
+
};
|
|
191
|
+
export type KitRemoveClientPayloadResponse = { ok: boolean };
|
|
192
|
+
export type KitProductSyncResponse = { jobId: string; deduped: boolean };
|
|
193
|
+
export type KitProductSyncJobResponse = {
|
|
194
|
+
_id: string;
|
|
195
|
+
_creationTime: number;
|
|
196
|
+
projectId: string;
|
|
197
|
+
platform: KitProductPlatform;
|
|
198
|
+
direction: "pull" | "push" | "both" | "purge-local";
|
|
199
|
+
dryRun: boolean;
|
|
200
|
+
status: "queued" | "running" | "succeeded" | "failed";
|
|
201
|
+
progress: {
|
|
202
|
+
phase: string;
|
|
203
|
+
current?: number;
|
|
204
|
+
total?: number;
|
|
205
|
+
failuresCount?: number;
|
|
206
|
+
};
|
|
207
|
+
result?: {
|
|
208
|
+
pulled: number;
|
|
209
|
+
pushed: number;
|
|
210
|
+
deleted?: number;
|
|
211
|
+
failures: Array<{ productId: string; reason: string }>;
|
|
212
|
+
failuresTruncated?: boolean;
|
|
213
|
+
plannedWrites?: Array<{
|
|
214
|
+
productId: string;
|
|
215
|
+
step: string;
|
|
216
|
+
detail?: string;
|
|
217
|
+
}>;
|
|
218
|
+
plannedWritesTruncated?: boolean;
|
|
219
|
+
manualActions?: Array<{
|
|
220
|
+
productId: string;
|
|
221
|
+
code: string;
|
|
222
|
+
message: string;
|
|
223
|
+
}>;
|
|
224
|
+
manualActionsTruncated?: boolean;
|
|
225
|
+
};
|
|
226
|
+
error?: string;
|
|
227
|
+
cancelRequested?: boolean;
|
|
228
|
+
expectedDeadline?: number;
|
|
229
|
+
createdBy?: string;
|
|
230
|
+
startedAt?: number;
|
|
231
|
+
completedAt?: number;
|
|
232
|
+
createdAt: number;
|
|
233
|
+
};
|
|
234
|
+
|
|
116
235
|
export type KitClientPayloadResponse = {
|
|
117
236
|
clientPayload: KitProductClientPayload;
|
|
118
237
|
};
|
|
@@ -135,7 +254,7 @@ const DEFAULT_BASE_URL = "https://kit.openiap.dev";
|
|
|
135
254
|
function mergeHeaders(
|
|
136
255
|
callerHeaders: Record<string, string> | undefined,
|
|
137
256
|
hasBody: boolean,
|
|
138
|
-
):
|
|
257
|
+
): RequestInit["headers"] {
|
|
139
258
|
if (typeof Headers === "function") {
|
|
140
259
|
const merged = new Headers(callerHeaders);
|
|
141
260
|
if (!merged.has("accept")) merged.set("accept", "application/json");
|
|
@@ -283,9 +402,11 @@ export function kitApi(options: KitApiOptions) {
|
|
|
283
402
|
if (!raw) return null;
|
|
284
403
|
const candidate = JSON.parse(raw) as Partial<CachedClientPayload>;
|
|
285
404
|
const payload = candidate.clientPayload;
|
|
405
|
+
// Only the invariants the cache depends on. `format` is opaque: evicting
|
|
406
|
+
// on an unknown one would kill ETag revalidation and offline reads.
|
|
286
407
|
if (
|
|
287
408
|
!payload ||
|
|
288
|
-
|
|
409
|
+
typeof payload.format !== "string" ||
|
|
289
410
|
typeof payload.body !== "string" ||
|
|
290
411
|
!Number.isSafeInteger(payload.version) ||
|
|
291
412
|
payload.version < 1 ||
|
package/src/specs/RnIap.nitro.ts
CHANGED
|
@@ -55,6 +55,7 @@ import type {
|
|
|
55
55
|
VerifyPurchaseGoogleOptions,
|
|
56
56
|
VerifyPurchaseHorizonOptions,
|
|
57
57
|
VerifyPurchaseResultAndroid,
|
|
58
|
+
VerifyPurchaseResultHorizon,
|
|
58
59
|
RequestPurchaseIosProps,
|
|
59
60
|
RequestPurchaseResult,
|
|
60
61
|
RequestSubscriptionAndroidProps,
|
|
@@ -408,6 +409,7 @@ export interface NitroPurchaseVerificationResultIOS {
|
|
|
408
409
|
}
|
|
409
410
|
|
|
410
411
|
export interface NitroPurchaseVerificationResultAndroid {
|
|
412
|
+
isValid: VerifyPurchaseResultAndroid['isValid'];
|
|
411
413
|
autoRenewing: VerifyPurchaseResultAndroid['autoRenewing'];
|
|
412
414
|
betaProduct: VerifyPurchaseResultAndroid['betaProduct'];
|
|
413
415
|
cancelDate: VerifyPurchaseResultAndroid['cancelDate'];
|
|
@@ -428,6 +430,12 @@ export interface NitroPurchaseVerificationResultAndroid {
|
|
|
428
430
|
testTransaction: VerifyPurchaseResultAndroid['testTransaction'];
|
|
429
431
|
}
|
|
430
432
|
|
|
433
|
+
export interface NitroPurchaseVerificationResultHorizon {
|
|
434
|
+
isValid: VerifyPurchaseResultHorizon['isValid'];
|
|
435
|
+
grantTime?: VerifyPurchaseResultHorizon['grantTime'];
|
|
436
|
+
success: VerifyPurchaseResultHorizon['success'];
|
|
437
|
+
}
|
|
438
|
+
|
|
431
439
|
// VerifyPurchaseWithProvider types
|
|
432
440
|
|
|
433
441
|
export interface NitroVerifyPurchaseWithIapkitAppleProps {
|
|
@@ -441,6 +449,8 @@ export interface NitroVerifyPurchaseWithIapkitGoogleProps {
|
|
|
441
449
|
}
|
|
442
450
|
|
|
443
451
|
export interface NitroVerifyPurchaseWithIapkitAmazonProps {
|
|
452
|
+
/** Available in OpenIAP Spec 3.2.0 / openiap-apple 3.2.0 / openiap-google 3.3.0. Optional Amazon product id that must match the product id verified by RVS. */
|
|
453
|
+
expectedProductId?: string | null;
|
|
444
454
|
/** Amazon Appstore receipt id returned by PurchaseResponse.getReceipt().getReceiptId(). */
|
|
445
455
|
receiptId: string;
|
|
446
456
|
/** Use Amazon RVS Cloud Sandbox for App Tester receipts. */
|
|
@@ -475,6 +485,8 @@ export interface NitroVerifyPurchaseWithProviderProps {
|
|
|
475
485
|
export interface NitroVerifyPurchaseWithIapkitResult {
|
|
476
486
|
/** Available in OpenIAP Spec 2.4.0 / openiap-apple 2.4.1 / openiap-google 2.4.1. */
|
|
477
487
|
clientPayload?: NitroIapkitProductClientPayload | null;
|
|
488
|
+
/** Available in OpenIAP Spec 3.2.0 / openiap-apple 3.2.0 / openiap-google 3.3.0. Amazon RVS environment selected by IAPKit. */
|
|
489
|
+
environment?: string | null;
|
|
478
490
|
isValid: boolean;
|
|
479
491
|
/** Available in OpenIAP Spec 2.4.0 / openiap-apple 2.4.1 / openiap-google 2.4.1. */
|
|
480
492
|
productId?: string | null;
|
|
@@ -990,12 +1002,14 @@ export interface RnIap extends HybridObject<{ios: 'swift'; android: 'kotlin'}> {
|
|
|
990
1002
|
/**
|
|
991
1003
|
* Verify a purchase on the appropriate platform.
|
|
992
1004
|
* @param params - Purchase verification parameters including platform-specific options
|
|
993
|
-
* @returns Promise
|
|
1005
|
+
* @returns Promise with the platform-specific verification result
|
|
994
1006
|
*/
|
|
995
1007
|
verifyPurchase(
|
|
996
1008
|
params: NitroPurchaseVerificationParams,
|
|
997
1009
|
): Promise<
|
|
998
|
-
|
|
1010
|
+
| NitroPurchaseVerificationResultIOS
|
|
1011
|
+
| NitroPurchaseVerificationResultAndroid
|
|
1012
|
+
| NitroPurchaseVerificationResultHorizon
|
|
999
1013
|
>;
|
|
1000
1014
|
|
|
1001
1015
|
/**
|
package/src/types.ts
CHANGED
|
@@ -885,11 +885,11 @@ export interface Mutation {
|
|
|
885
885
|
*/
|
|
886
886
|
syncIOS: Promise<boolean>;
|
|
887
887
|
/**
|
|
888
|
-
* Verify a purchase against your own backend.
|
|
889
|
-
* variant
|
|
890
|
-
*
|
|
891
|
-
* receipt fields
|
|
892
|
-
*
|
|
888
|
+
* Verify a purchase against your own backend. Every VerifyPurchaseResult
|
|
889
|
+
* variant exposes isValid, so entitlement can be gated without inspecting the
|
|
890
|
+
* concrete type. Variants add their own metadata on top: IOS carries
|
|
891
|
+
* receipt/JWS fields, Android carries Play Store receipt fields, and Horizon
|
|
892
|
+
* carries grantTime.
|
|
893
893
|
* See: https://openiap.dev/docs/features/validation#verify-purchase
|
|
894
894
|
*/
|
|
895
895
|
verifyPurchase: Promise<VerifyPurchaseResult>;
|
|
@@ -1791,6 +1791,11 @@ export interface RequestSubscriptionPropsByPlatforms {
|
|
|
1791
1791
|
}
|
|
1792
1792
|
|
|
1793
1793
|
export interface RequestVerifyPurchaseWithIapkitAmazonProps {
|
|
1794
|
+
/**
|
|
1795
|
+
* Available in OpenIAP Spec 3.2.0 / openiap-apple 3.2.0 / openiap-google 3.3.0.
|
|
1796
|
+
* Optional Amazon product id that must match the product id verified by RVS.
|
|
1797
|
+
*/
|
|
1798
|
+
expectedProductId?: (string | null);
|
|
1794
1799
|
/** Amazon Appstore receipt id returned by PurchaseResponse.getReceipt().getReceiptId(). */
|
|
1795
1800
|
receiptId: string;
|
|
1796
1801
|
/** Use Amazon RVS Cloud Sandbox for App Tester receipts. */
|
|
@@ -1848,6 +1853,18 @@ export interface RequestVerifyPurchaseWithIapkitResult {
|
|
|
1848
1853
|
* Apple or Google receipt is valid, and a payload exists for that product.
|
|
1849
1854
|
*/
|
|
1850
1855
|
clientPayload?: (IapkitProductClientPayload | null);
|
|
1856
|
+
/**
|
|
1857
|
+
* Available in OpenIAP Spec 3.2.0 / openiap-apple 3.2.0 / openiap-google 3.3.0.
|
|
1858
|
+
* Amazon RVS environment selected by IAPKit. Present as `Sandbox` or
|
|
1859
|
+
* `Production` on handled Amazon verification results.
|
|
1860
|
+
*
|
|
1861
|
+
* Deliberately String, not an enum: the value space belongs to IAPKit and the
|
|
1862
|
+
* stores behind it, and Apple's App Store Server alone also names `Xcode` and
|
|
1863
|
+
* `LocalTesting`. SDKs must forward this value opaquely. Never reject a
|
|
1864
|
+
* verification because the environment is unrecognised — that fails a purchase
|
|
1865
|
+
* the store already confirmed.
|
|
1866
|
+
*/
|
|
1867
|
+
environment?: (string | null);
|
|
1851
1868
|
/**
|
|
1852
1869
|
* True when the purchase is valid and actionable.
|
|
1853
1870
|
* Only entitled, pending-acknowledgment, or ready-to-consume return true.
|
|
@@ -2176,7 +2193,7 @@ export interface VerifyPurchaseProps {
|
|
|
2176
2193
|
|
|
2177
2194
|
export type VerifyPurchaseResult = VerifyPurchaseResultAndroid | VerifyPurchaseResultHorizon | VerifyPurchaseResultIOS;
|
|
2178
2195
|
|
|
2179
|
-
export interface VerifyPurchaseResultAndroid {
|
|
2196
|
+
export interface VerifyPurchaseResultAndroid extends VerifyPurchaseResultCommon {
|
|
2180
2197
|
autoRenewing: boolean;
|
|
2181
2198
|
betaProduct: boolean;
|
|
2182
2199
|
cancelDate?: (number | null);
|
|
@@ -2185,6 +2202,11 @@ export interface VerifyPurchaseResultAndroid {
|
|
|
2185
2202
|
deferredSku?: (string | null);
|
|
2186
2203
|
freeTrialEndDate: number;
|
|
2187
2204
|
gracePeriodEndDate: number;
|
|
2205
|
+
/**
|
|
2206
|
+
* Whether the purchase is valid. Uniform across every VerifyPurchaseResult
|
|
2207
|
+
* variant so callers can gate entitlement without inspecting the concrete type.
|
|
2208
|
+
*/
|
|
2209
|
+
isValid: boolean;
|
|
2188
2210
|
parentProductId: string;
|
|
2189
2211
|
productId: string;
|
|
2190
2212
|
productType: string;
|
|
@@ -2197,18 +2219,32 @@ export interface VerifyPurchaseResultAndroid {
|
|
|
2197
2219
|
testTransaction: boolean;
|
|
2198
2220
|
}
|
|
2199
2221
|
|
|
2222
|
+
/** Validity shared by every store-specific purchase verification result. */
|
|
2223
|
+
export interface VerifyPurchaseResultCommon {
|
|
2224
|
+
/** Whether the purchase is valid, without inspecting the concrete result variant. */
|
|
2225
|
+
isValid: boolean;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2200
2228
|
/**
|
|
2201
2229
|
* Result from Meta Horizon verify_entitlement API.
|
|
2202
2230
|
* Returns verification status and grant time for the entitlement.
|
|
2203
2231
|
*/
|
|
2204
|
-
export interface VerifyPurchaseResultHorizon {
|
|
2232
|
+
export interface VerifyPurchaseResultHorizon extends VerifyPurchaseResultCommon {
|
|
2205
2233
|
/** Unix timestamp (seconds) when the entitlement was granted. */
|
|
2206
2234
|
grantTime?: (number | null);
|
|
2207
|
-
/**
|
|
2235
|
+
/**
|
|
2236
|
+
* Whether the purchase is valid. Uniform across every VerifyPurchaseResult
|
|
2237
|
+
* variant so callers can gate entitlement without inspecting the concrete type.
|
|
2238
|
+
*/
|
|
2239
|
+
isValid: boolean;
|
|
2240
|
+
/**
|
|
2241
|
+
* Whether the entitlement verification succeeded.
|
|
2242
|
+
* @deprecated Renamed to isValid so every VerifyPurchaseResult variant answers validity the same way. Scheduled for removal in OpenIAP 4.0.
|
|
2243
|
+
*/
|
|
2208
2244
|
success: boolean;
|
|
2209
2245
|
}
|
|
2210
2246
|
|
|
2211
|
-
export interface VerifyPurchaseResultIOS {
|
|
2247
|
+
export interface VerifyPurchaseResultIOS extends VerifyPurchaseResultCommon {
|
|
2212
2248
|
/** Whether the receipt is valid */
|
|
2213
2249
|
isValid: boolean;
|
|
2214
2250
|
/** JWS representation */
|
package/src/vega-adapter.ts
CHANGED
|
@@ -1271,8 +1271,15 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1271
1271
|
`IAPKit returned malformed response (HTTP ${status}).`,
|
|
1272
1272
|
);
|
|
1273
1273
|
}
|
|
1274
|
+
// Forwarded opaquely: `environment` is String in the spec.
|
|
1275
|
+
const rawEnvironment = json.environment;
|
|
1276
|
+
const environment =
|
|
1277
|
+
typeof rawEnvironment === 'string' && rawEnvironment.length > 0
|
|
1278
|
+
? rawEnvironment
|
|
1279
|
+
: undefined;
|
|
1274
1280
|
|
|
1275
1281
|
return {
|
|
1282
|
+
...(environment == null ? {} : {environment}),
|
|
1276
1283
|
isValid: json.isValid,
|
|
1277
1284
|
...(productId == null ? {} : {productId}),
|
|
1278
1285
|
state: normalizeIapkitState(json.state),
|
|
@@ -1341,6 +1348,9 @@ export function createVegaIapModule(service: VegaPurchasingService): RnIap {
|
|
|
1341
1348
|
store: 'amazon',
|
|
1342
1349
|
userId,
|
|
1343
1350
|
receiptId,
|
|
1351
|
+
...(amazon.expectedProductId == null
|
|
1352
|
+
? {}
|
|
1353
|
+
: {expectedProductId: amazon.expectedProductId}),
|
|
1344
1354
|
...(amazon.sandbox == null ? {} : {sandbox: amazon.sandbox}),
|
|
1345
1355
|
}),
|
|
1346
1356
|
signal: controller.signal,
|