react-native-iap 16.0.0 → 16.0.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 +15 -7
- package/ios/HybridRnIap.swift +11 -6
- package/ios/RnIapHelper.swift +33 -1
- package/lib/module/hooks/useIAP.js +27 -9
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +34 -22
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.kepler.js +3 -2
- package/lib/module/index.kepler.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/available-purchases.js +59 -0
- package/lib/module/utils/available-purchases.js.map +1 -0
- package/lib/module/utils/type-bridge.js +56 -6
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/module/vega-adapter.js +13 -7
- package/lib/module/vega-adapter.js.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -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/specs/RnIap.nitro.d.ts +2 -0
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +11 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/available-purchases.d.ts +8 -0
- package/lib/typescript/src/utils/available-purchases.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.map +1 -1
- package/nitrogen/generated/android/c++/JNitroPurchase.hpp +11 -3
- package/nitrogen/generated/android/c++/JPurchaseAndroid.hpp +11 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +14 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PurchaseAndroid.kt +14 -4
- package/nitrogen/generated/ios/swift/NitroPurchase.swift +75 -1
- package/nitrogen/generated/ios/swift/PurchaseAndroid.swift +75 -1
- package/nitrogen/generated/shared/c++/NitroPurchase.hpp +10 -2
- package/nitrogen/generated/shared/c++/PurchaseAndroid.hpp +10 -2
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/src/hooks/useIAP.ts +27 -10
- package/src/index.kepler.ts +5 -9
- package/src/index.ts +41 -45
- package/src/specs/RnIap.nitro.ts +2 -0
- package/src/types.ts +11 -0
- package/src/utils/available-purchases.ts +73 -0
- package/src/utils/type-bridge.ts +84 -9
- package/src/vega-adapter.ts +24 -3
|
@@ -117,10 +117,12 @@ namespace margelo::nitro::iap {
|
|
|
117
117
|
std::optional<std::variant<nitro::NullType, std::string>> developerPayloadAndroid SWIFT_PRIVATE;
|
|
118
118
|
std::optional<std::variant<nitro::NullType, bool>> isSuspendedAndroid SWIFT_PRIVATE;
|
|
119
119
|
std::optional<std::variant<nitro::NullType, PendingPurchaseUpdateAndroid>> pendingPurchaseUpdateAndroid SWIFT_PRIVATE;
|
|
120
|
+
std::optional<std::variant<nitro::NullType, std::string>> userIdAmazon SWIFT_PRIVATE;
|
|
121
|
+
std::optional<std::variant<nitro::NullType, std::string>> userMarketplaceAmazon SWIFT_PRIVATE;
|
|
120
122
|
|
|
121
123
|
public:
|
|
122
124
|
NitroPurchase() = default;
|
|
123
|
-
explicit NitroPurchase(std::string id, std::optional<std::variant<nitro::NullType, std::string>> transactionId, std::string productId, double transactionDate, std::optional<std::variant<nitro::NullType, std::string>> purchaseToken, std::optional<std::variant<nitro::NullType, std::string>> currentPlanId, std::optional<std::variant<nitro::NullType, std::vector<std::string>>> ids, IapStore store, double quantity, PurchaseState purchaseState, bool isAutoRenewing, std::optional<std::variant<nitro::NullType, AdvancedCommerceInfoIOS>> advancedCommerceInfoIOS, std::optional<SubscriptionBillingPlanTypeIOS> billingPlanTypeIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleOriginalTransactionIdIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleProductIdIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleSubscriptionGroupIdIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleTransactionIdIOS, std::optional<std::variant<nitro::NullType, TransactionCommitmentInfoIOS>> commitmentInfoIOS, std::optional<std::variant<nitro::NullType, double>> quantityIOS, std::optional<std::variant<nitro::NullType, double>> originalTransactionDateIOS, std::optional<std::variant<nitro::NullType, std::string>> originalTransactionIdentifierIOS, std::optional<std::variant<nitro::NullType, std::string>> previousOriginalTransactionIdIOS, std::optional<std::variant<nitro::NullType, std::string>> appAccountToken, std::optional<std::variant<nitro::NullType, std::string>> appBundleIdIOS, std::optional<std::variant<nitro::NullType, std::string>> countryCodeIOS, std::optional<std::variant<nitro::NullType, std::string>> currencyCodeIOS, std::optional<std::variant<nitro::NullType, std::string>> currencySymbolIOS, std::optional<std::variant<nitro::NullType, std::string>> environmentIOS, std::optional<std::variant<nitro::NullType, double>> expirationDateIOS, std::optional<std::variant<nitro::NullType, bool>> isUpgradedIOS, std::optional<std::variant<nitro::NullType, std::string>> offerIOS, std::optional<std::variant<nitro::NullType, std::string>> ownershipTypeIOS, std::optional<std::variant<nitro::NullType, std::string>> reasonIOS, std::optional<std::variant<nitro::NullType, std::string>> reasonStringRepresentationIOS, std::optional<std::variant<nitro::NullType, double>> revocationDateIOS, std::optional<std::variant<nitro::NullType, std::string>> revocationReasonIOS, std::optional<std::variant<nitro::NullType, std::string>> revocationTypeIOS, std::optional<std::variant<nitro::NullType, std::string>> storefrontCountryCodeIOS, std::optional<std::variant<nitro::NullType, std::string>> subscriptionGroupIdIOS, std::optional<std::variant<nitro::NullType, std::string>> transactionReasonIOS, std::optional<std::variant<nitro::NullType, std::string>> webOrderLineItemIdIOS, std::optional<std::variant<nitro::NullType, NitroRenewalInfoIOS>> renewalInfoIOS, std::optional<std::variant<nitro::NullType, std::string>> purchaseTokenAndroid, std::optional<std::variant<nitro::NullType, std::string>> dataAndroid, std::optional<std::variant<nitro::NullType, std::string>> signatureAndroid, std::optional<std::variant<nitro::NullType, bool>> autoRenewingAndroid, std::optional<std::variant<nitro::NullType, double>> purchaseStateAndroid, std::optional<std::variant<nitro::NullType, bool>> isAcknowledgedAndroid, std::optional<std::variant<nitro::NullType, std::string>> packageNameAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedAccountIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedProfileIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> developerPayloadAndroid, std::optional<std::variant<nitro::NullType, bool>> isSuspendedAndroid, std::optional<std::variant<nitro::NullType, PendingPurchaseUpdateAndroid>> pendingPurchaseUpdateAndroid): id(id), transactionId(transactionId), productId(productId), transactionDate(transactionDate), purchaseToken(purchaseToken), currentPlanId(currentPlanId), ids(ids), store(store), quantity(quantity), purchaseState(purchaseState), isAutoRenewing(isAutoRenewing), advancedCommerceInfoIOS(advancedCommerceInfoIOS), billingPlanTypeIOS(billingPlanTypeIOS), bundleOriginalTransactionIdIOS(bundleOriginalTransactionIdIOS), bundleProductIdIOS(bundleProductIdIOS), bundleSubscriptionGroupIdIOS(bundleSubscriptionGroupIdIOS), bundleTransactionIdIOS(bundleTransactionIdIOS), commitmentInfoIOS(commitmentInfoIOS), quantityIOS(quantityIOS), originalTransactionDateIOS(originalTransactionDateIOS), originalTransactionIdentifierIOS(originalTransactionIdentifierIOS), previousOriginalTransactionIdIOS(previousOriginalTransactionIdIOS), appAccountToken(appAccountToken), appBundleIdIOS(appBundleIdIOS), countryCodeIOS(countryCodeIOS), currencyCodeIOS(currencyCodeIOS), currencySymbolIOS(currencySymbolIOS), environmentIOS(environmentIOS), expirationDateIOS(expirationDateIOS), isUpgradedIOS(isUpgradedIOS), offerIOS(offerIOS), ownershipTypeIOS(ownershipTypeIOS), reasonIOS(reasonIOS), reasonStringRepresentationIOS(reasonStringRepresentationIOS), revocationDateIOS(revocationDateIOS), revocationReasonIOS(revocationReasonIOS), revocationTypeIOS(revocationTypeIOS), storefrontCountryCodeIOS(storefrontCountryCodeIOS), subscriptionGroupIdIOS(subscriptionGroupIdIOS), transactionReasonIOS(transactionReasonIOS), webOrderLineItemIdIOS(webOrderLineItemIdIOS), renewalInfoIOS(renewalInfoIOS), purchaseTokenAndroid(purchaseTokenAndroid), dataAndroid(dataAndroid), signatureAndroid(signatureAndroid), autoRenewingAndroid(autoRenewingAndroid), purchaseStateAndroid(purchaseStateAndroid), isAcknowledgedAndroid(isAcknowledgedAndroid), packageNameAndroid(packageNameAndroid), obfuscatedAccountIdAndroid(obfuscatedAccountIdAndroid), obfuscatedProfileIdAndroid(obfuscatedProfileIdAndroid), developerPayloadAndroid(developerPayloadAndroid), isSuspendedAndroid(isSuspendedAndroid), pendingPurchaseUpdateAndroid(pendingPurchaseUpdateAndroid) {}
|
|
125
|
+
explicit NitroPurchase(std::string id, std::optional<std::variant<nitro::NullType, std::string>> transactionId, std::string productId, double transactionDate, std::optional<std::variant<nitro::NullType, std::string>> purchaseToken, std::optional<std::variant<nitro::NullType, std::string>> currentPlanId, std::optional<std::variant<nitro::NullType, std::vector<std::string>>> ids, IapStore store, double quantity, PurchaseState purchaseState, bool isAutoRenewing, std::optional<std::variant<nitro::NullType, AdvancedCommerceInfoIOS>> advancedCommerceInfoIOS, std::optional<SubscriptionBillingPlanTypeIOS> billingPlanTypeIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleOriginalTransactionIdIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleProductIdIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleSubscriptionGroupIdIOS, std::optional<std::variant<nitro::NullType, std::string>> bundleTransactionIdIOS, std::optional<std::variant<nitro::NullType, TransactionCommitmentInfoIOS>> commitmentInfoIOS, std::optional<std::variant<nitro::NullType, double>> quantityIOS, std::optional<std::variant<nitro::NullType, double>> originalTransactionDateIOS, std::optional<std::variant<nitro::NullType, std::string>> originalTransactionIdentifierIOS, std::optional<std::variant<nitro::NullType, std::string>> previousOriginalTransactionIdIOS, std::optional<std::variant<nitro::NullType, std::string>> appAccountToken, std::optional<std::variant<nitro::NullType, std::string>> appBundleIdIOS, std::optional<std::variant<nitro::NullType, std::string>> countryCodeIOS, std::optional<std::variant<nitro::NullType, std::string>> currencyCodeIOS, std::optional<std::variant<nitro::NullType, std::string>> currencySymbolIOS, std::optional<std::variant<nitro::NullType, std::string>> environmentIOS, std::optional<std::variant<nitro::NullType, double>> expirationDateIOS, std::optional<std::variant<nitro::NullType, bool>> isUpgradedIOS, std::optional<std::variant<nitro::NullType, std::string>> offerIOS, std::optional<std::variant<nitro::NullType, std::string>> ownershipTypeIOS, std::optional<std::variant<nitro::NullType, std::string>> reasonIOS, std::optional<std::variant<nitro::NullType, std::string>> reasonStringRepresentationIOS, std::optional<std::variant<nitro::NullType, double>> revocationDateIOS, std::optional<std::variant<nitro::NullType, std::string>> revocationReasonIOS, std::optional<std::variant<nitro::NullType, std::string>> revocationTypeIOS, std::optional<std::variant<nitro::NullType, std::string>> storefrontCountryCodeIOS, std::optional<std::variant<nitro::NullType, std::string>> subscriptionGroupIdIOS, std::optional<std::variant<nitro::NullType, std::string>> transactionReasonIOS, std::optional<std::variant<nitro::NullType, std::string>> webOrderLineItemIdIOS, std::optional<std::variant<nitro::NullType, NitroRenewalInfoIOS>> renewalInfoIOS, std::optional<std::variant<nitro::NullType, std::string>> purchaseTokenAndroid, std::optional<std::variant<nitro::NullType, std::string>> dataAndroid, std::optional<std::variant<nitro::NullType, std::string>> signatureAndroid, std::optional<std::variant<nitro::NullType, bool>> autoRenewingAndroid, std::optional<std::variant<nitro::NullType, double>> purchaseStateAndroid, std::optional<std::variant<nitro::NullType, bool>> isAcknowledgedAndroid, std::optional<std::variant<nitro::NullType, std::string>> packageNameAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedAccountIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedProfileIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> developerPayloadAndroid, std::optional<std::variant<nitro::NullType, bool>> isSuspendedAndroid, std::optional<std::variant<nitro::NullType, PendingPurchaseUpdateAndroid>> pendingPurchaseUpdateAndroid, std::optional<std::variant<nitro::NullType, std::string>> userIdAmazon, std::optional<std::variant<nitro::NullType, std::string>> userMarketplaceAmazon): id(id), transactionId(transactionId), productId(productId), transactionDate(transactionDate), purchaseToken(purchaseToken), currentPlanId(currentPlanId), ids(ids), store(store), quantity(quantity), purchaseState(purchaseState), isAutoRenewing(isAutoRenewing), advancedCommerceInfoIOS(advancedCommerceInfoIOS), billingPlanTypeIOS(billingPlanTypeIOS), bundleOriginalTransactionIdIOS(bundleOriginalTransactionIdIOS), bundleProductIdIOS(bundleProductIdIOS), bundleSubscriptionGroupIdIOS(bundleSubscriptionGroupIdIOS), bundleTransactionIdIOS(bundleTransactionIdIOS), commitmentInfoIOS(commitmentInfoIOS), quantityIOS(quantityIOS), originalTransactionDateIOS(originalTransactionDateIOS), originalTransactionIdentifierIOS(originalTransactionIdentifierIOS), previousOriginalTransactionIdIOS(previousOriginalTransactionIdIOS), appAccountToken(appAccountToken), appBundleIdIOS(appBundleIdIOS), countryCodeIOS(countryCodeIOS), currencyCodeIOS(currencyCodeIOS), currencySymbolIOS(currencySymbolIOS), environmentIOS(environmentIOS), expirationDateIOS(expirationDateIOS), isUpgradedIOS(isUpgradedIOS), offerIOS(offerIOS), ownershipTypeIOS(ownershipTypeIOS), reasonIOS(reasonIOS), reasonStringRepresentationIOS(reasonStringRepresentationIOS), revocationDateIOS(revocationDateIOS), revocationReasonIOS(revocationReasonIOS), revocationTypeIOS(revocationTypeIOS), storefrontCountryCodeIOS(storefrontCountryCodeIOS), subscriptionGroupIdIOS(subscriptionGroupIdIOS), transactionReasonIOS(transactionReasonIOS), webOrderLineItemIdIOS(webOrderLineItemIdIOS), renewalInfoIOS(renewalInfoIOS), purchaseTokenAndroid(purchaseTokenAndroid), dataAndroid(dataAndroid), signatureAndroid(signatureAndroid), autoRenewingAndroid(autoRenewingAndroid), purchaseStateAndroid(purchaseStateAndroid), isAcknowledgedAndroid(isAcknowledgedAndroid), packageNameAndroid(packageNameAndroid), obfuscatedAccountIdAndroid(obfuscatedAccountIdAndroid), obfuscatedProfileIdAndroid(obfuscatedProfileIdAndroid), developerPayloadAndroid(developerPayloadAndroid), isSuspendedAndroid(isSuspendedAndroid), pendingPurchaseUpdateAndroid(pendingPurchaseUpdateAndroid), userIdAmazon(userIdAmazon), userMarketplaceAmazon(userMarketplaceAmazon) {}
|
|
124
126
|
|
|
125
127
|
public:
|
|
126
128
|
// NitroPurchase is not equatable because these properties are not equatable: ids, advancedCommerceInfoIOS, pendingPurchaseUpdateAndroid
|
|
@@ -189,7 +191,9 @@ namespace margelo::nitro {
|
|
|
189
191
|
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "obfuscatedProfileIdAndroid"))),
|
|
190
192
|
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "developerPayloadAndroid"))),
|
|
191
193
|
JSIConverter<std::optional<std::variant<nitro::NullType, bool>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isSuspendedAndroid"))),
|
|
192
|
-
JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::PendingPurchaseUpdateAndroid>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "pendingPurchaseUpdateAndroid")))
|
|
194
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::PendingPurchaseUpdateAndroid>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "pendingPurchaseUpdateAndroid"))),
|
|
195
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userIdAmazon"))),
|
|
196
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userMarketplaceAmazon")))
|
|
193
197
|
);
|
|
194
198
|
}
|
|
195
199
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroPurchase& arg) {
|
|
@@ -248,6 +252,8 @@ namespace margelo::nitro {
|
|
|
248
252
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "developerPayloadAndroid"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.developerPayloadAndroid));
|
|
249
253
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "isSuspendedAndroid"), JSIConverter<std::optional<std::variant<nitro::NullType, bool>>>::toJSI(runtime, arg.isSuspendedAndroid));
|
|
250
254
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "pendingPurchaseUpdateAndroid"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::PendingPurchaseUpdateAndroid>>>::toJSI(runtime, arg.pendingPurchaseUpdateAndroid));
|
|
255
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "userIdAmazon"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.userIdAmazon));
|
|
256
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "userMarketplaceAmazon"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.userMarketplaceAmazon));
|
|
251
257
|
return obj;
|
|
252
258
|
}
|
|
253
259
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -312,6 +318,8 @@ namespace margelo::nitro {
|
|
|
312
318
|
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "developerPayloadAndroid")))) return false;
|
|
313
319
|
if (!JSIConverter<std::optional<std::variant<nitro::NullType, bool>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "isSuspendedAndroid")))) return false;
|
|
314
320
|
if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::PendingPurchaseUpdateAndroid>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "pendingPurchaseUpdateAndroid")))) return false;
|
|
321
|
+
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userIdAmazon")))) return false;
|
|
322
|
+
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userMarketplaceAmazon")))) return false;
|
|
315
323
|
return true;
|
|
316
324
|
}
|
|
317
325
|
};
|
|
@@ -72,10 +72,12 @@ namespace margelo::nitro::iap {
|
|
|
72
72
|
IapStore store SWIFT_PRIVATE;
|
|
73
73
|
double transactionDate SWIFT_PRIVATE;
|
|
74
74
|
std::optional<std::variant<nitro::NullType, std::string>> transactionId SWIFT_PRIVATE;
|
|
75
|
+
std::optional<std::variant<nitro::NullType, std::string>> userIdAmazon SWIFT_PRIVATE;
|
|
76
|
+
std::optional<std::variant<nitro::NullType, std::string>> userMarketplaceAmazon SWIFT_PRIVATE;
|
|
75
77
|
|
|
76
78
|
public:
|
|
77
79
|
PurchaseAndroid() = default;
|
|
78
|
-
explicit PurchaseAndroid(std::optional<std::variant<nitro::NullType, bool>> autoRenewingAndroid, std::optional<std::variant<nitro::NullType, std::string>> currentPlanId, std::optional<std::variant<nitro::NullType, std::string>> dataAndroid, std::optional<std::variant<nitro::NullType, std::string>> developerPayloadAndroid, std::string id, std::optional<std::variant<nitro::NullType, std::vector<std::string>>> ids, std::optional<std::variant<nitro::NullType, bool>> isAcknowledgedAndroid, bool isAutoRenewing, std::optional<std::variant<nitro::NullType, bool>> isSuspendedAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedAccountIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedProfileIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> packageNameAndroid, std::optional<std::variant<nitro::NullType, PendingPurchaseUpdateAndroid>> pendingPurchaseUpdateAndroid, std::string productId, PurchaseState purchaseState, std::optional<std::variant<nitro::NullType, std::string>> purchaseToken, double quantity, std::optional<std::variant<nitro::NullType, std::string>> signatureAndroid, IapStore store, double transactionDate, std::optional<std::variant<nitro::NullType, std::string>> transactionId): autoRenewingAndroid(autoRenewingAndroid), currentPlanId(currentPlanId), dataAndroid(dataAndroid), developerPayloadAndroid(developerPayloadAndroid), id(id), ids(ids), isAcknowledgedAndroid(isAcknowledgedAndroid), isAutoRenewing(isAutoRenewing), isSuspendedAndroid(isSuspendedAndroid), obfuscatedAccountIdAndroid(obfuscatedAccountIdAndroid), obfuscatedProfileIdAndroid(obfuscatedProfileIdAndroid), packageNameAndroid(packageNameAndroid), pendingPurchaseUpdateAndroid(pendingPurchaseUpdateAndroid), productId(productId), purchaseState(purchaseState), purchaseToken(purchaseToken), quantity(quantity), signatureAndroid(signatureAndroid), store(store), transactionDate(transactionDate), transactionId(transactionId) {}
|
|
80
|
+
explicit PurchaseAndroid(std::optional<std::variant<nitro::NullType, bool>> autoRenewingAndroid, std::optional<std::variant<nitro::NullType, std::string>> currentPlanId, std::optional<std::variant<nitro::NullType, std::string>> dataAndroid, std::optional<std::variant<nitro::NullType, std::string>> developerPayloadAndroid, std::string id, std::optional<std::variant<nitro::NullType, std::vector<std::string>>> ids, std::optional<std::variant<nitro::NullType, bool>> isAcknowledgedAndroid, bool isAutoRenewing, std::optional<std::variant<nitro::NullType, bool>> isSuspendedAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedAccountIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> obfuscatedProfileIdAndroid, std::optional<std::variant<nitro::NullType, std::string>> packageNameAndroid, std::optional<std::variant<nitro::NullType, PendingPurchaseUpdateAndroid>> pendingPurchaseUpdateAndroid, std::string productId, PurchaseState purchaseState, std::optional<std::variant<nitro::NullType, std::string>> purchaseToken, double quantity, std::optional<std::variant<nitro::NullType, std::string>> signatureAndroid, IapStore store, double transactionDate, std::optional<std::variant<nitro::NullType, std::string>> transactionId, std::optional<std::variant<nitro::NullType, std::string>> userIdAmazon, std::optional<std::variant<nitro::NullType, std::string>> userMarketplaceAmazon): autoRenewingAndroid(autoRenewingAndroid), currentPlanId(currentPlanId), dataAndroid(dataAndroid), developerPayloadAndroid(developerPayloadAndroid), id(id), ids(ids), isAcknowledgedAndroid(isAcknowledgedAndroid), isAutoRenewing(isAutoRenewing), isSuspendedAndroid(isSuspendedAndroid), obfuscatedAccountIdAndroid(obfuscatedAccountIdAndroid), obfuscatedProfileIdAndroid(obfuscatedProfileIdAndroid), packageNameAndroid(packageNameAndroid), pendingPurchaseUpdateAndroid(pendingPurchaseUpdateAndroid), productId(productId), purchaseState(purchaseState), purchaseToken(purchaseToken), quantity(quantity), signatureAndroid(signatureAndroid), store(store), transactionDate(transactionDate), transactionId(transactionId), userIdAmazon(userIdAmazon), userMarketplaceAmazon(userMarketplaceAmazon) {}
|
|
79
81
|
|
|
80
82
|
public:
|
|
81
83
|
// PurchaseAndroid is not equatable because these properties are not equatable: ids, pendingPurchaseUpdateAndroid
|
|
@@ -111,7 +113,9 @@ namespace margelo::nitro {
|
|
|
111
113
|
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "signatureAndroid"))),
|
|
112
114
|
JSIConverter<margelo::nitro::iap::IapStore>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "store"))),
|
|
113
115
|
JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "transactionDate"))),
|
|
114
|
-
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "transactionId")))
|
|
116
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "transactionId"))),
|
|
117
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userIdAmazon"))),
|
|
118
|
+
JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userMarketplaceAmazon")))
|
|
115
119
|
);
|
|
116
120
|
}
|
|
117
121
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::PurchaseAndroid& arg) {
|
|
@@ -137,6 +141,8 @@ namespace margelo::nitro {
|
|
|
137
141
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "store"), JSIConverter<margelo::nitro::iap::IapStore>::toJSI(runtime, arg.store));
|
|
138
142
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "transactionDate"), JSIConverter<double>::toJSI(runtime, arg.transactionDate));
|
|
139
143
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "transactionId"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.transactionId));
|
|
144
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "userIdAmazon"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.userIdAmazon));
|
|
145
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "userMarketplaceAmazon"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.userMarketplaceAmazon));
|
|
140
146
|
return obj;
|
|
141
147
|
}
|
|
142
148
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -168,6 +174,8 @@ namespace margelo::nitro {
|
|
|
168
174
|
if (!JSIConverter<margelo::nitro::iap::IapStore>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "store")))) return false;
|
|
169
175
|
if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "transactionDate")))) return false;
|
|
170
176
|
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "transactionId")))) return false;
|
|
177
|
+
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userIdAmazon")))) return false;
|
|
178
|
+
if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userMarketplaceAmazon")))) return false;
|
|
171
179
|
return true;
|
|
172
180
|
}
|
|
173
181
|
};
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
package/src/hooks/useIAP.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {useCallback, useEffect, useState, useRef} from 'react';
|
|
3
3
|
import {Platform} from 'react-native';
|
|
4
4
|
import {RnIapConsole} from '../utils/debug';
|
|
5
|
+
import {createPurchaseError} from '../utils/errorMapping';
|
|
5
6
|
|
|
6
7
|
// Internal modules
|
|
7
8
|
import {
|
|
@@ -467,6 +468,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
467
468
|
} catch (error) {
|
|
468
469
|
RnIapConsole.error('Error fetching available purchases:', error);
|
|
469
470
|
invokeOnError(error);
|
|
471
|
+
throw error;
|
|
470
472
|
}
|
|
471
473
|
},
|
|
472
474
|
[invokeOnError],
|
|
@@ -483,7 +485,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
483
485
|
} catch (error) {
|
|
484
486
|
RnIapConsole.error('Error getting active subscriptions:', error);
|
|
485
487
|
invokeOnError(error);
|
|
486
|
-
|
|
488
|
+
throw error;
|
|
487
489
|
}
|
|
488
490
|
},
|
|
489
491
|
[invokeOnError],
|
|
@@ -496,7 +498,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
496
498
|
} catch (error) {
|
|
497
499
|
RnIapConsole.error('Error checking active subscriptions:', error);
|
|
498
500
|
invokeOnError(error);
|
|
499
|
-
|
|
501
|
+
throw error;
|
|
500
502
|
}
|
|
501
503
|
},
|
|
502
504
|
[invokeOnError],
|
|
@@ -523,16 +525,26 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
523
525
|
|
|
524
526
|
const restorePurchases = useCallback(
|
|
525
527
|
async (options?: PurchaseOptions): Promise<void> => {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
await syncIOS();
|
|
528
|
+
if (Platform.OS === 'ios') {
|
|
529
|
+
try {
|
|
530
|
+
const synced = await syncIOS();
|
|
531
|
+
if (!synced) {
|
|
532
|
+
throw createPurchaseError({
|
|
533
|
+
code: ErrorCode.SyncError,
|
|
534
|
+
message: 'App Store purchase sync did not complete',
|
|
535
|
+
platform: 'ios',
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
RnIapConsole.warn('Failed to restore purchases:', error);
|
|
540
|
+
invokeOnError(error);
|
|
541
|
+
throw error;
|
|
529
542
|
}
|
|
530
|
-
|
|
531
|
-
await getAvailablePurchasesInternal(options);
|
|
532
|
-
} catch (error) {
|
|
533
|
-
RnIapConsole.warn('Failed to restore purchases:', error);
|
|
534
|
-
invokeOnError(error);
|
|
535
543
|
}
|
|
544
|
+
|
|
545
|
+
// The query helper reports and rethrows its own error, avoiding a second
|
|
546
|
+
// onError call while keeping restore failure observable to the caller.
|
|
547
|
+
await getAvailablePurchasesInternal(options);
|
|
536
548
|
},
|
|
537
549
|
[getAvailablePurchasesInternal, invokeOnError],
|
|
538
550
|
);
|
|
@@ -591,6 +603,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
591
603
|
} catch (e) {
|
|
592
604
|
RnIapConsole.warn('[useIAP] post-purchase refresh failed:', e);
|
|
593
605
|
}
|
|
606
|
+
|
|
607
|
+
// Deliver even if the hook unmounted while the refresh was pending:
|
|
608
|
+
// onPurchaseSuccess is where apps call finishTransaction, and a
|
|
609
|
+
// dropped event has no in-session redelivery. Internal setState is
|
|
610
|
+
// mount-guarded inside the refresh helpers.
|
|
594
611
|
if (optionsRef.current?.onPurchaseSuccess) {
|
|
595
612
|
optionsRef.current.onPurchaseSuccess(purchase);
|
|
596
613
|
}
|
package/src/index.kepler.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
validateNitroProduct,
|
|
27
27
|
validateNitroPurchase,
|
|
28
28
|
} from './utils/type-bridge';
|
|
29
|
+
import {convertAndroidPurchasesOrThrow} from './utils/available-purchases';
|
|
29
30
|
|
|
30
31
|
export * from './types';
|
|
31
32
|
export * from './utils/error';
|
|
@@ -128,7 +129,8 @@ export const fetchProducts = async (
|
|
|
128
129
|
const normalizedType = normalizeProductQueryType(type);
|
|
129
130
|
const nitroProducts = await getModule().fetchProducts(skus, normalizedType);
|
|
130
131
|
return mapProducts(nitroProducts, normalizedType) as
|
|
131
|
-
|
|
132
|
+
| Product[]
|
|
133
|
+
| ProductSubscription[];
|
|
132
134
|
};
|
|
133
135
|
|
|
134
136
|
export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
@@ -152,9 +154,7 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
152
154
|
const result = await getModule().requestPurchase(nitroRequest);
|
|
153
155
|
if (!Array.isArray(result)) return null;
|
|
154
156
|
const purchases = result as unknown as NitroPurchase[];
|
|
155
|
-
return purchases
|
|
156
|
-
.filter(validateNitroPurchase)
|
|
157
|
-
.map((purchase) => convertNitroPurchaseToPurchase(purchase));
|
|
157
|
+
return convertAndroidPurchasesOrThrow(purchases);
|
|
158
158
|
};
|
|
159
159
|
|
|
160
160
|
export const getAvailablePurchases: QueryField<
|
|
@@ -167,11 +167,7 @@ export const getAvailablePurchases: QueryField<
|
|
|
167
167
|
};
|
|
168
168
|
return getModule()
|
|
169
169
|
.getAvailablePurchases(nitroOptions)
|
|
170
|
-
.then(
|
|
171
|
-
purchases
|
|
172
|
-
.filter(validateNitroPurchase)
|
|
173
|
-
.map(convertNitroPurchaseToPurchase),
|
|
174
|
-
);
|
|
170
|
+
.then(convertAndroidPurchasesOrThrow);
|
|
175
171
|
};
|
|
176
172
|
|
|
177
173
|
export const finishTransaction: MutationField<'finishTransaction'> = async (
|
package/src/index.ts
CHANGED
|
@@ -59,6 +59,10 @@ import {
|
|
|
59
59
|
import {RnIapConsole} from './utils/debug';
|
|
60
60
|
import {getSuccessFromPurchaseVariant} from './utils/purchase';
|
|
61
61
|
import {parseAppTransactionPayload} from './utils';
|
|
62
|
+
import {
|
|
63
|
+
convertAndroidPurchasesOrThrow,
|
|
64
|
+
convertApplePurchasesOrThrow,
|
|
65
|
+
} from './utils/available-purchases';
|
|
62
66
|
import {getVegaIapModule, isVegaOS} from './vega';
|
|
63
67
|
|
|
64
68
|
// ------------------------------
|
|
@@ -451,6 +455,13 @@ export const purchaseUpdatedListener = (
|
|
|
451
455
|
return;
|
|
452
456
|
}
|
|
453
457
|
|
|
458
|
+
// StoreKit-backed Nitro listener disposal can abort in iOS release builds
|
|
459
|
+
// when a native modal is being popped. Keep the singleton native listener
|
|
460
|
+
// attached for the app session and only remove the JS callback above.
|
|
461
|
+
if (Platform.OS === 'ios') {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
454
465
|
const token = receiveDuplicateTransactionUpdatesIOS
|
|
455
466
|
? purchaseUpdateDuplicateNativeToken
|
|
456
467
|
: purchaseUpdateNativeToken;
|
|
@@ -498,7 +509,19 @@ export const purchaseErrorListener = (
|
|
|
498
509
|
return {
|
|
499
510
|
remove: () => {
|
|
500
511
|
purchaseErrorJsListeners.delete(listener);
|
|
501
|
-
if (purchaseErrorJsListeners.size
|
|
512
|
+
if (purchaseErrorJsListeners.size > 0) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Same iOS policy as purchaseUpdatedListener: releasing the stored
|
|
517
|
+
// native callback from a JS unsubscribe can race an in-flight dispatch
|
|
518
|
+
// snapshot on another thread. Keep the singleton attached; endConnection
|
|
519
|
+
// owns native disposal.
|
|
520
|
+
if (Platform.OS === 'ios') {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (purchaseErrorNativeAttached) {
|
|
502
525
|
try {
|
|
503
526
|
IAP.instance.removePurchaseErrorListener(purchaseErrorNativeHandler);
|
|
504
527
|
purchaseErrorNativeAttached = false;
|
|
@@ -865,7 +888,8 @@ export const fetchProducts: QueryField<'fetchProducts'> = async (request) => {
|
|
|
865
888
|
|
|
866
889
|
if (normalizedType === 'all') {
|
|
867
890
|
const converted = (await fetchAndConvert('all')) as (
|
|
868
|
-
|
|
891
|
+
| Product
|
|
892
|
+
| ProductSubscription
|
|
869
893
|
)[];
|
|
870
894
|
|
|
871
895
|
RnIapConsole.debug(
|
|
@@ -972,14 +996,7 @@ export const getAvailablePurchases: QueryField<
|
|
|
972
996
|
const nitroPurchases =
|
|
973
997
|
await IAP.instance.getAvailablePurchases(nitroOptions);
|
|
974
998
|
|
|
975
|
-
|
|
976
|
-
if (validPurchases.length !== nitroPurchases.length) {
|
|
977
|
-
RnIapConsole.warn(
|
|
978
|
-
`[getAvailablePurchases] Some purchases failed validation: ${nitroPurchases.length - validPurchases.length} invalid`,
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
return validPurchases.map(convertNitroPurchaseToPurchase);
|
|
999
|
+
return convertApplePurchasesOrThrow(nitroPurchases);
|
|
983
1000
|
} else if (isAndroidStoreRuntime()) {
|
|
984
1001
|
const includeSuspended = Boolean(
|
|
985
1002
|
options?.includeSuspendedAndroid ?? false,
|
|
@@ -989,14 +1006,7 @@ export const getAvailablePurchases: QueryField<
|
|
|
989
1006
|
const nitroPurchases = await IAP.instance.getAvailablePurchases({
|
|
990
1007
|
android: {includeSuspended},
|
|
991
1008
|
});
|
|
992
|
-
|
|
993
|
-
if (validPurchases.length !== nitroPurchases.length) {
|
|
994
|
-
RnIapConsole.warn(
|
|
995
|
-
`[getAvailablePurchases] Some Vega purchases failed validation: ${nitroPurchases.length - validPurchases.length} invalid`,
|
|
996
|
-
);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
return validPurchases.map(convertNitroPurchaseToPurchase);
|
|
1009
|
+
return convertAndroidPurchasesOrThrow(nitroPurchases);
|
|
1000
1010
|
}
|
|
1001
1011
|
|
|
1002
1012
|
// For Android Play/Horizon/Fire OS, query in-app items and subscriptions separately.
|
|
@@ -1007,16 +1017,8 @@ export const getAvailablePurchases: QueryField<
|
|
|
1007
1017
|
android: {type: 'subs', includeSuspended},
|
|
1008
1018
|
});
|
|
1009
1019
|
|
|
1010
|
-
// Validate and convert both sets of purchases
|
|
1011
1020
|
const allNitroPurchases = [...inappNitroPurchases, ...subsNitroPurchases];
|
|
1012
|
-
|
|
1013
|
-
if (validPurchases.length !== allNitroPurchases.length) {
|
|
1014
|
-
RnIapConsole.warn(
|
|
1015
|
-
`[getAvailablePurchases] Some Android purchases failed validation: ${allNitroPurchases.length - validPurchases.length} invalid`,
|
|
1016
|
-
);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
return validPurchases.map(convertNitroPurchaseToPurchase);
|
|
1021
|
+
return convertAndroidPurchasesOrThrow(allNitroPurchases);
|
|
1020
1022
|
} else {
|
|
1021
1023
|
throw unsupportedPlatformError();
|
|
1022
1024
|
}
|
|
@@ -1279,11 +1281,7 @@ export const getPendingTransactionsIOS: QueryField<
|
|
|
1279
1281
|
|
|
1280
1282
|
try {
|
|
1281
1283
|
const nitroPurchases = await IAP.instance.getPendingTransactionsIOS();
|
|
1282
|
-
return nitroPurchases
|
|
1283
|
-
.map(convertNitroPurchaseToPurchase)
|
|
1284
|
-
.filter(
|
|
1285
|
-
(purchase): purchase is PurchaseIOS => purchase.store === 'apple',
|
|
1286
|
-
);
|
|
1284
|
+
return convertApplePurchasesOrThrow(nitroPurchases);
|
|
1287
1285
|
} catch (error) {
|
|
1288
1286
|
const parsedError = parseErrorAndLogIfNeeded(
|
|
1289
1287
|
'[getPendingTransactionsIOS] Failed:',
|
|
@@ -1312,11 +1310,7 @@ export const getAllTransactionsIOS: QueryField<
|
|
|
1312
1310
|
|
|
1313
1311
|
try {
|
|
1314
1312
|
const nitroPurchases = await IAP.instance.getAllTransactionsIOS();
|
|
1315
|
-
return nitroPurchases
|
|
1316
|
-
.map(convertNitroPurchaseToPurchase)
|
|
1317
|
-
.filter(
|
|
1318
|
-
(purchase): purchase is PurchaseIOS => purchase.store === 'apple',
|
|
1319
|
-
);
|
|
1313
|
+
return convertApplePurchasesOrThrow(nitroPurchases);
|
|
1320
1314
|
} catch (error) {
|
|
1321
1315
|
const parsedError = parseErrorAndLogIfNeeded(
|
|
1322
1316
|
'[getAllTransactionsIOS] Failed:',
|
|
@@ -1347,11 +1341,7 @@ export const showManageSubscriptionsIOS: MutationField<
|
|
|
1347
1341
|
|
|
1348
1342
|
try {
|
|
1349
1343
|
const nitroPurchases = await IAP.instance.showManageSubscriptionsIOS();
|
|
1350
|
-
return nitroPurchases
|
|
1351
|
-
.map(convertNitroPurchaseToPurchase)
|
|
1352
|
-
.filter(
|
|
1353
|
-
(purchase): purchase is PurchaseIOS => purchase.store === 'apple',
|
|
1354
|
-
);
|
|
1344
|
+
return convertApplePurchasesOrThrow(nitroPurchases);
|
|
1355
1345
|
} catch (error) {
|
|
1356
1346
|
const parsedError = parseErrorAndLogIfNeeded(
|
|
1357
1347
|
'[showManageSubscriptionsIOS] Failed:',
|
|
@@ -1608,7 +1598,14 @@ export const endConnection: MutationField<'endConnection'> = async () => {
|
|
|
1608
1598
|
export const restorePurchases: MutationField<'restorePurchases'> = async () => {
|
|
1609
1599
|
try {
|
|
1610
1600
|
if (Platform.OS === 'ios') {
|
|
1611
|
-
await syncIOS();
|
|
1601
|
+
const synced = await syncIOS();
|
|
1602
|
+
if (!synced) {
|
|
1603
|
+
throw createPurchaseError({
|
|
1604
|
+
code: ErrorCode.SyncError,
|
|
1605
|
+
message: 'App Store purchase sync did not complete',
|
|
1606
|
+
platform: 'ios',
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1612
1609
|
}
|
|
1613
1610
|
|
|
1614
1611
|
await getAvailablePurchases({
|
|
@@ -2466,8 +2463,7 @@ export const getActiveSubscriptions: QueryField<
|
|
|
2466
2463
|
sub.renewalInfoIOS.autoRenewPreference ?? null,
|
|
2467
2464
|
bundleOriginalTransactionId:
|
|
2468
2465
|
sub.renewalInfoIOS.bundleOriginalTransactionId ?? null,
|
|
2469
|
-
bundleProductId:
|
|
2470
|
-
sub.renewalInfoIOS.bundleProductId ?? null,
|
|
2466
|
+
bundleProductId: sub.renewalInfoIOS.bundleProductId ?? null,
|
|
2471
2467
|
bundleSubscriptionGroupId:
|
|
2472
2468
|
sub.renewalInfoIOS.bundleSubscriptionGroupId ?? null,
|
|
2473
2469
|
commitmentInfo: sub.renewalInfoIOS.commitmentInfo ?? null,
|
package/src/specs/RnIap.nitro.ts
CHANGED
|
@@ -590,6 +590,8 @@ export interface NitroPurchase {
|
|
|
590
590
|
developerPayloadAndroid?: string | null;
|
|
591
591
|
isSuspendedAndroid?: null | boolean;
|
|
592
592
|
pendingPurchaseUpdateAndroid?: PendingPurchaseUpdateAndroid | null;
|
|
593
|
+
userIdAmazon?: string | null;
|
|
594
|
+
userMarketplaceAmazon?: string | null;
|
|
593
595
|
}
|
|
594
596
|
|
|
595
597
|
/**
|
package/src/types.ts
CHANGED
|
@@ -1214,6 +1214,17 @@ export interface PurchaseAndroid extends PurchaseCommon {
|
|
|
1214
1214
|
/** Unix timestamp in milliseconds since January 1, 1970 UTC. */
|
|
1215
1215
|
transactionDate: number;
|
|
1216
1216
|
transactionId?: (string | null);
|
|
1217
|
+
/**
|
|
1218
|
+
* Amazon Appstore user id (PurchaseResponse.getUserData().getUserId()).
|
|
1219
|
+
* Only populated on the Amazon flavor; required for server-side Amazon RVS
|
|
1220
|
+
* receipt verification (userId + receiptId). Null on Google Play and Horizon.
|
|
1221
|
+
*/
|
|
1222
|
+
userIdAmazon?: (string | null);
|
|
1223
|
+
/**
|
|
1224
|
+
* Amazon Appstore marketplace (PurchaseResponse.getUserData().getMarketplace()),
|
|
1225
|
+
* for example "US" or "FR". Only populated on the Amazon flavor.
|
|
1226
|
+
*/
|
|
1227
|
+
userMarketplaceAmazon?: (string | null);
|
|
1217
1228
|
}
|
|
1218
1229
|
|
|
1219
1230
|
export interface PurchaseCommon {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {ErrorCode} from '../types';
|
|
2
|
+
import type {Purchase, PurchaseIOS} from '../types';
|
|
3
|
+
import {createPurchaseError} from './errorMapping';
|
|
4
|
+
import {
|
|
5
|
+
convertNitroPurchaseToPurchase,
|
|
6
|
+
validateNitroPurchase,
|
|
7
|
+
} from './type-bridge';
|
|
8
|
+
|
|
9
|
+
const ANDROID_STORES = new Set(['google', 'amazon', 'horizon']);
|
|
10
|
+
|
|
11
|
+
/** Decode an authoritative native purchase list atomically. */
|
|
12
|
+
export const convertAvailablePurchasesOrThrow = (
|
|
13
|
+
purchases: unknown,
|
|
14
|
+
): Purchase[] => {
|
|
15
|
+
if (!Array.isArray(purchases)) {
|
|
16
|
+
throw createPurchaseError({
|
|
17
|
+
code: ErrorCode.BillingResponseJsonParseError,
|
|
18
|
+
message: 'Malformed purchase list returned by the native bridge',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return purchases.map((purchase, index) => {
|
|
23
|
+
if (!validateNitroPurchase(purchase)) {
|
|
24
|
+
throw createPurchaseError({
|
|
25
|
+
code: ErrorCode.BillingResponseJsonParseError,
|
|
26
|
+
message: `Malformed purchase payload returned by the native bridge at index ${index}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
return convertNitroPurchaseToPurchase(purchase);
|
|
32
|
+
} catch {
|
|
33
|
+
throw createPurchaseError({
|
|
34
|
+
code: ErrorCode.BillingResponseJsonParseError,
|
|
35
|
+
message: `Failed to decode native purchase payload at index ${index}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Decode an authoritative StoreKit purchase list without filtering entries. */
|
|
42
|
+
export const convertApplePurchasesOrThrow = (
|
|
43
|
+
purchases: unknown,
|
|
44
|
+
): PurchaseIOS[] => {
|
|
45
|
+
const decoded = convertAvailablePurchasesOrThrow(purchases);
|
|
46
|
+
const invalidIndex = decoded.findIndex(
|
|
47
|
+
(purchase) => purchase.store !== 'apple',
|
|
48
|
+
);
|
|
49
|
+
if (invalidIndex !== -1) {
|
|
50
|
+
throw createPurchaseError({
|
|
51
|
+
code: ErrorCode.BillingResponseJsonParseError,
|
|
52
|
+
message: `Native StoreKit bridge returned a non-Apple purchase at index ${invalidIndex}`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return decoded as PurchaseIOS[];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Decode an authoritative Android-family purchase list without foreign stores. */
|
|
59
|
+
export const convertAndroidPurchasesOrThrow = (
|
|
60
|
+
purchases: unknown,
|
|
61
|
+
): Purchase[] => {
|
|
62
|
+
const decoded = convertAvailablePurchasesOrThrow(purchases);
|
|
63
|
+
const invalidIndex = decoded.findIndex(
|
|
64
|
+
(purchase) => !ANDROID_STORES.has(purchase.store),
|
|
65
|
+
);
|
|
66
|
+
if (invalidIndex !== -1) {
|
|
67
|
+
throw createPurchaseError({
|
|
68
|
+
code: ErrorCode.BillingResponseJsonParseError,
|
|
69
|
+
message: `Native Android bridge returned a foreign purchase at index ${invalidIndex}`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return decoded;
|
|
73
|
+
};
|
package/src/utils/type-bridge.ts
CHANGED
|
@@ -504,6 +504,16 @@ export function convertNitroPurchaseToPurchase(
|
|
|
504
504
|
? legacyAndroidId
|
|
505
505
|
: null);
|
|
506
506
|
|
|
507
|
+
// Amazon identity metadata must never leak onto other stores' purchases.
|
|
508
|
+
const amazonUserId =
|
|
509
|
+
nitroPurchase.userIdAmazon != null && store === STORE_AMAZON
|
|
510
|
+
? toNullableString(nitroPurchase.userIdAmazon)
|
|
511
|
+
: null;
|
|
512
|
+
const amazonUserMarketplace =
|
|
513
|
+
nitroPurchase.userMarketplaceAmazon != null && store === STORE_AMAZON
|
|
514
|
+
? toNullableString(nitroPurchase.userMarketplaceAmazon)
|
|
515
|
+
: null;
|
|
516
|
+
|
|
507
517
|
const androidPurchase: PurchaseAndroid = {
|
|
508
518
|
id: nitroPurchase.id,
|
|
509
519
|
productId: nitroPurchase.productId,
|
|
@@ -540,6 +550,8 @@ export function convertNitroPurchaseToPurchase(
|
|
|
540
550
|
isSuspendedAndroid: toNullableBoolean(nitroPurchase.isSuspendedAndroid),
|
|
541
551
|
pendingPurchaseUpdateAndroid:
|
|
542
552
|
nitroPurchase.pendingPurchaseUpdateAndroid ?? null,
|
|
553
|
+
userIdAmazon: amazonUserId,
|
|
554
|
+
userMarketplaceAmazon: amazonUserMarketplace,
|
|
543
555
|
};
|
|
544
556
|
|
|
545
557
|
return androidPurchase;
|
|
@@ -592,17 +604,80 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
|
|
|
592
604
|
return false;
|
|
593
605
|
}
|
|
594
606
|
|
|
595
|
-
const
|
|
596
|
-
|
|
607
|
+
const invalidField = (field: string): false => {
|
|
608
|
+
RnIapConsole.error(`NitroPurchase has invalid required field: ${field}`);
|
|
609
|
+
return false;
|
|
610
|
+
};
|
|
611
|
+
if (typeof nitroPurchase.id !== 'string' || !nitroPurchase.id) {
|
|
612
|
+
return invalidField('id');
|
|
613
|
+
}
|
|
614
|
+
if (typeof nitroPurchase.productId !== 'string' || !nitroPurchase.productId) {
|
|
615
|
+
return invalidField('productId');
|
|
616
|
+
}
|
|
617
|
+
if (
|
|
618
|
+
typeof nitroPurchase.transactionDate !== 'number' ||
|
|
619
|
+
!Number.isFinite(nitroPurchase.transactionDate)
|
|
620
|
+
) {
|
|
621
|
+
return invalidField('transactionDate');
|
|
622
|
+
}
|
|
623
|
+
if (typeof nitroPurchase.store !== 'string' || !nitroPurchase.store) {
|
|
624
|
+
return invalidField('store');
|
|
625
|
+
}
|
|
626
|
+
if (
|
|
627
|
+
typeof nitroPurchase.purchaseState !== 'string' ||
|
|
628
|
+
!nitroPurchase.purchaseState
|
|
629
|
+
) {
|
|
630
|
+
return invalidField('purchaseState');
|
|
631
|
+
}
|
|
632
|
+
if (
|
|
633
|
+
typeof nitroPurchase.quantity !== 'number' ||
|
|
634
|
+
!Number.isInteger(nitroPurchase.quantity)
|
|
635
|
+
) {
|
|
636
|
+
return invalidField('quantity');
|
|
637
|
+
}
|
|
638
|
+
if (typeof nitroPurchase.isAutoRenewing !== 'boolean') {
|
|
639
|
+
return invalidField('isAutoRenewing');
|
|
640
|
+
}
|
|
641
|
+
if (
|
|
642
|
+
nitroPurchase.store === 'apple' &&
|
|
643
|
+
(typeof nitroPurchase.transactionId !== 'string' ||
|
|
644
|
+
!nitroPurchase.transactionId)
|
|
645
|
+
) {
|
|
646
|
+
return invalidField('transactionId');
|
|
647
|
+
}
|
|
648
|
+
const candidate = nitroPurchase as unknown as Record<string, unknown>;
|
|
649
|
+
if (
|
|
650
|
+
candidate.ids != null &&
|
|
651
|
+
(!Array.isArray(candidate.ids) ||
|
|
652
|
+
candidate.ids.some((id) => typeof id !== 'string'))
|
|
653
|
+
) {
|
|
654
|
+
return invalidField('ids');
|
|
655
|
+
}
|
|
656
|
+
for (const field of [
|
|
657
|
+
'pendingPurchaseUpdateAndroid',
|
|
658
|
+
'renewalInfoIOS',
|
|
659
|
+
'commitmentInfoIOS',
|
|
660
|
+
'advancedCommerceInfoIOS',
|
|
661
|
+
]) {
|
|
662
|
+
const nested = candidate[field];
|
|
597
663
|
if (
|
|
598
|
-
|
|
599
|
-
|
|
664
|
+
nested != null &&
|
|
665
|
+
(typeof nested !== 'object' || Array.isArray(nested))
|
|
600
666
|
) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
667
|
+
return invalidField(field);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (candidate.offerIOS != null) {
|
|
671
|
+
if (typeof candidate.offerIOS !== 'string') {
|
|
672
|
+
return invalidField('offerIOS');
|
|
673
|
+
}
|
|
674
|
+
try {
|
|
675
|
+
const offer = JSON.parse(candidate.offerIOS);
|
|
676
|
+
if (offer == null || typeof offer !== 'object' || Array.isArray(offer)) {
|
|
677
|
+
return invalidField('offerIOS');
|
|
678
|
+
}
|
|
679
|
+
} catch {
|
|
680
|
+
return invalidField('offerIOS');
|
|
606
681
|
}
|
|
607
682
|
}
|
|
608
683
|
|