expo-iap 2.9.0-rc.4 → 2.9.0
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/build/helpers/subscription.d.ts.map +1 -1
- package/build/helpers/subscription.js +2 -3
- package/build/helpers/subscription.js.map +1 -1
- package/ios/ExpoIapModule.swift +16 -9
- package/package.json +2 -3
- package/plugin/build/withIAP.js +8 -4
- package/plugin/build/withLocalOpenIAP.js +5 -1
- package/plugin/src/withIAP.ts +12 -7
- package/plugin/src/withLocalOpenIAP.ts +5 -1
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/helpers/subscription.ts +2 -3
- package/ios/expoiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/expoiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/helpers/subscription.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,kBAAkB,MAAM,EAAE,KACzB,OAAO,CAAC,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/helpers/subscription.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,kBAAkB,MAAM,EAAE,KACzB,OAAO,CAAC,kBAAkB,EAAE,CAkG9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,kBAAkB,MAAM,EAAE,KACzB,OAAO,CAAC,OAAO,CAGjB,CAAC"}
|
|
@@ -19,9 +19,8 @@ export const getActiveSubscriptions = async (subscriptionIds) => {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
// Check if this purchase has subscription-specific fields
|
|
22
|
-
const hasSubscriptionFields = ('expirationDateIOS' in purchase && purchase.expirationDateIOS) ||
|
|
23
|
-
'autoRenewingAndroid' in purchase
|
|
24
|
-
('environmentIOS' in purchase && purchase.environmentIOS === 'Sandbox');
|
|
22
|
+
const hasSubscriptionFields = ('expirationDateIOS' in purchase && !!purchase.expirationDateIOS) ||
|
|
23
|
+
'autoRenewingAndroid' in purchase;
|
|
25
24
|
if (!hasSubscriptionFields) {
|
|
26
25
|
return false;
|
|
27
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/helpers/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AACtC,OAAO,EAAC,qBAAqB,EAAC,MAAM,UAAU,CAAC;AAe/C;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EACzC,eAA0B,EACK,EAAE;IACjC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAChD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,MAAM,mBAAmB,GAAyB,EAAE,CAAC;QAErD,gDAAgD;QAChD,MAAM,iBAAiB,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE;YACtD,2CAA2C;YAC3C,IAAI,eAAe,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;oBAClD,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC;YAED,0DAA0D;YAC1D,MAAM,qBAAqB,GACzB,CAAC,mBAAmB,IAAI,QAAQ,IAAI,QAAQ,CAAC,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/helpers/subscription.ts"],"names":[],"mappings":"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","sourcesContent":["import {Platform} from 'react-native';\nimport {getAvailablePurchases} from '../index';\n\nexport interface ActiveSubscription {\n productId: string;\n isActive: boolean;\n transactionId: string; // Transaction identifier for backend validation\n purchaseToken?: string; // JWT token (iOS) or purchase token (Android) for backend validation\n transactionDate: number; // Transaction timestamp\n expirationDateIOS?: Date;\n autoRenewingAndroid?: boolean;\n environmentIOS?: string;\n willExpireSoon?: boolean;\n daysUntilExpirationIOS?: number;\n}\n\n/**\n * Get all active subscriptions with detailed information\n * @param subscriptionIds - Optional array of subscription product IDs to filter. If not provided, returns all active subscriptions.\n * @returns Promise<ActiveSubscription[]> array of active subscriptions with details\n */\nexport const getActiveSubscriptions = async (\n subscriptionIds?: string[],\n): Promise<ActiveSubscription[]> => {\n try {\n const purchases = await getAvailablePurchases();\n const currentTime = Date.now();\n const activeSubscriptions: ActiveSubscription[] = [];\n\n // Filter purchases to find active subscriptions\n const filteredPurchases = purchases.filter((purchase) => {\n // If specific IDs provided, filter by them\n if (subscriptionIds && subscriptionIds.length > 0) {\n if (!subscriptionIds.includes(purchase.productId)) {\n return false;\n }\n }\n\n // Check if this purchase has subscription-specific fields\n const hasSubscriptionFields =\n ('expirationDateIOS' in purchase && !!purchase.expirationDateIOS) ||\n 'autoRenewingAndroid' in purchase;\n\n if (!hasSubscriptionFields) {\n return false;\n }\n\n // Check if it's actually active\n if (Platform.OS === 'ios') {\n if ('expirationDateIOS' in purchase && purchase.expirationDateIOS) {\n return purchase.expirationDateIOS > currentTime;\n }\n // For iOS purchases without expiration date (like Sandbox), we consider them active\n // if they have the environmentIOS field and were created recently\n if ('environmentIOS' in purchase && purchase.environmentIOS) {\n const dayInMs = 24 * 60 * 60 * 1000;\n // If no expiration date, consider active if transaction is recent (within 24 hours for Sandbox)\n if (\n !('expirationDateIOS' in purchase) ||\n !purchase.expirationDateIOS\n ) {\n if (\n purchase.environmentIOS === 'Sandbox' &&\n purchase.transactionDate &&\n currentTime - purchase.transactionDate < dayInMs\n ) {\n return true;\n }\n }\n }\n } else if (Platform.OS === 'android') {\n // For Android, if it's in the purchases list, it's active\n return true;\n }\n\n return false;\n });\n\n // Convert to ActiveSubscription format\n for (const purchase of filteredPurchases) {\n const subscription: ActiveSubscription = {\n productId: purchase.productId,\n isActive: true,\n transactionId: purchase.transactionId || purchase.id,\n purchaseToken: purchase.purchaseToken,\n transactionDate: purchase.transactionDate,\n };\n\n // Add platform-specific details\n if (Platform.OS === 'ios') {\n if ('expirationDateIOS' in purchase && purchase.expirationDateIOS) {\n const expirationDate = new Date(purchase.expirationDateIOS);\n subscription.expirationDateIOS = expirationDate;\n\n // Calculate days until expiration (round to nearest day)\n const daysUntilExpiration = Math.round(\n (purchase.expirationDateIOS - currentTime) / (1000 * 60 * 60 * 24),\n );\n subscription.daysUntilExpirationIOS = daysUntilExpiration;\n subscription.willExpireSoon = daysUntilExpiration <= 7;\n }\n\n if ('environmentIOS' in purchase) {\n subscription.environmentIOS = purchase.environmentIOS;\n }\n } else if (Platform.OS === 'android') {\n if ('autoRenewingAndroid' in purchase) {\n subscription.autoRenewingAndroid = purchase.autoRenewingAndroid;\n // If auto-renewing is false, subscription will expire soon\n subscription.willExpireSoon = !purchase.autoRenewingAndroid;\n }\n }\n\n activeSubscriptions.push(subscription);\n }\n\n return activeSubscriptions;\n } catch (error) {\n console.error('Error getting active subscriptions:', error);\n return [];\n }\n};\n\n/**\n * Check if user has any active subscriptions\n * @param subscriptionIds - Optional array of subscription product IDs to check. If not provided, checks all subscriptions.\n * @returns Promise<boolean> true if user has at least one active subscription\n */\nexport const hasActiveSubscriptions = async (\n subscriptionIds?: string[],\n): Promise<boolean> => {\n const subscriptions = await getActiveSubscriptions(subscriptionIds);\n return subscriptions.length > 0;\n};\n"]}
|
package/ios/ExpoIapModule.swift
CHANGED
|
@@ -26,6 +26,12 @@ public class ExpoIapModule: Module {
|
|
|
26
26
|
private var purchaseUpdatedSub: Subscription?
|
|
27
27
|
private var purchaseErrorSub: Subscription?
|
|
28
28
|
private var promotedProductSub: Subscription?
|
|
29
|
+
|
|
30
|
+
// Helper to safely remove a listener and nil out the reference
|
|
31
|
+
private func removeListener(_ sub: inout Subscription?) {
|
|
32
|
+
if let s = sub { OpenIapModule.shared.removeListener(s) }
|
|
33
|
+
sub = nil
|
|
34
|
+
}
|
|
29
35
|
|
|
30
36
|
nonisolated public func definition() -> ModuleDefinition {
|
|
31
37
|
Name("ExpoIap")
|
|
@@ -266,6 +272,8 @@ public class ExpoIapModule: Module {
|
|
|
266
272
|
"isValid": result.isValid,
|
|
267
273
|
"receiptData": result.receiptData,
|
|
268
274
|
"jwsRepresentation": result.jwsRepresentation,
|
|
275
|
+
// Populate unified purchaseToken for iOS as alias of JWS
|
|
276
|
+
"purchaseToken": result.jwsRepresentation,
|
|
269
277
|
"latestTransaction": result.latestTransaction.map { OpenIapSerialization.purchase($0) },
|
|
270
278
|
]
|
|
271
279
|
}
|
|
@@ -407,10 +415,12 @@ public class ExpoIapModule: Module {
|
|
|
407
415
|
Task { @MainActor in
|
|
408
416
|
guard let self else { return }
|
|
409
417
|
logDebug("❌ Purchase error callback - sending error event")
|
|
418
|
+
// Use OpenIapPurchaseError alias for clarity/parity
|
|
419
|
+
let err: OpenIapPurchaseError = error
|
|
410
420
|
let errorData: [String: Any?] = [
|
|
411
|
-
"code":
|
|
412
|
-
"message":
|
|
413
|
-
"productId":
|
|
421
|
+
"code": err.code,
|
|
422
|
+
"message": err.message,
|
|
423
|
+
"productId": err.productId,
|
|
414
424
|
]
|
|
415
425
|
self.sendEvent(OpenIapEvent.PurchaseError, errorData)
|
|
416
426
|
}
|
|
@@ -428,12 +438,9 @@ public class ExpoIapModule: Module {
|
|
|
428
438
|
@MainActor
|
|
429
439
|
private func cleanupStore() async {
|
|
430
440
|
logDebug("Cleaning up listeners and ending connection")
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
purchaseUpdatedSub = nil
|
|
435
|
-
purchaseErrorSub = nil
|
|
436
|
-
promotedProductSub = nil
|
|
441
|
+
removeListener(&purchaseUpdatedSub)
|
|
442
|
+
removeListener(&purchaseErrorSub)
|
|
443
|
+
removeListener(&promotedProductSub)
|
|
437
444
|
_ = try? await OpenIapModule.shared.endConnection()
|
|
438
445
|
}
|
|
439
446
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-iap",
|
|
3
|
-
"version": "2.9.0
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "In App Purchase module in Expo",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -61,6 +61,5 @@
|
|
|
61
61
|
},
|
|
62
62
|
"expo": {
|
|
63
63
|
"plugin": "./app.plugin.js"
|
|
64
|
-
}
|
|
65
|
-
"packageManager": "yarn@3.6.1+sha512.de524adec81a6c3d7a26d936d439d2832e351cdfc5728f9d91f3fc85dd20b04391c038e9b4ecab11cae2b0dd9f0d55fd355af766bc5c1a7f8d25d96bb2a0b2ca"
|
|
64
|
+
}
|
|
66
65
|
}
|
package/plugin/build/withIAP.js
CHANGED
|
@@ -141,10 +141,14 @@ const withIap = (config, options) => {
|
|
|
141
141
|
let result = withIapAndroid(config);
|
|
142
142
|
// iOS: choose one path to avoid overlap
|
|
143
143
|
if (options?.enableLocalDev || options?.localPath) {
|
|
144
|
-
|
|
145
|
-
'
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
if (!options?.localPath) {
|
|
145
|
+
config_plugins_1.WarningAggregator.addWarningIOS('expo-iap', 'enableLocalDev is true but no localPath provided. Skipping local OpenIAP integration.');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const localPath = path.resolve(options.localPath);
|
|
149
|
+
logOnce(`🔧 [expo-iap] Enabling local OpenIAP development at: ${localPath}`);
|
|
150
|
+
result = (0, withLocalOpenIAP_1.default)(result, { localPath });
|
|
151
|
+
}
|
|
148
152
|
}
|
|
149
153
|
else {
|
|
150
154
|
// Ensure iOS Podfile is set up to resolve public CocoaPods specs
|
|
@@ -48,7 +48,7 @@ const withLocalOpenIAP = (config, props) => {
|
|
|
48
48
|
const podfilePath = path.join(platformProjectRoot, 'Podfile');
|
|
49
49
|
// Default local path or use provided one
|
|
50
50
|
const localOpenIapPath = props?.localPath ||
|
|
51
|
-
'
|
|
51
|
+
path.resolve(config.modRequest.projectRoot, 'openiap-apple');
|
|
52
52
|
// Check if local path exists
|
|
53
53
|
if (!fs.existsSync(localOpenIapPath)) {
|
|
54
54
|
console.warn(`⚠️ Local openiap-apple path not found: ${localOpenIapPath}`);
|
|
@@ -56,6 +56,10 @@ const withLocalOpenIAP = (config, props) => {
|
|
|
56
56
|
return config;
|
|
57
57
|
}
|
|
58
58
|
// Read Podfile
|
|
59
|
+
if (!fs.existsSync(podfilePath)) {
|
|
60
|
+
console.warn(`⚠️ Podfile not found at ${podfilePath}. Skipping.`);
|
|
61
|
+
return config;
|
|
62
|
+
}
|
|
59
63
|
let podfileContent = fs.readFileSync(podfilePath, 'utf8');
|
|
60
64
|
// Check if already has the local pod reference
|
|
61
65
|
if (podfileContent.includes("pod 'openiap',")) {
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -155,13 +155,18 @@ const withIap: ConfigPlugin<ExpoIapPluginOptions | void> = (
|
|
|
155
155
|
|
|
156
156
|
// iOS: choose one path to avoid overlap
|
|
157
157
|
if (options?.enableLocalDev || options?.localPath) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
if (!options?.localPath) {
|
|
159
|
+
WarningAggregator.addWarningIOS(
|
|
160
|
+
'expo-iap',
|
|
161
|
+
'enableLocalDev is true but no localPath provided. Skipping local OpenIAP integration.',
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
const localPath = path.resolve(options.localPath);
|
|
165
|
+
logOnce(
|
|
166
|
+
`🔧 [expo-iap] Enabling local OpenIAP development at: ${localPath}`,
|
|
167
|
+
);
|
|
168
|
+
result = withLocalOpenIAP(result, {localPath});
|
|
169
|
+
}
|
|
165
170
|
} else {
|
|
166
171
|
// Ensure iOS Podfile is set up to resolve public CocoaPods specs
|
|
167
172
|
result = withIapIOS(result);
|
|
@@ -19,7 +19,7 @@ const withLocalOpenIAP: ConfigPlugin<{localPath?: string} | void> = (
|
|
|
19
19
|
// Default local path or use provided one
|
|
20
20
|
const localOpenIapPath =
|
|
21
21
|
props?.localPath ||
|
|
22
|
-
'
|
|
22
|
+
path.resolve(config.modRequest.projectRoot, 'openiap-apple');
|
|
23
23
|
|
|
24
24
|
// Check if local path exists
|
|
25
25
|
if (!fs.existsSync(localOpenIapPath)) {
|
|
@@ -33,6 +33,10 @@ const withLocalOpenIAP: ConfigPlugin<{localPath?: string} | void> = (
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Read Podfile
|
|
36
|
+
if (!fs.existsSync(podfilePath)) {
|
|
37
|
+
console.warn(`⚠️ Podfile not found at ${podfilePath}. Skipping.`);
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
36
40
|
let podfileContent = fs.readFileSync(podfilePath, 'utf8');
|
|
37
41
|
|
|
38
42
|
// Check if already has the local pod reference
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/
|
|
1
|
+
{"root":["./src/withIAP.ts","./src/withLocalOpenIAP.ts"],"version":"5.9.2"}
|
|
@@ -38,9 +38,8 @@ export const getActiveSubscriptions = async (
|
|
|
38
38
|
|
|
39
39
|
// Check if this purchase has subscription-specific fields
|
|
40
40
|
const hasSubscriptionFields =
|
|
41
|
-
('expirationDateIOS' in purchase && purchase.expirationDateIOS) ||
|
|
42
|
-
'autoRenewingAndroid' in purchase
|
|
43
|
-
('environmentIOS' in purchase && purchase.environmentIOS === 'Sandbox');
|
|
41
|
+
('expirationDateIOS' in purchase && !!purchase.expirationDateIOS) ||
|
|
42
|
+
'autoRenewingAndroid' in purchase;
|
|
44
43
|
|
|
45
44
|
if (!hasSubscriptionFields) {
|
|
46
45
|
return false;
|