expo-iap 3.1.1-rc.2 → 3.1.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.
Files changed (47) hide show
  1. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +0 -11
  2. package/build/ExpoIapModule.d.ts +0 -1
  3. package/build/ExpoIapModule.d.ts.map +1 -1
  4. package/build/ExpoIapModule.js +4 -29
  5. package/build/ExpoIapModule.js.map +1 -1
  6. package/build/useIAP.d.ts +0 -2
  7. package/build/useIAP.d.ts.map +1 -1
  8. package/build/useIAP.js +3 -8
  9. package/build/useIAP.js.map +1 -1
  10. package/coverage/clover.xml +7 -7
  11. package/coverage/coverage-final.json +5 -5
  12. package/coverage/lcov-report/index.html +1 -1
  13. package/coverage/lcov-report/src/helpers/index.html +1 -1
  14. package/coverage/lcov-report/src/helpers/subscription.ts.html +1 -1
  15. package/coverage/lcov-report/src/index.html +1 -1
  16. package/coverage/lcov-report/src/index.ts.html +1 -1
  17. package/coverage/lcov-report/src/modules/android.ts.html +1 -1
  18. package/coverage/lcov-report/src/modules/index.html +1 -1
  19. package/coverage/lcov-report/src/modules/ios.ts.html +1 -1
  20. package/coverage/lcov-report/src/utils/errorMapping.ts.html +1 -1
  21. package/coverage/lcov-report/src/utils/index.html +1 -1
  22. package/expo-module.config.json +3 -10
  23. package/jest.config.js +0 -1
  24. package/openiap-versions.json +1 -1
  25. package/package.json +3 -4
  26. package/plugin/build/withIAP.d.ts +9 -22
  27. package/plugin/build/withIAP.js +9 -157
  28. package/plugin/jest.config.js +3 -13
  29. package/plugin/src/expoConfig.augmentation.d.ts +1 -30
  30. package/plugin/src/withIAP.ts +18 -258
  31. package/plugin/tsconfig.json +1 -2
  32. package/plugin/tsconfig.tsbuildinfo +1 -1
  33. package/src/ExpoIapModule.ts +4 -45
  34. package/src/useIAP.ts +3 -11
  35. package/build/utils/constants.d.ts +0 -6
  36. package/build/utils/constants.d.ts.map +0 -1
  37. package/build/utils/constants.js +0 -19
  38. package/build/utils/constants.js.map +0 -1
  39. package/coverage/lcov-report/src/ExpoIap.types.ts.html +0 -1243
  40. package/coverage/lcov-report/src/PurchaseError.ts.html +0 -787
  41. package/coverage/lcov-report/src/purchase-error.ts.html +0 -880
  42. package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +0 -493
  43. package/coverage/lcov-report/src/types/index.html +0 -116
  44. package/coverage/lcov-report/src/useIap.ts.html +0 -1483
  45. package/coverage/lcov-report/src/utils/purchase.ts.html +0 -241
  46. package/ios/onside/OnsideIapModule.swift +0 -489
  47. package/src/utils/constants.ts +0 -23
@@ -6,11 +6,9 @@ import {
6
6
  withAppBuildGradle,
7
7
  withDangerousMod,
8
8
  } from 'expo/config-plugins';
9
- import type {ExpoConfig} from '@expo/config-types';
10
9
  import * as fs from 'fs';
11
10
  import * as path from 'path';
12
11
  import withLocalOpenIAP from './withLocalOpenIAP';
13
- import type {ExpoIapPluginCommonOptions} from './expoConfig.augmentation';
14
12
 
15
13
  const pkg = require('../../package.json');
16
14
  const openiapVersions = JSON.parse(
@@ -20,10 +18,6 @@ const openiapVersions = JSON.parse(
20
18
  ),
21
19
  );
22
20
  const OPENIAP_ANDROID_VERSION = openiapVersions.google;
23
- const AUTOLINKING_CONFIG_PATH = path.resolve(
24
- __dirname,
25
- '../../expo-module.config.json',
26
- );
27
21
 
28
22
  // Log a message only once per Node process
