Skip to content

Commit 4c2549f

Browse files
committed
Add mandate text to payment option
1 parent f8612c8 commit 4c2549f

5 files changed

Lines changed: 65 additions & 5 deletions

File tree

android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,15 @@ class EmbeddedPaymentElementViewManager :
195195
),
196196
).allowsRemovalOfLastSavedPaymentMethod(allowsRemovalOfLastSavedPaymentMethod)
197197
.cardBrandAcceptance(mapToCardBrandAcceptance(toBundleObject(map)))
198+
.embeddedViewDisplaysMandateText(
199+
if (map.hasKey("embeddedViewDisplaysMandateText") &&
200+
map.getType("embeddedViewDisplaysMandateText") == ReadableType.Boolean
201+
) {
202+
map.getBoolean("embeddedViewDisplaysMandateText")
203+
} else {
204+
true // default value
205+
},
206+
)
198207
// Serialize original ReadableMap because toBundleObject cannot keep arrays of objects
199208
.customPaymentMethods(
200209
parseCustomPaymentMethods(

android/src/main/java/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import androidx.compose.ui.text.AnnotatedString
2+
import androidx.core.text.toHtml
3+
import androidx.core.text.toSpannable
14
import com.facebook.react.bridge.Arguments
25
import com.facebook.react.bridge.WritableMap
36
import com.reactnativestripesdk.utils.mapFromPaymentSheetBillingDetails
@@ -12,4 +15,16 @@ fun EmbeddedPaymentElement.PaymentOptionDisplayData.toWritableMap(): WritableMap
1215
putString("label", label)
1316
putString("paymentMethodType", paymentMethodType)
1417
putMap("billingDetails", mapFromPaymentSheetBillingDetails(billingDetails))
18+
19+
val mandateTextHTML = mandateText?.toHtmlString()
20+
if (mandateTextHTML != null) {
21+
putString("mandateText", mandateTextHTML)
22+
} else {
23+
putNull("mandateText")
24+
}
1525
}
26+
27+
fun AnnotatedString.toHtmlString(): String {
28+
val spanned = this.toSpannable()
29+
return spanned.toHtml()
30+
}

ios/PaymentOptionDisplayData+ReactNative.swift

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ extension EmbeddedPaymentElement.PaymentOptionDisplayData {
1616
guard let data = image.pngData() else { return "" }
1717
return data.base64EncodedString()
1818
}()
19-
19+
2020
// Convert BillingDetails to a dictionary
2121
let billingDetailsDict: [String: Any] = {
2222
guard let billing = billingDetails else {
2323
return [:]
2424
}
25-
25+
2626
// Extract address
2727
let addressDict: [String: Any] = {
2828
let addr = billing.address
@@ -35,21 +35,41 @@ extension EmbeddedPaymentElement.PaymentOptionDisplayData {
3535
"state": addr.state ?? ""
3636
]
3737
}()
38-
38+
3939
return [
4040
"name": billing.name ?? "",
4141
"email": billing.email ?? "",
4242
"phone": billing.phone ?? "",
4343
"address": addressDict
4444
]
4545
}()
46-
46+
47+
// Convert NSAttributedString mandateText to HTML
48+
let mandateTextHTML: String? = {
49+
guard let mandateText = mandateText else { return nil }
50+
51+
do {
52+
let htmlData = try mandateText.data(
53+
from: NSRange(location: 0, length: mandateText.length),
54+
documentAttributes: [
55+
.documentType: NSAttributedString.DocumentType.html,
56+
.characterEncoding: String.Encoding.utf8.rawValue
57+
]
58+
)
59+
return String(data: htmlData, encoding: .utf8)
60+
} catch {
61+
// Fallback to plain string if HTML conversion fails
62+
return mandateText.string
63+
}
64+
}()
65+
4766
// Return as a dictionary
4867
return [
4968
"image": imageBase64,
5069
"label": label,
5170
"billingDetails": billingDetailsDict,
52-
"paymentMethodType": paymentMethodType
71+
"paymentMethodType": paymentMethodType,
72+
"mandateText": mandateTextHTML
5373
]
5474
}
5575
}

ios/StripeSdkImpl+Embedded.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ extension StripeSdkImpl {
215215
configuration.allowsDelayedPaymentMethods = allowsDelayedPaymentMethods
216216
}
217217

218+
if let embeddedViewDisplaysMandateText = params["embeddedViewDisplaysMandateText"] as? Bool {
219+
configuration.embeddedViewDisplaysMandateText = embeddedViewDisplaysMandateText
220+
}
221+
218222
if let removeSavedPaymentMethodMessage = params["removeSavedPaymentMethodMessage"] as? String {
219223
configuration.removeSavedPaymentMethodMessage = removeSavedPaymentMethodMessage
220224
}

src/types/EmbeddedPaymentElement.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export interface PaymentOptionDisplayData {
6363
* Apple Pay: "apple_pay"
6464
*/
6565
paymentMethodType: string;
66+
/**
67+
* If you set `configuration.embeddedViewDisplaysMandateText = false`, this HTML text must be displayed to the customer near your "Buy" button to comply with regulations.
68+
* This text may contain formatting, colors, and links that should be preserved when rendering.
69+
*/
70+
mandateText?: string;
6671
}
6772

6873
/**
@@ -195,6 +200,13 @@ export interface EmbeddedPaymentElementConfiguration {
195200
customPaymentMethodConfiguration?: PaymentSheetTypes.CustomPaymentMethodConfiguration;
196201
/** Describes how the EmbeddedPaymentElement handles payment method row selections. */
197202
rowSelectionBehavior?: EmbeddedRowSelectionBehavior;
203+
/**
204+
* Controls whether the view displays mandate text at the bottom for payment methods that require it.
205+
* If set to `false`, your integration must display `PaymentOptionDisplayData.mandateText` to the customer near your "Buy" button to comply with regulations.
206+
* Note: This doesn't affect mandates displayed in the form sheet.
207+
* Defaults to `true`.
208+
*/
209+
embeddedViewDisplaysMandateText?: boolean;
198210
}
199211

200212
// -----------------------------------------------------------------------------

0 commit comments

Comments
 (0)