@purchasely/cordova-plugin-purchasely 2.3.2 → 4.2.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/example/android.sh +8 -0
- package/example/config.xml +1 -1
- package/example/ios.sh +16 -0
- package/example/package-lock.json +426 -477
- package/example/package.json +5 -4
- package/example/www/index.html +12 -0
- package/example/www/js/index.js +114 -10
- package/package.json +9 -1
- package/plugin.xml +16 -5
- package/purchasely.iml +11 -0
- package/src/android/PLYProductActivity.kt +171 -0
- package/src/android/PLYSubscriptionsActivity.java +1 -1
- package/src/android/PurchaselyPlugin.kt +925 -0
- package/src/android/activity_ply_product_activity.xml +2 -2
- package/src/android/activity_ply_subscriptions_activity.xml +6 -0
- package/src/ios/CDVPurchasely.h +24 -6
- package/src/ios/CDVPurchasely.m +441 -37
- package/src/ios/Hybrid/PLYPlan+Hybrid.m +0 -1
- package/src/ios/Hybrid/PLYPresentationPlan+Hybrid.h +20 -0
- package/src/ios/Hybrid/PLYPresentationPlan+Hybrid.m +31 -0
- package/src/ios/Hybrid/Purchasely_Hybrid.h +1 -0
- package/src/ios/Hybrid/UIColor+PLYHelper.h +19 -0
- package/src/ios/Hybrid/UIColor+PLYHelper.m +66 -0
- package/www/Purchasely.js +101 -28
- package/src/android/PLYProductActivity.java +0 -87
- package/src/android/PurchaselyPlugin.java +0 -739
package/src/ios/CDVPurchasely.m
CHANGED
|
@@ -8,20 +8,39 @@
|
|
|
8
8
|
#import "CDVPurchasely.h"
|
|
9
9
|
#import "Purchasely_Hybrid.h"
|
|
10
10
|
#import "CDVPurchasely+Events.h"
|
|
11
|
+
#import "UIColor+PLYHelper.h"
|
|
11
12
|
|
|
12
13
|
@implementation CDVPurchasely
|
|
13
14
|
|
|
14
|
-
- (
|
|
15
|
+
- (instancetype)init {
|
|
16
|
+
self = [super init];
|
|
17
|
+
|
|
18
|
+
self.presentationsLoaded = [NSMutableArray new];
|
|
19
|
+
self.shouldReopenPaywall = NO;
|
|
20
|
+
|
|
21
|
+
return self;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
- (void)start:(CDVInvokedUrlCommand*)command {
|
|
15
25
|
NSString *apiKey = [command argumentAtIndex:0];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
NSInteger
|
|
19
|
-
|
|
26
|
+
BOOL storeKit1 = [[command argumentAtIndex:2] boolValue];
|
|
27
|
+
NSString *userId = [command argumentAtIndex:3];
|
|
28
|
+
NSInteger logLevel = [[command argumentAtIndex:4] intValue];
|
|
29
|
+
NSInteger runningMode = [[command argumentAtIndex:5] intValue];
|
|
30
|
+
NSString *purchaselySdkVersion = [command argumentAtIndex:6];
|
|
20
31
|
|
|
21
32
|
[Purchasely setSdkBridgeVersion:purchaselySdkVersion];
|
|
22
|
-
|
|
33
|
+
|
|
23
34
|
[Purchasely setAppTechnology:PLYAppTechnologyCordova];
|
|
24
|
-
|
|
35
|
+
|
|
36
|
+
[Purchasely startWithAPIKey:apiKey
|
|
37
|
+
appUserId:userId
|
|
38
|
+
runningMode:runningMode
|
|
39
|
+
paywallActionsInterceptor:nil
|
|
40
|
+
storekitSettings: storeKit1 ? [StorekitSettings storeKit1] : [StorekitSettings storeKit2]
|
|
41
|
+
logLevel:logLevel
|
|
42
|
+
initialized:^(BOOL initialized, NSError * _Nullable error) {
|
|
43
|
+
|
|
25
44
|
if (error != nil) {
|
|
26
45
|
[self failureFor:command resultString: error.localizedDescription];
|
|
27
46
|
} else {
|
|
@@ -46,6 +65,12 @@
|
|
|
46
65
|
[Purchasely userLogout];
|
|
47
66
|
}
|
|
48
67
|
|
|
68
|
+
- (void)setThemeMode:(CDVInvokedUrlCommand *)command {
|
|
69
|
+
NSInteger mode = [[command argumentAtIndex:0] intValue];
|
|
70
|
+
|
|
71
|
+
[Purchasely setThemeMode:(enum PLYThemeMode) mode];
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
- (void)setAttribute:(CDVInvokedUrlCommand*)command {
|
|
50
75
|
NSInteger attribute = [[command argumentAtIndex:0] intValue];
|
|
51
76
|
NSString *value = [command argumentAtIndex:1];
|
|
@@ -58,9 +83,9 @@
|
|
|
58
83
|
[self successFor:command resultString:anonymousId];
|
|
59
84
|
}
|
|
60
85
|
|
|
61
|
-
- (void)
|
|
86
|
+
- (void)readyToOpenDeeplink:(CDVInvokedUrlCommand*)command {
|
|
62
87
|
BOOL isReadyToPurchase = [[command argumentAtIndex:0] boolValue];
|
|
63
|
-
[Purchasely
|
|
88
|
+
[Purchasely readyToOpenDeeplink: isReadyToPurchase];
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
- (void)setDefaultPresentationResultHandler:(CDVInvokedUrlCommand*)command {
|
|
@@ -91,7 +116,7 @@
|
|
|
91
116
|
[navCtrl.navigationBar setTintColor: [UIColor whiteColor]];
|
|
92
117
|
|
|
93
118
|
self.presentedPresentationViewController = navCtrl;
|
|
94
|
-
|
|
119
|
+
|
|
95
120
|
if (isFullscreen) {
|
|
96
121
|
navCtrl.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
97
122
|
}
|
|
@@ -117,7 +142,7 @@
|
|
|
117
142
|
[navCtrl.navigationBar setTintColor: [UIColor whiteColor]];
|
|
118
143
|
|
|
119
144
|
self.presentedPresentationViewController = navCtrl;
|
|
120
|
-
|
|
145
|
+
|
|
121
146
|
if (isFullscreen) {
|
|
122
147
|
navCtrl.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
123
148
|
}
|
|
@@ -130,7 +155,7 @@
|
|
|
130
155
|
NSString *presentationVendorId = [command argumentAtIndex:1];
|
|
131
156
|
NSString *contentId = [command argumentAtIndex:2];
|
|
132
157
|
BOOL isFullscreen = [[command argumentAtIndex:3] boolValue];
|
|
133
|
-
|
|
158
|
+
|
|
134
159
|
UIViewController *ctrl = [Purchasely planControllerFor:planVendorId with:presentationVendorId contentId:contentId loaded:nil completion:^(enum PLYProductViewControllerResult result, PLYPlan * _Nullable plan) {
|
|
135
160
|
NSDictionary *resultDict = [self resultDictionaryForPresentationController:result plan:plan];
|
|
136
161
|
[self successFor:command resultDict:resultDict];
|
|
@@ -142,9 +167,9 @@
|
|
|
142
167
|
[navCtrl.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
|
143
168
|
[navCtrl.navigationBar setShadowImage: [UIImage new]];
|
|
144
169
|
[navCtrl.navigationBar setTintColor: [UIColor whiteColor]];
|
|
145
|
-
|
|
170
|
+
|
|
146
171
|
self.presentedPresentationViewController = navCtrl;
|
|
147
|
-
|
|
172
|
+
|
|
148
173
|
if (isFullscreen) {
|
|
149
174
|
navCtrl.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
150
175
|
}
|
|
@@ -157,7 +182,7 @@
|
|
|
157
182
|
NSString *presentationVendorId = [command argumentAtIndex:1];
|
|
158
183
|
NSString *contentId = [command argumentAtIndex:2];
|
|
159
184
|
BOOL isFullscreen = [[command argumentAtIndex:3] boolValue];
|
|
160
|
-
|
|
185
|
+
|
|
161
186
|
UIViewController *ctrl = [Purchasely productControllerFor:productVendorId with:presentationVendorId contentId:contentId loaded:nil completion:^(enum PLYProductViewControllerResult result, PLYPlan * _Nullable plan) {
|
|
162
187
|
NSDictionary *resultDict = [self resultDictionaryForPresentationController:result plan:plan];
|
|
163
188
|
[self successFor:command resultDict:resultDict];
|
|
@@ -169,9 +194,9 @@
|
|
|
169
194
|
[navCtrl.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
|
170
195
|
[navCtrl.navigationBar setShadowImage: [UIImage new]];
|
|
171
196
|
[navCtrl.navigationBar setTintColor: [UIColor whiteColor]];
|
|
172
|
-
|
|
197
|
+
|
|
173
198
|
self.presentedPresentationViewController = navCtrl;
|
|
174
|
-
|
|
199
|
+
|
|
175
200
|
if (isFullscreen) {
|
|
176
201
|
navCtrl.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
177
202
|
}
|
|
@@ -189,18 +214,50 @@
|
|
|
189
214
|
|
|
190
215
|
- (void)purchaseWithPlanVendorId:(CDVInvokedUrlCommand*)command {
|
|
191
216
|
NSString *planVendorId = [command argumentAtIndex:0];
|
|
192
|
-
|
|
217
|
+
NSString *offerId = [command argumentAtIndex:1];
|
|
218
|
+
NSString *contentId = [command argumentAtIndex:2];
|
|
219
|
+
|
|
193
220
|
[Purchasely planWith:planVendorId
|
|
194
221
|
success:^(PLYPlan * _Nonnull plan) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
222
|
+
|
|
223
|
+
if (@available(iOS 12.2, macOS 12.0, tvOS 15.0, watchOS 8.0, *)) {
|
|
224
|
+
|
|
225
|
+
NSString *storeOfferId = nil;
|
|
226
|
+
for (PLYPromoOffer *promoOffer in plan.promoOffers) {
|
|
227
|
+
if ([promoOffer.vendorId isEqualToString:offerId]) {
|
|
228
|
+
storeOfferId = promoOffer.storeOfferId;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (storeOfferId) {
|
|
234
|
+
[Purchasely purchaseWithPromotionalOfferWithPlan:plan
|
|
235
|
+
contentId:contentId
|
|
236
|
+
storeOfferId:storeOfferId
|
|
237
|
+
success:^{
|
|
238
|
+
[self successFor:command resultDict: plan.asDictionary];
|
|
239
|
+
} failure:^(NSError * _Nonnull error) {
|
|
240
|
+
[self failureFor:command resultString: error.localizedDescription];
|
|
241
|
+
}];
|
|
242
|
+
} else {
|
|
243
|
+
[Purchasely purchaseWithPlan:plan
|
|
244
|
+
contentId:contentId
|
|
245
|
+
success:^{
|
|
246
|
+
[self successFor:command resultDict: plan.asDictionary];
|
|
247
|
+
} failure:^(NSError * _Nonnull error) {
|
|
248
|
+
[self failureFor:command resultString: error.localizedDescription];
|
|
249
|
+
}];
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
[Purchasely purchaseWithPlan:plan
|
|
253
|
+
contentId:contentId
|
|
254
|
+
success:^{
|
|
255
|
+
[self successFor:command resultDict: plan.asDictionary];
|
|
256
|
+
} failure:^(NSError * _Nonnull error) {
|
|
257
|
+
[self failureFor:command resultString: error.localizedDescription];
|
|
258
|
+
}];
|
|
198
259
|
}
|
|
199
|
-
|
|
200
|
-
[self failureFor:command resultString: error.localizedDescription];
|
|
201
|
-
}];
|
|
202
|
-
}
|
|
203
|
-
failure:^(NSError * _Nullable error) {
|
|
260
|
+
} failure:^(NSError * _Nullable error) {
|
|
204
261
|
[self failureFor:command resultString: error.localizedDescription];
|
|
205
262
|
}];
|
|
206
263
|
}
|
|
@@ -214,7 +271,7 @@
|
|
|
214
271
|
}
|
|
215
272
|
|
|
216
273
|
- (void)silentRestoreAllProducts:(CDVInvokedUrlCommand*)command {
|
|
217
|
-
[Purchasely
|
|
274
|
+
[Purchasely synchronizeWithSuccess:^{
|
|
218
275
|
[self successFor:command];
|
|
219
276
|
} failure:^(NSError * _Nonnull error) {
|
|
220
277
|
[self failureFor:command resultString:error.localizedDescription];
|
|
@@ -270,14 +327,15 @@
|
|
|
270
327
|
}
|
|
271
328
|
|
|
272
329
|
- (void)userSubscriptions:(CDVInvokedUrlCommand*)command {
|
|
273
|
-
[Purchasely
|
|
330
|
+
[Purchasely userSubscriptions:false
|
|
331
|
+
success:^(NSArray<PLYSubscription *> * _Nullable subscriptions) {
|
|
274
332
|
NSMutableArray *result = [NSMutableArray new];
|
|
275
333
|
for (PLYSubscription *subscription in subscriptions) {
|
|
276
334
|
[result addObject:subscription.asDictionary];
|
|
277
335
|
}
|
|
278
336
|
[self successFor:command resultArray:result];
|
|
279
337
|
}
|
|
280
|
-
|
|
338
|
+
failure:^(NSError * _Nonnull error) {
|
|
281
339
|
[self failureFor:command resultString:error.localizedDescription];
|
|
282
340
|
}];
|
|
283
341
|
}
|
|
@@ -292,27 +350,55 @@
|
|
|
292
350
|
self.eventCommand = nil;
|
|
293
351
|
}
|
|
294
352
|
|
|
295
|
-
- (void)
|
|
353
|
+
- (void)isDeeplinkHandled:(CDVInvokedUrlCommand*)command {
|
|
296
354
|
NSString *deeplinkString = [command argumentAtIndex:0];
|
|
297
355
|
NSURL *deeplink = [NSURL URLWithString:deeplinkString];
|
|
298
356
|
|
|
299
357
|
if (deeplink != nil) {
|
|
300
|
-
BOOL result = [Purchasely
|
|
358
|
+
BOOL result = [Purchasely isDeeplinkHandledWithDeeplink:deeplink];
|
|
301
359
|
[self successFor:command resultBool:result];
|
|
302
360
|
} else {
|
|
303
361
|
[self successFor:command resultBool:NO];
|
|
304
362
|
}
|
|
305
363
|
}
|
|
306
364
|
|
|
307
|
-
- (void)close:(CDVInvokedUrlCommand*)command {
|
|
308
|
-
}
|
|
309
|
-
|
|
310
365
|
- (void)setLanguage:(CDVInvokedUrlCommand*)command {
|
|
311
366
|
NSString *language = [command argumentAtIndex:0];
|
|
312
367
|
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:language];
|
|
313
368
|
[Purchasely setLanguageFrom:locale];
|
|
314
369
|
}
|
|
315
370
|
|
|
371
|
+
- (void)signPromotionalOffer:(CDVInvokedUrlCommand*)command {
|
|
372
|
+
NSString *storeProductId = [command argumentAtIndex:0];
|
|
373
|
+
NSString *storeOfferId = [command argumentAtIndex:1];
|
|
374
|
+
|
|
375
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
376
|
+
if (@available(iOS 12.2, *)) {
|
|
377
|
+
[Purchasely signPromotionalOfferWithStoreProductId:storeProductId storeOfferId:storeOfferId success:^(PLYOfferSignature * _Nonnull signature) {
|
|
378
|
+
NSDictionary* result = [self resultSignatureForSignPromoOffer:signature];
|
|
379
|
+
[self successFor:command resultBool:result];
|
|
380
|
+
} failure:^(NSError * _Nullable error) {
|
|
381
|
+
[self failureFor:command resultString:error.localizedDescription];
|
|
382
|
+
}];
|
|
383
|
+
} else {
|
|
384
|
+
[self failureFor:command resultString:@"This fonctionality is unavailable before ios 12.2"];
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
- (void)isEligibleForIntroOffer:(CDVInvokedUrlCommand*)command {
|
|
390
|
+
NSString *planVendorId = [command argumentAtIndex:0];
|
|
391
|
+
|
|
392
|
+
[Purchasely planWith:planVendorId
|
|
393
|
+
success:^(PLYPlan * _Nonnull plan) {
|
|
394
|
+
[plan isUserEligibleForIntroductoryOfferWithCompletion:^(BOOL isEligible) {
|
|
395
|
+
[self successFor:command resultBool:isEligible];
|
|
396
|
+
}];
|
|
397
|
+
} failure:^(NSError * _Nullable error) {
|
|
398
|
+
[self failureFor:command resultString:error.localizedDescription];
|
|
399
|
+
}];
|
|
400
|
+
}
|
|
401
|
+
|
|
316
402
|
// Helpers
|
|
317
403
|
|
|
318
404
|
- (NSDictionary<NSString *, NSObject *> *) resultDictionaryForPresentationController:(PLYProductViewControllerResult)result plan:(PLYPlan * _Nullable)plan {
|
|
@@ -389,7 +475,7 @@
|
|
|
389
475
|
if (infos.abTestVariantId != nil) {
|
|
390
476
|
[infosResult setObject:infos.abTestVariantId forKey:@"abTestVariantId"];
|
|
391
477
|
}
|
|
392
|
-
|
|
478
|
+
|
|
393
479
|
[actionInterceptorResult setObject:infosResult forKey:@"info"];
|
|
394
480
|
}
|
|
395
481
|
if (params != nil) {
|
|
@@ -406,9 +492,15 @@
|
|
|
406
492
|
if (params.presentation != nil) {
|
|
407
493
|
[paramsResult setObject:params.presentation forKey:@"presentation"];
|
|
408
494
|
}
|
|
495
|
+
if (params.promoOffer != nil) {
|
|
496
|
+
NSMutableDictionary<NSString *, NSObject *> *promoOffer = [NSMutableDictionary new];
|
|
497
|
+
[promoOffer setObject:params.promoOffer.vendorId forKey:@"vendorId"];
|
|
498
|
+
[promoOffer setObject:params.promoOffer.storeOfferId forKey:@"storeOfferId"];
|
|
499
|
+
[paramsResult setObject:promoOffer forKey:@"offer"];
|
|
500
|
+
}
|
|
409
501
|
[actionInterceptorResult setObject:paramsResult forKey:@"parameters"];
|
|
410
502
|
}
|
|
411
|
-
|
|
503
|
+
|
|
412
504
|
return actionInterceptorResult;
|
|
413
505
|
}
|
|
414
506
|
|
|
@@ -432,20 +524,332 @@
|
|
|
432
524
|
}
|
|
433
525
|
}
|
|
434
526
|
|
|
435
|
-
- (void)
|
|
527
|
+
- (void)closePresentation:(CDVInvokedUrlCommand*)command {
|
|
436
528
|
if (self.presentedPresentationViewController != nil) {
|
|
437
529
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
438
530
|
[self.presentedPresentationViewController dismissViewControllerAnimated:true completion:^{
|
|
439
531
|
self.presentedPresentationViewController = nil;
|
|
532
|
+
self.shouldReopenPaywall = NO;
|
|
440
533
|
}];
|
|
441
534
|
});
|
|
442
535
|
}
|
|
443
536
|
}
|
|
444
537
|
|
|
538
|
+
- (void)hidePresentation:(CDVInvokedUrlCommand*)command {
|
|
539
|
+
if (self.presentedPresentationViewController != nil) {
|
|
540
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
541
|
+
[self.presentedPresentationViewController dismissViewControllerAnimated:true completion:^{ }];
|
|
542
|
+
self.shouldReopenPaywall = YES;
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
- (void)showPresentation:(CDVInvokedUrlCommand*)command {
|
|
548
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
549
|
+
if (self.presentedPresentationViewController && self.shouldReopenPaywall) {
|
|
550
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
|
551
|
+
self.shouldReopenPaywall = NO;
|
|
552
|
+
[Purchasely showController:self.presentedPresentationViewController type:PLYUIControllerTypeProductPage];
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
445
558
|
- (void)userDidConsumeSubscriptionContent:(CDVInvokedUrlCommand*)command {
|
|
446
559
|
[Purchasely userDidConsumeSubscriptionContent];
|
|
447
560
|
}
|
|
448
561
|
|
|
562
|
+
- (void)setUserAttributeWithString:(CDVInvokedUrlCommand*)command {
|
|
563
|
+
NSString *key = [command argumentAtIndex:0];
|
|
564
|
+
NSString *value = [command argumentAtIndex:1];
|
|
565
|
+
[Purchasely setUserAttributeWithStringValue:value forKey:key];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
- (void)setUserAttributeWithBoolean:(CDVInvokedUrlCommand*)command {
|
|
569
|
+
NSString *key = [command argumentAtIndex:0];
|
|
570
|
+
BOOL value = [[command argumentAtIndex:1] boolValue];
|
|
571
|
+
[Purchasely setUserAttributeWithBoolValue:value forKey:key];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
- (void)setUserAttributeWithInt:(CDVInvokedUrlCommand*)command {
|
|
575
|
+
NSString *key = [command argumentAtIndex:0];
|
|
576
|
+
NSInteger value = [[command argumentAtIndex:1] intValue];
|
|
577
|
+
[Purchasely setUserAttributeWithIntValue:value forKey:key];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
- (void)setUserAttributeWithDouble:(CDVInvokedUrlCommand*)command {
|
|
581
|
+
NSString *key = [command argumentAtIndex:0];
|
|
582
|
+
double value = [[command argumentAtIndex:1] doubleValue];
|
|
583
|
+
[Purchasely setUserAttributeWithDoubleValue:value forKey:key];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
- (void)setUserAttributeWithDate:(CDVInvokedUrlCommand*)command {
|
|
587
|
+
NSString *key = [command argumentAtIndex:0];
|
|
588
|
+
NSString *value = [command argumentAtIndex:1];
|
|
589
|
+
|
|
590
|
+
NSDateFormatter * dateFormatter = [NSDateFormatter new];
|
|
591
|
+
dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
|
|
592
|
+
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
|
|
593
|
+
NSDate *date = [dateFormatter dateFromString:value];
|
|
594
|
+
if (date != nil) {
|
|
595
|
+
[Purchasely setUserAttributeWithDateValue:date forKey:key];
|
|
596
|
+
} else {
|
|
597
|
+
NSLog(@"[Purchasely] Cannot save date attribute %@", key);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
- (void)userAttribute:(CDVInvokedUrlCommand*)command {
|
|
602
|
+
NSString *key = [command argumentAtIndex:0];
|
|
603
|
+
|
|
604
|
+
id _Nullable result = [self getUserAttributeValueForCordova:[Purchasely getUserAttributeFor:key]];
|
|
605
|
+
if (result != nil) {
|
|
606
|
+
[self successFor:command resultDict:result];
|
|
607
|
+
} else {
|
|
608
|
+
[self failureFor:command resultString:@"Cannot get user attribute"];
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
- (id _Nullable) getUserAttributeValueForCordova:(id _Nullable) value {
|
|
613
|
+
if ([value isKindOfClass:[NSDate class]]) {
|
|
614
|
+
NSDateFormatter * dateFormatter = [NSDateFormatter new];
|
|
615
|
+
dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
|
|
616
|
+
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
|
|
617
|
+
NSString *dateStr = [dateFormatter stringFromDate:value];
|
|
618
|
+
return dateStr;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return value;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
- (void)clearUserAttribute:(CDVInvokedUrlCommand*)command {
|
|
625
|
+
NSString *key = [command argumentAtIndex:0];
|
|
626
|
+
[Purchasely clearUserAttributeForKey:key];
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
- (void)clearUserAttributes:(CDVInvokedUrlCommand*)command {
|
|
630
|
+
[Purchasely clearUserAttributes];
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
- (void)fetchPresentation:(CDVInvokedUrlCommand*)command {
|
|
634
|
+
NSString *placementId = [command argumentAtIndex:0];
|
|
635
|
+
NSString *presentationId = [command argumentAtIndex:1];
|
|
636
|
+
NSString *contentId = [command argumentAtIndex:2];
|
|
637
|
+
|
|
638
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
639
|
+
if (placementId != nil) {
|
|
640
|
+
[Purchasely fetchPresentationFor:placementId contentId: contentId fetchCompletion:^(PLYPresentation * _Nullable presentation, NSError * _Nullable error) {
|
|
641
|
+
if (error != nil) {
|
|
642
|
+
[self failureFor:command resultString: error.localizedDescription];
|
|
643
|
+
} else if (presentation != nil) {
|
|
644
|
+
[self.presentationsLoaded addObject:presentation];
|
|
645
|
+
[self successFor:command resultDict:[self resultDictionaryForFetchPresentation:presentation]];
|
|
646
|
+
}
|
|
647
|
+
} completion:^(enum PLYProductViewControllerResult result, PLYPlan * _Nullable plan) {
|
|
648
|
+
if (self.purchaseResolve != nil){
|
|
649
|
+
[self successFor:self.purchaseResolve resultDict:[self resultDictionaryForPresentationController:result plan:plan]];
|
|
650
|
+
}
|
|
651
|
+
}];
|
|
652
|
+
} else {
|
|
653
|
+
[Purchasely fetchPresentationWith:presentationId contentId: contentId fetchCompletion:^(PLYPresentation * _Nullable presentation, NSError * _Nullable error) {
|
|
654
|
+
if (error != nil) {
|
|
655
|
+
[self failureFor:command resultString: error.localizedDescription];
|
|
656
|
+
} else if (presentation != nil) {
|
|
657
|
+
[self.presentationsLoaded addObject:presentation];
|
|
658
|
+
[self successFor:command resultDict:[self resultDictionaryForFetchPresentation:presentation]];
|
|
659
|
+
}
|
|
660
|
+
} completion:^(enum PLYProductViewControllerResult result, PLYPlan * _Nullable plan) {
|
|
661
|
+
if (self.purchaseResolve != nil) {
|
|
662
|
+
[self successFor:self.purchaseResolve resultDict:[self resultDictionaryForPresentationController:result plan:plan]];
|
|
663
|
+
}
|
|
664
|
+
}];
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
- (void)presentPresentation:(CDVInvokedUrlCommand *)command {
|
|
670
|
+
NSDictionary<NSString *, id> *presentationDictionary = [command argumentAtIndex:0];
|
|
671
|
+
BOOL isFullscreen = [[command argumentAtIndex:1] boolValue];
|
|
672
|
+
NSString *loadingBackgroundColor = [command argumentAtIndex:2];
|
|
673
|
+
|
|
674
|
+
if (presentationDictionary == nil) {
|
|
675
|
+
[self failureFor:command resultString: @"Presentation cannot be null"];
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
self.purchaseResolve = command;
|
|
680
|
+
|
|
681
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
682
|
+
|
|
683
|
+
PLYPresentation *presentationLoaded = [self findPresentationLoadedFor:(NSString *)[presentationDictionary objectForKey:@"id"]];
|
|
684
|
+
|
|
685
|
+
if (presentationLoaded == nil || presentationLoaded.controller == nil) {
|
|
686
|
+
[self failureFor:command resultString: @"Presentation not loaded"];
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
[self.presentationsLoaded removeObjectAtIndex:[self findIndexPresentationLoadedFor:(NSString *)[presentationDictionary objectForKey:@"id"]]];
|
|
691
|
+
|
|
692
|
+
if (presentationLoaded.controller != nil) {
|
|
693
|
+
if (loadingBackgroundColor != nil) {
|
|
694
|
+
UIColor *backColor = [UIColor ply_fromHex:loadingBackgroundColor];
|
|
695
|
+
if (backColor != nil) {
|
|
696
|
+
[presentationLoaded.controller.view setBackgroundColor:backColor];
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (isFullscreen) {
|
|
701
|
+
presentationLoaded.controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
self.shouldReopenPaywall = NO;
|
|
705
|
+
|
|
706
|
+
[Purchasely closeDisplayedPresentation];
|
|
707
|
+
self.presentedPresentationViewController = presentationLoaded.controller;
|
|
708
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
|
709
|
+
[Purchasely showController:presentationLoaded.controller type: PLYUIControllerTypeProductPage];
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
- (PLYPresentation *) findPresentationLoadedFor:(NSString * _Nullable) presentationId {
|
|
716
|
+
for (PLYPresentation *presentationLoaded in self.presentationsLoaded) {
|
|
717
|
+
if ([presentationLoaded.id isEqualToString: presentationId]) {
|
|
718
|
+
return presentationLoaded;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return nil;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
- (NSInteger) findIndexPresentationLoadedFor:(NSString * _Nullable) presentationId {
|
|
725
|
+
NSInteger index = 0;
|
|
726
|
+
for (PLYPresentation *presentationLoaded in self.presentationsLoaded) {
|
|
727
|
+
if ([presentationLoaded.id isEqualToString: presentationId]) {
|
|
728
|
+
return index;
|
|
729
|
+
}
|
|
730
|
+
index++;
|
|
731
|
+
}
|
|
732
|
+
return -1;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
- (NSDictionary *)resultSignatureForSignPromoOffer:(PLYOfferSignature * _Nullable) signature {
|
|
736
|
+
NSMutableDictionary<NSString *, NSObject *> *dict = [NSMutableDictionary new];
|
|
737
|
+
|
|
738
|
+
[dict setObject:signature.planVendorId forKey:@"planVendorId"];
|
|
739
|
+
[dict setObject:signature.identifier forKey:@"identifier"];
|
|
740
|
+
[dict setObject:signature.signature forKey:@"signature"];
|
|
741
|
+
[dict setObject:signature.keyIdentifier forKey:@"keyIdentifier"];
|
|
742
|
+
|
|
743
|
+
NSString *nonceString = [signature.nonce UUIDString];
|
|
744
|
+
NSObject *nonce = (NSObject *)nonceString;
|
|
745
|
+
if (nonce != nil) {
|
|
746
|
+
[dict setObject:nonce forKey:@"nonce"];
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
NSNumber *timestamp = [NSNumber numberWithDouble:signature.timestamp];
|
|
750
|
+
if (timestamp != nil) {
|
|
751
|
+
[dict setObject:timestamp forKey:@"timestamp"];
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return dict;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
- (NSDictionary<NSString *, NSObject *> *) resultDictionaryForFetchPresentation:(PLYPresentation * _Nullable) presentation {
|
|
758
|
+
NSMutableDictionary<NSString *, NSObject *> *presentationResult = [NSMutableDictionary new];
|
|
759
|
+
|
|
760
|
+
// TODO: fill all parameters.
|
|
761
|
+
if (presentation != nil) {
|
|
762
|
+
|
|
763
|
+
if (presentation.id != nil) {
|
|
764
|
+
[presentationResult setObject:presentation.id forKey:@"id"];
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (presentation.placementId != nil) {
|
|
768
|
+
[presentationResult setObject:presentation.placementId forKey:@"placementId"];
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (presentation.audienceId != nil) {
|
|
772
|
+
[presentationResult setObject:presentation.audienceId forKey:@"audienceId"];
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (presentation.abTestId != nil) {
|
|
776
|
+
[presentationResult setObject:presentation.abTestId forKey:@"abTestId"];
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (presentation.abTestVariantId != nil) {
|
|
780
|
+
[presentationResult setObject:presentation.abTestVariantId forKey:@"abTestVariantId"];
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (presentation.language != nil) {
|
|
784
|
+
[presentationResult setObject:presentation.language forKey:@"language"];
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (presentation.plans != nil) {
|
|
788
|
+
NSMutableArray *plans = [NSMutableArray new];
|
|
789
|
+
|
|
790
|
+
for (PLYPresentationPlan *plan in presentation.plans) {
|
|
791
|
+
[plans addObject:plan.asDictionary];
|
|
792
|
+
}
|
|
793
|
+
[presentationResult setObject:plans forKey:@"plans"];
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/*if (presentation.metadata != nil) {
|
|
797
|
+
|
|
798
|
+
NSDictionary<NSString *,id> *rawMetadata = [presentation.metadata getRawMetadata];
|
|
799
|
+
NSMutableDictionary<NSString *,id> *resultDict = [NSMutableDictionary dictionary];
|
|
800
|
+
|
|
801
|
+
dispatch_group_t group = dispatch_group_create();
|
|
802
|
+
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
|
803
|
+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
804
|
+
|
|
805
|
+
for (NSString *key in rawMetadata) {
|
|
806
|
+
id value = rawMetadata[key];
|
|
807
|
+
|
|
808
|
+
if ([value isKindOfClass: [NSString class]]) {
|
|
809
|
+
dispatch_group_enter(group); // Enter the dispatch group before making the async call
|
|
810
|
+
[presentation.metadata getStringWith:key completion:^(NSString * _Nullable result) {
|
|
811
|
+
[resultDict setObject:result forKey:key];
|
|
812
|
+
dispatch_group_leave(group); // Leave the dispatch group after the async call is completed
|
|
813
|
+
}];
|
|
814
|
+
} else {
|
|
815
|
+
[resultDict setObject:value forKey:key];
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
dispatch_group_notify(group, queue, ^{
|
|
820
|
+
// Code to execute after all async calls are completed
|
|
821
|
+
[presentationResult setObject:resultDict forKey:@"metadata"];
|
|
822
|
+
dispatch_semaphore_signal(semaphore);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
// Wait until all async calls are completed
|
|
826
|
+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
827
|
+
}*/
|
|
828
|
+
|
|
829
|
+
int resultString;
|
|
830
|
+
|
|
831
|
+
switch (presentation.type) {
|
|
832
|
+
case PLYPresentationTypeNormal:
|
|
833
|
+
resultString = PLYPresentationTypeNormal;
|
|
834
|
+
break;
|
|
835
|
+
case PLYPresentationTypeClient:
|
|
836
|
+
resultString = PLYPresentationTypeClient;
|
|
837
|
+
break;
|
|
838
|
+
case PLYPresentationTypeFallback:
|
|
839
|
+
resultString = PLYPresentationTypeFallback;
|
|
840
|
+
break;
|
|
841
|
+
case PLYPresentationTypeDeactivated:
|
|
842
|
+
resultString = PLYPresentationTypeDeactivated;
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
[presentationResult setObject:[NSNumber numberWithInt:resultString] forKey:@"type"];
|
|
847
|
+
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return presentationResult;
|
|
851
|
+
}
|
|
852
|
+
|
|
449
853
|
- (void)successFor:(CDVInvokedUrlCommand *)command {
|
|
450
854
|
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
|
451
855
|
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
[dict setObject:self.vendorId forKey:@"vendorId"];
|
|
16
16
|
[dict setObject:@(self.hasIntroductoryPrice) forKey:@"hasIntroductoryPrice"];
|
|
17
17
|
[dict setObject:@([self type]) forKey:@"type"];
|
|
18
|
-
[dict setObject:@([self isEligibleForIntroOffer]) forKey:@"isEligibleForIntroOffer"];
|
|
19
18
|
|
|
20
19
|
if (self.hasIntroductoryPrice && [[self introAmount] intValue] == 0) {
|
|
21
20
|
[dict setObject:@(YES) forKey:@"hasFreeTrial"];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PLYPresentationPlan+Hybrid.h
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Florian Huet on 29/09/2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
#import <Purchasely/Purchasely-Swift.h>
|
|
10
|
+
//@import Purchasely;
|
|
11
|
+
|
|
12
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
13
|
+
|
|
14
|
+
@interface PLYPresentationPlan (Hybrid)
|
|
15
|
+
|
|
16
|
+
- (NSDictionary *)asDictionary;
|
|
17
|
+
|
|
18
|
+
@end
|
|
19
|
+
|
|
20
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PLYPresentationPlan+Hybrid.m
|
|
3
|
+
// react-native-purchasely
|
|
4
|
+
//
|
|
5
|
+
// Created by Florian Huet on 29/09/2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "PLYPresentationPlan+Hybrid.h"
|
|
9
|
+
#import <Foundation/Foundation.h>
|
|
10
|
+
|
|
11
|
+
@implementation PLYPresentationPlan (Hybrid)
|
|
12
|
+
|
|
13
|
+
- (NSDictionary *)asDictionary {
|
|
14
|
+
NSMutableDictionary<NSString *, NSObject *> *dict = [NSMutableDictionary new];
|
|
15
|
+
|
|
16
|
+
if (self.offerId) {
|
|
17
|
+
[dict setObject:self.offerId forKey:@"offerId"];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (self.storeProductId) {
|
|
21
|
+
[dict setObject:self.storeProductId forKey:@"storeProductId"];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (self.planVendorId) {
|
|
25
|
+
[dict setObject:self.planVendorId forKey:@"planVendorId"];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return dict;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@end
|