react-native-iap 14.7.3 → 14.7.4

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.
@@ -42,17 +42,18 @@ namespace margelo::nitro::iap {
42
42
  struct NitroRequestPurchaseAndroid {
43
43
  public:
44
44
  std::vector<std::string> skus SWIFT_PRIVATE;
45
- std::optional<std::string> obfuscatedAccountIdAndroid SWIFT_PRIVATE;
46
- std::optional<std::string> obfuscatedProfileIdAndroid SWIFT_PRIVATE;
45
+ std::optional<std::string> obfuscatedAccountId SWIFT_PRIVATE;
46
+ std::optional<std::string> obfuscatedProfileId SWIFT_PRIVATE;
47
47
  std::optional<bool> isOfferPersonalized SWIFT_PRIVATE;
48
+ std::optional<std::string> offerToken SWIFT_PRIVATE;
48
49
  std::optional<std::vector<AndroidSubscriptionOfferInput>> subscriptionOffers SWIFT_PRIVATE;
49
- std::optional<double> replacementModeAndroid SWIFT_PRIVATE;
50
- std::optional<std::string> purchaseTokenAndroid SWIFT_PRIVATE;
50
+ std::optional<double> replacementMode SWIFT_PRIVATE;
51
+ std::optional<std::string> purchaseToken SWIFT_PRIVATE;
51
52
  std::optional<SubscriptionProductReplacementParamsAndroid> subscriptionProductReplacementParams SWIFT_PRIVATE;
52
53
 
53
54
  public:
54
55
  NitroRequestPurchaseAndroid() = default;
55
- explicit NitroRequestPurchaseAndroid(std::vector<std::string> skus, std::optional<std::string> obfuscatedAccountIdAndroid, std::optional<std::string> obfuscatedProfileIdAndroid, std::optional<bool> isOfferPersonalized, std::optional<std::vector<AndroidSubscriptionOfferInput>> subscriptionOffers, std::optional<double> replacementModeAndroid, std::optional<std::string> purchaseTokenAndroid, std::optional<SubscriptionProductReplacementParamsAndroid> subscriptionProductReplacementParams): skus(skus), obfuscatedAccountIdAndroid(obfuscatedAccountIdAndroid), obfuscatedProfileIdAndroid(obfuscatedProfileIdAndroid), isOfferPersonalized(isOfferPersonalized), subscriptionOffers(subscriptionOffers), replacementModeAndroid(replacementModeAndroid), purchaseTokenAndroid(purchaseTokenAndroid), subscriptionProductReplacementParams(subscriptionProductReplacementParams) {}
56
+ explicit NitroRequestPurchaseAndroid(std::vector<std::string> skus, std::optional<std::string> obfuscatedAccountId, std::optional<std::string> obfuscatedProfileId, std::optional<bool> isOfferPersonalized, std::optional<std::string> offerToken, std::optional<std::vector<AndroidSubscriptionOfferInput>> subscriptionOffers, std::optional<double> replacementMode, std::optional<std::string> purchaseToken, std::optional<SubscriptionProductReplacementParamsAndroid> subscriptionProductReplacementParams): skus(skus), obfuscatedAccountId(obfuscatedAccountId), obfuscatedProfileId(obfuscatedProfileId), isOfferPersonalized(isOfferPersonalized), offerToken(offerToken), subscriptionOffers(subscriptionOffers), replacementMode(replacementMode), purchaseToken(purchaseToken), subscriptionProductReplacementParams(subscriptionProductReplacementParams) {}
56
57
  };
57
58
 
58
59
  } // namespace margelo::nitro::iap
@@ -66,24 +67,26 @@ namespace margelo::nitro {
66
67
  jsi::Object obj = arg.asObject(runtime);
67
68
  return margelo::nitro::iap::NitroRequestPurchaseAndroid(
68
69
  JSIConverter<std::vector<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "skus")),
69
- JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "obfuscatedAccountIdAndroid")),
70
- JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "obfuscatedProfileIdAndroid")),
70
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "obfuscatedAccountId")),
71
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "obfuscatedProfileId")),
71
72
  JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, "isOfferPersonalized")),
