@purchasely/cordova-plugin-purchasely 4.4.0 → 5.3.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.
@@ -16,7 +16,7 @@
16
16
  "@purchasely/cordova-plugin-puchasely-google": "file:../../purchasely-google",
17
17
  "@purchasely/cordova-plugin-purchasely": "file:..",
18
18
  "cordova-android": "^13.0.0",
19
- "cordova-ios": "^7.1.0",
19
+ "cordova-ios": "^7.0.1",
20
20
  "cordova-plugin-puchasely-google": "file:../../purchasely-google",
21
21
  "cordova-plugin-purchasely": "file:.."
22
22
  },
@@ -26,7 +26,8 @@
26
26
  "@purchasely/cordova-plugin-purchasely": {}
27
27
  },
28
28
  "platforms": [
29
- "android"
29
+ "android",
30
+ "ios"
30
31
  ]
31
32
  }
32
33
  }
@@ -17,6 +17,19 @@
17
17
  * under the License.
18
18
  */
19
19
 
20
+ function safeStringify(obj, space = 2) {
21
+ const seen = new WeakSet();
22
+ return JSON.stringify(obj, (key, value) => {
23
+ if (typeof value === 'object' && value !== null) {
24
+ if (seen.has(value)) {
25
+ return '[Circular Reference]';
26
+ }
27
+ seen.add(value);
28
+ }
29
+ return value;
30
+ }, space);
31
+ }
32
+
20
33
  // Wait for the deviceready event before using any of Cordova's device APIs.
21
34
  // See https://cordova.apache.org/docs/en/latest/cordova/events/events.html#deviceready
22
35
  document.addEventListener('deviceready', onDeviceReady, false);
