@purchasely/cordova-plugin-purchasely 6.0.0-rc.3 → 6.0.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/README.md +22 -14
- package/__tests__/Purchasely.test.js +820 -155
- package/example/IOS_8_1_1_TEST_MATRIX.md +25 -0
- package/example/e2e/README.md +2 -2
- package/example/e2e/helpers/driver.js +135 -18
- package/example/e2e/specs/bridge.e2e.js +116 -13
- package/example/e2e/specs/dismiss.e2e.js +32 -12
- package/example/e2e/tools/ci_run_e2e.sh +15 -1
- package/example/e2e/tools/ci_run_e2e_ios.sh +12 -2
- package/example/e2e/wdio.android.conf.js +3 -0
- package/example/e2e/wdio.ios.conf.js +26 -1
- package/example/ios.sh +6 -5
- package/example/package-lock.json +3 -3
- package/example/package.json +2 -2
- package/example/www/js/index.js +62 -58
- package/package.json +1 -1
- package/plugin.xml +3 -3
- package/src/android/PurchaselyPlugin.kt +247 -67
- package/src/ios/CDVPurchasely+Events.m +7 -1
- package/src/ios/CDVPurchasely.h +11 -0
- package/src/ios/CDVPurchasely.m +158 -6
- package/src/ios/Hybrid/PLYPlan+Hybrid.m +19 -0
- package/src/ios/Hybrid/PLYSubscription+Hybrid.m +13 -0
- package/www/Purchasely.js +390 -46
package/src/ios/CDVPurchasely.m
CHANGED
|
@@ -110,13 +110,22 @@
|
|
|
110
110
|
|
|
111
111
|
- (void)userLogin:(CDVInvokedUrlCommand*)command {
|
|
112
112
|
NSString *userId = [command argumentAtIndex:0];
|
|
113
|
+
// CDV-W-08: userLoginWith:appUserId: is _Nonnull; align with Android's guard instead of
|
|
114
|
+
// forwarding nil into the native call.
|
|
115
|
+
if (![userId isKindOfClass:[NSString class]]) {
|
|
116
|
+
[self successFor:command resultBool:NO];
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
113
119
|
[Purchasely userLoginWith:userId shouldRefresh:^(BOOL refresh) {
|
|
114
120
|
[self successFor:command resultBool:refresh];
|
|
115
121
|
}];
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
- (void)userLogout:(CDVInvokedUrlCommand*)command {
|
|
119
|
-
|
|
125
|
+
// PAR-30: clearUserAttributes defaults to true (matches the JS-side default).
|
|
126
|
+
NSNumber *clearUserAttributes = [command argumentAtIndex:0];
|
|
127
|
+
BOOL clear = [clearUserAttributes isKindOfClass:[NSNumber class]] ? clearUserAttributes.boolValue : YES;
|
|
128
|
+
[Purchasely userLogout:clear];
|
|
120
129
|
}
|
|
121
130
|
|
|
122
131
|
- (void)setThemeMode:(CDVInvokedUrlCommand *)command {
|
|
@@ -201,6 +210,9 @@
|
|
|
201
210
|
case CordovaPLYAttributeBatchCustomUserId:
|
|
202
211
|
attribute = PLYAttributeBatchCustomUserId;
|
|
203
212
|
break;
|
|
213
|
+
case CordovaPLYAttributeOneSignalUserId:
|
|
214
|
+
attribute = PLYAttributeOneSignalUserId;
|
|
215
|
+
break;
|
|
204
216
|
default:
|
|
205
217
|
attributeFound = NO;
|
|
206
218
|
break;
|
|
@@ -219,6 +231,11 @@
|
|
|
219
231
|
[self successFor:command resultString:anonymousId];
|
|
220
232
|
}
|
|
221
233
|
|
|
234
|
+
// REC-12 / PAR-04
|
|
235
|
+
- (void)isAnonymous:(CDVInvokedUrlCommand*)command {
|
|
236
|
+
[self successFor:command resultBool:[Purchasely isAnonymous]];
|
|
237
|
+
}
|
|
238
|
+
|
|
222
239
|
- (void)allowDeeplink:(CDVInvokedUrlCommand*)command {
|
|
223
240
|
BOOL allow = [[command argumentAtIndex:0] boolValue];
|
|
224
241
|
[Purchasely allowDeeplink: allow];
|
|
@@ -408,6 +425,11 @@
|
|
|
408
425
|
|
|
409
426
|
- (void)purchasedSubscription:(CDVInvokedUrlCommand*)command {
|
|
410
427
|
self.purchasedCommand = command;
|
|
428
|
+
// CDV-W-14: a repeat JS call (re-subscribe/hot reload) must not stack observers, or a
|
|
429
|
+
// single purchase/restoration fires reloadContent: once per accumulated registration.
|
|
430
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
431
|
+
name: @"ply_purchasedSubscription"
|
|
432
|
+
object:nil];
|
|
411
433
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
412
434
|
selector:@selector(reloadContent:)
|
|
413
435
|
name: @"ply_purchasedSubscription"
|
|
@@ -455,7 +477,10 @@
|
|
|
455
477
|
}
|
|
456
478
|
|
|
457
479
|
- (void)userSubscriptions:(CDVInvokedUrlCommand*)command {
|
|
458
|
-
|
|
480
|
+
// PAR-29: invalidateCache defaults to false.
|
|
481
|
+
NSNumber *invalidateCache = [command argumentAtIndex:0];
|
|
482
|
+
BOOL invalidate = [invalidateCache isKindOfClass:[NSNumber class]] ? invalidateCache.boolValue : NO;
|
|
483
|
+
[Purchasely userSubscriptions:invalidate
|
|
459
484
|
success:^(NSArray<PLYSubscription *> * _Nullable subscriptions) {
|
|
460
485
|
NSMutableArray *result = [NSMutableArray new];
|
|
461
486
|
for (PLYSubscription *subscription in subscriptions) {
|
|
@@ -469,7 +494,10 @@
|
|
|
469
494
|
}
|
|
470
495
|
|
|
471
496
|
- (void)userSubscriptionsHistory:(CDVInvokedUrlCommand*)command {
|
|
472
|
-
|
|
497
|
+
// PAR-29: invalidateCache defaults to false.
|
|
498
|
+
NSNumber *invalidateCache = [command argumentAtIndex:0];
|
|
499
|
+
BOOL invalidate = [invalidateCache isKindOfClass:[NSNumber class]] ? invalidateCache.boolValue : NO;
|
|
500
|
+
[Purchasely userSubscriptionsHistory:invalidate
|
|
473
501
|
success:^(NSArray<PLYSubscription *> * _Nullable subscriptions) {
|
|
474
502
|
NSMutableArray *result = [NSMutableArray new];
|
|
475
503
|
for (PLYSubscription *subscription in subscriptions) {
|
|
@@ -530,7 +558,10 @@
|
|
|
530
558
|
if (@available(iOS 12.2, *)) {
|
|
531
559
|
[Purchasely signPromotionalOfferWithStoreProductId:storeProductId storeOfferId:storeOfferId success:^(PLYOfferSignature * _Nonnull signature) {
|
|
532
560
|
NSDictionary* result = [self resultSignatureForSignPromoOffer:signature];
|
|
533
|
-
|
|
561
|
+
// CDV-W-02: was resultBool: (an NSDictionary implicitly truncated to a bare
|
|
562
|
+
// BOOL), discarding the whole signature payload. resultDict: is the matching
|
|
563
|
+
// overload (declared below) for this NSDictionary result.
|
|
564
|
+
[self successFor:command resultDict:result];
|
|
534
565
|
} failure:^(NSError * _Nullable error) {
|
|
535
566
|
[self failureFor:command resultString:error.localizedDescription];
|
|
536
567
|
}];
|
|
@@ -553,6 +584,53 @@
|
|
|
553
584
|
}];
|
|
554
585
|
}
|
|
555
586
|
|
|
587
|
+
// PAR-05: Dynamic Offerings. Payload keys (reference/planVendorId/offerVendorId) match the
|
|
588
|
+
// Cordova JS↔native contract (not the native SDK's own planId/offerId property names).
|
|
589
|
+
- (void)setDynamicOffering:(CDVInvokedUrlCommand*)command {
|
|
590
|
+
NSString *reference = [command argumentAtIndex:0];
|
|
591
|
+
NSString *planVendorId = [command argumentAtIndex:1];
|
|
592
|
+
NSString *offerVendorId = [command argumentAtIndex:2];
|
|
593
|
+
if (![offerVendorId isKindOfClass:[NSString class]]) {
|
|
594
|
+
offerVendorId = nil;
|
|
595
|
+
}
|
|
596
|
+
// iOS 26.4+ Apple commitment billing plan type (0 unspecified / 1 upFront / 2 monthly);
|
|
597
|
+
// JS defaults it to unspecified when omitted (see Purchasely.BillingPlanType).
|
|
598
|
+
PLYBillingPlanType billingPlanType = [[command argumentAtIndex:3 withDefault:@(PLYBillingPlanTypeUnspecified)] integerValue];
|
|
599
|
+
|
|
600
|
+
[Purchasely setDynamicOfferingWithReference:reference
|
|
601
|
+
planVendorId:planVendorId
|
|
602
|
+
offerVendorId:offerVendorId
|
|
603
|
+
billingPlanType:billingPlanType
|
|
604
|
+
completion:^(BOOL success) {
|
|
605
|
+
[self successFor:command resultBool:success];
|
|
606
|
+
}];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
- (void)getDynamicOfferings:(CDVInvokedUrlCommand*)command {
|
|
610
|
+
[Purchasely getDynamicOfferingsWithCompletion:^(NSArray<PLYOffering *> * _Nonnull offerings) {
|
|
611
|
+
NSMutableArray *result = [NSMutableArray new];
|
|
612
|
+
for (PLYOffering *offering in offerings) {
|
|
613
|
+
NSMutableDictionary<NSString *, id> *dict = [NSMutableDictionary new];
|
|
614
|
+
dict[@"reference"] = offering.reference;
|
|
615
|
+
dict[@"planVendorId"] = offering.planId;
|
|
616
|
+
dict[@"offerVendorId"] = offering.offerId ?: [NSNull null];
|
|
617
|
+
[result addObject:dict];
|
|
618
|
+
}
|
|
619
|
+
[self successFor:command resultArray:result];
|
|
620
|
+
}];
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
- (void)removeDynamicOffering:(CDVInvokedUrlCommand*)command {
|
|
624
|
+
NSString *reference = [command argumentAtIndex:0];
|
|
625
|
+
if ([reference isKindOfClass:[NSString class]]) {
|
|
626
|
+
[Purchasely removeDynamicOfferingWithReference:reference];
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
- (void)clearDynamicOfferings:(CDVInvokedUrlCommand*)command {
|
|
631
|
+
[Purchasely clearDynamicOfferings];
|
|
632
|
+
}
|
|
633
|
+
|
|
556
634
|
// Helpers
|
|
557
635
|
|
|
558
636
|
// v6: builds a PLYTransition from the JS transition object (see normalizeTransition):
|
|
@@ -890,6 +968,9 @@ static BOOL PLYPresentationActionFromString(NSString *kind, PLYPresentationActio
|
|
|
890
968
|
completion(result);
|
|
891
969
|
}
|
|
892
970
|
|
|
971
|
+
// PAR-19: closeAllScreens is the canonical native action; closePresentation is kept as a
|
|
972
|
+
// separate (deprecated, fire-and-forget) action since existing native `close` behavior is
|
|
973
|
+
// preserved as-is, while closeAllScreens additionally reports success/error to JS.
|
|
893
974
|
- (void)closePresentation:(CDVInvokedUrlCommand*)command {
|
|
894
975
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
895
976
|
[Purchasely closeAllScreens];
|
|
@@ -897,6 +978,14 @@ static BOOL PLYPresentationActionFromString(NSString *kind, PLYPresentationActio
|
|
|
897
978
|
});
|
|
898
979
|
}
|
|
899
980
|
|
|
981
|
+
- (void)closeAllScreens:(CDVInvokedUrlCommand*)command {
|
|
982
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
983
|
+
[Purchasely closeAllScreens];
|
|
984
|
+
self.currentPresentation = nil;
|
|
985
|
+
[self successFor:command];
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
900
989
|
- (void)backPresentation:(CDVInvokedUrlCommand*)command {
|
|
901
990
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
902
991
|
if (self.currentPresentation != nil) {
|
|
@@ -1070,6 +1159,34 @@ static BOOL PLYPresentationActionFromString(NSString *kind, PLYPresentationActio
|
|
|
1070
1159
|
}
|
|
1071
1160
|
}
|
|
1072
1161
|
|
|
1162
|
+
// REC-12 / PAR-03: bulk read, same per-value conversion as the single-key read above.
|
|
1163
|
+
- (void)userAttributes:(CDVInvokedUrlCommand*)command {
|
|
1164
|
+
NSMutableDictionary<NSString *, id> *result = [NSMutableDictionary new];
|
|
1165
|
+
NSDictionary<NSString *, id> *attributes = [Purchasely userAttributes];
|
|
1166
|
+
for (NSString *key in attributes) {
|
|
1167
|
+
id value = [self getUserAttributeValueForCordova:attributes[key]];
|
|
1168
|
+
if (value != nil) {
|
|
1169
|
+
result[key] = value;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
[self successFor:command resultDict:result];
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// REC-12 / PAR-02
|
|
1176
|
+
- (void)incrementUserAttribute:(CDVInvokedUrlCommand*)command {
|
|
1177
|
+
NSString *key = [command argumentAtIndex:0];
|
|
1178
|
+
NSNumber *valueNumber = [command argumentAtIndex:1];
|
|
1179
|
+
NSInteger value = [valueNumber isKindOfClass:[NSNumber class]] ? valueNumber.integerValue : 1;
|
|
1180
|
+
[Purchasely incrementUserAttributeWithKey:key value:value];
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
- (void)decrementUserAttribute:(CDVInvokedUrlCommand*)command {
|
|
1184
|
+
NSString *key = [command argumentAtIndex:0];
|
|
1185
|
+
NSNumber *valueNumber = [command argumentAtIndex:1];
|
|
1186
|
+
NSInteger value = [valueNumber isKindOfClass:[NSNumber class]] ? valueNumber.integerValue : 1;
|
|
1187
|
+
[Purchasely decrementUserAttributeWithKey:key value:value];
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1073
1190
|
- (id _Nullable) getUserAttributeValueForCordova:(id _Nullable) value {
|
|
1074
1191
|
if ([value isKindOfClass:[NSDate class]]) {
|
|
1075
1192
|
NSDateFormatter * dateFormatter = [NSDateFormatter new];
|
|
@@ -1095,6 +1212,31 @@ static BOOL PLYPresentationActionFromString(NSString *kind, PLYPresentationActio
|
|
|
1095
1212
|
[Purchasely clearBuiltInAttributes];
|
|
1096
1213
|
}
|
|
1097
1214
|
|
|
1215
|
+
// PAR-07
|
|
1216
|
+
- (void)getBuiltInAttributes:(CDVInvokedUrlCommand*)command {
|
|
1217
|
+
NSMutableDictionary<NSString *, id> *result = [NSMutableDictionary new];
|
|
1218
|
+
NSDictionary<NSString *, id> *attributes = [Purchasely getBuiltInAttributes];
|
|
1219
|
+
for (NSString *key in attributes) {
|
|
1220
|
+
id value = [self getUserAttributeValueForCordova:attributes[key]];
|
|
1221
|
+
if (value != nil) {
|
|
1222
|
+
result[key] = value;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
[self successFor:command resultDict:result];
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
- (void)getBuiltInAttribute:(CDVInvokedUrlCommand*)command {
|
|
1229
|
+
NSString *key = [command argumentAtIndex:0];
|
|
1230
|
+
id _Nullable result = [self getUserAttributeValueForCordova:[Purchasely getBuiltInAttributeWith:key]];
|
|
1231
|
+
if (result != nil) {
|
|
1232
|
+
[self successFor:command resultDict:result];
|
|
1233
|
+
} else {
|
|
1234
|
+
// No attribute for this key: resolve success with no value (undefined in JS),
|
|
1235
|
+
// matching Android's nullable Any? return.
|
|
1236
|
+
[self successFor:command];
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1098
1240
|
- (void)fetchPresentation:(CDVInvokedUrlCommand*)command {
|
|
1099
1241
|
NSString *placementId = [command argumentAtIndex:0];
|
|
1100
1242
|
NSString *presentationId = [command argumentAtIndex:1];
|
|
@@ -1270,7 +1412,15 @@ static BOOL PLYPresentationActionFromString(NSString *kind, PLYPresentationActio
|
|
|
1270
1412
|
if (presentation != nil) {
|
|
1271
1413
|
|
|
1272
1414
|
if (presentation.screenId != nil) {
|
|
1415
|
+
// `screenId` is the sole authoritative, public presentation identifier (matches
|
|
1416
|
+
// Android's presentationToMap() key, so shared JS reads either platform the same
|
|
1417
|
+
// way). `id` is kept ONLY as a private/internal re-display lookup key --
|
|
1418
|
+
// findPresentationLoadedFor:/findIndexPresentationLoadedFor: key off it, and on
|
|
1419
|
+
// this platform it always equals screenId (iOS has no separate synthetic fetch
|
|
1420
|
+
// handle, unlike Android's `fetchId`) -- it is NOT a documented public field; the
|
|
1421
|
+
// JS bridge normalizes with `screenId ?? id` tolerance and never surfaces `id`.
|
|
1273
1422
|
[presentationResult setObject:presentation.screenId forKey:@"id"];
|
|
1423
|
+
[presentationResult setObject:presentation.screenId forKey:@"screenId"];
|
|
1274
1424
|
}
|
|
1275
1425
|
|
|
1276
1426
|
if (presentation.placementId != nil) {
|
|
@@ -1411,7 +1561,8 @@ static BOOL PLYPresentationActionFromString(NSString *kind, PLYPresentationActio
|
|
|
1411
1561
|
}
|
|
1412
1562
|
|
|
1413
1563
|
|
|
1414
|
-
// WARNING: This enum must be strictly identical to the one in
|
|
1564
|
+
// WARNING: This enum must be strictly identical (same declaration order) to the one in
|
|
1565
|
+
// the JS side (Purchasely.js) and Android's CordovaPLYAttribute enum class.
|
|
1415
1566
|
typedef NS_ENUM(NSInteger, CordovaPLYAttribute) {
|
|
1416
1567
|
CordovaPLYAttributeFirebaseAppInstanceId,
|
|
1417
1568
|
CordovaPLYAttributeAirshipChannelId,
|
|
@@ -1433,7 +1584,8 @@ typedef NS_ENUM(NSInteger, CordovaPLYAttribute) {
|
|
|
1433
1584
|
CordovaPLYAttributeAmplitudeDeviceId,
|
|
1434
1585
|
CordovaPLYAttributeMoengageUniqueId,
|
|
1435
1586
|
CordovaPLYAttributeOneSignalExternalId,
|
|
1436
|
-
CordovaPLYAttributeBatchCustomUserId
|
|
1587
|
+
CordovaPLYAttributeBatchCustomUserId,
|
|
1588
|
+
CordovaPLYAttributeOneSignalUserId
|
|
1437
1589
|
};
|
|
1438
1590
|
|
|
1439
1591
|
@end
|
|
@@ -79,6 +79,25 @@
|
|
|
79
79
|
[dict setObject:introPeriod forKey:@"introPeriod"];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// Commitment installment details (iOS 26.4+ multi-period commitments, e.g. "monthly
|
|
83
|
+
// subscription with 12-month commitment"). Apple-only: the array is empty for every other
|
|
84
|
+
// plan, so the key is omitted then. Mirrors PLYPlan.commitmentInfo: [PLYCommitmentInfo].
|
|
85
|
+
NSArray<PLYCommitmentInfo *> *commitmentInfo = self.commitmentInfo;
|
|
86
|
+
if (commitmentInfo.count > 0) {
|
|
87
|
+
NSMutableArray<NSDictionary *> *commitmentArray = [NSMutableArray new];
|
|
88
|
+
for (PLYCommitmentInfo *info in commitmentInfo) {
|
|
89
|
+
[commitmentArray addObject:@{
|
|
90
|
+
@"billingPlanType": @(info.billingPlanType),
|
|
91
|
+
@"billingPrice": info.billingPrice,
|
|
92
|
+
@"billingPeriod": info.billingPeriod,
|
|
93
|
+
@"totalPrice": info.totalPrice,
|
|
94
|
+
@"totalPeriod": info.totalPeriod,
|
|
95
|
+
@"totalDuration": @(info.totalDuration)
|
|
96
|
+
}];
|
|
97
|
+
}
|
|
98
|
+
[dict setObject:commitmentArray forKey:@"commitmentInfo"];
|
|
99
|
+
}
|
|
100
|
+
|
|
82
101
|
return dict;
|
|
83
102
|
}
|
|
84
103
|
|
|
@@ -28,6 +28,19 @@
|
|
|
28
28
|
[dict setObject:[dateFormat stringFromDate:self.cancelledDate] forKey:@"cancelledDate"];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Commitment progress (iOS 26.4+ monthly commitment, e.g. billing period 3 of 12).
|
|
32
|
+
// Apple-only and nil for every non-committed subscription, so the key is omitted then.
|
|
33
|
+
// Mirrors PLYSubscription.commitmentProgress: PLYCommitmentProgress?
|
|
34
|
+
PLYCommitmentProgress *commitmentProgress = self.commitmentProgress;
|
|
35
|
+
if (commitmentProgress != nil) {
|
|
36
|
+
[dict setObject:@{
|
|
37
|
+
@"billingPeriodNumber": @(commitmentProgress.billingPeriodNumber),
|
|
38
|
+
@"totalBillingPeriods": @(commitmentProgress.totalBillingPeriods),
|
|
39
|
+
@"commitmentExpiresDate": [dateFormat stringFromDate:commitmentProgress.commitmentExpiresDate],
|
|
40
|
+
@"commitmentPrice": commitmentProgress.commitmentPrice
|
|
41
|
+
} forKey:@"commitmentProgress"];
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
return dict;
|
|
32
45
|
}
|
|
33
46
|
|