73
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "offerToken")),
72
74
  JSIConverter<std::optional<std::vector<margelo::nitro::iap::AndroidSubscriptionOfferInput>>>::fromJSI(runtime, obj.getProperty(runtime, "subscriptionOffers")),
73
- JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, "replacementModeAndroid")),
74
- JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "purchaseTokenAndroid")),
75
+ JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, "replacementMode")),
76
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "purchaseToken")),
75
77
  JSIConverter<std::optional<margelo::nitro::iap::SubscriptionProductReplacementParamsAndroid>>::fromJSI(runtime, obj.getProperty(runtime, "subscriptionProductReplacementParams"))
76
78
  );
77
79
  }
78
80
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroRequestPurchaseAndroid& arg) {
79
81
  jsi::Object obj(runtime);
80
82
  obj.setProperty(runtime, "skus", JSIConverter<std::vector<std::string>>::toJSI(runtime, arg.skus));
81
- obj.setProperty(runtime, "obfuscatedAccountIdAndroid", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.obfuscatedAccountIdAndroid));
82
- obj.setProperty(runtime, "obfuscatedProfileIdAndroid", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.obfuscatedProfileIdAndroid));
83
+ obj.setProperty(runtime, "obfuscatedAccountId", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.obfuscatedAccountId));
84
+ obj.setProperty(runtime, "obfuscatedProfileId", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.obfuscatedProfileId));
83
85
  obj.setProperty(runtime, "isOfferPersonalized", JSIConverter<std::optional<bool>>::toJSI(runtime, arg.isOfferPersonalized));
86
+ obj.setProperty(runtime, "offerToken", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.offerToken));
84
87
  obj.setProperty(runtime, "subscriptionOffers", JSIConverter<std::optional<std::vector<margelo::nitro::iap::AndroidSubscriptionOfferInput>>>::toJSI(runtime, arg.subscriptionOffers));
85
- obj.setProperty(runtime, "replacementModeAndroid", JSIConverter<std::optional<double>>::toJSI(runtime, arg.replacementModeAndroid));
86
- obj.setProperty(runtime, "purchaseTokenAndroid", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.purchaseTokenAndroid));
88
+ obj.setProperty(runtime, "replacementMode", JSIConverter<std::optional<double>>::toJSI(runtime, arg.replacementMode));
89
+ obj.setProperty(runtime, "purchaseToken", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.purchaseToken));
87
90
  obj.setProperty(runtime, "subscriptionProductReplacementParams", JSIConverter<std::optional<margelo::nitro::iap::SubscriptionProductReplacementParamsAndroid>>::toJSI(runtime, arg.subscriptionProductReplacementParams));
88
91
  return obj;
89
92
  }
@@ -96,12 +99,13 @@ namespace margelo::nitro {
96
99
  return false;
97
100
  }
98
101
  if (!JSIConverter<std::vector<std::string>>::canConvert(runtime, obj.getProperty(runtime, "skus"))) return false;
99
- if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "obfuscatedAccountIdAndroid"))) return false;
100
- if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "obfuscatedProfileIdAndroid"))) return false;
102
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "obfuscatedAccountId"))) return false;
103
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "obfuscatedProfileId"))) return false;
101
104
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, "isOfferPersonalized"))) return false;
105
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "offerToken"))) return false;
102
106
  if (!JSIConverter<std::optional<std::vector<margelo::nitro::iap::AndroidSubscriptionOfferInput>>>::canConvert(runtime, obj.getProperty(runtime, "subscriptionOffers"))) return false;
103
- if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, "replacementModeAndroid"))) return false;
104
- if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "purchaseTokenAndroid"))) return false;
107
+ if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, "replacementMode"))) return false;
108
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "purchaseToken"))) return false;
105
109
  if (!JSIConverter<std::optional<margelo::nitro::iap::SubscriptionProductReplacementParamsAndroid>>::canConvert(runtime, obj.getProperty(runtime, "subscriptionProductReplacementParams"))) return false;
106
110
  return true;
107
111
  }
