@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.
@@ -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
- [Purchasely userLogout:YES];
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
- [Purchasely userSubscriptions:false
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
- [Purchasely userSubscriptionsHistory:false
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
- [self successFor:command resultBool:result];
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 the JS side (Purchasely.js).
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