29
23
  const logOnce = (() => {
@@ -56,7 +50,7 @@ const addLineToGradle = (
56
50
  return lines.join('\n');
57
51
  };
58
52
 
59
- export const modifyAppBuildGradle = (
53
+ const modifyAppBuildGradle = (
60
54
  gradle: string,
61
55
  language: 'groovy' | 'kotlin',
62
56
  ): string => {
@@ -144,136 +138,7 @@ const withIapAndroid: ConfigPlugin<{addDeps?: boolean} | void> = (
144
138
  };
145
139
 
146
140
  /** Ensure Podfile uses CocoaPods CDN and no stale local OpenIAP entry remains. */
147
- type WithIapIosOptions = {
148
- enableOnside?: boolean;
149
- };
150
-
151
- const ensureOnsidePod = (content: string): string => {
152
- const podLine =
153
- " pod 'OnsideKit', :git => 'https://github.com/onside-io/OnsideKit-iOS.git'";
154
- const podRegex = /^\s*pod\s+'OnsideKit'\b.*$/m;
155
-
156
- if (podRegex.test(content)) {
157
- return content;
158
- }
159
-
160
- const targetMatch = content.match(/target\s+'[^']+'\s+do\s*\n/);
161
- if (!targetMatch) {
162
- WarningAggregator.addWarningIOS(
163
- 'expo-iap',
164
- 'Could not find a target block in Podfile when adding OnsideKit; skipping installation.',
165
- );
166
- return content;
167
- }
168
-
169
- const insertIndex = targetMatch.index! + targetMatch[0].length;
170
- const before = content.slice(0, insertIndex);
171
- const after = content.slice(insertIndex);
172
-
173
- logOnce('📦 expo-iap: Added OnsideKit pod to Podfile');
174
-
175
- return `${before}${podLine}\n${after}`;
176
- };
177
-
178
- export type AutolinkState = {expoIap: boolean; onside: boolean};
179
-
180
- type AutolinkEntry = {name: string; enable: boolean};
181
-
182
- export function computeAutolinkModules(
183
- existing: string[],
184
- desired: AutolinkEntry[],
185
- ): {modules: string[]; added: string[]; removed: string[]} {
186
- let modules = [...existing];
187
- const added: string[] = [];
188
- const removed: string[] = [];
189
-
190
- for (const entry of desired) {
191
- const hasModule = modules.includes(entry.name);
192
- if (entry.enable && !hasModule) {
193
- modules = [...modules, entry.name];
194
- added.push(entry.name);
195
- } else if (!entry.enable && hasModule) {
196
- modules = modules.filter((module) => module !== entry.name);
197
- removed.push(entry.name);
198
- }
199
- }
200
-
201
- return {modules, added, removed};
202
- }
203
-
204
- const syncAutolinking = (state: AutolinkState) => {
205
- if (!fs.existsSync(AUTOLINKING_CONFIG_PATH)) {
206
- return;
207
- }
208
-
209
- try {
210
- const raw = fs.readFileSync(AUTOLINKING_CONFIG_PATH, 'utf8');
211
- const config = JSON.parse(raw);
212
- const iosConfig = config.ios ?? (config.ios = {});
213
- const existing: string[] = Array.isArray(iosConfig.modules)
214
- ? iosConfig.modules.filter((module: string) => module !== 'OneSideModule')
215
- : [];
216
-
217
- const desiredEntries: {
218
- name: string;
219
- enable: boolean;
220
- addLog: string;
221
- removeLog: string;
222
- }[] = [
223
- {
224
- name: 'ExpoIapModule',
225
- enable: state.expoIap,
226
- addLog: '🔗 expo-iap: Enabled ExpoIapModule autolinking',
227
- removeLog: '🧹 expo-iap: Disabled ExpoIapModule autolinking',
228
- },
229
- {
230
- name: 'OnsideIapModule',
231
- enable: state.onside,
232
- addLog: '🔗 expo-iap: Enabled OnsideIapModule autolinking',
233
- removeLog: '🧹 expo-iap: Disabled OnsideIapModule autolinking',
234
- },
235
- ];
236
-
237
- const {
238
- modules: nextModules,
239
- added,
240
- removed,
241
- } = computeAutolinkModules(
242
- existing,
243
- desiredEntries.map(({name, enable}) => ({name, enable})),
244
- );
245
-
246
- for (const name of added) {
247
- const entry = desiredEntries.find((candidate) => candidate.name === name);
248
- if (entry) {
249
- logOnce(entry.addLog);
250
- }
251
- }
252
-
253
- for (const name of removed) {
254
- const entry = desiredEntries.find((candidate) => candidate.name === name);
255
- if (entry) {
256
- logOnce(entry.removeLog);
257
- }
258
- }
259
-
260
- if (added.length > 0 || removed.length > 0) {
261
- iosConfig.modules = nextModules;
262
- fs.writeFileSync(
263
- AUTOLINKING_CONFIG_PATH,
264
- `${JSON.stringify(config, null, 2)}\n`,
265
- 'utf8',
266
- );
267
- }
268
- } catch (error) {
269
- WarningAggregator.addWarningIOS(
270
- 'expo-iap',
271
- `Failed to sync Expo IAP autolinking modules: ${String(error)}`,
272
- );
273
- }
274
- };
275
-
276
- const withIapIOS: ConfigPlugin<WithIapIosOptions | void> = (config, props) => {
141
+ const withIapIOS: ConfigPlugin = (config) => {
277
142
  return withDangerousMod(config, [
278
143
  'ios',
279
144
  async (config) => {
@@ -301,134 +166,33 @@ const withIapIOS: ConfigPlugin<WithIapIosOptions | void> = (config, props) => {
301
166
  logOnce('🧹 expo-iap: Removed local OpenIAP pod from Podfile');
302
167
  }
303
168
 
304
- // 3) Optionally install OnsideKit when enabled in config
305
- if (props?.enableOnside) {
306
- content = ensureOnsidePod(content);
307
- }
308
-
309
169
  fs.writeFileSync(podfilePath, content);
310
170
  return config;
311
171
  },
312
172
  ]);
313
173
  };
314
174
 
315
- export interface ModuleSelectionResult {
316
- selection: 'auto' | 'expo-iap' | 'onside';
317
- includeExpoIap: boolean;
318
- includeOnside: boolean;
319
- }
320
-
321
- type ModuleKey = 'expoIap' | 'onside';
322
-
323
- type ModuleRules = Record<
324
- ModuleKey,
325
- {
326
- when: Partial<Record<ModuleSelectionResult['selection'], boolean>>;
327
- default: (args: {
328
- config: ExpoConfig;
329
- options?: ExpoIapPluginCommonOptions;
330
- }) => boolean;
331
- }
332
- >;
333
-
334
- const MODULE_RULES: ModuleRules = {
335
- expoIap: {
336
- when: {
337
- 'expo-iap': true,
338
- onside: false,
339
- },
340
- default: ({options}) => options?.modules?.expoIap ?? true,
341
- },
342
- onside: {
343
- when: {
344
- 'expo-iap': false,
345
- onside: true,
346
- },
347
- default: ({config, options}) =>
348
- options?.modules?.onside ?? config.ios?.onside?.enabled ?? true,
349
- },
350
- };
351
-
352
- export function resolveModuleSelection(
353
- config: ExpoConfig,
354
- options?: ExpoIapPluginCommonOptions | void,
355
- ): ModuleSelectionResult {
356
- const normalizedOptions = (options ?? undefined) as
357
- | ExpoIapPluginCommonOptions
358
- | undefined;
359
-
360
- const selection = normalizedOptions?.module ?? 'auto';
361
-
362
- const includeExpoIap = pickModuleState(
363
- 'expoIap',
364
- selection,
365
- config,
366
- normalizedOptions,
367
- );
368
- const includeOnside = pickModuleState(
369
- 'onside',
370
- selection,
371
- config,
372
- normalizedOptions,
373
- );
374
-
375
- return {selection, includeExpoIap, includeOnside};
376
- }
377
-
378
- function pickModuleState(
379
- key: ModuleKey,
380
- selection: ModuleSelectionResult['selection'],
381
- config: ExpoConfig,
382
- options?: ExpoIapPluginCommonOptions,
383
- ): boolean {
384
- const rules = MODULE_RULES[key];
385
- const explicit = rules.when[selection];
386
- if (explicit !== undefined) {
387
- return explicit;
388
- }
389
- const override = options?.modules?.[key];
390
- if (override !== undefined) {
391
- return override;
392
- }
393
- return rules.default({config, options});
175
+ export interface ExpoIapPluginOptions {
176
+ /** Local development path for OpenIAP library */
177
+ localPath?:
178
+ | string
179
+ | {
180
+ ios?: string;
181
+ android?: string;
182
+ };
183
+ /** Enable local development mode */
184
+ enableLocalDev?: boolean;
394
185
  }
395
186
 
396
- const withIAP: ConfigPlugin<ExpoIapPluginCommonOptions | void> = (
187
+ const withIap: ConfigPlugin<ExpoIapPluginOptions | void> = (
397
188
  config,
398
189
  options,
399
190
  ) => {
400
191
  try {
401
- const {includeExpoIap, includeOnside} = resolveModuleSelection(
402
- config as ExpoConfig,
403
- options,
404
- );
405
-
406
- const autolinkState: AutolinkState = {
407
- expoIap: includeExpoIap,
408
- onside: includeOnside,
409
- };
410
-
411
- if (includeOnside) {
412
- config.ios = {
413
- ...config.ios,
414
- onside: {
415
- ...(config.ios?.onside ?? {}),
416
- enabled: true,
417
- },
418
- } as typeof config.ios;
419
- } else if (config.ios?.onside?.enabled) {
420
- config.ios.onside.enabled = false;
421
- }
422
-
423
192
  // Respect explicit flag; fall back to presence of localPath only when flag is unset
424
193
  const isLocalDev = options?.enableLocalDev ?? !!options?.localPath;
425
- const shouldConfigureAndroid = includeExpoIap;
426
- const shouldAddAndroidDeps = includeExpoIap && !isLocalDev;
427
-
428
- // Apply Android modifications (skip when Expo IAP disabled)
429
- let result = shouldConfigureAndroid
430
- ? withIapAndroid(config, {addDeps: shouldAddAndroidDeps})
431
- : config;
194
+ // Apply Android modifications (skip adding deps when linking local module)
195
+ let result = withIapAndroid(config, {addDeps: !isLocalDev});
432
196
 
433
197
  // iOS: choose one path to avoid overlap
434
198
  if (isLocalDev) {
@@ -458,14 +222,10 @@ const withIAP: ConfigPlugin<ExpoIapPluginCommonOptions | void> = (
458
222
  }
459
223
  } else {
460
224
  // Ensure iOS Podfile is set up to resolve public CocoaPods specs
461
- result = withIapIOS(result, {enableOnside: includeOnside});
462
- if (includeExpoIap) {
463
- logOnce('📦 [expo-iap] Using OpenIAP from CocoaPods');
464
- }
225
+ result = withIapIOS(result);
226
+ logOnce('📦 [expo-iap] Using OpenIAP from CocoaPods');
465
227
  }
466
228
 
467
- syncAutolinking(autolinkState);
468
-
469
229
  return result;
470
230
  } catch (error) {
471
231
  WarningAggregator.addWarningAndroid(
@@ -477,4 +237,4 @@ const withIAP: ConfigPlugin<ExpoIapPluginCommonOptions | void> = (
477
237
  }
478
238
  };
479
239
 
480
- export default createRunOncePlugin(withIAP, pkg.name, pkg.version);
240
+ export default createRunOncePlugin(withIap, pkg.name, pkg.version);
@@ -2,8 +2,7 @@
2
2
  "extends": "expo-module-scripts/tsconfig.plugin",
3
3
  "compilerOptions": {
4
4
  "outDir": "build",
5
- "rootDir": "src",
6
- "isolatedModules": true
5
+ "rootDir": "src"
7
6
  },
8
7
  "include": ["./src"],
9
8
  "exclude": ["**/__mocks__/*", "**/__tests__/*"]
@@ -1 +1 @@
1
- {"root":["./src/expoconfig.augmentation.d.ts","./src/withiap.ts","./src/withlocalopeniap.ts"],"version":"5.9.2"}
1
+ {"root":["./src/expoConfig.augmentation.d.ts","./src/withIAP.ts","./src/withLocalOpenIAP.ts"],"version":"5.9.2"}
@@ -1,51 +1,10 @@
1
- import {requireNativeModule, UnavailabilityError} from 'expo-modules-core';
1
+ import {requireNativeModule} from 'expo-modules-core';
2
2
 
3
- type NativeIapModuleName = 'ExpoIapOnside' | 'ExpoIap';
4
-
5
- const {module: ExpoIapModule, name: resolvedNativeModuleName} =
6
- resolveNativeModule();
7
-
8
- export const USING_ONSIDE_SDK = resolvedNativeModuleName === 'ExpoIapOnside';
3
+ // It loads the native module object from the JSI or falls back to
4
+ // the bridge module (from NativeModulesProxy) if the remote debugger is on.
5
+ const ExpoIapModule = requireNativeModule('ExpoIap');
9
6
 
10
7
  // Platform-specific error codes from native modules
11
8
  export const NATIVE_ERROR_CODES = ExpoIapModule.ERROR_CODES || {};
12
9
 
13
10
  export default ExpoIapModule;
14
-
15
- function resolveNativeModule(): {
16
- module: any;
17
- name: NativeIapModuleName;
18
- } {
19
- const candidates: NativeIapModuleName[] = ['ExpoIapOnside', 'ExpoIap'];
20
-
21
- for (const name of candidates) {
22
- try {
23
- const module = requireNativeModule(name);
24
- return {module, name};
25
- } catch (error) {
26
- if (name === 'ExpoIapOnside' && isMissingModuleError(error, name)) {
27
- // Onside module is optional. If unavailable, fall back to ExpoIap.
28
- continue;
29
- }
30
-
31
- throw error;
32
- }
33
- }
34
-
35
- throw new UnavailabilityError(
36
- 'expo-iap',
37
- 'ExpoIap native module is unavailable',
38
- );
39
- }
40
-
41
- function isMissingModuleError(error: unknown, moduleName: string): boolean {
42
- if (error instanceof UnavailabilityError) {
43
- return true;
44
- }
45
-
46
- if (error instanceof Error) {
47
- return error.message.includes(`Cannot find native module '${moduleName}'`);
48
- }
49
-
50
- return false;
51
- }
package/src/useIAP.ts CHANGED
@@ -49,8 +49,6 @@ import {
49
49
  type UseIap = {
50
50
  connected: boolean;
51
51
  products: Product[];
52
- promotedProductsIOS: Purchase[];
53
- promotedProductIdIOS?: string;
54
52
  subscriptions: ProductSubscription[];
55
53
  availablePurchases: Purchase[];
56
54
  promotedProductIOS?: Product;
@@ -96,12 +94,10 @@ export interface UseIAPOptions {
96
94
  export function useIAP(options?: UseIAPOptions): UseIap {
97
95
  const [connected, setConnected] = useState<boolean>(false);
98
96
  const [products, setProducts] = useState<Product[]>([]);
99
- const [promotedProductsIOS] = useState<Purchase[]>([]);
100
97
  const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);
101
98
 
102
99
  const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);
103
100
  const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
104
- const [promotedProductIdIOS] = useState<string>();
105
101
  const [activeSubscriptions, setActiveSubscriptions] = useState<
106
102
  ActiveSubscription[]
107
103
  >([]);
@@ -141,7 +137,6 @@ export function useIAP(options?: UseIAPOptions): UseIap {
141
137
  const subscriptionsRef = useRef<{
142
138
  purchaseUpdate?: EventSubscription;
143
139
  purchaseError?: EventSubscription;
144
- promotedProductsIOS?: EventSubscription;
145
140
  promotedProductIOS?: EventSubscription;
146
141
  }>({});
147
142
 
@@ -376,7 +371,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
376
371
 
377
372
  if (Platform.OS === 'ios') {
378
373
  // iOS promoted products listener
379
- subscriptionsRef.current.promotedProductsIOS = promotedProductListenerIOS(
374
+ subscriptionsRef.current.promotedProductIOS = promotedProductListenerIOS(
380
375
  (product: Product) => {
381
376
  setPromotedProductIOS(product);
382
377
 
@@ -394,9 +389,9 @@ export function useIAP(options?: UseIAPOptions): UseIap {
394
389
  // If connection failed, clean up listeners
395
390
  console.warn('[useIAP] Connection failed, cleaning up listeners...');
396
391
  subscriptionsRef.current.purchaseUpdate?.remove();
397
- subscriptionsRef.current.promotedProductsIOS?.remove();
392
+ subscriptionsRef.current.promotedProductIOS?.remove();
398
393
  subscriptionsRef.current.purchaseUpdate = undefined;
399
- subscriptionsRef.current.promotedProductsIOS = undefined;
394
+ subscriptionsRef.current.promotedProductIOS = undefined;
400
395
  // Keep purchaseError listener registered to capture subsequent retries
401
396
  return;
402
397
  }
@@ -409,7 +404,6 @@ export function useIAP(options?: UseIAPOptions): UseIap {
409
404
  return () => {
410
405
  currentSubscriptions.purchaseUpdate?.remove();
411
406
  currentSubscriptions.purchaseError?.remove();
412
- currentSubscriptions.promotedProductsIOS?.remove();
413
407
  currentSubscriptions.promotedProductIOS?.remove();
414
408
  endConnection();
415
409
  setConnected(false);
@@ -419,8 +413,6 @@ export function useIAP(options?: UseIAPOptions): UseIap {
419
413
  return {
420
414
  connected,
421
415
  products,
422
- promotedProductsIOS,
423
- promotedProductIdIOS,
424
416
  subscriptions,
425
417
  finishTransaction,
426
418
  availablePurchases,
@@ -1,6 +0,0 @@
1
- export declare const CONSUMABLE_PRODUCT_IDS: string[];
2
- export declare const NON_CONSUMABLE_PRODUCT_IDS: string[];
3
- export declare const PRODUCT_IDS: string[];
4
- export declare const SUBSCRIPTION_PRODUCT_IDS: string[];
5
- export declare const DEFAULT_SUBSCRIPTION_PRODUCT_ID: string;
6
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAG1C,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,MAAM,EAE9C,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,EAG/B,CAAC;AAGF,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAA+B,CAAC;AAG7E,eAAO,MAAM,+BAA+B,QAA8B,CAAC"}
@@ -1,19 +0,0 @@
1
- // Centralized product ID constants for examples and internal usage
2
- // Rename guide: subscriptionIds -> SUBSCRIPTION_PRODUCT_IDS, PRODUCT_IDS remains the same name
3
- // One-time purchase product IDs split by consumption behavior
4
- export const CONSUMABLE_PRODUCT_IDS = [
5
- 'dev.hyo.martie.10bulbs',
6
- 'dev.hyo.martie.30bulbs',
7
- ];
8
- export const NON_CONSUMABLE_PRODUCT_IDS = [
9
- 'dev.hyo.martie.certified',
10
- ];
11
- export const PRODUCT_IDS = [
12
- ...CONSUMABLE_PRODUCT_IDS,
13
- ...NON_CONSUMABLE_PRODUCT_IDS,
14
- ];
15
- // Subscription product IDs
16
- export const SUBSCRIPTION_PRODUCT_IDS = ['dev.hyo.martie.premium'];
17
- // Optionally export a single default subscription for convenience
18
- export const DEFAULT_SUBSCRIPTION_PRODUCT_ID = SUBSCRIPTION_PRODUCT_IDS[0];
19
- //# sourceMappingURL=constants.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,+FAA+F;AAE/F,8DAA8D;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAAa;IAC9C,wBAAwB;IACxB,wBAAwB;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAa;IAClD,0BAA0B;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAa;IACnC,GAAG,sBAAsB;IACzB,GAAG,0BAA0B;CAC9B,CAAC;AAEF,2BAA2B;AAC3B,MAAM,CAAC,MAAM,wBAAwB,GAAa,CAAC,wBAAwB,CAAC,CAAC;AAE7E,kEAAkE;AAClE,MAAM,CAAC,MAAM,+BAA+B,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAC","sourcesContent":["// Centralized product ID constants for examples and internal usage\n// Rename guide: subscriptionIds -> SUBSCRIPTION_PRODUCT_IDS, PRODUCT_IDS remains the same name\n\n// One-time purchase product IDs split by consumption behavior\nexport const CONSUMABLE_PRODUCT_IDS: string[] = [\n 'dev.hyo.martie.10bulbs',\n 'dev.hyo.martie.30bulbs',\n];\n\nexport const NON_CONSUMABLE_PRODUCT_IDS: string[] = [\n 'dev.hyo.martie.certified',\n];\n\nexport const PRODUCT_IDS: string[] = [\n ...CONSUMABLE_PRODUCT_IDS,\n ...NON_CONSUMABLE_PRODUCT_IDS,\n];\n\n// Subscription product IDs\nexport const SUBSCRIPTION_PRODUCT_IDS: string[] = ['dev.hyo.martie.premium'];\n\n// Optionally export a single default subscription for convenience\nexport const DEFAULT_SUBSCRIPTION_PRODUCT_ID = SUBSCRIPTION_PRODUCT_IDS[0];\n"]}