@@ -68,12 +81,27 @@ function onPuchaselySdkReady() {
68
81
 
69
82
  Purchasely.addEventsListener((event) => {
70
83
  console.log("Event Name " + event.name);
71
- console.log(event.properties);
72
- console.log(event);
84
+ console.log(safeStringify(event.properties));
85
+ console.log(safeStringify(event));
73
86
  }, (error) => {
74
87
  console.log(error);
75
88
  });
76
89
 
90
+ Purchasely.addUserAttributeListener((attribute) => {
91
+ if (attribute.action == Purchasely.UserAttributeAction.ADD) {
92
+ console.log("ADD Attribute: " + attribute.key)
93
+ console.log("ADD Attribute: " + attribute.value)
94
+ console.log("ADD Attribute: " + attribute.source)
95
+ console.log("ADD Attribute: " + attribute.type)
96
+ } else if (attribute.action == Purchasely.UserAttributeAction.REMOVE) {
97
+ console.log("REMOVE Attribute: " + attribute.key)
98
+ console.log("REMOVE Attribute: " + attribute.source)
99
+ }
100
+
101
+ }, (error) => {
102
+ console.log("Error: " + error)
103
+ })
104
+
77
105
  Purchasely.purchasedSubscription(() => {
78
106
  console.log("Purchased performed, reload content to unlock.");
79
107
  });
@@ -86,7 +114,27 @@ function onPuchaselySdkReady() {
86
114
 
87
115
  Purchasely.setLogLevel(Purchasely.LogLevel.DEBUG);
88
116
 
89
- Purchasely.setAttribute(Purchasely.Attribute.FIREBASE_APP_INSTANCE_ID, "1");
117
+ Purchasely.setAttribute(Purchasely.Attribute.FIREBASE_APP_INSTANCE_ID, "firebase_instance_id");
118
+ Purchasely.setAttribute(Purchasely.Attribute.AIRSHIP_CHANNEL_ID, "airship_channel_id");
119
+ Purchasely.setAttribute(Purchasely.Attribute.AIRSHIP_USER_ID, "airship_user_id");
120
+ Purchasely.setAttribute(Purchasely.Attribute.BATCH_INSTALLATION_ID, "batch_installation_id");
121
+ Purchasely.setAttribute(Purchasely.Attribute.ADJUST_ID, "adjust_id");
122
+ Purchasely.setAttribute(Purchasely.Attribute.APPSFLYER_ID, "appsflyer_id");
123
+ Purchasely.setAttribute(Purchasely.Attribute.MIXPANEL_DISTINCT_ID, "mixpanel_distinct_id");
124
+ Purchasely.setAttribute(Purchasely.Attribute.CLEVER_TAP_ID, "clever_tap_id");
125
+ Purchasely.setAttribute(Purchasely.Attribute.SENDINBLUE_USER_EMAIL, "sendinblue_user_email");
126
+ Purchasely.setAttribute(Purchasely.Attribute.ITERABLE_USER_EMAIL, "iterable_user_email");
127
+ Purchasely.setAttribute(Purchasely.Attribute.ITERABLE_USER_ID, "iterable_user_id");
128
+ Purchasely.setAttribute(Purchasely.Attribute.AT_INTERNET_ID_CLIENT, "at_internet_id_client");
129
+ Purchasely.setAttribute(Purchasely.Attribute.MPARTICLE_USER_ID, "mparticle_user_id");
130
+ Purchasely.setAttribute(Purchasely.Attribute.CUSTOMERIO_USER_ID, "customerio_user_id");
131
+ Purchasely.setAttribute(Purchasely.Attribute.CUSTOMERIO_USER_EMAIL, "customerio_user_email");
132
+ Purchasely.setAttribute(Purchasely.Attribute.BRANCH_USER_DEVELOPER_IDENTITY, "branch_user_developer_identity");
133
+ Purchasely.setAttribute(Purchasely.Attribute.AMPLITUDE_USER_ID, "amplitude_user_id");
134
+ Purchasely.setAttribute(Purchasely.Attribute.AMPLITUDE_DEVICE_ID, "amplitude_device_id");
135
+ Purchasely.setAttribute(Purchasely.Attribute.MOENGAGE_UNIQUE_ID, "moengage_unique_id");
136
+ Purchasely.setAttribute(Purchasely.Attribute.ONESIGNAL_EXTERNAL_ID, "onesignal_external_id");
137
+ Purchasely.setAttribute(Purchasely.Attribute.BATCH_CUSTOM_USER_ID, "batch_custom_user_id");
90
138
  Purchasely.setAttribute(Purchasely.Attribute.BATCH_INSTALLATION_ID, "testBatch1");
91
139
 
92
140
  Purchasely.readyToOpenDeeplink(true);
@@ -119,26 +167,55 @@ function onPuchaselySdkReady() {
119
167
  console.log("Error with purchase : " + error);
120
168
  });
121
169
 
170
+ Purchasely.setUserAttributeWithString("key_string", "value_string");
171
+ Purchasely.setUserAttributeWithBoolean("key_boolean", true);
172
+ Purchasely.setUserAttributeWithInt("key_int", 7);
173
+ Purchasely.setUserAttributeWithDouble("key_double", 4.5);
174
+ Purchasely.setUserAttributeWithDate("key_date", new Date().toISOString());
175
+
176
+ Purchasely.clearUserAttributes();
177
+
122
178
  Purchasely.setUserAttributeWithString("key_string", "value_string");
123
179
  Purchasely.setUserAttributeWithBoolean("key_boolean", true);
124
180
  Purchasely.setUserAttributeWithInt("key_int", 7);
125
181
  Purchasely.setUserAttributeWithDouble("key_double", 4.5);
126
182
  Purchasely.setUserAttributeWithDate("key_date", new Date().toISOString());
127
183
 
184
+ Purchasely.setUserAttributeWithStringArray("key_string_array", ["value1", "value2"]);
185
+ Purchasely.setUserAttributeWithIntArray("key_int_array", [1, 2, 3]);
186
+ Purchasely.setUserAttributeWithDoubleArray("key_double_array", [1.1, 2.2, 3.3]);
187
+ Purchasely.setUserAttributeWithBooleanArray("key_boolean_array", [true, false, true]);
188
+
128
189
  Purchasely.userAttribute("key_string", value => {
129
- console.log("User attribute string " + value);
190
+ console.log("User attribute string: " + value);
130
191
  });
131
192
 
132
193
  Purchasely.userAttribute("key_boolean", value => {
133
- console.log("User attribute boolean " + value);
194
+ console.log("User attribute boolean: " + value);
134
195
  });
135
196
 
136
197
  Purchasely.userAttribute("key_int", value => {
137
- console.log("User attribute int " + value);
198
+ console.log("User attribute int: " + value);
138
199
  });
139
200
 
140
201
  Purchasely.userAttribute("key_double", value => {
141
- console.log("User attribute double " + value);
202
+ console.log("User attribute double: " + value);
203
+ });
204
+
205
+ Purchasely.userAttribute("key_string_array", value => {
206
+ console.log("User attribute string array: " + value);
207
+ });
208
+
209
+ Purchasely.userAttribute("key_int_array", value => {
210
+ console.log("User attribute int array: " + value);
211
+ });
212
+
213
+ Purchasely.userAttribute("key_double_array", value => {
214
+ console.log("User attribute double array: " + value);
215
+ });
216
+
217
+ Purchasely.userAttribute("key_boolean_array", value => {
218
+ console.log("User attribute boolean array: " + value);
142
219
  });
143
220
 
144
221
  Purchasely.userAttribute("key_date", value => {
@@ -155,6 +232,8 @@ function onPuchaselySdkReady() {
155
232
  });
156
233
  });
