expo-iap 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,321 @@
1
+ # GitHub Copilot Instructions for expo-iap
2
+
3
+ ## Package Manager
4
+
5
+ **IMPORTANT: This project uses Bun exclusively. Do not suggest npm, yarn, or pnpm commands.**
6
+
7
+ - Install dependencies: `bun install`
8
+ - Run scripts: `bun run <script>`
9
+ - Run tests: `bun test`
10
+ - Add packages: `bun add <package>`
11
+ - Add dev dependencies: `bun add -d <package>`
12
+
13
+ **NEVER suggest creating package-lock.json or yarn.lock files. Only bun.lock should exist.**
14
+
15
+ ## Platform-Specific Function Naming
16
+
17
+ When suggesting code for expo-iap, follow these naming conventions:
18
+
19
+ ### Platform-Specific Functions
20
+
21
+ Functions that only work on one platform MUST have platform suffixes:
22
+
23
+ - iOS: `functionNameIos()`
24
+ - Android: `functionNameAndroid()`
25
+
26
+ ```typescript
27
+ // Correct examples:
28
+ export const getStorefrontIos = async (): Promise<string> => { ... }
29
+ export const consumeProductAndroid = async (token: string): Promise<void> => { ... }
30
+ export const getAppTransactionIos = async (): Promise<AppTransactionIOS | null> => { ... }
31
+ ```
32
+
33
+ ### Cross-Platform Functions
34
+
35
+ Functions that abstract platform differences don't need suffixes:
36
+
37
+ ```typescript
38
+ // Correct cross-platform example:
39
+ export const getProducts = async (skus: string[]): Promise<Product[]> => {
40
+ return Platform.select({
41
+ ios: async () => {
42
+ /* iOS implementation */
43
+ },
44
+ android: async () => {
45
+ /* Android implementation */
46
+ },
47
+ })();
48
+ };
49
+ ```
50
+
51
+ ## Project Overview
52
+
53
+ This is the **expo-iap** library - a modern React Native/Expo module for handling in-app purchases across iOS and Android platforms. The library provides a unified, TypeScript-first API with automatic type inference and platform abstraction.
54
+
55
+ ## Key Principles
56
+
57
+ ### 🎯 Modern TypeScript-First API
58
+
59
+ - **Automatic Type Inference**: No manual type casting required
60
+ - **Unified Platform API**: Single API that works across iOS and Android
61
+ - **Consistent Results**: Unified data structure regardless of platform
62
+ - **Clean Error Handling**: Result pattern with detailed error information
63
+
64
+ ### 🚀 Primary API Pattern
65
+
66
+ ```typescript
67
+ const result = await requestPurchase({
68
+ request: {sku: 'product.id'},
69
+ type: 'inapp',
70
+ });
71
+
72
+ if (result.success) {
73
+ console.log('Purchase successful:', result.transactionId);
74
+ } else {
75
+ console.error('Purchase failed:', result.error.message);
76
+ }
77
+ ```
78
+
79
+ ### 🏗️ Code Style Guidelines
80
+
81
+ #### TypeScript Best Practices
82
+
83
+ - **Explicit Type Definitions**: Always define types explicitly for public APIs
84
+ - **Function Overloads**: Provide multiple signatures for better IntelliSense
85
+ - **Discriminated Unions**: Use for platform-specific types and conditional returns
86
+ - **Strict Type Safety**: No `any` types in production code
87
+
88
+ #### Naming Conventions
89
+
90
+ - **PascalCase**: Interfaces, types, enums, classes
91
+ - **camelCase**: Functions, variables, methods, properties
92
+ - **kebab-case**: File names (except for React components)
93
+ - **Descriptive Names**: Avoid abbreviations, use clear intent-revealing names
94
+
95
+ #### Error Handling Pattern
96
+
97
+ ```typescript
98
+ type PurchaseResult<T = unknown> =
99
+ | {success: true; data: T; platform: 'ios' | 'android'}
100
+ | {success: false; error: PurchaseError; platform: 'ios' | 'android'};
101
+ ```
102
+
103
+ ## API Implementation Patterns
104
+
105
+ ### Core Purchase Functions
106
+
107
+ ```typescript
108
+ export function requestPurchase(params: {
109
+ readonly request: {sku: string; quantity?: number};
110
+ readonly type: 'inapp';
111
+ }): Promise<ProductPurchase | ProductPurchase[]>;
112
+
113
+ export function requestSubscription(params: {
114
+ readonly request: {sku: string};
115
+ }): Promise<SubscriptionPurchase | SubscriptionPurchase[]>;
116
+ ```
117
+
118
+ ### Platform Abstraction
119
+
120
+ The library automatically handles platform differences:
121
+
122
+ - **iOS**: Uses `sku` directly with StoreKit
123
+ - **Android**: Converts to Google Play Billing format
124
+ - **Unified Properties**: Both platforms return consistent data
125
+
126
+ ### Type Guards for Advanced Usage
127
+
128
+ ```typescript
129
+ export const isPurchaseResult = (
130
+ result: unknown,
131
+ ): result is ProductPurchase | SubscriptionPurchase => {
132
+ return (
133
+ result !== null &&
134
+ typeof result === 'object' &&
135
+ 'transactionId' in result &&
136
+ 'productId' in result
137
+ );
138
+ };
139
+ ```
140
+
141
+ ## File Organization
142
+
143
+ ```
144
+ src/
145
+ ├── index.ts # Main exports
146
+ ├── ExpoIap.types.ts # Core type definitions
147
+ ├── ExpoIapModule.ts # Native module interface
148
+ ├── useIap.ts # React hook (legacy support)
149
+ ├── modules/
150
+ │ ├── android.ts # Android-specific utilities
151
+ │ └── ios.ts # iOS-specific utilities
152
+ └── types/
153
+ ├── ExpoIapAndroid.types.ts # Android type definitions
154
+ └── ExpoIapIos.types.ts # iOS type definitions
155
+ ```
156
+
157
+ ## Usage Examples
158
+
159
+ ### Simple Product Purchase
160
+
161
+ ```typescript
162
+ import {requestPurchase} from 'expo-iap';
163
+
164
+ const handlePurchase = async (productId: string) => {
165
+ const result = await requestPurchase({
166
+ request: {sku: productId},
167
+ type: 'inapp',
168
+ });
169
+
170
+ if (result.success) {
171
+ console.log('Purchase successful:', result.data);
172
+ } else {
173
+ console.error('Purchase failed:', result.error.message);
174
+ }
175
+ };
176
+ ```
177
+
178
+ ### Subscription Purchase
179
+
180
+ ```typescript
181
+ import {requestSubscription} from 'expo-iap';
182
+
183
+ const handleSubscription = async (subscriptionId: string) => {
184
+ const result = await requestSubscription({
185
+ request: {sku: subscriptionId},
186
+ });
187
+
188
+ if (result.success) {
189
+ console.log('Subscription activated:', result.data);
190
+ } else {
191
+ console.error('Subscription failed:', result.error.message);
192
+ }
193
+ };
194
+ ```
195
+
196
+ ## Type Naming
197
+
198
+ - Platform-specific types: `ProductIos`, `ProductAndroid`, `PurchaseErrorIos`
199
+ - Cross-platform types: `Product`, `Purchase`, `PurchaseError`
200
+
201
+ ## Code Suggestions
202
+
203
+ When generating code:
204
+
205
+ 1. Check if the function is platform-specific
206
+ 2. Add appropriate suffix if it only works on one platform
207
+ 3. Use Platform.select() for cross-platform implementations
208
+ 4. Always use TypeScript
209
+ 5. Include proper error handling
210
+ 6. Add JSDoc comments for public APIs
211
+
212
+ ## Testing
213
+
214
+ Suggest tests that:
215
+
216
+ - Cover both iOS and Android paths
217
+ - Use `bun test` commands
218
+ - Include platform mocking when needed
219
+ - Test error scenarios
220
+
221
+ ## Common Patterns
222
+
223
+ ### Platform Selection
224
+
225
+ ```typescript
226
+ Platform.select({
227
+ ios: () => {
228
+ /* iOS code */
229
+ },
230
+ android: () => {
231
+ /* Android code */
232
+ },
233
+ default: () => {
234
+ /* Fallback */
235
+ },
236
+ });
237
+ ```
238
+
239
+ ### Receipt Validation (Platform-specific parameters)
240
+
241
+ ```typescript
242
+ // iOS only needs SKU
243
+ await validateReceiptIos(sku);
244
+
245
+ // Android needs additional parameters
246
+ await validateReceiptAndroid({
247
+ packageName,
248
+ productToken,
249
+ accessToken,
250
+ });
251
+ ```
252
+
253
+ ## Documentation Standards
254
+
255
+ ### Code Comments
256
+
257
+ - **Document the "why"**, not just the "what"
258
+ - **Explain platform differences** and how they're handled
259
+ - **Provide usage examples** for complex APIs
260
+ - **Use JSDoc format** for all public APIs
261
+
262
+ ### Example Documentation Pattern
263
+
264
+ ````typescript
265
+ /**
266
+ * Enhanced requestPurchase with unified API support
267
+ *
268
+ * This function automatically handles platform differences:
269
+ * - iOS: Uses `sku` directly with StoreKit
270
+ * - Android: Converts to Google Play Billing format
271
+ *
272
+ * @param params - Purchase request parameters
273
+ * @param params.request - Request object with product details
274
+ * @param params.type - Purchase type: 'inapp' for products
275
+ *
276
+ * @returns Promise resolving to purchase result
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * const result = await requestPurchase({
281
+ * request: { sku: 'com.example.premium' },
282
+ * type: 'inapp'
283
+ * });
284
+ * ```
285
+ */
286
+ ````
287
+
288
+ ## Development Philosophy
289
+
290
+ The expo-iap library provides a **world-class developer experience**:
291
+
292
+ ### ✅ Core Achievements
293
+
294
+ - **🎯 Zero Manual Casting**: Automatic type inference
295
+ - **🌍 Unified API**: Single codebase for iOS and Android
296
+ - **⚡ Enhanced DX**: Better IntelliSense and error messages
297
+ - **🛡️ Full Backward Compatibility**: Existing code continues to work
298
+ - **📚 Modern Patterns**: TypeScript-first, result pattern error handling
299
+
300
+ ### 🎯 When Contributing, Always Consider:
301
+
302
+ 1. **Developer Experience First** - Eliminate manual work and reduce cognitive load
303
+ 2. **Platform Differences** - Handle iOS/Android disparities transparently
304
+ 3. **Type Safety** - Provide automatic inference with compile-time guarantees
305
+ 4. **Documentation Excellence** - Explain the "why" behind solutions
306
+ 5. **Backward Compatibility** - Never break existing code
307
+ 6. **Performance** - Optimize for common use cases
308
+ 7. **Testing** - Comprehensive coverage across platforms and scenarios
309
+
310
+ ### 🚀 Ultimate Goal
311
+
312
+ Transform in-app purchases from a complex, error-prone process into something as simple as:
313
+
314
+ ```typescript
315
+ const result = await requestPurchase({
316
+ request: {sku: 'premium.product'},
317
+ type: 'inapp',
318
+ });
319
+ ```
320
+
321
+ While maintaining full power and flexibility for advanced enterprise use cases.
package/.cursorrules ADDED
@@ -0,0 +1,303 @@
1
+ # GitHub Copilot and Cursor Rules for expo-iap
2
+
3
+ ## Package Manager
4
+ **IMPORTANT: This project uses Bun exclusively. Do not suggest npm, yarn, or pnpm commands.**
5
+
6
+ - Install dependencies: `bun install`
7
+ - Run scripts: `bun run <script>`
8
+ - Run tests: `bun test`
9
+ - Add packages: `bun add <package>`
10
+ - Add dev dependencies: `bun add -d <package>`
11
+
12
+ **NEVER suggest creating package-lock.json or yarn.lock files. Only bun.lock should exist.**
13
+
14
+ ## Platform-Specific Function Naming
15
+
16
+ When suggesting code for expo-iap, follow these naming conventions:
17
+
18
+ ### Platform-Specific Functions
19
+ Functions that only work on one platform MUST have platform suffixes:
20
+ - iOS: `functionNameIos()`
21
+ - Android: `functionNameAndroid()`
22
+
23
+ ```typescript
24
+ // Correct examples:
25
+ export const getStorefrontIos = async (): Promise<string> => { ... }
26
+ export const consumeProductAndroid = async (token: string): Promise<void> => { ... }
27
+ export const getAppTransactionIos = async (): Promise<AppTransactionIOS | null> => { ... }
28
+ ```
29
+
30
+ ### Cross-Platform Functions
31
+ Functions that abstract platform differences don't need suffixes:
32
+
33
+ ```typescript
34
+ // Correct cross-platform example:
35
+ export const getProducts = async (skus: string[]): Promise<Product[]> => {
36
+ return Platform.select({
37
+ ios: async () => { /* iOS implementation */ },
38
+ android: async () => { /* Android implementation */ },
39
+ })();
40
+ }
41
+ ```
42
+
43
+ ## Project Overview
44
+
45
+ This is the **expo-iap** library - a modern React Native/Expo module for handling in-app purchases across iOS and Android platforms. The library provides a unified, TypeScript-first API with automatic type inference and platform abstraction.
46
+
47
+ ## Key Principles
48
+
49
+ ### 🎯 Modern TypeScript-First API
50
+
51
+ - **Automatic Type Inference**: No manual type casting required
52
+ - **Unified Platform API**: Single API that works across iOS and Android
53
+ - **Consistent Results**: Unified data structure regardless of platform
54
+ - **Clean Error Handling**: Result pattern with detailed error information
55
+
56
+ ### 🚀 Primary API Pattern
57
+
58
+ ```typescript
59
+ const result = await requestPurchase({
60
+ request: {sku: 'product.id'},
61
+ type: 'inapp',
62
+ });
63
+
64
+ if (result.success) {
65
+ console.log('Purchase successful:', result.transactionId);
66
+ } else {
67
+ console.error('Purchase failed:', result.error.message);
68
+ }
69
+ ```
70
+
71
+ ### 🏗️ Code Style Guidelines
72
+
73
+ #### TypeScript Best Practices
74
+
75
+ - **Explicit Type Definitions**: Always define types explicitly for public APIs
76
+ - **Function Overloads**: Provide multiple signatures for better IntelliSense
77
+ - **Discriminated Unions**: Use for platform-specific types and conditional returns
78
+ - **Strict Type Safety**: No `any` types in production code
79
+
80
+ #### Naming Conventions
81
+
82
+ - **PascalCase**: Interfaces, types, enums, classes
83
+ - **camelCase**: Functions, variables, methods, properties
84
+ - **kebab-case**: File names (except for React components)
85
+ - **Descriptive Names**: Avoid abbreviations, use clear intent-revealing names
86
+
87
+ #### Error Handling Pattern
88
+
89
+ ```typescript
90
+ type PurchaseResult<T = unknown> =
91
+ | {success: true; data: T; platform: 'ios' | 'android'}
92
+ | {success: false; error: PurchaseError; platform: 'ios' | 'android'};
93
+ ```
94
+
95
+ ## API Implementation Patterns
96
+
97
+ ### Core Purchase Functions
98
+
99
+ ```typescript
100
+ export function requestPurchase(params: {
101
+ readonly request: {sku: string; quantity?: number};
102
+ readonly type: 'inapp';
103
+ }): Promise<ProductPurchase | ProductPurchase[]>;
104
+
105
+ export function requestSubscription(params: {
106
+ readonly request: {sku: string};
107
+ }): Promise<SubscriptionPurchase | SubscriptionPurchase[]>;
108
+ ```
109
+
110
+ ### Platform Abstraction
111
+
112
+ The library automatically handles platform differences:
113
+
114
+ - **iOS**: Uses `sku` directly with StoreKit
115
+ - **Android**: Converts to Google Play Billing format
116
+ - **Unified Properties**: Both platforms return consistent data
117
+
118
+ ### Type Guards for Advanced Usage
119
+
120
+ ```typescript
121
+ export const isPurchaseResult = (
122
+ result: unknown,
123
+ ): result is ProductPurchase | SubscriptionPurchase => {
124
+ return (
125
+ result !== null &&
126
+ typeof result === 'object' &&
127
+ 'transactionId' in result &&
128
+ 'productId' in result
129
+ );
130
+ };
131
+ ```
132
+
133
+ ## File Organization
134
+
135
+ ```
136
+ src/
137
+ ├── index.ts # Main exports
138
+ ├── ExpoIap.types.ts # Core type definitions
139
+ ├── ExpoIapModule.ts # Native module interface
140
+ ├── useIap.ts # React hook (legacy support)
141
+ ├── modules/
142
+ │ ├── android.ts # Android-specific utilities
143
+ │ └── ios.ts # iOS-specific utilities
144
+ └── types/
145
+ ├── ExpoIapAndroid.types.ts # Android type definitions
146
+ └── ExpoIapIos.types.ts # iOS type definitions
147
+ ```
148
+
149
+ ## Usage Examples
150
+
151
+ ### Simple Product Purchase
152
+
153
+ ```typescript
154
+ import {requestPurchase} from 'expo-iap';
155
+
156
+ const handlePurchase = async (productId: string) => {
157
+ const result = await requestPurchase({
158
+ request: {sku: productId},
159
+ type: 'inapp',
160
+ });
161
+
162
+ if (result.success) {
163
+ console.log('Purchase successful:', result.data);
164
+ } else {
165
+ console.error('Purchase failed:', result.error.message);
166
+ }
167
+ };
168
+ ```
169
+
170
+ ### Subscription Purchase
171
+
172
+ ```typescript
173
+ import {requestSubscription} from 'expo-iap';
174
+
175
+ const handleSubscription = async (subscriptionId: string) => {
176
+ const result = await requestSubscription({
177
+ request: {sku: subscriptionId},
178
+ });
179
+
180
+ if (result.success) {
181
+ console.log('Subscription activated:', result.data);
182
+ } else {
183
+ console.error('Subscription failed:', result.error.message);
184
+ }
185
+ };
186
+ ```
187
+
188
+ ## Type Naming
189
+
190
+ - Platform-specific types: `ProductIos`, `ProductAndroid`, `PurchaseErrorIos`
191
+ - Cross-platform types: `Product`, `Purchase`, `PurchaseError`
192
+
193
+ ## Code Suggestions
194
+
195
+ When generating code:
196
+ 1. Check if the function is platform-specific
197
+ 2. Add appropriate suffix if it only works on one platform
198
+ 3. Use Platform.select() for cross-platform implementations
199
+ 4. Always use TypeScript
200
+ 5. Include proper error handling
201
+ 6. Add JSDoc comments for public APIs
202
+
203
+ ## Testing
204
+ Suggest tests that:
205
+ - Cover both iOS and Android paths
206
+ - Use `bun test` commands
207
+ - Include platform mocking when needed
208
+ - Test error scenarios
209
+
210
+ ## Common Patterns
211
+
212
+ ### Platform Selection
213
+ ```typescript
214
+ Platform.select({
215
+ ios: () => { /* iOS code */ },
216
+ android: () => { /* Android code */ },
217
+ default: () => { /* Fallback */ },
218
+ })
219
+ ```
220
+
221
+ ### Receipt Validation (Platform-specific parameters)
222
+
223
+ ```typescript
224
+ // iOS only needs SKU
225
+ await validateReceiptIos(sku);
226
+
227
+ // Android needs additional parameters
228
+ await validateReceiptAndroid({
229
+ packageName,
230
+ productToken,
231
+ accessToken,
232
+ });
233
+ ```
234
+
235
+ ## Documentation Standards
236
+
237
+ ### Code Comments
238
+
239
+ - **Document the "why"**, not just the "what"
240
+ - **Explain platform differences** and how they're handled
241
+ - **Provide usage examples** for complex APIs
242
+ - **Use JSDoc format** for all public APIs
243
+
244
+ ### Example Documentation Pattern
245
+
246
+ ````typescript
247
+ /**
248
+ * Enhanced requestPurchase with unified API support
249
+ *
250
+ * This function automatically handles platform differences:
251
+ * - iOS: Uses `sku` directly with StoreKit
252
+ * - Android: Converts to Google Play Billing format
253
+ *
254
+ * @param params - Purchase request parameters
255
+ * @param params.request - Request object with product details
256
+ * @param params.type - Purchase type: 'inapp' for products
257
+ *
258
+ * @returns Promise resolving to purchase result
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const result = await requestPurchase({
263
+ * request: { sku: 'com.example.premium' },
264
+ * type: 'inapp'
265
+ * });
266
+ * ```
267
+ */
268
+ ````
269
+
270
+ ## Development Philosophy
271
+
272
+ The expo-iap library provides a **world-class developer experience**:
273
+
274
+ ### ✅ Core Achievements
275
+
276
+ - **🎯 Zero Manual Casting**: Automatic type inference
277
+ - **🌍 Unified API**: Single codebase for iOS and Android
278
+ - **⚡ Enhanced DX**: Better IntelliSense and error messages
279
+ - **🛡️ Full Backward Compatibility**: Existing code continues to work
280
+ - **📚 Modern Patterns**: TypeScript-first, result pattern error handling
281
+
282
+ ### 🎯 When Contributing, Always Consider:
283
+
284
+ 1. **Developer Experience First** - Eliminate manual work and reduce cognitive load
285
+ 2. **Platform Differences** - Handle iOS/Android disparities transparently
286
+ 3. **Type Safety** - Provide automatic inference with compile-time guarantees
287
+ 4. **Documentation Excellence** - Explain the "why" behind solutions
288
+ 5. **Backward Compatibility** - Never break existing code
289
+ 6. **Performance** - Optimize for common use cases
290
+ 7. **Testing** - Comprehensive coverage across platforms and scenarios
291
+
292
+ ### 🚀 Ultimate Goal
293
+
294
+ Transform in-app purchases from a complex, error-prone process into something as simple as:
295
+
296
+ ```typescript
297
+ const result = await requestPurchase({
298
+ request: {sku: 'premium.product'},
299
+ type: 'inapp',
300
+ });
301
+ ```
302
+
303
+ While maintaining full power and flexibility for advanced enterprise use cases.