@revenuecat/purchases-capacitor 6.0.0-beta.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,352 @@
1
+ // swiftlint:disable file_length type_body_length
2
+
3
+ import Foundation
4
+ import Capacitor
5
+ import PurchasesHybridCommon
6
+ import RevenueCat
7
+
8
+ /**
9
+ * Please read the Capacitor iOS Plugin Development Guide
10
+ * here: https://capacitorjs.com/docs/plugins/ios
11
+ */
12
+ @objc(PurchasesPlugin)
13
+ public class PurchasesPlugin: CAPPlugin, PurchasesDelegate {
14
+ private let platformFlavor = "capacitor"
15
+ private let platformVersion = "6.0.0-beta.1"
16
+
17
+ private let customerInfoKey = "customerInfo"
18
+
19
+ private enum RefundRequestStatus: Int {
20
+ case success = 0
21
+ case userCancelled
22
+ case error
23
+ }
24
+
25
+ // WIP: Need to handle concurrency for these...
26
+ private var customerInfoUpdateListeners: [String] = []
27
+ private var lastReceivedCustomerInfo: CustomerInfo?
28
+
29
+ @objc func configure(_ call: CAPPluginCall) {
30
+ guard let apiKey = call.getOrRejectString("apiKey") else { return }
31
+ let appUserID = call.getString("appUserID")
32
+ let observerMode = call.getBool("observerMode") ?? false
33
+ let userDefaultsSuiteName = call.getString("userDefaultsSuiteName")
34
+ let usesStoreKit2IfAvailable = call.getBool("usesStoreKit2IfAvailable") ?? false
35
+ let purchases = Purchases.configure(apiKey: apiKey,
36
+ appUserID: appUserID,
37
+ observerMode: observerMode,
38
+ userDefaultsSuiteName: userDefaultsSuiteName,
39
+ platformFlavor: self.platformFlavor,
40
+ platformFlavorVersion: self.platformVersion,
41
+ dangerousSettings: DangerousSettings())
42
+ purchases.delegate = self
43
+ call.resolve()
44
+ }
45
+
46
+ @objc func setFinishTransactions(_ call: CAPPluginCall) {
47
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
48
+ guard let finishTransactions = call.getOrRejectBool("finishTransactions") else { return }
49
+ CommonFunctionality.setFinishTransactions(finishTransactions)
50
+ call.resolve()
51
+ }
52
+
53
+ @objc func setSimulatesAskToBuyInSandbox(_ call: CAPPluginCall) {
54
+ guard let simulatesAskToBuyInSandbox = call.getOrRejectBool("simulatesAskToBuyInSandbox") else { return }
55
+ CommonFunctionality.simulatesAskToBuyInSandbox = simulatesAskToBuyInSandbox
56
+ call.resolve()
57
+ }
58
+
59
+ @objc func addCustomerInfoUpdateListener(_ call: CAPPluginCall) {
60
+ call.keepAlive = true
61
+ self.customerInfoUpdateListeners.append(call.callbackId)
62
+ if let lastReceivedCustomerInfo {
63
+ call.resolve(CommonFunctionality.encode(customerInfo: lastReceivedCustomerInfo))
64
+ }
65
+ }
66
+
67
+ @objc func removeCustomerInfoUpdateListener(_ call: CAPPluginCall) {
68
+ guard let callbackId = call.getOrRejectString("listenerToRemove") else { return }
69
+
70
+ var wasRemoved = false
71
+ if let index = self.customerInfoUpdateListeners.firstIndex(of: callbackId) {
72
+ self.customerInfoUpdateListeners.remove(at: index)
73
+ self.bridge?.savedCall(withID: callbackId)?.keepAlive = false
74
+ wasRemoved = true
75
+ }
76
+ call.resolve([
77
+ "wasRemoved": wasRemoved
78
+ ])
79
+ }
80
+
81
+ @objc func getOfferings(_ call: CAPPluginCall) {
82
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
83
+ CommonFunctionality.getOfferings(completion: self.getCompletionBlockHandler(call))
84
+ }
85
+
86
+ @objc func getProducts(_ call: CAPPluginCall) {
87
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
88
+ guard let productIds = call.getOrRejectStringArray("productIdentifiers") else { return }
89
+ CommonFunctionality.getProductInfo(productIds) { products in
90
+ call.resolve([
91
+ "products": products
92
+ ])
93
+ }
94
+ }
95
+
96
+ @objc func purchaseStoreProduct(_ call: CAPPluginCall) {
97
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
98
+ guard let storeProduct = call.getOrRejectObject("product") else { return }
99
+ guard let productId = storeProduct["identifier"] as? String else {
100
+ call.reject("Product parameter did not have identifier key")
101
+ return
102
+ }
103
+ CommonFunctionality.purchase(product: productId,
104
+ signedDiscountTimestamp: nil,
105
+ completion: self.getCompletionBlockHandler(call))
106
+ }
107
+
108
+ @objc func purchaseDiscountedProduct(_ call: CAPPluginCall) {
109
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
110
+ guard let storeProduct = call.getOrRejectObject("product") else { return }
111
+ guard let productId = storeProduct["identifier"] as? String else {
112
+ call.reject("Product parameter did not have identifier key")
113
+ return
114
+ }
115
+ guard let discount = call.getOrRejectObject("discount") else { return }
116
+ guard let signedDiscounTimestamp = discount["timestamp"] as? String else {
117
+ call.reject("Discount parameter did not have timestamp key")
118
+ return
119
+ }
120
+ CommonFunctionality.purchase(product: productId,
121
+ signedDiscountTimestamp: signedDiscounTimestamp,
122
+ completion: self.getCompletionBlockHandler(call))
123
+ }
124
+
125
+ @objc func purchasePackage(_ call: CAPPluginCall) {
126
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
127
+ guard let package = call.getOrRejectObject("aPackage") else { return }
128
+ guard let packageId = package["identifier"] as? String else {
129
+ call.reject("aPackage parameter did not have identifier key")
130
+ return
131
+ }
132
+ guard let offeringIdentifier = package["offeringIdentifier"] as? String else {
133
+ call.reject("aPackage parameter did not have offeringIdentifier key")
134
+ return
135
+ }
136
+ CommonFunctionality.purchase(package: packageId,
137
+ offeringIdentifier: offeringIdentifier,
138
+ signedDiscountTimestamp: nil,
139
+ completion: self.getCompletionBlockHandler(call))
140
+ }
141
+
142
+ @objc func purchaseSubscriptionOption(_ call: CAPPluginCall) {
143
+ self.rejectUnsupportedInIOS(call)
144
+ }
145
+
146
+ @objc func purchaseDiscountedPackage(_ call: CAPPluginCall) {
147
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
148
+ guard let package = call.getOrRejectObject("aPackage") else { return }
149
+ guard let packageId = package["identifier"] as? String else {
150
+ call.reject("aPackage parameter did not have identifier key")
151
+ return
152
+ }
153
+ guard let offeringIdentifier = package["offeringIdentifier"] as? String else {
154
+ call.reject("aPackage parameter did not have offeringIdentifier key")
155
+ return
156
+ }
157
+ guard let discount = call.getOrRejectObject("discount") else { return }
158
+ guard let signedDiscounTimestamp = discount["timestamp"] as? String else {
159
+ call.reject("Discount parameter did not have timestamp key")
160
+ return
161
+ }
162
+ CommonFunctionality.purchase(package: packageId,
163
+ offeringIdentifier: offeringIdentifier,
164
+ signedDiscountTimestamp: signedDiscounTimestamp,
165
+ completion: self.getCompletionBlockHandler(call))
166
+ }
167
+
168
+ @objc func restorePurchases(_ call: CAPPluginCall) {
169
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
170
+ CommonFunctionality.restorePurchases(completion:
171
+ self.getCompletionBlockHandler(call, wrapperKey: self.customerInfoKey))
172
+ }
173
+
174
+ @objc func getAppUserID(_ call: CAPPluginCall) {
175
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
176
+ call.resolve([
177
+ "appUserID": CommonFunctionality.appUserID
178
+ ])
179
+ }
180
+
181
+ @objc func logIn(_ call: CAPPluginCall) {
182
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
183
+ guard let appUserID = call.getOrRejectString("appUserID") else { return }
184
+ CommonFunctionality.logIn(appUserID: appUserID, completion: self.getCompletionBlockHandler(call))
185
+ }
186
+
187
+ @objc func logOut(_ call: CAPPluginCall) {
188
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
189
+ CommonFunctionality.logOut(completion: self.getCompletionBlockHandler(call, wrapperKey: self.customerInfoKey))
190
+ }
191
+
192
+ @objc func setLogLevel(_ call: CAPPluginCall) {
193
+ guard let logLevel = call.getOrRejectString("level") else { return }
194
+ CommonFunctionality.setLogLevel(logLevel)
195
+ call.resolve()
196
+ }
197
+
198
+ @objc func setLogHandler(_ call: CAPPluginCall) {
199
+ // WIP: Need to clear previous call if calling setLogHandler multiple times.
200
+ call.keepAlive = true
201
+ CommonFunctionality.setLogHander { log in
202
+ call.resolve(log)
203
+ }
204
+ }
205
+
206
+ @objc func getCustomerInfo(_ call: CAPPluginCall) {
207
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
208
+ CommonFunctionality.customerInfo(completion: self.getCompletionBlockHandler(call,
209
+ wrapperKey: self.customerInfoKey))
210
+ }
211
+
212
+ @objc func syncPurchases(_ call: CAPPluginCall) {
213
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
214
+ CommonFunctionality.syncPurchases(completion: self.getCompletionBlockHandler(call))
215
+ }
216
+
217
+ @objc func syncObserverModeAmazonPurchase(_ call: CAPPluginCall) {
218
+ self.rejectUnsupportedInIOS(call)
219
+ }
220
+
221
+ @objc func enableAdServicesAttributionTokenCollection(_ call: CAPPluginCall) {
222
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
223
+ if #available(iOS 14.3, *) {
224
+ CommonFunctionality.enableAdServicesAttributionTokenCollection()
225
+ }
226
+ call.resolve()
227
+ }
228
+
229
+ @objc func isAnonymous(_ call: CAPPluginCall) {
230
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
231
+ call.resolve([
232
+ "isAnonymous": CommonFunctionality.isAnonymous
233
+ ])
234
+ }
235
+
236
+ @objc func checkTrialOrIntroductoryPriceEligibility(_ call: CAPPluginCall) {
237
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
238
+ guard let productIds = call.getOrRejectStringArray("productIdentifiers") else { return }
239
+
240
+ CommonFunctionality.checkTrialOrIntroductoryPriceEligibility(for: productIds) { eligibilityResults in
241
+ call.resolve(eligibilityResults)
242
+ }
243
+ }
244
+
245
+ @objc func getPromotionalOffer(_ call: CAPPluginCall) {
246
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
247
+ guard let storeProduct = call.getOrRejectObject("product") else { return }
248
+ guard let productId = storeProduct["identifier"] as? String else {
249
+ call.reject("Product parameter did not have identifier key")
250
+ return
251
+ }
252
+ guard let discount = call.getOrRejectObject("discount") else { return }
253
+ guard let discountIdentifier = discount["identifier"] as? String else {
254
+ call.reject("Discount parameter did not have identifier key")
255
+ return
256
+ }
257
+
258
+ CommonFunctionality.promotionalOffer(for: productId,
259
+ discountIdentifier: discountIdentifier,
260
+ completion: self.getCompletionBlockHandler(call))
261
+ }
262
+
263
+ @objc func invalidateCustomerInfoCache(_ call: CAPPluginCall) {
264
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
265
+ CommonFunctionality.invalidateCustomerInfoCache()
266
+ call.resolve()
267
+ }
268
+
269
+ @objc func presentCodeRedemptionSheet(_ call: CAPPluginCall) {
270
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
271
+ if #available(iOS 14.0, *) {
272
+ CommonFunctionality.presentCodeRedemptionSheet()
273
+ }
274
+ call.resolve()
275
+ }
276
+
277
+ @objc func setProxyURL(_ call: CAPPluginCall) {
278
+ let url = call.getString("url")
279
+ CommonFunctionality.proxyURLString = url
280
+ call.resolve()
281
+ }
282
+
283
+ @objc func collectDeviceIdentifiers(_ call: CAPPluginCall) {
284
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
285
+ CommonFunctionality.collectDeviceIdentifiers()
286
+ call.resolve()
287
+ }
288
+
289
+ @objc func canMakePayments(_ call: CAPPluginCall) {
290
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
291
+ let features = call.getArray("features", []) as? [Int] ?? []
292
+ let canMakePayments = CommonFunctionality.canMakePaymentsWithFeatures(features)
293
+ call.resolve([
294
+ "canMakePayments": canMakePayments
295
+ ])
296
+ }
297
+
298
+ @objc func beginRefundRequestForActiveEntitlement(_ call: CAPPluginCall) {
299
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
300
+ if #available(iOS 15.0, *) {
301
+ CommonFunctionality.beginRefundRequestForActiveEntitlement(completion:
302
+ self.getBeginRefundRequestCompletion(call))
303
+ } else {
304
+ call.unavailable()
305
+ }
306
+ }
307
+
308
+ @objc func beginRefundRequestForEntitlement(_ call: CAPPluginCall) {
309
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
310
+ guard let entitlementInfo = call.getOrRejectObject("entitlementInfo") else { return }
311
+ guard let entitlementId = entitlementInfo["identifier"] as? String else {
312
+ call.reject("entitlementInfo parameter did not have identifier field")
313
+ return
314
+ }
315
+
316
+ if #available(iOS 15.0, *) {
317
+ CommonFunctionality.beginRefundRequest(entitlementId: entitlementId,
318
+ completion: self.getBeginRefundRequestCompletion(call))
319
+ } else {
320
+ call.unavailable()
321
+ }
322
+ }
323
+
324
+ @objc func beginRefundRequestForProduct(_ call: CAPPluginCall) {
325
+ guard self.rejectIfPurchasesNotConfigured(call) else { return }
326
+ guard let storeProduct = call.getOrRejectObject("storeProduct") else { return }
327
+ guard let productId = storeProduct["identifier"] as? String else {
328
+ call.reject("storeProduct parameter did not have identifier field")
329
+ return
330
+ }
331
+
332
+ if #available(iOS 15.0, *) {
333
+ CommonFunctionality.beginRefundRequest(productId: productId,
334
+ completion: self.getBeginRefundRequestCompletion(call))
335
+ } else {
336
+ call.unavailable()
337
+ }
338
+ }
339
+
340
+ @objc func isConfigured(_ call: CAPPluginCall) {
341
+ call.resolve([
342
+ "isConfigured": Purchases.isConfigured
343
+ ])
344
+ }
345
+
346
+ public func purchases(_ purchases: Purchases, receivedUpdated customerInfo: CustomerInfo) {
347
+ self.customerInfoUpdateListeners.forEach { [weak self] callbackId in
348
+ self?.bridge?.savedCall(withID: callbackId)?.resolve(CommonFunctionality.encode(customerInfo: customerInfo))
349
+ }
350
+ self.lastReceivedCustomerInfo = customerInfo
351
+ }
352
+ }
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@revenuecat/purchases-capacitor",
3
+ "version": "6.0.0-beta.1",
4
+ "description": "Capacitor in-app purchases and subscriptions made easy. Support for iOS and Android.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "RevenuecatPurchasesCapacitor.podspec"
15
+ ],
16
+ "author": "RevenueCat, Inc.",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/RevenueCat/purchases-capacitor.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/RevenueCat/purchases-capacitor/issues"
24
+ },
25
+ "keywords": [
26
+ "capacitor",
27
+ "plugin",
28
+ "native"
29
+ ],
30
+ "scripts": {
31
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
32
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
33
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
34
+ "verify:web": "npm run build",
35
+ "lint": "npm run eslint && npm run prettier -- --check",
36
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write",
37
+ "eslint": "eslint . --ext ts",
38
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
39
+ "docgen": "docgen --api PurchasesPlugin --output-readme README.md --output-json dist/docs.json",
40
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
41
+ "clean": "rimraf ./dist",
42
+ "watch": "tsc --watch",
43
+ "prepublishOnly": "npm run build"
44
+ },
45
+ "devDependencies": {
46
+ "@capacitor/android": "^5.0.0",
47
+ "@capacitor/core": "^5.0.0",
48
+ "@capacitor/docgen": "^0.0.18",
49
+ "@capacitor/ios": "^5.0.0",
50
+ "@ionic/eslint-config": "^0.3.0",
51
+ "@ionic/prettier-config": "^1.0.1",
52
+ "eslint": "^7.11.0",
53
+ "prettier": "~2.3.0",
54
+ "prettier-plugin-java": "~1.0.2",
55
+ "rimraf": "^3.0.2",
56
+ "rollup": "^2.32.0",
57
+ "typescript": "~4.1.5"
58
+ },
59
+ "peerDependencies": {
60
+ "@capacitor/core": "^5.0.0"
61
+ },
62
+ "prettier": "@ionic/prettier-config",
63
+ "eslintConfig": {
64
+ "extends": "@ionic/eslint-config/recommended"
65
+ },
66
+ "capacitor": {
67
+ "ios": {
68
+ "src": "ios"
69
+ },
70
+ "android": {
71
+ "src": "android"
72
+ }
73
+ },
74
+ "dependencies": {
75
+ "@revenuecat/purchases-typescript-internal": "6.1.0"
76
+ }
77
+ }