@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/www/Purchasely.js CHANGED
@@ -8,25 +8,32 @@ var defaultError = (e) => { console.log(e); }
8
8
  // - a full transition object { type, dismissible?, width?, height?, backgroundColor? }
9
9
  // where width/height are { type: 'pixel'|'percentage', value: Number } (width is
10
10
  // popin-only, height drives drawer+popin) and backgroundColor is a hex string.
11
- // Always returns a transition object for the native bridge.
11
+ // CDV-W-12: when no displayMode is given, sends undefined (not a forced fullScreen
12
+ // default) so both natives' nil/null handling honors the backend-configured transition
13
+ // for that placement/screen, as documented in their own displayModeFromTransition /
14
+ // transitionFromMap helpers.
12
15
  function normalizeTransition(mode) {
13
16
  if (mode === true) return { type: 'fullScreen' };
14
17
  if (mode === false) return { type: 'modal' };
15
18
  if (typeof mode === 'string') return { type: mode };
16
19
  if (mode && typeof mode === 'object' && mode.type) return mode;
17
- return { type: 'fullScreen' };
20
+ return undefined;
18
21
  }
19
22
 
20
- // Wire a present* command's callback stream. Purchasely 6.0 emits presentation
21
- // lifecycle events during display: the native side sends keep-alive envelopes
22
- // { event: 'presented', presentation } and { event: 'closeRequested' }, then the
23
+ // Wire a present* command's callback stream -- used identically for the direct present*
24
+ // actions and for the preload() -> display() re-display path (native `presentPresentation`),
25
+ // so both surface the same lifecycle regardless of how display() was reached. Purchasely 6.0
26
+ // emits presentation lifecycle events during display: the native side sends keep-alive
27
+ // envelopes { event: 'presented', presentation } and { event: 'closeRequested' }, then the
23
28
  // dismiss OUTCOME (which has no `event` key) as the final, non-kept callback.
