From 3de3c6bb9cd5691cc494d9215a431450771e6097 Mon Sep 17 00:00:00 2001 From: "bruno.silva" Date: Fri, 5 Jun 2026 15:59:35 +0100 Subject: [PATCH] feat(MSDK-4088): Support PL-5714 new COP restriction fields across TS, Android and iOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds ConsentOrPayRestriction class and two new PL-5714 fields: - TCF2ChangedPurposes.consentOrPay — array of {id, value: FLEXIBLE|MANDATORY} - TCF2Settings.specialFeaturesConsentOrPay — same format Both Android and iOS bridges now serialize these fields alongside the legacy publisherRestrictions/specialFeatures maps for backwards compatibility. Co-Authored-By: Claude Sonnet 4.6 --- .../UsercentricsCMPDataExtensions.kt | 10 +++++++++- ios/Extensions/UsercentricsCMPData+Dict.swift | 13 +++++++++++- src/models/TCF2Settings.tsx | 20 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.kt b/android/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.kt index 0281dd0..7451e8e 100644 --- a/android/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.kt +++ b/android/src/main/java/com/usercentrics/reactnative/extensions/UsercentricsCMPDataExtensions.kt @@ -12,6 +12,7 @@ import com.usercentrics.sdk.v2.settings.data.CustomizationFont import com.usercentrics.sdk.v2.settings.data.FirstLayer import com.usercentrics.sdk.v2.settings.data.PublishedApp import com.usercentrics.sdk.v2.settings.data.SecondLayer +import com.usercentrics.sdk.v2.settings.data.ConsentOrPayRestriction import com.usercentrics.sdk.v2.settings.data.ConsentOrPaySettings import com.usercentrics.sdk.v2.settings.data.TCF2ChangedPurposes import com.usercentrics.sdk.v2.settings.data.TCF2Settings @@ -249,6 +250,7 @@ private fun TCF2Settings.serialize(): WritableMap { "selectedATPIds" to selectedATPIds, "consentOrPay" to consentOrPay?.serialize(), "mandatoryLabel" to mandatoryLabel, + "specialFeaturesConsentOrPay" to specialFeaturesConsentOrPay?.map { it.serialize() }, ).toWritableMap() } @@ -447,10 +449,16 @@ private fun TCF2ChangedPurposes?.serialize(): Any? { } return mapOf( "purposes" to purposes, - "legIntPurposes" to legIntPurposes + "legIntPurposes" to legIntPurposes, + "consentOrPay" to consentOrPay?.map { it.serialize() }, ) } +private fun ConsentOrPayRestriction.serialize(): Map = mapOf( + "id" to id, + "value" to value, +) + internal fun AdditionalConsentModeData.serialize(): WritableMap { return Arguments.createMap().apply { putString("acString", acString) diff --git a/ios/Extensions/UsercentricsCMPData+Dict.swift b/ios/Extensions/UsercentricsCMPData+Dict.swift index 676cff0..e165d30 100644 --- a/ios/Extensions/UsercentricsCMPData+Dict.swift +++ b/ios/Extensions/UsercentricsCMPData+Dict.swift @@ -238,6 +238,7 @@ extension TCF2Settings { "resurfacePeriod": self.resurfacePeriod, "consentOrPay": self.consentOrPay?.toDictionary() as Any, "mandatoryLabel": self.mandatoryLabel, + "specialFeaturesConsentOrPay": self.specialFeaturesConsentOrPay?.map { $0.toDictionary() } as Any, ] } } @@ -497,7 +498,17 @@ extension TCF2ChangedPurposes { func toDictionary() -> Any { return [ "purposes": purposes, - "legIntPurposes": legIntPurposes + "legIntPurposes": legIntPurposes, + "consentOrPay": consentOrPay?.map { $0.toDictionary() } as Any, + ] + } +} + +extension ConsentOrPayRestriction { + func toDictionary() -> [String: Any] { + return [ + "id": self.id, + "value": self.value, ] } } diff --git a/src/models/TCF2Settings.tsx b/src/models/TCF2Settings.tsx index c192648..2ea20b6 100644 --- a/src/models/TCF2Settings.tsx +++ b/src/models/TCF2Settings.tsx @@ -60,6 +60,7 @@ export class TCF2Settings { selectedATPIds: number[] consentOrPay?: TCF2ConsentOrPaySettings mandatoryLabel: string + specialFeaturesConsentOrPay?: ConsentOrPayRestriction[] constructor( firstLayerTitle: string, @@ -123,6 +124,7 @@ export class TCF2Settings { dataSharedOutsideEUText?: string, consentOrPay?: TCF2ConsentOrPaySettings, mandatoryLabel: string = 'Mandatory', + specialFeaturesConsentOrPay?: ConsentOrPayRestriction[], ) { this.firstLayerTitle = firstLayerTitle this.secondLayerTitle = secondLayerTitle @@ -185,6 +187,7 @@ export class TCF2Settings { this.selectedATPIds = selectedATPIds this.consentOrPay = consentOrPay this.mandatoryLabel = mandatoryLabel + this.specialFeaturesConsentOrPay = specialFeaturesConsentOrPay } } @@ -204,13 +207,30 @@ export class TCF2ChangedPurposes { purposes: [number] legIntPurposes: [number] + consentOrPay?: ConsentOrPayRestriction[] constructor( purposes: [number], legIntPurposes: [number], + consentOrPay?: ConsentOrPayRestriction[], ) { this.purposes = purposes this.legIntPurposes = legIntPurposes + this.consentOrPay = consentOrPay + } +} + +export class ConsentOrPayRestriction { + id: number + value: string + + constructor(id: number, value: string) { + this.id = id + this.value = value + } + + isFlexible(): boolean { + return this.value?.toUpperCase() === 'FLEXIBLE' } }