@@ -1,6 +1,6 @@
1
1
  {
2
- "apple": "1.3.12",
3
- "google": "1.3.25",
4
- "gql": "1.3.14",
5
- "docs": "1.3.14"
2
+ "apple": "1.3.13",
3
+ "google": "1.3.26",
4
+ "gql": "1.3.15",
5
+ "docs": "1.3.15"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "14.7.3",
3
+ "version": "14.7.4",
4
4
  "description": "React Native In-App Purchases module for iOS and Android using Nitro",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -104,6 +104,8 @@ type UseIap = {
104
104
  export interface UseIapOptions {
105
105
  onPurchaseSuccess?: (purchase: Purchase) => void;
106
106
  onPurchaseError?: (error: PurchaseError) => void;
107
+ /** Callback for non-purchase errors (fetchProducts, getAvailablePurchases, etc.) */
108
+ onError?: (error: Error) => void;
107
109
  onPromotedProductIOS?: (product: Product) => void;
108
110
  onUserChoiceBillingAndroid?: (details: UserChoiceBillingDetails) => void;
109
111
  /**
@@ -180,6 +182,15 @@ export function useIAP(options?: UseIapOptions): UseIap {
180
182
  subscriptionsRefState.current = subscriptions;
181
183
  }, [subscriptions]);
182
184
 
185
+ // Helper function to invoke onError callback
186
+ const invokeOnError = useCallback((error: unknown) => {
187
+ if (optionsRef.current?.onError) {
188
+ optionsRef.current.onError(
189
+ error instanceof Error ? error : new Error(String(error)),
190
+ );
191
+ }
192
+ }, []);
193
+
183
194
  const fetchProductsInternal = useCallback(
184
195
  async (params: {
185
196
  skus: string[];
@@ -253,9 +264,10 @@ export function useIAP(options?: UseIapOptions): UseIap {
253
264
  );
254
265
  } catch (error) {
255
266
  RnIapConsole.error('Error fetching products:', error);
267
+ invokeOnError(error);
256
268
  }
257
269
  },
258
- [mergeWithDuplicateCheck],
270
+ [mergeWithDuplicateCheck, invokeOnError],
259
271
  );
260
272
 
261
273
  const getAvailablePurchasesInternal = useCallback(
@@ -268,9 +280,10 @@ export function useIAP(options?: UseIapOptions): UseIap {
268
280
  setAvailablePurchases(result);
269
281
  } catch (error) {
270
282
  RnIapConsole.error('Error fetching available purchases:', error);
283
+ invokeOnError(error);
271
284
  }
272
285
  },
273
- [],
286
+ [invokeOnError],
274
287
  );
275
288
 
276
289
  const getActiveSubscriptionsInternal = useCallback(
@@ -281,12 +294,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
281
294
  return result;
282
295
  } catch (error) {
283
296
  RnIapConsole.error('Error getting active subscriptions:', error);
284
- // Don't clear existing activeSubscriptions on error - preserve current state
285
- // This prevents the UI from showing empty state when there are temporary network issues
297
+ invokeOnError(error);
286
298
  return [];
287
299
  }
288
300
  },
289
- [],
301
+ [invokeOnError],
290
302
  );
291
303
 
292
304
  const hasActiveSubscriptionsInternal = useCallback(
@@ -295,10 +307,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
295
307
  return await hasActiveSubscriptions(subscriptionIds);
296
308
  } catch (error) {
297
309
  RnIapConsole.error('Error checking active subscriptions:', error);
310
+ invokeOnError(error);
298
311
  return false;
299
312
  }
300
313
  },
301
- [],
314
+ [invokeOnError],
302
315
  );
303
316
 
304
317
  const finishTransaction = useCallback(
@@ -469,8 +482,9 @@ export function useIAP(options?: UseIapOptions): UseIap {
469
482
  try {
470
483
  await restorePurchasesTopLevel();
471
484
  await getAvailablePurchasesInternal();
472
- } catch (e) {
473
- RnIapConsole.warn('Failed to restore purchases:', e);
485
+ } catch (error) {
486
+ RnIapConsole.warn('Failed to restore purchases:', error);
487
+ invokeOnError(error);
474
488
  }
475
489
  },
476
490
  getPromotedProductIOS,
package/src/index.ts CHANGED
@@ -1241,27 +1241,35 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
1241
1241
  skus: androidRequest.skus,
1242
1242
  };
1243
1243
 
1244
- if (androidRequest.obfuscatedAccountIdAndroid) {
1245
- androidPayload.obfuscatedAccountIdAndroid =
1246
- androidRequest.obfuscatedAccountIdAndroid;
1244
+ if (androidRequest.obfuscatedAccountId) {
1245
+ androidPayload.obfuscatedAccountId = androidRequest.obfuscatedAccountId;
1247
1246
  }
1248
- if (androidRequest.obfuscatedProfileIdAndroid) {
1249
- androidPayload.obfuscatedProfileIdAndroid =
1250
- androidRequest.obfuscatedProfileIdAndroid;
1247
+ if (androidRequest.obfuscatedProfileId) {
1248
+ androidPayload.obfuscatedProfileId = androidRequest.obfuscatedProfileId;
1251
1249
  }
1252
1250
  if (androidRequest.isOfferPersonalized != null) {
1253
1251
  androidPayload.isOfferPersonalized = androidRequest.isOfferPersonalized;
1254
1252
  }
1255
1253
 
1254
+ // One-time purchase offerToken (Android 7.0+)
1255
+ if (!isSubs) {
1256
+ const purchaseRequest = androidRequest as RequestPurchaseAndroidProps;
1257
+ if (purchaseRequest.offerToken) {
1258
+ androidPayload.offerToken = purchaseRequest.offerToken;
1259
+ }
1260
+ }
1261
+
1256
1262
  if (isSubs) {
1257
1263
  const subsRequest = androidRequest as RequestSubscriptionAndroidProps;
1258
- if (subsRequest.purchaseTokenAndroid) {
1259
- androidPayload.purchaseTokenAndroid =
1260
- subsRequest.purchaseTokenAndroid;
1264
+ if (subsRequest.purchaseToken) {
1265
+ androidPayload.purchaseToken = subsRequest.purchaseToken;
1266
+ }
1267
+ if (subsRequest.replacementMode != null) {
1268
+ androidPayload.replacementMode = subsRequest.replacementMode;
1261
1269
  }
1262
- if (subsRequest.replacementModeAndroid != null) {
1263
- androidPayload.replacementModeAndroid =
1264
- subsRequest.replacementModeAndroid;
1270
+ if (subsRequest.subscriptionProductReplacementParams) {
1271
+ androidPayload.subscriptionProductReplacementParams =
1272
+ subsRequest.subscriptionProductReplacementParams;
1265
1273
  }
1266
1274
  androidPayload.subscriptionOffers = (
1267
1275
  subsRequest.subscriptionOffers ?? []
@@ -145,16 +145,22 @@ export interface NitroRequestPurchaseIos {
145
145
 
146
146
  export interface NitroRequestPurchaseAndroid {
147
147
  skus: RequestSubscriptionAndroidProps['skus'];
148
- obfuscatedAccountIdAndroid?: RequestSubscriptionAndroidProps['obfuscatedAccountIdAndroid'];
149
- obfuscatedProfileIdAndroid?: RequestSubscriptionAndroidProps['obfuscatedProfileIdAndroid'];
148
+ obfuscatedAccountId?: RequestSubscriptionAndroidProps['obfuscatedAccountId'];
149
+ obfuscatedProfileId?: RequestSubscriptionAndroidProps['obfuscatedProfileId'];
150
150
  isOfferPersonalized?: RequestSubscriptionAndroidProps['isOfferPersonalized'];
151
+ /**
152
+ * Offer token for one-time purchase discounts (7.0+).
153
+ * Pass the offerToken from oneTimePurchaseOfferDetailsAndroid or discountOffers
154
+ * to apply a discount offer to the purchase.
155
+ */
156
+ offerToken?: string | null;
151
157
  subscriptionOffers?: AndroidSubscriptionOfferInput[] | null;
152
158
  /** @deprecated Use subscriptionProductReplacementParams instead for item-level replacement (8.1.0+) */
153
- replacementModeAndroid?: RequestSubscriptionAndroidProps['replacementModeAndroid'];
154
- purchaseTokenAndroid?: RequestSubscriptionAndroidProps['purchaseTokenAndroid'];
159
+ replacementMode?: RequestSubscriptionAndroidProps['replacementMode'];
160
+ purchaseToken?: RequestSubscriptionAndroidProps['purchaseToken'];
155
161
  /**
156
162
  * Product-level replacement parameters (8.1.0+)
157
- * Use this instead of replacementModeAndroid for item-level replacement
163
+ * Use this instead of replacementMode for item-level replacement
158
164
  */
159
165
  subscriptionProductReplacementParams?: SubscriptionProductReplacementParamsAndroid | null;
160
166
  }
package/src/types.ts CHANGED
@@ -1194,12 +1194,21 @@ export interface RequestPurchaseAndroidProps {
1194
1194
  * Google Play Billing and the developer's external payment option.
1195
1195
  */
1196
1196
  developerBillingOption?: (DeveloperBillingOptionParamsAndroid | null);
1197
- /** Personalized offer flag */
1197
+ /**
1198
+ * Personalized offer flag.
1199
+ * When true, indicates the price was customized for this user.
1200
+ */
1198
1201
  isOfferPersonalized?: (boolean | null);
1199
1202
  /** Obfuscated account ID */
1200
- obfuscatedAccountIdAndroid?: (string | null);
1203
+ obfuscatedAccountId?: (string | null);
1201
1204
  /** Obfuscated profile ID */
1202
- obfuscatedProfileIdAndroid?: (string | null);
1205
+ obfuscatedProfileId?: (string | null);
1206
+ /**
1207
+ * Offer token for one-time purchase discounts (7.0+).
1208
+ * Pass the offerToken from oneTimePurchaseOfferDetailsAndroid or discountOffers
1209
+ * to apply a discount offer to the purchase.
1210
+ */
1211
+ offerToken?: (string | null);
1203
1212
  /** List of product SKUs */
1204
1213
  skus: string[];
1205
1214
  }
@@ -1271,26 +1280,29 @@ export interface RequestSubscriptionAndroidProps {
1271
1280
  * Google Play Billing and the developer's external payment option.
1272
1281
  */
1273
1282
  developerBillingOption?: (DeveloperBillingOptionParamsAndroid | null);
1274
- /** Personalized offer flag */
1283
+ /**
1284
+ * Personalized offer flag.
1285
+ * When true, indicates the price was customized for this user.
1286
+ */
1275
1287
  isOfferPersonalized?: (boolean | null);
1276
1288
  /** Obfuscated account ID */
1277
- obfuscatedAccountIdAndroid?: (string | null);
1289
+ obfuscatedAccountId?: (string | null);
1278
1290
  /** Obfuscated profile ID */
1279
- obfuscatedProfileIdAndroid?: (string | null);
1291
+ obfuscatedProfileId?: (string | null);
1280
1292
  /** Purchase token for upgrades/downgrades */
1281
- purchaseTokenAndroid?: (string | null);
1293
+ purchaseToken?: (string | null);
1282
1294
  /**
1283
1295
  * Replacement mode for subscription changes
1284
1296
  * @deprecated Use subscriptionProductReplacementParams instead for item-level replacement (8.1.0+)
1285
1297
  */
1286
- replacementModeAndroid?: (number | null);
1298
+ replacementMode?: (number | null);
1287
1299
  /** List of subscription SKUs */
1288
1300
  skus: string[];
1289
1301
  /** Subscription offers */
1290
1302
  subscriptionOffers?: (AndroidSubscriptionOfferInput[] | null);
1291
1303
  /**
1292
1304
  * Product-level replacement parameters (8.1.0+)
1293
- * Use this instead of replacementModeAndroid for item-level replacement
1305
+ * Use this instead of replacementMode for item-level replacement
1294
1306
  */
1295
1307
  subscriptionProductReplacementParams?: (SubscriptionProductReplacementParamsAndroid | null);
1296
1308
  }