157
234
 
235
+ Purchasely.removeUserAttributeListener();
236
+
158
237
  Purchasely.setPaywallActionInterceptor((result) => {
159
238
  console.log(result);
160
239
  console.log('Received action from paywall ' + result.info.presentationId);
@@ -193,6 +272,8 @@ function onPuchaselySdkReady() {
193
272
  Purchasely.onProcessAction(true);
194
273
  }
195
274
  });
275
+
276
+ Purchasely.clearBuiltInAttributes();
196
277
  }
197
278
 
198
279
  function openPresentation() {
@@ -219,7 +300,7 @@ function fetchPresentation() {
219
300
  'onboarding', //placementId
220
301
  null, //contentId
221
302
  (presentation) => {
222
- console.log(presentation);
303
+ console.log(safeStringify(presentation));
223
304
  Purchasely.presentPresentation(presentation, false, null,
224
305
  (callback) => {
225
306
  console.log(callback);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purchasely/cordova-plugin-purchasely",
3
- "version": "4.4.0",
3
+ "version": "5.3.0",
4
4
  "description": "Purchasely is a solution to ease the integration and boost your In-App Purchases & Subscriptions on the App Store, Google Play Store, Amazon Appstore and Huawei App Gallery.",
5
5
  "cordova": {
6
6
  "id": "@purchasely/cordova-plugin-purchasely",
package/plugin.xml CHANGED
@@ -1,5 +1,5 @@
1
1
  <?xml version='1.0' encoding='utf-8'?>
2
- <plugin id="@purchasely/cordova-plugin-purchasely" version="4.4.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <plugin id="@purchasely/cordova-plugin-purchasely" version="5.3.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
3
3
  <name>Purchasely</name>
4
4
  <js-module name="Purchasely" src="www/Purchasely.js">
5
5
  <clobbers target="Purchasely" />
@@ -18,6 +18,8 @@
18
18
  <source-file src="src/ios/CDVPurchasely.m" />
19
19
  <header-file src="src/ios/CDVPurchasely+Events.h" />
20
20
  <source-file src="src/ios/CDVPurchasely+Events.m" />
21
+ <header-file src="src/ios/CDVPurchasely+UserAttributes.h" />
22
+ <source-file src="src/ios/CDVPurchasely+UserAttributes.m" />
21
23
  <header-file src="src/ios/Hybrid/Purchasely_Hybrid.h" />
22
24
  <header-file src="src/ios/Hybrid/UIViewController+Hybrid.h" />
23
25
  <source-file src="src/ios/Hybrid/UIViewController+Hybrid.m" />
@@ -36,7 +38,7 @@
36
38
  <source url="https://github.com/CocoaPods/Specs.git"/>
37
39
  </config>
38
40
  <pods use-frameworks="true">
39
- <pod name="Purchasely" spec="4.4.2"/>
41
+ <pod name="Purchasely" spec="5.3.7"/>
40
42
  </pods>
41
43
  </podspec>
42
44
  </platform>
@@ -61,7 +63,7 @@
61
63
  android:theme="@style/Theme.AppCompat.Light.NoActionBar">
62
64
  </activity>
63
65
  </config-file>
64
- <framework src="io.purchasely:core:4.4.2" />
66
+ <framework src="io.purchasely:core:5.3.7" />
65
67
  <source-file src="src/android/PurchaselyPlugin.kt" target-dir="java/cordova/plugin/purchasely" />
66
68
  <source-file src="src/android/PLYProductActivity.kt" target-dir="java/cordova/plugin/purchasely" />
67
69
  <source-file src="src/android/PLYSubscriptionsActivity.java" target-dir="src/cordova/plugin/purchasely" />
@@ -10,7 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
10
10
  import androidx.core.view.WindowCompat
11
11
  import cordova.plugin.purchasely.PurchaselyPlugin.ProductActivity
12
12
  import io.purchasely.ext.PLYPresentation
13
- import io.purchasely.ext.PLYPresentationViewProperties
13
+ import io.purchasely.ext.PLYPresentationProperties
14
14
  import io.purchasely.ext.PLYProductViewResult
15
15
  import io.purchasely.ext.Purchasely
16
16
  import io.purchasely.models.PLYPlan
@@ -77,21 +77,21 @@ class PLYProductActivity : AppCompatActivity() {
77
77
  presentation = intent.extras?.getParcelable("presentation")
78
78
 
79
79
  paywallView = if(presentation != null) {
80
- presentation?.buildView(this, viewProperties = PLYPresentationViewProperties(onClose = {
80
+ presentation?.buildView(this, properties = PLYPresentationProperties(onClose = {
81
81
  container.removeAllViews()
82
82
  supportFinishAfterTransition()
83
83
  }), callback)
84
84
  } else {
85
85
  Purchasely.presentationView(
86
- this@PLYProductActivity,
87
- PLYPresentationViewProperties(
86
+ context = this@PLYProductActivity,
87
+ properties = PLYPresentationProperties(
88
88
  placementId = placementId,
89
89
  contentId = contentId,
90
90
  presentationId = presentationId,
91
91
  planId = planId,
92
92
  productId = productId,
93
93
  onLoaded = { isLoaded ->
94
- if(!isLoaded) return@PLYPresentationViewProperties
94
+ if(!isLoaded) return@PLYPresentationProperties
95
95
 
96
96
  val backgroundPaywall = paywallView?.findViewById<FrameLayout>(io.purchasely.R.id.content)?.background
97
97
  if(backgroundPaywall != null) {
@@ -103,7 +103,7 @@ class PLYProductActivity : AppCompatActivity() {
103
103
  supportFinishAfterTransition()
104
104
  }
105
105
  ),
106
- callback
106
+ callback = callback
107
107
  )
108
108
  }
109
109
 
@@ -142,7 +142,7 @@ class PLYProductActivity : AppCompatActivity() {
142
142
  companion object {
143
143
  @JvmStatic
144
144
  fun newIntent(activity: Activity?,
145
- properties: PLYPresentationViewProperties = PLYPresentationViewProperties(),
145
+ properties: PLYPresentationProperties = PLYPresentationProperties(),
146
146
  isFullScreen: Boolean = false,
147
147
  backgroundColor: String? = null) = Intent(activity, PLYProductActivity::class.java).apply {
148
148
  //remove old activity if still referenced to avoid issues
@@ -16,7 +16,7 @@ import io.purchasely.ext.PLYEvent
16
16
  import io.purchasely.ext.PLYPresentation
17
17
  import io.purchasely.ext.PLYPresentationAction
18
18
  import io.purchasely.ext.PLYPresentationType
19
- import io.purchasely.ext.PLYPresentationViewProperties
19
+ import io.purchasely.ext.PLYPresentationProperties
20
20
  import io.purchasely.ext.PLYProductViewResult
21
21
  import io.purchasely.ext.PLYRunningMode
22
22
  import io.purchasely.ext.PLYRunningMode.Full
@@ -29,6 +29,9 @@ import io.purchasely.ext.Purchasely
29
29
  import io.purchasely.ext.State
30
30
  import io.purchasely.ext.StoreType
31
31
  import io.purchasely.ext.SubscriptionsListener
32
+ import io.purchasely.ext.UserAttributeListener
33
+ import io.purchasely.storage.userData.PLYUserAttributeSource
34
+ import io.purchasely.storage.userData.PLYUserAttributeType
32
35
  import io.purchasely.models.PLYError
33
36
  import io.purchasely.models.PLYPlan
34
37
  import io.purchasely.models.PLYPresentationPlan
@@ -76,6 +79,8 @@ class PurchaselyPlugin : CordovaPlugin() {
76
79
 
77
80
  "close" -> close()
78
81
  "addEventsListener" -> addEventsListener(callbackContext)
82
+ "addUserAttributeListener" -> addUserAttributesListener(callbackContext)
83
+ "removeUserAttributeListener" -> removeUserAttributesListener()
79
84
  "removeEventsListener" -> removeEventsListener()
80
85
  "getAnonymousUserId" -> getAnonymousUserId(callbackContext)
81
86
  "userLogin" -> userLogin(getStringFromJson(args.getString(0)), callbackContext)
@@ -170,9 +175,14 @@ class PurchaselyPlugin : CordovaPlugin() {
170
175
  "setUserAttributeWithInt" -> setUserAttributeWithInt(getStringFromJson(args.getString(0)), args.getInt(1))
171
176
  "setUserAttributeWithDouble" -> setUserAttributeWithDouble(getStringFromJson(args.getString(0)), args.getDouble(1))
172
177
  "setUserAttributeWithDate" -> setUserAttributeWithDate(getStringFromJson(args.getString(0)), getStringFromJson(args.getString(1)))
178
+ "setUserAttributeWithStringArray" -> setUserAttributeWithStringArray(getStringFromJson(args.getString(0)), args.getJSONArray(1))
179
+ "setUserAttributeWithIntArray" -> setUserAttributeWithIntArray(getStringFromJson(args.getString(0)), args.getJSONArray(1))
180
+ "setUserAttributeWithDoubleArray" -> setUserAttributeWithDoubleArray(getStringFromJson(args.getString(0)), args.getJSONArray(1))
181
+ "setUserAttributeWithBooleanArray" -> setUserAttributeWithBooleanArray(getStringFromJson(args.getString(0)), args.getJSONArray(1))
173
182
  "userAttribute" -> userAttribute(getStringFromJson(args.getString(0)), callbackContext)
174
183
  "clearUserAttribute" -> clearUserAttribute(getStringFromJson(args.getString(0)))
175
184
  "clearUserAttributes" -> clearUserAttributes()
185
+ "clearBuiltInAttributes" -> clearBuiltInAttributes()
176
186
  "isEligibleForIntroOffer" -> isEligibleForIntroOffer(getStringFromJson(args.getString(0)), callbackContext)
177
187
  "signPromotionalOffer" -> signPromotionalOffer(getStringFromJson(args.getString(0)), getStringFromJson(args.getString(1)), callbackContext)
178
188
  else -> return false
@@ -270,6 +280,40 @@ class PurchaselyPlugin : CordovaPlugin() {
270
280
  Purchasely.close()
271
281
  }
272
282
 
283
+ private fun addUserAttributesListener(callbackContext: CallbackContext) {
284
+ attributesCallback = callbackContext
285
+ Purchasely.userAttributeListener = object: UserAttributeListener {
286
+ override fun onUserAttributeSet(
287
+ key: String,
288
+ type: PLYUserAttributeType,
289
+ value: Any,
290
+ source: PLYUserAttributeSource
291
+ ) {
292
+ val map = HashMap<String, Any?>()
293
+ map["action"] = "add"
294
+ map["key"] = key
295
+ map["type"] = type.name
296
+ map["value"] = value
297
+ map["source"] = source.name
298
+
299
+ val pluginResult = PluginResult(PluginResult.Status.OK, JSONObject(map))
300
+ pluginResult.keepCallback = true
301
+ attributesCallback?.sendPluginResult(pluginResult)
302
+ }
303
+
304
+ override fun onUserAttributeRemoved(key: String, source: PLYUserAttributeSource) {
305
+ val map = HashMap<String, Any?>()
306
+ map["action"] = "remove"
307
+ map["key"] = key
308
+ map["source"] = source.name
309
+
310
+ val pluginResult = PluginResult(PluginResult.Status.OK, JSONObject(map))
311
+ pluginResult.keepCallback = true
312
+ attributesCallback?.sendPluginResult(pluginResult)
313
+ }
314
+ }
315
+ }
316
+
273
317
  private fun addEventsListener(callbackContext: CallbackContext) {
274
318
  eventsCallback = callbackContext
275
319
  Purchasely.eventListener = object: EventListener {
@@ -284,6 +328,11 @@ class PurchaselyPlugin : CordovaPlugin() {
284
328
  }
285
329
  }
286
330
 
331
+ private fun removeUserAttributesListener() {
332
+ attributesCallback = null
333
+ Purchasely.userAttributeListener = null
334
+ }
335
+
287
336
  private fun removeEventsListener() {
288
337
  eventsCallback = null
289
338
  Purchasely.eventListener = null
@@ -331,7 +380,34 @@ class PurchaselyPlugin : CordovaPlugin() {
331
380
  private fun setAttribute(attribute: Int, value: String?) {
332
381
  if(value == null) return
333
382
 
334
- Purchasely.setAttribute(Attribute.values()[attribute], value)
383
+ val attributeKey = when (attribute) {
384
+ CordovaPLYAttribute.firebase_app_instance_id.ordinal -> Attribute.FIREBASE_APP_INSTANCE_ID
385
+ CordovaPLYAttribute.airship_channel_id.ordinal -> Attribute.AIRSHIP_CHANNEL_ID
386
+ CordovaPLYAttribute.airship_user_id.ordinal -> Attribute.AIRSHIP_USER_ID
387
+ CordovaPLYAttribute.batch_installation_id.ordinal -> Attribute.BATCH_INSTALLATION_ID
388
+ CordovaPLYAttribute.adjust_id.ordinal -> Attribute.ADJUST_ID
389
+ CordovaPLYAttribute.appsflyer_id.ordinal -> Attribute.APPSFLYER_ID
390
+ CordovaPLYAttribute.mixpanel_distinct_id.ordinal -> Attribute.MIXPANEL_DISTINCT_ID
391
+ CordovaPLYAttribute.clever_tap_id.ordinal -> Attribute.CLEVER_TAP_ID
392
+ CordovaPLYAttribute.sendinblueUserEmail.ordinal -> Attribute.SENDINBLUE_USER_EMAIL
393
+ CordovaPLYAttribute.iterableUserEmail.ordinal -> Attribute.ITERABLE_USER_EMAIL
394
+ CordovaPLYAttribute.iterableUserId.ordinal -> Attribute.ITERABLE_USER_ID
395
+ CordovaPLYAttribute.atInternetIdClient.ordinal -> Attribute.AT_INTERNET_ID_CLIENT
396
+ CordovaPLYAttribute.mParticleUserId.ordinal -> Attribute.MPARTICLE_USER_ID
397
+ CordovaPLYAttribute.customerioUserId.ordinal -> Attribute.CUSTOMERIO_USER_ID
398
+ CordovaPLYAttribute.customerioUserEmail.ordinal -> Attribute.CUSTOMERIO_USER_EMAIL
399
+ CordovaPLYAttribute.branchUserDeveloperIdentity.ordinal -> Attribute.BRANCH_USER_DEVELOPER_IDENTITY
400
+ CordovaPLYAttribute.amplitudeUserId.ordinal -> Attribute.AMPLITUDE_USER_ID
401
+ CordovaPLYAttribute.amplitudeDeviceId.ordinal -> Attribute.AMPLITUDE_DEVICE_ID
402
+ CordovaPLYAttribute.moengageUniqueId.ordinal -> Attribute.MOENGAGE_UNIQUE_ID
403
+ CordovaPLYAttribute.oneSignalExternalId.ordinal -> Attribute.ONESIGNAL_EXTERNAL_ID
404
+ CordovaPLYAttribute.batchCustomUserId.ordinal -> Attribute.BATCH_CUSTOM_USER_ID
405
+ else -> null
406
+ }
407
+
408
+ attributeKey?.let {
409
+ Purchasely.setAttribute(attribute = it, value = value)
410
+ }
335
411
  }
336
412
 
337
413
  private fun setDefaultPresentationResultHandler(callbackContext: CallbackContext) {
@@ -430,7 +506,7 @@ class PurchaselyPlugin : CordovaPlugin() {
430
506
  presentationId: String?,
431
507
  contentId: String?,
432
508
  callbackContext: CallbackContext) {
433
- val properties = PLYPresentationViewProperties(
509
+ val properties = PLYPresentationProperties(
434
510
  placementId = placementId,
435
511
  presentationId = presentationId,
436
512
  contentId = contentId)
@@ -461,7 +537,7 @@ class PurchaselyPlugin : CordovaPlugin() {
461
537
  Pair("planVendorId", planVendorId),
462
538
  Pair("storeProductId", storeProductId),
463
539
  Pair("basePlanId", basePlanId),
464
- Pair("offerId", offerId)
540
+ //Pair("offerId", offerId)
465
541
  )
466
542
  }
467
543
 
@@ -486,10 +562,17 @@ class PurchaselyPlugin : CordovaPlugin() {
486
562
  purchaseCallback = callbackContext
487
563
 
488
564
  cordova.activity.let { activity ->
489
- val intent = PLYProductActivity.newIntent(activity, PLYPresentationViewProperties(), isFullScreen, loadingBackgroundColor).apply {
490
- putExtra("presentation", presentation)
565
+ if (presentation.flowId != null) {
566
+ presentation.display(activity) { result, plan ->
567
+ sendPurchaseResult(result, plan)
568
+ }
569
+ } else {
570
+ // Open legacy Activity for now if not a flow
571
+ val intent = PLYProductActivity.newIntent(activity, PLYPresentationProperties(), isFullScreen, loadingBackgroundColor).apply {
572
+ putExtra("presentation", presentation)
573
+ }
574
+ activity.startActivity(intent)
491
575
  }
492
- activity.startActivity(intent)
493
576
  }
494
577
 
495
578
  }
@@ -758,6 +841,58 @@ class PurchaselyPlugin : CordovaPlugin() {
758
841
  )
759
842
  }
760
843
 
844
+ fun setUserAttributeWithStringArray(key: String?, value: JSONArray?) {
845
+ if(key == null || value == null) return
846
+ val list = mutableListOf<String>()
847
+ for (i in 0 until value.length()) {
848
+ try {
849
+ list.add(value.getString(i))
850
+ } catch (e: JSONException) {
851
+ Log.e("Purchasely", "Error in string array" + e.message, e)
852
+ }
853
+ }
854
+ Purchasely.setUserAttribute(key, list.toTypedArray())
855
+ }
856
+
857
+ fun setUserAttributeWithIntArray(key: String?, value: JSONArray?) {
858
+ if(key == null || value == null) return
859
+ val list = mutableListOf<Int>()
860
+ for (i in 0 until value.length()) {
861
+ try {
862
+ list.add(value.getInt(i))
863
+ } catch (e: JSONException) {
864
+ Log.e("Purchasely", "Error in int array" + e.message, e)
865
+ }
866
+ }
867
+ Purchasely.setUserAttribute(key, list.toTypedArray())
868
+ }
869
+
870
+ fun setUserAttributeWithDoubleArray(key: String?, value: JSONArray?) {
871
+ if (key == null || value == null) return
872
+ val list = mutableListOf<Float>()
873
+ for (i in 0 until value.length()) {
874
+ try {
875
+ list.add(value.getDouble(i).toFloat())
876
+ } catch (e: JSONException) {
877
+ Log.e("Purchasely", "Error in double array: ${e.message}", e)
878
+ }
879
+ }
880
+ Purchasely.setUserAttribute(key, list.toTypedArray())
881
+ }
882
+
883
+ fun setUserAttributeWithBooleanArray(key: String?, value: JSONArray?) {
884
+ if(key == null || value == null) return
885
+ val list = mutableListOf<Boolean>()
886
+ for (i in 0 until value.length()) {
887
+ try {
888
+ list.add(value.getBoolean(i))
889
+ } catch (e: JSONException) {
890
+ Log.e("Purchasely", "Error in boolean array" + e.message, e)
891
+ }
892
+ }
893
+ Purchasely.setUserAttribute(key, list.toTypedArray())
894
+ }
895
+
761
896
  fun setUserAttributeWithString(key: String?, value: String?) {
762
897
  if(key == null || value == null) return
763
898
  Purchasely.setUserAttribute(key, value)
@@ -800,14 +935,16 @@ class PurchaselyPlugin : CordovaPlugin() {
800
935
  fun userAttribute(key: String?, callbackContext: CallbackContext) {
801
936
  if(key == null) return
802
937
  val result = getUserAttributeValueForCordova(Purchasely.userAttribute(key))
803
- if(result != null) {
804
- callbackContext.success(getUserAttributeValueForCordova(result))
805
- } else {
806
- callbackContext.error("No user attribute found with $key")
938
+ when (result) {
939
+ is JSONArray -> callbackContext.success(result)
940
+ is String -> callbackContext.success(result)
941
+ is Int -> callbackContext.success(result)
942
+ is Boolean -> callbackContext.success(if (result) 1 else 0)
943
+ else -> callbackContext.error("No user attribute found with $key")
807
944
  }
808
945
  }
809
946
 
810
- private fun getUserAttributeValueForCordova(value: Any?): String? {
947
+ private fun getUserAttributeValueForCordova(value: Any?): Any? {
811
948
  return when (value) {
812
949
  is Date -> {
813
950
  val format = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -822,11 +959,18 @@ class PurchaselyPlugin : CordovaPlugin() {
822
959
  ""
823
960
  }
824
961
  }
825
- is Int -> value.toString()
962
+ is Int -> value
826
963
  //awful but to keep same precision so 1.2f = 1.2 double and not 1.20000056
827
- is Float -> value.toString().toDouble().toString()
964
+ is Float -> value.toString().toDouble()
828
965
  is String -> value
829
- is Boolean -> value.toString()
966
+ is Boolean -> value
967
+ is Array<*> -> {
968
+ val jsonArray = JSONArray()
969
+ value.forEach {
970
+ jsonArray.put(getUserAttributeValueForCordova(it))
971
+ }
972
+ jsonArray
973
+ }
830
974
  else -> null
831
975
  }
832
976
  }
@@ -840,6 +984,10 @@ class PurchaselyPlugin : CordovaPlugin() {
840
984
  Purchasely.clearUserAttributes()
841
985
  }
842
986
 
987
+ fun clearBuiltInAttributes() {
988
+ Purchasely.clearBuiltInAttributes()
989
+ }
990
+
843
991
  private fun isEligibleForIntroOffer(planId: String?, callbackContext: CallbackContext) {
844
992
  Purchasely.plan(planId,
845
993
  onSuccess = { plan ->
@@ -906,6 +1054,7 @@ class PurchaselyPlugin : CordovaPlugin() {
906
1054
  var defaultCallback: CallbackContext? = null
907
1055
  var purchaseCallback: CallbackContext? = null
908
1056
  var eventsCallback: CallbackContext? = null
1057
+ var attributesCallback: CallbackContext? = null
909
1058
  var productActivity: ProductActivity? = null
910
1059
 
911
1060
  var interceptorActivity: WeakReference<Activity>? = null
@@ -955,4 +1104,53 @@ class PurchaselyPlugin : CordovaPlugin() {
955
1104
  return map
956
1105
  }
957
1106
  }
1107
+
1108
+ // WARNING: This enum must be strictly identical to the one in the JS side (Purchasely.js).
1109
+ enum class CordovaPLYAttribute {
1110
+ firebase_app_instance_id,
1111
+ airship_channel_id,
1112
+ airship_user_id,
1113
+ batch_installation_id,
1114
+ adjust_id,
1115
+ appsflyer_id,
1116
+ mixpanel_distinct_id,
1117
+ clever_tap_id,
1118
+ sendinblueUserEmail,
1119
+ iterableUserEmail,
1120
+ iterableUserId,
1121
+ atInternetIdClient,
1122
+ mParticleUserId,
1123
+ customerioUserId,
1124
+ customerioUserEmail,
1125
+ branchUserDeveloperIdentity,
1126
+ amplitudeUserId,
1127
+ amplitudeDeviceId,
1128
+ moengageUniqueId,
1129
+ oneSignalExternalId,
1130
+ batchCustomUserId,
1131
+
1132
+ /*
1133
+ FIREBASE_APP_INSTANCE_ID: 0,
1134
+ AIRSHIP_CHANNEL_ID: 1,
1135
+ AIRSHIP_USER_ID: 2,
1136
+ BATCH_INSTALLATION_ID: 3,
1137
+ ADJUST_ID: 4,
1138
+ APPSFLYER_ID: 5,
1139
+ MIXPANEL_DISTINCT_ID: 6,
1140
+ CLEVER_TAP_ID: 7,
1141
+ SENDINBLUE_USER_EMAIL: 8,
1142
+ ITERABLE_USER_EMAIL: 9,
1143
+ ITERABLE_USER_ID: 10,
1144
+ AT_INTERNET_ID_CLIENT: 11,
1145
+ MPARTICLE_USER_ID: 12,
1146
+ CUSTOMERIO_USER_ID: 13,
1147
+ CUSTOMERIO_USER_EMAIL: 14,
1148
+ BRANCH_USER_DEVELOPER_IDENTITY: 15,
1149
+ AMPLITUDE_USER_ID: 16,
1150
+ AMPLITUDE_DEVICE_ID: 17,
1151
+ MOENGAGE_UNIQUE_ID: 18,
1152
+ ONESIGNAL_EXTERNAL_ID: 19,
1153
+ BATCH_CUSTOM_USER_ID: 20,
1154
+ */
1155
+ }
958
1156
  }
@@ -0,0 +1,35 @@
1
+ //
2
+ // CDVPurchasely+UserAttributes.h
3
+ // Purchasely Cordova Plugin
4
+ //
5
+ // Created by Florian HUET on 26/03/2025.
6
+ //
7
+
8
+ #import <Cordova/CDVPlugin.h>
9
+ #import "CDVPurchasely.h"
10
+
11
+ @interface CDVPurchasely (UserAttributes) {
12
+
13
+ }
14
+
15
+ - (void)onUserAttributeSetWithKey:(NSString * _Nonnull)key
16
+ type:(enum PLYUserAttributeType)type
17
+ value:(id _Nullable)value
18
+ source:(enum PLYUserAttributeSource)source;
19
+
20
+ - (void)onUserAttributeRemovedWithKey:(NSString * _Nonnull)key
21
+ source:(enum PLYUserAttributeSource)source;
22
+
23
+ @end
24
+
25
+ @interface PLYUserAttributeTypeHelper : NSObject
26
+
27
+ + (NSString *_Nullable)stringFromUserAttributeType:(PLYUserAttributeType)type;
28
+
29
+ @end
30
+
31
+ @interface PLYUserAttributeSourceHelper : NSObject
32
+
33
+ + (NSString *_Nullable)stringFromUserAttributeSource:(PLYUserAttributeSource)source;
34
+
35
+ @end