24
- // `callbacks` may carry onPresented(presentation, error) and onCloseRequested().
29
+ // `callbacks` may carry onPresented(presentation, error) and onCloseRequested(). The
30
+ // 'presented' envelope's presentation is screenId-normalized like everywhere else (preload(),
31
+ // outcome.presentation) -- never the raw native payload.
25
32
  function presentationDispatcher(success, callbacks) {
26
33
  callbacks = callbacks || {};
27
34
  return function (payload) {
28
35
  if (payload && payload.event === 'presented') {
29
- if (callbacks.onPresented) callbacks.onPresented(payload.presentation || null, payload.error || null);
36
+ if (callbacks.onPresented) callbacks.onPresented(normalizePresentation(payload.presentation), payload.error || null);
30
37
  return;
31
38
  }
32
39
  if (payload && payload.event === 'closeRequested') {
@@ -56,12 +63,57 @@ exports.start = function (options, success, error) {
56
63
  var opts = options || {};
57
64
  var cordovaSdkVersion = cordova.define.moduleMap['cordova/plugin_list'].exports['metadata']['cordova-plugin-purchasely']
58
65
  if(!cordovaSdkVersion) {
59
- cordovaSdkVersion = "6.0.0-rc.3";
66
+ cordovaSdkVersion = "6.0.0";
60
67
  }
61
68
  opts.sdkVersion = cordovaSdkVersion;
62
69
  exec(success, error, 'Purchasely', 'start', [opts]);
63
70
  };
64
71
 
72
+ // Purchasely 6.0: fluent alias of exports.start(options, ...) (parity with the
73
+ // RN/Flutter Purchasely.builder(apiKey)). Accumulates options via the chain
74
+ // below, then delegates to the very same exports.start -- no new native action.
75
+ // .start(success, error) supports both idioms: pass callbacks for the Cordova
76
+ // (success, error) style, or omit them for a Promise resolving the
77
+ // isConfigured boolean (rejecting on native failure).
78
+ function PLYStartBuilder(apiKey) {
79
+ this._options = { apiKey: apiKey };
80
+ }
81
+
82
+ PLYStartBuilder.prototype.appUserId = function (value) { this._options.appUserId = value; return this; };
83
+ PLYStartBuilder.prototype.runningMode = function (value) { this._options.runningMode = value; return this; };
84
+ PLYStartBuilder.prototype.logLevel = function (value) { this._options.logLevel = value; return this; };
85
+ PLYStartBuilder.prototype.allowDeeplink = function (value) { this._options.allowDeeplink = value; return this; };
86
+ PLYStartBuilder.prototype.allowCampaigns = function (value) { this._options.allowCampaigns = value; return this; };
87
+ PLYStartBuilder.prototype.stores = function (value) { this._options.stores = value; return this; };
88
+ PLYStartBuilder.prototype.storekitVersion = function (value) { this._options.storekitVersion = value; return this; };
89
+ PLYStartBuilder.prototype.storeKit1 = function (value) { this._options.storeKit1 = value; return this; };
90
+ PLYStartBuilder.prototype.deeplink = function (value) { this._options.deeplink = value; return this; };
91
+
92
+ PLYStartBuilder.prototype.start = function (success, error) {
93
+ if (success) {
94
+ exports.start(this._options, success, error);
95
+ return undefined;
96
+ }
97
+ var options = this._options;
98
+ return new Promise(function (resolve, reject) {
99
+ exports.start(options, resolve, function (nativeError) {
100
+ reject(normalizeError(nativeError));
101
+ });
102
+ });
103
+ };
104
+
105
+ exports.builder = function (apiKey) {
106
+ return new PLYStartBuilder(apiKey);
107
+ };
108
+
109
+ // REC-18 / PAR-18: addEventListener is the canonical name (matches RN's naming).
110
+ // addEventsListener (plural "Events", the original Cordova-only spelling) is kept as a
111
+ // deprecated alias.
112
+ exports.addEventListener = function (success, error) {
113
+ exec(success, error, 'Purchasely', 'addEventsListener', []);
114
+ };
115
+
116
+ // @deprecated use addEventListener instead.
65
117
  exports.addEventsListener = function (success, error) {
66
118
  exec(success, error, 'Purchasely', 'addEventsListener', []);
67
119
  };
@@ -74,6 +126,12 @@ exports.removeUserAttributeListener = function () {
74
126
  exec(() => {}, defaultError, 'Purchasely', 'removeUserAttributeListener', []);
75
127
  };
76
128
 
129
+ // REC-18 / PAR-18: canonical name, paired with addEventListener.
130
+ exports.removeEventListener = function () {
131
+ exec(() => {}, defaultError, 'Purchasely', 'removeEventsListener', []);
132
+ };
133
+
134
+ // @deprecated use removeEventListener instead.
77
135
  exports.removeEventsListener = function () {
78
136
  exec(() => {}, defaultError, 'Purchasely', 'removeEventsListener', []);
79
137
  };
@@ -86,8 +144,11 @@ exports.userLogin = function (userId, success) {
86
144
  exec(success, defaultError, 'Purchasely', 'userLogin', [userId]);
87
145
  };
88
146
 
89
- exports.userLogout = function () {
90
- exec(() => {}, defaultError, 'Purchasely', 'userLogout', []);
147
+ // PAR-30: clearUserAttributes controls whether logout also clears locally cached user
148
+ // attributes (native default true on both platforms).
149
+ exports.userLogout = function (clearUserAttributes) {
150
+ var clear = clearUserAttributes === undefined ? true : clearUserAttributes;
151
+ exec(() => {}, defaultError, 'Purchasely', 'userLogout', [clear]);
91
152
  };
92
153
 
93
154
  exports.setLogLevel = function (logLevel) {
@@ -123,39 +184,240 @@ exports.synchronize = function (success, error) {
123
184
  exec(success || (() => {}), error || defaultError, 'Purchasely', 'synchronize', []);
124
185
  };
125
186
 
126
- // present* methods: `displayMode` accepts a mode string, a legacy boolean, or a
127
- // full transition object (see normalizeTransition). `callbacks` is optional and
128
- // may carry { onPresented(presentation, error), onCloseRequested() }; `success`
129
- // still receives the final dismiss outcome.
130
- exports.presentPresentationWithIdentifier = function (presentationId, contentId, displayMode, success, error, callbacks) {
131
- exec(presentationDispatcher(success, callbacks), error, 'Purchasely', 'presentPresentationWithIdentifier', [presentationId, contentId, normalizeTransition(displayMode)]);
187
+ // Purchasely 6.0: the v5 presentation surface (fetchPresentation*, present-
188
+ // Presentation*, presentPresentation, backPresentation) is REMOVED, not
189
+ // deprecated -- replaced by the v6 builder below (parity with the React
190
+ // Native/Flutter SDKs; see MIGRATION-v6.md). It re-wraps the very same native
191
+ // exec actions used by v5 (fetchPresentation, presentPresentation,
192
+ // presentPresentationWithIdentifier/ForPlacement/ForDefault, backPresentation,
193
+ // closeAllScreens): no new native action is introduced.
194
+ //
195
+ // Purchasely.presentation.placement(id) | .screen(id) | .defaultSource() // alias: .default()
196
+ // .contentId(id)
197
+ // .backgroundColor(hex)
198
+ // .onLoaded(cb) / .onPresented(cb) / .onCloseRequested(cb) / .onDismissed(cb)
199
+ // .build()
200
+ // .preload() -> Promise<loadedPresentation> (screenId is authoritative;
201
+ // the resolved object also exposes display()/close()/back())
202
+ // .display(transition?) -> Promise<outcome> ({ presentation, purchaseResult, plan, closeReason, error })
203
+ // .close() -> closeAllScreens()
204
+ // .back() -> navigate back within the displayed presentation
205
+ //
206
+ // screenId is authoritative: normalizePresentation always resolves it (tolerating
207
+ // a raw `id` fallback) and only exposes the documented presentation fields. Any
208
+ // native re-display handle (Android's synthetic fetchId; iOS's internal `id`,
209
+ // which already equals screenId there) never leaves the private `_raw` field
210
+ // kept on the request -- it is not part of the presentation object handed back
211
+ // to callers.
212
+ //
213
+ // Modifier parity note: .backgroundColor(hex) is the ONLY presentation-style
214
+ // modifier the Cordova native layer actually supports -- it is wired to
215
+ // presentPresentation's native backgroundColor argument (preload -> display
216
+ // re-display path) and merged into the transition object for the direct present*
217
+ // paths (iOS reads transition.backgroundColor). It sets the loading/background
218
+ // color and takes effect on iOS; on Android it only applies through drawer/popin
219
+ // transitions (native limitation). The RN/Flutter builder's progressColor,
220
+ // displayCloseButton and displayBackButton are intentionally NOT exposed here: no
221
+ // Cordova native present action accepts them.
222
+
223
+ function normalizeError(error) {
224
+ if (error === undefined || error === null) return null;
225
+ if (typeof error === 'object' && error.message) return error;
226
+ return { message: String(error) };
227
+ }
228
+
229
+ function normalizePresentation(raw) {
230
+ if (!raw || typeof raw !== 'object') return null;
231
+ var screenId = raw.screenId != null ? raw.screenId : raw.id;
232
+ if (screenId == null) return null;
233
+ return {
234
+ screenId: screenId,
235
+ placementId: raw.placementId != null ? raw.placementId : null,
236
+ contentId: raw.contentId != null ? raw.contentId : null,
237
+ audienceId: raw.audienceId != null ? raw.audienceId : null,
238
+ abTestId: raw.abTestId != null ? raw.abTestId : null,
239
+ abTestVariantId: raw.abTestVariantId != null ? raw.abTestVariantId : null,
240
+ campaignId: raw.campaignId != null ? raw.campaignId : null,
241
+ flowId: raw.flowId != null ? raw.flowId : null,
242
+ language: raw.language != null ? raw.language : null,
243
+ type: raw.type != null ? raw.type : null,
244
+ plans: raw.plans != null ? raw.plans : null,
245
+ metadata: raw.metadata != null ? raw.metadata : null,
246
+ height: raw.height != null ? raw.height : null
247
+ };
248
+ }
249
+
250
+ function normalizeOutcome(raw) {
251
+ raw = raw || {};
252
+ return {
253
+ presentation: normalizePresentation(raw.presentation),
254
+ purchaseResult: raw.purchaseResult != null ? raw.purchaseResult : null,
255
+ plan: raw.plan != null ? raw.plan : null,
256
+ closeReason: raw.closeReason != null ? raw.closeReason : null,
257
+ error: raw.error != null ? raw.error : null
258
+ };
259
+ }
260
+
261
+ // A single presentation request: preload it (fetch without display), display it
262
+ // (resolves at dismiss with a 5-field outcome), close it, or navigate back. Calling
263
+ // `display()` after `preload()` re-displays the exact presentation that was fetched
264
+ // (via the native presentPresentation action, carrying its private re-display
265
+ // handle); `display()` alone (no prior preload) fetches and displays directly
266
+ // through the present* action matching this request's source.
267
+ function PLYPresentationRequest(config) {
268
+ this._config = config; // { placementId?, screenId?, contentId?, callbacks }
269
+ this._raw = null; // private: native fetch payload (carries the re-display handle)
270
+ }
271
+
272
+ PLYPresentationRequest.prototype.preload = function () {
273
+ var self = this;
274
+ var callbacks = this._config.callbacks;
275
+ return new Promise(function (resolve, reject) {
276
+ exec(function (raw) {
277
+ self._raw = raw;
278
+ var presentation = normalizePresentation(raw);
279
+ // Purchasely 6.0: onLoaded fires once the presentation has loaded
280
+ // (parity with RN/Flutter's onLoaded, which only fires on a
281
+ // successful load -- a failed preload() rejects below instead).
282
+ if (callbacks.onLoaded) callbacks.onLoaded(presentation, null);
283
+ // Resolve a "loaded presentation": the screenId-normalized data plus
284
+ // display()/close()/back() delegating to this request (parity with RN's
285
+ // PLYLoadedPresentation and the native preload()->display() flow).
286
+ resolve(Object.assign({}, presentation, {
287
+ display: function (transition) { return self.display(transition); },
288
+ close: function () { return self.close(); },
289
+ back: function () { return self.back(); }
290
+ }));
291
+ }, function (error) {
292
+ reject(normalizeError(error));
293
+ }, 'Purchasely', 'fetchPresentation', [
294
+ self._config.placementId || null,
295
+ self._config.screenId || null,
296
+ self._config.contentId || null
297
+ ]);
298
+ });
132
299
  };
133
300
 
134
- exports.presentPresentationForPlacement = function (placementId, contentId, displayMode, success, error, callbacks) {
135
- exec(presentationDispatcher(success, callbacks), error, 'Purchasely', 'presentPresentationForPlacement', [placementId, contentId, normalizeTransition(displayMode)]);
301
+ PLYPresentationRequest.prototype.display = function (transition) {
302
+ var self = this;
303
+ var callbacks = this._config.callbacks;
304
+ var normalizedTransition = normalizeTransition(transition);
305
+ // .backgroundColor() sugar: merge into the transition object so the direct
306
+ // present* paths (iOS reads transition.backgroundColor) pick it up. The
307
+ // transition's own backgroundColor, if any, still wins. Keeping no `type`
308
+ // when none was given preserves CDV-W-12 (backend default honored).
309
+ if (self._config.backgroundColor != null) {
310
+ normalizedTransition = Object.assign({ backgroundColor: self._config.backgroundColor }, normalizedTransition || {});
311
+ }
312
+ var backgroundColor = self._config.backgroundColor != null ? self._config.backgroundColor : null;
313
+
314
+ return new Promise(function (resolve) {
315
+ function settle(rawOutcome) {
316
+ var outcome = normalizeOutcome(rawOutcome);
317
+ if (callbacks.onDismissed) callbacks.onDismissed(outcome);
318
+ resolve(outcome);
319
+ }
320
+ var dispatch = presentationDispatcher(settle, {
321
+ onPresented: callbacks.onPresented,
322
+ onCloseRequested: callbacks.onCloseRequested
323
+ });
324
+ function onNativeError(error) {
325
+ var normalized = normalizeError(error);
326
+ settle({ error: normalized ? normalized.message : 'Unable to display presentation' });
327
+ }
328
+
329
+ if (self._raw) {
330
+ // Re-display the presentation preloaded by preload() -- same native
331
+ // 'presentPresentation' action v5 used, carrying its private handle.
332
+ exec(dispatch, onNativeError, 'Purchasely', 'presentPresentation', [self._raw, normalizedTransition, backgroundColor]);
333
+ } else if (self._config.screenId) {
334
+ exec(dispatch, onNativeError, 'Purchasely', 'presentPresentationWithIdentifier',
335
+ [self._config.screenId, self._config.contentId || null, normalizedTransition]);
336
+ } else if (self._config.placementId) {
337
+ exec(dispatch, onNativeError, 'Purchasely', 'presentPresentationForPlacement',
338
+ [self._config.placementId, self._config.contentId || null, normalizedTransition]);
339
+ } else {
340
+ exec(dispatch, onNativeError, 'Purchasely', 'presentPresentationForDefault',
341
+ [self._config.contentId || null, normalizedTransition]);
342
+ }
343
+ });
136
344
  };
137
345
 
138
- // Purchasely 6.0: present the default (audience-targeted) presentation, with no
139
- // placement or presentation id (mirrors the native default presentation source).
140
- exports.presentPresentationForDefault = function (contentId, displayMode, success, error, callbacks) {
141
- exec(presentationDispatcher(success, callbacks), error, 'Purchasely', 'presentPresentationForDefault', [contentId, normalizeTransition(displayMode)]);
346
+ // Purchasely 6.0: dismisses via the same native action as the top-level
347
+ // Purchasely.closeAllScreens() / closePresentation() (current bridge semantics:
348
+ // closes every displayed screen, not just this request's).
349
+ PLYPresentationRequest.prototype.close = function () {
350
+ exports.closeAllScreens();
142
351
  };
143
352
 
144
- exports.fetchPresentation = function (presentationId, contentId, success, error) {
145
- exec(success, error, 'Purchasely', 'fetchPresentation', [null, presentationId, contentId]);
353
+ // Purchasely 6.0: navigate back within the displayed presentation (was the
354
+ // standalone Purchasely.backPresentation(), now request-scoped).
355
+ PLYPresentationRequest.prototype.back = function () {
356
+ exec(() => {}, defaultError, 'Purchasely', 'backPresentation', []);
146
357
  };
147
358
 
148
- exports.fetchPresentationForPlacement = function (placementId, contentId, success, error) {
149
- exec(success, error, 'Purchasely', 'fetchPresentation', [placementId, null, contentId]);
359
+ function PLYPresentationBuilder(config) {
360
+ this._config = config; // { placementId?, screenId?, contentId?, callbacks }
361
+ }
362
+
363
+ PLYPresentationBuilder.prototype.contentId = function (id) {
364
+ this._config.contentId = id;
365
+ return this;
366
+ };
367
+
368
+ // Loading/background color (hex). See the modifier parity note above: this is the
369
+ // only style modifier the Cordova native layer supports; progressColor /
370
+ // displayCloseButton / displayBackButton are intentionally not exposed.
371
+ PLYPresentationBuilder.prototype.backgroundColor = function (hex) {
372
+ this._config.backgroundColor = hex;
373
+ return this;
374
+ };
375
+
376
+ // Purchasely 6.0: fires (presentation, error) once preload() loads the
377
+ // presentation. Only fires on a successful load; preload() still rejects on
378
+ // failure (parity with RN/Flutter's onLoaded).
379
+ PLYPresentationBuilder.prototype.onLoaded = function (handler) {
380
+ this._config.callbacks.onLoaded = handler;
381
+ return this;
382
+ };
383
+
384
+ PLYPresentationBuilder.prototype.onPresented = function (handler) {
385
+ this._config.callbacks.onPresented = handler;
386
+ return this;
387
+ };
388
+
389
+ PLYPresentationBuilder.prototype.onCloseRequested = function (handler) {
390
+ this._config.callbacks.onCloseRequested = handler;
391
+ return this;
150
392
  };
151
393
 
152
- // Purchasely 6.0: fetch the default (audience-targeted) presentation.
153
- exports.fetchPresentationForDefault = function (contentId, success, error) {
154
- exec(success, error, 'Purchasely', 'fetchPresentation', [null, null, contentId]);
394
+ PLYPresentationBuilder.prototype.onDismissed = function (handler) {
395
+ this._config.callbacks.onDismissed = handler;
396
+ return this;
155
397
  };
156
398
 
157
- exports.presentPresentation = function (presentation, displayMode, backgroundColor, success, error, callbacks) {
158
- exec(presentationDispatcher(success, callbacks), error, 'Purchasely', 'presentPresentation', [presentation, normalizeTransition(displayMode), backgroundColor]);
399
+ PLYPresentationBuilder.prototype.build = function () {
400
+ return new PLYPresentationRequest(this._config);
401
+ };
402
+
403
+ // Purchasely 6.0: the v6 presentation builder (parity with the React Native /
404
+ // Flutter Purchasely.presentation). Pick exactly one source, chain
405
+ // .contentId() / .onPresented() / .onCloseRequested() / .onDismissed(), then
406
+ // .build().
407
+ exports.presentation = {
408
+ placement: function (placementId) {
409
+ return new PLYPresentationBuilder({ placementId: placementId, callbacks: {} });
410
+ },
411
+ screen: function (screenId) {
412
+ return new PLYPresentationBuilder({ screenId: screenId, callbacks: {} });
413
+ },
414
+ defaultSource: function () {
415
+ return new PLYPresentationBuilder({ callbacks: {} });
416
+ },
417
+ // Alias of defaultSource(), kept for parity with the iOS native API name.
418
+ default: function () {
419
+ return exports.presentation.defaultSource();
420
+ }
159
421
  };
160
422
 
161
423
  exports.purchaseWithPlanVendorId = function (planId, offerId, contentId, success, error) {
@@ -244,26 +506,30 @@ exports.userDidConsumeSubscriptionContent = function () {
244
506
  exec(() => {}, defaultError, 'Purchasely', 'userDidConsumeSubscriptionContent', []);
245
507
  };
246
508
 
247
- exports.userSubscriptions = function (success, error) {
248
- exec(success, defaultError, 'Purchasely', 'userSubscriptions', []);
509
+ // PAR-29: invalidateCache forces a fresh fetch instead of returning the cached list
510
+ // (native default false on both platforms).
511
+ exports.userSubscriptions = function (success, error, invalidateCache) {
512
+ exec(success, defaultError, 'Purchasely', 'userSubscriptions', [!!invalidateCache]);
249
513
  };
250
514
 
251
- exports.userSubscriptionsHistory = function (success, error) {
252
- exec(success, defaultError, 'Purchasely', 'userSubscriptionsHistory', []);
515
+ exports.userSubscriptionsHistory = function (success, error, invalidateCache) {
516
+ exec(success, defaultError, 'Purchasely', 'userSubscriptionsHistory', [!!invalidateCache]);
253
517
  };
254
518
 
255
519
  exports.setLanguage = function (language) {
256
520
  exec(() => {}, defaultError, 'Purchasely', 'setLanguage', [language]);
257
521
  };
258
522
 
259
- // Purchasely 6.0: close the displayed presentation.
260
- exports.closePresentation = function () {
261
- exec(() => {}, defaultError, 'Purchasely', 'closePresentation', []);
523
+ // PAR-19: closeAllScreens is the canonical name (matches the iOS/Android Purchasely-level
524
+ // API). success/error are optional.
525
+ exports.closeAllScreens = function (success, error) {
526
+ exec(success || (() => {}), error || defaultError, 'Purchasely', 'closeAllScreens', []);
262
527
  };
263
528
 
264
- // Purchasely 6.0: navigate back within the displayed presentation.
265
- exports.backPresentation = function () {
266
- exec(() => {}, defaultError, 'Purchasely', 'backPresentation', []);
529
+ // @deprecated use closeAllScreens instead; kept as an alias (same native action both
530
+ // platforms already call: Purchasely.closeAllScreens()).
531
+ exports.closePresentation = function (success, error) {
532
+ exports.closeAllScreens(success, error);
267
533
  };
268
534
 
269
535
  exports.setUserAttributeWithString = function (key, value, processLegalBasis) {
@@ -306,6 +572,22 @@ exports.userAttribute = function (key, success, error) {
306
572
  exec(success, error, 'Purchasely', 'userAttribute', [key]);
307
573
  };
308
574
 
575
+ // REC-12 / PAR-03: bulk read of every user attribute currently stored, with the same
576
+ // per-value type conversions as the single-key userAttribute(key) read.
577
+ exports.userAttributes = function (success, error) {
578
+ exec(success, error || defaultError, 'Purchasely', 'userAttributes', []);
579
+ };
580
+
581
+ // REC-12 / PAR-02: increment/decrement a numerical user attribute. value defaults to 1
582
+ // natively when omitted.
583
+ exports.incrementUserAttribute = function (key, value) {
584
+ exec(() => {}, defaultError, 'Purchasely', 'incrementUserAttribute', [key, value]);
585
+ };
586
+
587
+ exports.decrementUserAttribute = function (key, value) {
588
+ exec(() => {}, defaultError, 'Purchasely', 'decrementUserAttribute', [key, value]);
589
+ };
590
+
309
591
  exports.clearUserAttribute = function (key) {
310
592
  exec(() => {}, defaultError, 'Purchasely', 'clearUserAttribute', [key]);
311
593
  };
@@ -318,10 +600,64 @@ exports.clearBuiltInAttributes = function () {
318
600
  exec(() => {}, defaultError, 'Purchasely', 'clearBuiltInAttributes', []);
319
601
  }
320
602
 
603
+ // PAR-07: read-only accessors for the built-in (SDK-collected) attributes.
604
+ exports.getBuiltInAttributes = function (success, error) {
605
+ exec(success, error || defaultError, 'Purchasely', 'getBuiltInAttributes', []);
606
+ };
607
+
608
+ exports.getBuiltInAttribute = function (key, success, error) {
609
+ exec(success, error || defaultError, 'Purchasely', 'getBuiltInAttribute', [key]);
610
+ };
611
+
612
+ // REC-12 / PAR-04: whether the current user is anonymous (no userLogin call yet).
613
+ exports.isAnonymous = function (success, error) {
614
+ exec(success, error || defaultError, 'Purchasely', 'isAnonymous', []);
615
+ };
616
+
617
+ // PAR-05: Dynamic Offerings -- force a specific plan (and optionally offer) to be shown
618
+ // in a specific context, keyed by an app-chosen reference.
619
+ // Purchasely 6.0 (iOS 26.4+, Apple only): billing plan type of a subscription with a
620
+ // multi-period commitment (e.g. "monthly subscription with 12-month commitment"). Passed to
621
+ // setDynamicOffering and surfaced on the commitment fields below. Android always reports
622
+ // `unspecified`.
623
+ exports.BillingPlanType = { unspecified: 0, upFront: 1, monthly: 2 };
624
+
625
+ // Purchasely 6.0 commitment fields (iOS 26.4+ only; absent on Android and on plans without a
626
+ // commitment):
627
+ // - A plan object (from allProducts()/planWithIdentifier(), a presentation outcome's `plan`,
628
+ // and the interceptAction('purchase') `parameters.plan`) may carry `commitmentInfo`: an
629
+ // array of { billingPlanType (Number, see Purchasely.BillingPlanType), billingPrice
630
+ // (Number), billingPeriod (ISO 8601 duration string, e.g. "P1M"), totalPrice (Number),
631
+ // totalPeriod (ISO 8601 duration string, e.g. "P1Y"), totalDuration (Number of billing
632
+ // cycles) }.
633
+ // - A subscription object (from userSubscriptions()/userSubscriptionsHistory()) may carry
634
+ // `commitmentProgress`: { billingPeriodNumber (Number), totalBillingPeriods (Number),
635
+ // commitmentExpiresDate (ISO 8601 date string), commitmentPrice (Number) }.
636
+ exports.setDynamicOffering = function (reference, planVendorId, offerVendorId, billingPlanType, success, error) {
637
+ exec(success || (() => {}), error || defaultError, 'Purchasely', 'setDynamicOffering',
638
+ [reference, planVendorId, offerVendorId != null ? offerVendorId : null,
639
+ billingPlanType != null ? billingPlanType : 0]);
640
+ };
641
+
642
+ // Returns a list of { reference, planVendorId, offerVendorId }.
643
+ exports.getDynamicOfferings = function (success, error) {
644
+ exec(success, error || defaultError, 'Purchasely', 'getDynamicOfferings', []);
645
+ };
646
+
647
+ exports.removeDynamicOffering = function (reference) {
648
+ exec(() => {}, defaultError, 'Purchasely', 'removeDynamicOffering', [reference]);
649
+ };
650
+
651
+ exports.clearDynamicOfferings = function () {
652
+ exec(() => {}, defaultError, 'Purchasely', 'clearDynamicOfferings', []);
653
+ };
654
+
321
655
  exports.isEligibleForIntroOffer = function (planId, success, error) {
322
656
  exec(success, error, 'Purchasely', 'isEligibleForIntroOffer', [planId]);
323
657
  };
324
658
 
659
+ // REC-04: iOS-only (StoreKit promotional offer signing). On Android this is a no-op that
660
+ // resolves success (no signing is required there); no error is raised.
325
661
  exports.signPromotionalOffer = function (storeProductId, storeOfferId, success, error) {
326
662
  exec(success, error, 'Purchasely', 'signPromotionalOffer', [storeProductId, storeOfferId]);
327
663
  };
@@ -345,6 +681,11 @@ exports.LogLevel = {
345
681
  ERROR: 3,
346
682
  }
347
683
 
684
+ // WARNING: this list, iOS's CordovaPLYAttribute typedef, and Android's CordovaPLYAttribute
685
+ // enum class must be kept in strictly identical declaration order across all 3. The bridge
686
+ // matches an attribute by ordinal/symbol POSITION (not by the real native SDK's raw value,
687
+ // which does churn -- see the native PLYAttribute/Attribute enums), so appending a new
688
+ // attribute here always requires the matching append, in the same position, on both natives.
348
689
  exports.Attribute = {
349
690
  FIREBASE_APP_INSTANCE_ID: 0,
350
691
  AIRSHIP_CHANNEL_ID: 1,
@@ -367,6 +708,7 @@ exports.Attribute = {
367
708
  MOENGAGE_UNIQUE_ID: 18,
368
709
  ONESIGNAL_EXTERNAL_ID: 19,
369
710
  BATCH_CUSTOM_USER_ID: 20,
711
+ ONESIGNAL_USER_ID: 21, // ENM-02 / REC-11
370
712
  }
371
713
 
372
714
  exports.DataProcessingLegalBasis = {
@@ -413,17 +755,19 @@ exports.RunningMode = {
413
755
  }
414
756
 
415
757
  // Purchasely 6.0: the paywall action kinds handled by interceptAction.
758
+ // Keys are camelCase (RN/Flutter parity); values are the wire-format
759
+ // snake_case strings the native bridges map/emit -- unchanged.
416
760
  exports.PresentationAction = {
417
761
  close: 'close',
418
- close_all: 'close_all',
762
+ closeAll: 'close_all',
419
763
  login: 'login',
420
764
  navigate: 'navigate',
421
765
  purchase: 'purchase',
422
766
  restore: 'restore',
423
- open_presentation: 'open_presentation',
424
- open_placement: 'open_placement',
425
- promo_code: 'promo_code',
426
- web_checkout: 'web_checkout'
767
+ openPresentation: 'open_presentation',
768
+ openPlacement: 'open_placement',
769
+ promoCode: 'promo_code',
770
+ webCheckout: 'web_checkout'
427
771
  }
428
772
 
429
773
  // Purchasely 6.0: result returned by an interceptAction handler after handling