framepayments-react-native 2.0.5 → 2.0.7

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/README.md CHANGED
@@ -299,6 +299,86 @@ On non-Android platforms `Frame.presentGooglePay` rejects synchronously with a n
299
299
 
300
300
  ---
301
301
 
302
+ ### `Frame.setTheme(theme)` (iOS)
303
+
304
+ Customizes colors, fonts, and corner radii on Frame's reusable components — checkout, cart, and the onboarding flow. Backed by `FrameTheme` introduced in Frame-iOS 2.1.2.
305
+
306
+ Call once at app startup (after `Frame.initialize`). The theme applies to every subsequent `present*` call. Modals already on screen are not re-themed mid-flow. Pass `null` or `{}` to reset to defaults; pass a partial dict to override only specific tokens.
307
+
308
+ ```ts
309
+ import Frame from 'framepayments-react-native';
310
+
311
+ await Frame.setTheme({
312
+ colors: {
313
+ primaryButton: '#5B2DFF',
314
+ primaryButtonText: '#FFFFFF',
315
+ surface: '#0A0A0A',
316
+ textPrimary: '#FFFFFF',
317
+ error: '#E53935',
318
+ },
319
+ fonts: {
320
+ title: { name: 'Inter-Bold', size: 24 },
321
+ button: { name: 'Inter-SemiBold', size: 16 },
322
+ },
323
+ radii: { medium: 16 },
324
+ });
325
+ ```
326
+
327
+ **Android**: `setTheme()` resolves immediately and has no effect — `frame-android` does not yet have a matching theme API.
328
+
329
+ #### Tokens
330
+
331
+ **Colors** — hex strings (`#RGB`, `#RRGGBB`, or `#RRGGBBAA`, with or without leading `#`):
332
+
333
+ | Key | Used by |
334
+ |-----|---------|
335
+ | `primaryButton` / `primaryButtonText` | Primary CTAs |
336
+ | `secondaryButton` / `secondaryButtonText` | Secondary CTAs |
337
+ | `disabledButton` / `disabledButtonStroke` / `disabledButtonText` | Disabled CTAs |
338
+ | `surface` / `surfaceStroke` | Cards, sheets, input backgrounds |
339
+ | `textPrimary` / `textSecondary` | Body and supporting text |
340
+ | `error` | Validation messages |
341
+ | `onboardingHeaderBackground` | Onboarding header bar |
342
+ | `onboardingProgressFilledOnBrand` / `onboardingProgressEmptyOnBrand` | Onboarding progress indicator |
343
+
344
+ **Fonts** — `{ name: string; size: number }` objects. `name` must match a PostScript font name registered in your app's `Info.plist` (`UIAppFonts`) and bundled as a resource. Use `name: 'system'` for the system font.
345
+
346
+ | Key | Default | Used by |
347
+ |-----|---------|---------|
348
+ | `title` | `.title` | Page titles |
349
+ | `heading` | 18pt semibold | Section headers |
350
+ | `headline` | `.headline` | Card headlines |
351
+ | `body` | `.body` | Body text |
352
+ | `bodySmall` | 14pt | Smaller body |
353
+ | `label` | `.subheadline` | Field labels |
354
+ | `caption` | `.caption` | Captions, footnotes |
355
+ | `button` | `.headline` | Button text |
356
+
357
+ **Radii** — numbers (in points):
358
+
359
+ | Key | Default | Used by |
360
+ |-----|---------|---------|
361
+ | `small` | 8 | Small chips |
362
+ | `medium` | 10 | Buttons, inputs |
363
+ | `large` | 16 | Cards, sheets |
364
+
365
+ #### Custom fonts
366
+
367
+ Custom fonts are passed through to SwiftUI's `Font.custom(name:size:)`. Two requirements on the host iOS app:
368
+
369
+ 1. Add the font file to your app's bundle (Xcode → Build Phases → Copy Bundle Resources).
370
+ 2. Register it in your app's `Info.plist`:
371
+ ```xml
372
+ <key>UIAppFonts</key>
373
+ <array>
374
+ <string>Inter-Bold.ttf</string>
375
+ </array>
376
+ ```
377
+
378
+ If a font name doesn't resolve, iOS silently falls back to the system font.
379
+
380
+ ---
381
+
302
382
  ### Rendering wallet buttons
303
383
 
304
384
  Apple and Google both require their official button artwork (with light/dark variants) — custom-styled buttons can be rejected. Download the assets from [Apple's marketing page](https://developer.apple.com/apple-pay/marketing/) and [Google's brand guidelines page](https://developers.google.com/pay/api/android/guides/brand-guidelines), bundle them in your app, and pick a variant based on the active color scheme:
@@ -31,9 +31,9 @@ android {
31
31
  dependencies {
32
32
  implementation 'com.facebook.react:react-native:+'
33
33
  implementation "org.jetbrains.kotlin:kotlin-stdlib:2.1.0"
34
- implementation 'com.framepayments:framesdk:2.0.2'
35
- implementation 'com.framepayments:framesdk_ui:2.0.2'
36
- implementation 'com.framepayments:framesdk_onboarding:2.0.2'
34
+ implementation 'com.framepayments:framesdk:2.0.4'
35
+ implementation 'com.framepayments:framesdk_ui:2.0.4'
36
+ implementation 'com.framepayments:framesdk_onboarding:2.0.4'
37
37
  // Required by FrameGooglePayButton (PaymentsClient, WalletConstants).
38
38
  // framesdk_ui declares this as `implementation`, so it is not exposed transitively.
39
39
  implementation 'com.google.android.gms:play-services-wallet:19.4.0'
@@ -18,10 +18,12 @@ class FrameOnboardingActivity : ComponentActivity() {
18
18
  val accountId = intent.getStringExtra(EXTRA_ACCOUNT_ID)
19
19
  val capabilitiesJson = intent.getStringExtra(EXTRA_CAPABILITIES_JSON) ?: "[]"
20
20
  val capabilities = parseCapabilities(capabilitiesJson)
21
+ val googlePayMerchantId = intent.getStringExtra(EXTRA_GOOGLE_PAY_MERCHANT_ID)
21
22
 
22
23
  val config = OnboardingConfig(
23
24
  accountId = accountId,
24
- requiredCapabilities = capabilities
25
+ requiredCapabilities = capabilities,
26
+ googlePayMerchantId = googlePayMerchantId
25
27
  )
26
28
 
27
29
  setContent {
@@ -62,6 +64,7 @@ class FrameOnboardingActivity : ComponentActivity() {
62
64
  companion object {
63
65
  const val EXTRA_ACCOUNT_ID = "account_id"
64
66
  const val EXTRA_CAPABILITIES_JSON = "capabilities_json"
67
+ const val EXTRA_GOOGLE_PAY_MERCHANT_ID = "google_pay_merchant_id"
65
68
  const val EXTRA_PAYMENT_METHOD_ID = "payment_method_id"
66
69
  const val REQUEST_CODE = 9003
67
70
  }
@@ -119,6 +119,7 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
119
119
  fun presentOnboarding(
120
120
  accountId: String?,
121
121
  capabilities: com.facebook.react.bridge.ReadableArray,
122
+ googlePayMerchantId: String?,
122
123
  promise: Promise
123
124
  ) {
124
125
  val activity = reactApplicationContext.currentActivity ?: run {
@@ -131,6 +132,7 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
131
132
  val intent = Intent(activity, FrameOnboardingActivity::class.java).apply {
132
133
  putExtra(FrameOnboardingActivity.EXTRA_ACCOUNT_ID, accountId)
133
134
  putExtra(FrameOnboardingActivity.EXTRA_CAPABILITIES_JSON, capabilitiesJson)
135
+ putExtra(FrameOnboardingActivity.EXTRA_GOOGLE_PAY_MERCHANT_ID, googlePayMerchantId)
134
136
  }
135
137
  activity.startActivityForResult(intent, FrameOnboardingActivity.REQUEST_CODE)
136
138
  }
@@ -0,0 +1,119 @@
1
+ //
2
+ // FrameRNTheme.swift
3
+ // FrameReactNative
4
+ //
5
+ // Bridges JS theme dictionaries to Frame-iOS's FrameTheme and applies it to
6
+ // every SwiftUI root view we present. Read/written only on the main thread.
7
+ //
8
+
9
+ import Foundation
10
+ import SwiftUI
11
+ import Frame
12
+
13
+ enum FrameRNTheme {
14
+ // Main-thread only: written by FrameSDKBridge.setTheme (dispatched to main),
15
+ // read by present* methods (already on main).
16
+ static var current: FrameTheme? = nil
17
+
18
+ static func resolved() -> FrameTheme { current ?? .default }
19
+
20
+ static func parse(_ dict: [String: Any]) -> FrameTheme {
21
+ var theme = FrameTheme.default
22
+
23
+ if let colorsDict = dict["colors"] as? [String: Any] {
24
+ applyColor(colorsDict, "primaryButton") { theme.colors.primaryButton = $0 }
25
+ applyColor(colorsDict, "primaryButtonText") { theme.colors.primaryButtonText = $0 }
26
+ applyColor(colorsDict, "secondaryButton") { theme.colors.secondaryButton = $0 }
27
+ applyColor(colorsDict, "secondaryButtonText") { theme.colors.secondaryButtonText = $0 }
28
+ applyColor(colorsDict, "disabledButton") { theme.colors.disabledButton = $0 }
29
+ applyColor(colorsDict, "disabledButtonStroke") { theme.colors.disabledButtonStroke = $0 }
30
+ applyColor(colorsDict, "disabledButtonText") { theme.colors.disabledButtonText = $0 }
31
+ applyColor(colorsDict, "surface") { theme.colors.surface = $0 }
32
+ applyColor(colorsDict, "surfaceStroke") { theme.colors.surfaceStroke = $0 }
33
+ applyColor(colorsDict, "textPrimary") { theme.colors.textPrimary = $0 }
34
+ applyColor(colorsDict, "textSecondary") { theme.colors.textSecondary = $0 }
35
+ applyColor(colorsDict, "error") { theme.colors.error = $0 }
36
+ applyColor(colorsDict, "onboardingHeaderBackground") { theme.colors.onboardingHeaderBackground = $0 }
37
+ applyColor(colorsDict, "onboardingProgressFilledOnBrand") { theme.colors.onboardingProgressFilledOnBrand = $0 }
38
+ applyColor(colorsDict, "onboardingProgressEmptyOnBrand") { theme.colors.onboardingProgressEmptyOnBrand = $0 }
39
+ }
40
+
41
+ if let fontsDict = dict["fonts"] as? [String: Any] {
42
+ applyFont(fontsDict, "title") { theme.fonts.title = $0 }
43
+ applyFont(fontsDict, "heading") { theme.fonts.heading = $0 }
44
+ applyFont(fontsDict, "headline") { theme.fonts.headline = $0 }
45
+ applyFont(fontsDict, "body") { theme.fonts.body = $0 }
46
+ applyFont(fontsDict, "bodySmall") { theme.fonts.bodySmall = $0 }
47
+ applyFont(fontsDict, "label") { theme.fonts.label = $0 }
48
+ applyFont(fontsDict, "caption") { theme.fonts.caption = $0 }
49
+ applyFont(fontsDict, "button") { theme.fonts.button = $0 }
50
+ }
51
+
52
+ if let radiiDict = dict["radii"] as? [String: Any] {
53
+ if let v = radiiDict["small"] as? Double { theme.radii.small = CGFloat(v) }
54
+ if let v = radiiDict["medium"] as? Double { theme.radii.medium = CGFloat(v) }
55
+ if let v = radiiDict["large"] as? Double { theme.radii.large = CGFloat(v) }
56
+ }
57
+
58
+ return theme
59
+ }
60
+
61
+ private static func applyColor(_ dict: [String: Any], _ key: String, _ set: (Color) -> Void) {
62
+ guard let hex = dict[key] as? String, let color = Color(rnHex: hex) else { return }
63
+ set(color)
64
+ }
65
+
66
+ private static func applyFont(_ dict: [String: Any], _ key: String, _ set: (Font) -> Void) {
67
+ guard let entry = dict[key] as? [String: Any],
68
+ let name = entry["name"] as? String,
69
+ let size = entry["size"] as? Double else { return }
70
+ if name.lowercased() == "system" {
71
+ set(.system(size: CGFloat(size)))
72
+ } else {
73
+ set(.custom(name, size: CGFloat(size)))
74
+ }
75
+ }
76
+ }
77
+
78
+ // Applies the supplied FrameTheme to a single root view. Captured at present
79
+ // time so each UIHostingController has a stable, concrete rootView type.
80
+ struct ThemedRoot<Content: View>: View {
81
+ let theme: FrameTheme
82
+ let content: Content
83
+
84
+ init(_ content: Content, theme: FrameTheme) {
85
+ self.content = content
86
+ self.theme = theme
87
+ }
88
+
89
+ var body: some View {
90
+ content.frameTheme(theme)
91
+ }
92
+ }
93
+
94
+ extension Color {
95
+ // Accepts "#RGB", "#RRGGBB", "#RRGGBBAA" with or without the leading '#'.
96
+ init?(rnHex hex: String) {
97
+ var s = hex.trimmingCharacters(in: .whitespacesAndNewlines)
98
+ if s.hasPrefix("#") { s.removeFirst() }
99
+ if s.count == 3 {
100
+ s = s.map { "\($0)\($0)" }.joined()
101
+ }
102
+ guard s.count == 6 || s.count == 8 else { return nil }
103
+ var v: UInt64 = 0
104
+ guard Scanner(string: s).scanHexInt64(&v) else { return nil }
105
+ let r, g, b, a: Double
106
+ if s.count == 6 {
107
+ r = Double((v >> 16) & 0xff) / 255
108
+ g = Double((v >> 8) & 0xff) / 255
109
+ b = Double( v & 0xff) / 255
110
+ a = 1
111
+ } else {
112
+ r = Double((v >> 24) & 0xff) / 255
113
+ g = Double((v >> 16) & 0xff) / 255
114
+ b = Double((v >> 8) & 0xff) / 255
115
+ a = Double( v & 0xff) / 255
116
+ }
117
+ self = Color(.sRGB, red: r, green: g, blue: b, opacity: a)
118
+ }
119
+ }
@@ -31,6 +31,10 @@ RCT_EXTERN_METHOD(initialize:(NSString *)secretKey
31
31
  resolver:(RCTPromiseResolveBlock)resolve
32
32
  rejecter:(RCTPromiseRejectBlock)reject)
33
33
 
34
+ RCT_EXTERN_METHOD(setTheme:(NSDictionary *)theme
35
+ resolver:(RCTPromiseResolveBlock)resolve
36
+ rejecter:(RCTPromiseRejectBlock)reject)
37
+
34
38
  RCT_EXTERN_METHOD(presentCheckout:(id)customerId
35
39
  amount:(nonnull NSNumber *)amount
36
40
  resolver:(RCTPromiseResolveBlock)resolve
@@ -76,6 +80,10 @@ RCT_EXTERN_METHOD(presentApplePay:(NSString *)ownerType
76
80
  [[[ObjCFrameSDKBridge alloc] init] initialize:secretKey publishableKey:publishableKey debugMode:debugMode resolver:resolve rejecter:reject];
77
81
  }
78
82
 
83
+ - (void)setTheme:(NSDictionary *)theme resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
84
+ [[[ObjCFrameSDKBridge alloc] init] setTheme:theme resolver:resolve rejecter:reject];
85
+ }
86
+
79
87
  - (void)presentCheckout:(id)customerId amount:(NSNumber *)amount resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
80
88
  dispatch_async(dispatch_get_main_queue(), ^{
81
89
  UIViewController *topVC = FrameGetTopViewController();
@@ -27,6 +27,15 @@ public class FrameSDKBridge: NSObject {
27
27
  }
28
28
  }
29
29
 
30
+ @objc public
31
+ func setTheme(_ theme: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
32
+ DispatchQueue.main.async {
33
+ let dict = theme as? [String: Any] ?? [:]
34
+ FrameRNTheme.current = dict.isEmpty ? nil : FrameRNTheme.parse(dict)
35
+ resolve(nil)
36
+ }
37
+ }
38
+
30
39
  @objc public
31
40
  func presentCheckout(from viewController: UIViewController, customerId: String?, amount: Int, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
32
41
  presentCheckoutOnMain(from: viewController, customerId: customerId, amount: amount, resolve: resolve, reject: reject)
@@ -104,20 +113,23 @@ public class FrameSDKBridge: NSObject {
104
113
 
105
114
  private func presentCheckoutOnMain(from top: UIViewController, customerId: String?, amount: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
106
115
  var hosting: CheckoutHostingController!
107
- hosting = CheckoutHostingController(rootView: FrameCheckoutView(
108
- customerId: customerId,
109
- paymentAmount: amount,
110
- checkoutCallback: { [weak hosting] chargeIntent in
111
- hosting?.didComplete = true
112
- top.dismiss(animated: true)
113
- DispatchQueue.main.async {
114
- if let dict = Self.encodeChargeIntent(chargeIntent) {
115
- resolve(dict)
116
- } else {
117
- reject("ENCODE_ERROR", "Failed to encode charge intent", nil)
116
+ hosting = CheckoutHostingController(rootView: ThemedRoot(
117
+ FrameCheckoutView(
118
+ customerId: customerId,
119
+ paymentAmount: amount,
120
+ checkoutCallback: { [weak hosting] chargeIntent in
121
+ hosting?.didComplete = true
122
+ top.dismiss(animated: true)
123
+ DispatchQueue.main.async {
124
+ if let dict = Self.encodeChargeIntent(chargeIntent) {
125
+ resolve(dict)
126
+ } else {
127
+ reject("ENCODE_ERROR", "Failed to encode charge intent", nil)
128
+ }
118
129
  }
119
130
  }
120
- }
131
+ ),
132
+ theme: FrameRNTheme.resolved()
121
133
  ))
122
134
  hosting.onCancel = {
123
135
  DispatchQueue.main.async {
@@ -174,7 +186,7 @@ public class FrameSDKBridge: NSObject {
174
186
  cartItems: cartItems,
175
187
  shippingAmountInCents: shippingAmountInCents
176
188
  )
177
- let hosting = UIHostingController(rootView: cartView)
189
+ let hosting = UIHostingController(rootView: ThemedRoot(cartView, theme: FrameRNTheme.resolved()))
178
190
  hosting.modalPresentationStyle = UIModalPresentationStyle.pageSheet
179
191
  if let sheet = hosting.sheetPresentationController {
180
192
  sheet.detents = [UISheetPresentationController.Detent.large()]
@@ -188,17 +200,20 @@ public class FrameSDKBridge: NSObject {
188
200
  }
189
201
 
190
202
  private func presentOnboardingOnMain(from top: UIViewController, accountId: String?, capabilities: [FrameObjects.Capabilities], applePayMerchantId: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
191
- var hosting: OnboardingHostingController<OnboardingContainerView>!
203
+ var hosting: OnboardingHostingController<ThemedRoot<OnboardingContainerView>>!
192
204
  var delegate: OnboardingDismissDelegate!
193
205
  hosting = OnboardingHostingController(
194
- rootView: OnboardingContainerView(
195
- accountId: accountId,
196
- requiredCapabilities: capabilities,
197
- applePayMerchantId: applePayMerchantId,
198
- onComplete: { [weak hosting] in
199
- delegate?.finish(completed: true)
200
- hosting?.dismiss(animated: true)
201
- }
206
+ rootView: ThemedRoot(
207
+ OnboardingContainerView(
208
+ accountId: accountId,
209
+ requiredCapabilities: capabilities,
210
+ applePayMerchantId: applePayMerchantId,
211
+ onComplete: { [weak hosting] in
212
+ delegate?.finish(completed: true)
213
+ hosting?.dismiss(animated: true)
214
+ }
215
+ ),
216
+ theme: FrameRNTheme.resolved()
202
217
  )
203
218
  )
204
219
  // Embed in a UINavigationController so the outer sheet is a UIKit container,
@@ -227,7 +242,7 @@ public class FrameSDKBridge: NSObject {
227
242
 
228
243
  // MARK: - CheckoutHostingController
229
244
 
230
- private final class CheckoutHostingController: UIHostingController<FrameCheckoutView>, UIAdaptivePresentationControllerDelegate {
245
+ private final class CheckoutHostingController: UIHostingController<ThemedRoot<FrameCheckoutView>>, UIAdaptivePresentationControllerDelegate {
231
246
  var didComplete = false
232
247
  var onCancel: (() -> Void)?
233
248
  private var cancelled = false
package/lib/index.d.ts CHANGED
@@ -7,12 +7,12 @@
7
7
  * - Use presentApplePay / presentGooglePay to launch the platform wallet sheet from your own button UI.
8
8
  * - For API calls (customers, charge intents, refunds), use the framepayments (frame-node) package from JS.
9
9
  */
10
- import { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay } from './native';
11
- export { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, } from './native';
12
- export type { FrameCartItem, ChargeIntent, ChargeIntentStatus, AuthorizationMode, FrameError, BillingAddress, PaymentCard, BankAccount, PaymentMethod, OnboardingCapability, OnboardingResult, OnboardingResultStatus, ApplePayOwner, PresentApplePayOptions, PresentGooglePayOptions, } from './types';
10
+ import { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, setTheme } from './native';
11
+ export { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, setTheme, } from './native';
12
+ export type { FrameCartItem, ChargeIntent, ChargeIntentStatus, AuthorizationMode, FrameError, BillingAddress, PaymentCard, BankAccount, PaymentMethod, OnboardingCapability, OnboardingResult, OnboardingResultStatus, ApplePayOwner, PresentApplePayOptions, PresentGooglePayOptions, FrameTheme, FrameThemeColor, FrameThemeFont, FrameThemeColors, FrameThemeFonts, FrameThemeRadii, } from './types';
13
13
  export { ErrorCodes } from './errors';
14
14
  export type { FrameErrorShape, FrameErrorCode } from './errors';
15
- /** Default export for Frame.initialize(), Frame.presentCheckout(), Frame.presentCart(), Frame.presentOnboarding(), Frame.presentApplePay(), Frame.presentGooglePay() */
15
+ /** Default export for Frame.initialize(), Frame.presentCheckout(), Frame.presentCart(), Frame.presentOnboarding(), Frame.presentApplePay(), Frame.presentGooglePay(), Frame.setTheme() */
16
16
  declare const _default: {
17
17
  initialize: typeof initialize;
18
18
  presentCheckout: typeof presentCheckout;
@@ -20,6 +20,7 @@ declare const _default: {
20
20
  presentOnboarding: typeof presentOnboarding;
21
21
  presentApplePay: typeof presentApplePay;
22
22
  presentGooglePay: typeof presentGooglePay;
23
+ setTheme: typeof setTheme;
23
24
  };
24
25
  export default _default;
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,UAAU,EACV,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,WAAW,EACX,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,aAAa,EACb,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEhE,wKAAwK;;;;;;;;;AACxK,wBAOE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACT,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,UAAU,EACV,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,QAAQ,GACT,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,WAAW,EACX,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEhE,0LAA0L;;;;;;;;;;AAC1L,wBAQE"}
package/lib/index.js CHANGED
@@ -7,10 +7,10 @@
7
7
  * - Use presentApplePay / presentGooglePay to launch the platform wallet sheet from your own button UI.
8
8
  * - For API calls (customers, charge intents, refunds), use the framepayments (frame-node) package from JS.
9
9
  */
10
- import { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, } from './native';
11
- export { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, } from './native';
10
+ import { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, setTheme, } from './native';
11
+ export { initialize, presentCheckout, presentCart, presentOnboarding, presentApplePay, presentGooglePay, setTheme, } from './native';
12
12
  export { ErrorCodes } from './errors';
13
- /** Default export for Frame.initialize(), Frame.presentCheckout(), Frame.presentCart(), Frame.presentOnboarding(), Frame.presentApplePay(), Frame.presentGooglePay() */
13
+ /** Default export for Frame.initialize(), Frame.presentCheckout(), Frame.presentCart(), Frame.presentOnboarding(), Frame.presentApplePay(), Frame.presentGooglePay(), Frame.setTheme() */
14
14
  export default {
15
15
  initialize,
16
16
  presentCheckout,
@@ -18,4 +18,5 @@ export default {
18
18
  presentOnboarding,
19
19
  presentApplePay,
20
20
  presentGooglePay,
21
+ setTheme,
21
22
  };
package/lib/native.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Native module bridge. Uses NativeModules for classic React Native bridge.
3
3
  */
4
- import type { ChargeIntent, FrameCartItem, OnboardingCapability, OnboardingResult, PresentApplePayOptions, PresentGooglePayOptions } from './types';
4
+ import type { ChargeIntent, FrameCartItem, FrameTheme, OnboardingCapability, OnboardingResult, PresentApplePayOptions, PresentGooglePayOptions } from './types';
5
5
  export declare function initialize(options: {
6
6
  secretKey: string;
7
7
  publishableKey: string;
@@ -20,7 +20,17 @@ export declare function presentOnboarding(options: {
20
20
  accountId?: string | null;
21
21
  capabilities?: OnboardingCapability[];
22
22
  applePayMerchantId?: string | null;
23
+ googlePayMerchantId?: string | null;
23
24
  }): Promise<OnboardingResult>;
24
25
  export declare function presentApplePay(options: PresentApplePayOptions): Promise<ChargeIntent>;
25
26
  export declare function presentGooglePay(options: PresentGooglePayOptions): Promise<ChargeIntent>;
27
+ /**
28
+ * Configure colors, fonts, and corner radii for Frame's reusable iOS
29
+ * components. Applied to every subsequent `present*` call; an in-flight modal
30
+ * is not re-themed mid-flow.
31
+ *
32
+ * Pass `null` or `{}` to reset to SDK defaults. Android is a no-op until
33
+ * frame-android ships a matching theme API.
34
+ */
35
+ export declare function setTheme(theme: FrameTheme | null): Promise<void>;
26
36
  //# sourceMappingURL=native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAmBjB,wBAAgB,UAAU,CAAC,OAAO,EAAE;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB;AAwBD,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKxB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,GAAG,OAAO,CAAC,YAAY,CAAC,CASxB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiB5B;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoBtF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC,CAUxF"}
1
+ {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAmBjB,wBAAgB,UAAU,CAAC,OAAO,EAAE;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB;AAwBD,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKxB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,GAAG,OAAO,CAAC,YAAY,CAAC,CASxB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkB5B;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoBtF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC,CAUxF;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE"}
package/lib/native.js CHANGED
@@ -56,7 +56,7 @@ export function presentOnboarding(options) {
56
56
  if (Platform.OS === 'ios' && options.applePayMerchantId) {
57
57
  return wrapPromise(FrameSDK.presentOnboardingWithApplePay(options.accountId ?? null, options.capabilities ?? [], options.applePayMerchantId));
58
58
  }
59
- return wrapPromise(FrameSDK.presentOnboarding(options.accountId ?? null, options.capabilities ?? []));
59
+ return wrapPromise(FrameSDK.presentOnboarding(options.accountId ?? null, options.capabilities ?? [], options.googlePayMerchantId ?? null));
60
60
  }
61
61
  export function presentApplePay(options) {
62
62
  guardInitialized();
@@ -75,3 +75,19 @@ export function presentGooglePay(options) {
75
75
  guardInitialized();
76
76
  return wrapPromise(FrameSDK.presentGooglePay(options.amountCents, options.customerId ?? null, options.currencyCode ?? 'USD', options.googlePayMerchantId ?? null));
77
77
  }
78
+ /**
79
+ * Configure colors, fonts, and corner radii for Frame's reusable iOS
80
+ * components. Applied to every subsequent `present*` call; an in-flight modal
81
+ * is not re-themed mid-flow.
82
+ *
83
+ * Pass `null` or `{}` to reset to SDK defaults. Android is a no-op until
84
+ * frame-android ships a matching theme API.
85
+ */
86
+ export function setTheme(theme) {
87
+ if (theme !== null && (typeof theme !== 'object' || Array.isArray(theme))) {
88
+ throw new Error('Frame.setTheme requires a theme object or null');
89
+ }
90
+ if (Platform.OS !== 'ios')
91
+ return Promise.resolve();
92
+ return wrapPromise(FrameSDK.setTheme(theme ?? {}));
93
+ }
package/lib/types.d.ts CHANGED
@@ -128,4 +128,59 @@ export interface PresentGooglePayOptions {
128
128
  /** Optional override for the Google Pay merchant ID. */
129
129
  googlePayMerchantId?: string;
130
130
  }
131
+ /**
132
+ * Theming for Frame's reusable iOS components (checkout, cart, onboarding).
133
+ * iOS-only: on Android, Frame.setTheme() resolves immediately and has no effect
134
+ * until frame-android ships a matching theme API.
135
+ *
136
+ * Pass any subset — unspecified tokens fall back to SDK defaults.
137
+ */
138
+ /** Hex color: '#RGB', '#RRGGBB', or '#RRGGBBAA' (with or without leading '#'). */
139
+ export type FrameThemeColor = string;
140
+ /**
141
+ * Custom font reference. `name` must be a PostScript font name registered in
142
+ * the host app's Info.plist `UIAppFonts` and bundled as a resource. Pass
143
+ * `name: 'system'` to use the system font at the given size.
144
+ */
145
+ export interface FrameThemeFont {
146
+ name: string;
147
+ size: number;
148
+ }
149
+ export interface FrameThemeColors {
150
+ primaryButton?: FrameThemeColor;
151
+ primaryButtonText?: FrameThemeColor;
152
+ secondaryButton?: FrameThemeColor;
153
+ secondaryButtonText?: FrameThemeColor;
154
+ disabledButton?: FrameThemeColor;
155
+ disabledButtonStroke?: FrameThemeColor;
156
+ disabledButtonText?: FrameThemeColor;
157
+ surface?: FrameThemeColor;
158
+ surfaceStroke?: FrameThemeColor;
159
+ textPrimary?: FrameThemeColor;
160
+ textSecondary?: FrameThemeColor;
161
+ error?: FrameThemeColor;
162
+ onboardingHeaderBackground?: FrameThemeColor;
163
+ onboardingProgressFilledOnBrand?: FrameThemeColor;
164
+ onboardingProgressEmptyOnBrand?: FrameThemeColor;
165
+ }
166
+ export interface FrameThemeFonts {
167
+ title?: FrameThemeFont;
168
+ heading?: FrameThemeFont;
169
+ headline?: FrameThemeFont;
170
+ body?: FrameThemeFont;
171
+ bodySmall?: FrameThemeFont;
172
+ label?: FrameThemeFont;
173
+ caption?: FrameThemeFont;
174
+ button?: FrameThemeFont;
175
+ }
176
+ export interface FrameThemeRadii {
177
+ small?: number;
178
+ medium?: number;
179
+ large?: number;
180
+ }
181
+ export interface FrameTheme {
182
+ colors?: FrameThemeColors;
183
+ fonts?: FrameThemeFonts;
184
+ radii?: FrameThemeRadii;
185
+ }
131
186
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,UAAU,GACV,UAAU,GACV,WAAW,CAAC;AAEhB,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,+DAA+D;AAC/D,MAAM,MAAM,oBAAoB,GAC5B,KAAK,GACL,aAAa,GACb,oBAAoB,GACpB,gBAAgB,GAChB,mBAAmB,GACnB,WAAW,GACX,cAAc,GACd,sBAAsB,GACtB,2BAA2B,GAC3B,mBAAmB,GACnB,sBAAsB,GACtB,gBAAgB,GAChB,kBAAkB,CAAC;AAEvB,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,WAAW,CAAC;AAE/D,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,oFAAoF;IACpF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC,yCAAyC;AACzC,MAAM,WAAW,sBAAsB;IACrC,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,KAAK,EAAE,aAAa,CAAC;IACrB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,0CAA0C;AAC1C,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framepayments-react-native",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "React Native SDK for Frame Payments - modal checkout and cart, with API usage via frame-node.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -32,7 +32,7 @@ let presentCart: (opts: {
32
32
  items: Array<{ id: string; title: string; amountInCents: number; imageUrl: string }>;
33
33
  shippingAmountInCents: number;
34
34
  }) => Promise<unknown>;
35
- let presentOnboarding: (opts: { accountId?: string | null; capabilities?: string[]; applePayMerchantId?: string | null }) => Promise<unknown>;
35
+ let presentOnboarding: (opts: { accountId?: string | null; capabilities?: string[]; applePayMerchantId?: string | null; googlePayMerchantId?: string | null }) => Promise<unknown>;
36
36
 
37
37
  beforeEach(() => {
38
38
  jest.resetModules();
@@ -148,14 +148,14 @@ describe('presentOnboarding', () => {
148
148
  it('calls native presentOnboarding with accountId and capabilities after initialize', async () => {
149
149
  await initialize({ secretKey: 'sk_xxx', publishableKey: 'pk_xxx' });
150
150
  const result = await presentOnboarding({ accountId: 'acct_1', capabilities: ['kyc', 'bank_account_verification'] });
151
- expect(mockPresentOnboarding).toHaveBeenCalledWith('acct_1', ['kyc', 'bank_account_verification']);
151
+ expect(mockPresentOnboarding).toHaveBeenCalledWith('acct_1', ['kyc', 'bank_account_verification'], null);
152
152
  expect(result).toEqual({ status: 'completed', paymentMethodId: 'pm_1' });
153
153
  });
154
154
 
155
155
  it('passes null for accountId and empty array for capabilities when not provided', async () => {
156
156
  await initialize({ secretKey: 'sk_xxx', publishableKey: 'pk_xxx' });
157
157
  await presentOnboarding({});
158
- expect(mockPresentOnboarding).toHaveBeenCalledWith(null, []);
158
+ expect(mockPresentOnboarding).toHaveBeenCalledWith(null, [], null);
159
159
  });
160
160
 
161
161
  it('routes to presentOnboardingWithApplePay on iOS when applePayMerchantId is set', async () => {
@@ -170,7 +170,14 @@ describe('presentOnboarding', () => {
170
170
  mockPlatform.OS = 'android';
171
171
  await initialize({ secretKey: 'sk_xxx', publishableKey: 'pk_xxx' });
172
172
  await presentOnboarding({ accountId: 'acct_1', capabilities: ['kyc'], applePayMerchantId: 'merchant.com.example' });
173
- expect(mockPresentOnboarding).toHaveBeenCalledWith('acct_1', ['kyc']);
173
+ expect(mockPresentOnboarding).toHaveBeenCalledWith('acct_1', ['kyc'], null);
174
174
  expect(mockPresentOnboardingWithApplePay).not.toHaveBeenCalled();
175
175
  });
176
+
177
+ it('forwards googlePayMerchantId to native presentOnboarding on Android', async () => {
178
+ mockPlatform.OS = 'android';
179
+ await initialize({ secretKey: 'sk_xxx', publishableKey: 'pk_xxx' });
180
+ await presentOnboarding({ accountId: 'acct_1', capabilities: ['kyc'], googlePayMerchantId: 'BCR2DN4T...' });
181
+ expect(mockPresentOnboarding).toHaveBeenCalledWith('acct_1', ['kyc'], 'BCR2DN4T...');
182
+ });
176
183
  });
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  presentOnboarding,
16
16
  presentApplePay,
17
17
  presentGooglePay,
18
+ setTheme,
18
19
  } from './native';
19
20
 
20
21
  export {
@@ -24,6 +25,7 @@ export {
24
25
  presentOnboarding,
25
26
  presentApplePay,
26
27
  presentGooglePay,
28
+ setTheme,
27
29
  } from './native';
28
30
  export type {
29
31
  FrameCartItem,
@@ -41,11 +43,17 @@ export type {
41
43
  ApplePayOwner,
42
44
  PresentApplePayOptions,
43
45
  PresentGooglePayOptions,
46
+ FrameTheme,
47
+ FrameThemeColor,
48
+ FrameThemeFont,
49
+ FrameThemeColors,
50
+ FrameThemeFonts,
51
+ FrameThemeRadii,
44
52
  } from './types';
45
53
  export { ErrorCodes } from './errors';
46
54
  export type { FrameErrorShape, FrameErrorCode } from './errors';
47
55
 
48
- /** Default export for Frame.initialize(), Frame.presentCheckout(), Frame.presentCart(), Frame.presentOnboarding(), Frame.presentApplePay(), Frame.presentGooglePay() */
56
+ /** Default export for Frame.initialize(), Frame.presentCheckout(), Frame.presentCart(), Frame.presentOnboarding(), Frame.presentApplePay(), Frame.presentGooglePay(), Frame.setTheme() */
49
57
  export default {
50
58
  initialize,
51
59
  presentCheckout,
@@ -53,4 +61,5 @@ export default {
53
61
  presentOnboarding,
54
62
  presentApplePay,
55
63
  presentGooglePay,
64
+ setTheme,
56
65
  };
package/src/native.ts CHANGED
@@ -6,6 +6,7 @@ import { NativeModules, Platform } from 'react-native';
6
6
  import type {
7
7
  ChargeIntent,
8
8
  FrameCartItem,
9
+ FrameTheme,
9
10
  OnboardingCapability,
10
11
  OnboardingResult,
11
12
  PresentApplePayOptions,
@@ -102,6 +103,7 @@ export function presentOnboarding(options: {
102
103
  accountId?: string | null;
103
104
  capabilities?: OnboardingCapability[];
104
105
  applePayMerchantId?: string | null;
106
+ googlePayMerchantId?: string | null;
105
107
  }): Promise<OnboardingResult> {
106
108
  guardInitialized();
107
109
  if (Platform.OS === 'ios' && options.applePayMerchantId) {
@@ -116,7 +118,8 @@ export function presentOnboarding(options: {
116
118
  return wrapPromise(
117
119
  FrameSDK.presentOnboarding(
118
120
  options.accountId ?? null,
119
- options.capabilities ?? []
121
+ options.capabilities ?? [],
122
+ options.googlePayMerchantId ?? null
120
123
  )
121
124
  );
122
125
  }
@@ -154,3 +157,19 @@ export function presentGooglePay(options: PresentGooglePayOptions): Promise<Char
154
157
  )
155
158
  );
156
159
  }
160
+
161
+ /**
162
+ * Configure colors, fonts, and corner radii for Frame's reusable iOS
163
+ * components. Applied to every subsequent `present*` call; an in-flight modal
164
+ * is not re-themed mid-flow.
165
+ *
166
+ * Pass `null` or `{}` to reset to SDK defaults. Android is a no-op until
167
+ * frame-android ships a matching theme API.
168
+ */
169
+ export function setTheme(theme: FrameTheme | null): Promise<void> {
170
+ if (theme !== null && (typeof theme !== 'object' || Array.isArray(theme))) {
171
+ throw new Error('Frame.setTheme requires a theme object or null');
172
+ }
173
+ if (Platform.OS !== 'ios') return Promise.resolve();
174
+ return wrapPromise(FrameSDK.setTheme(theme ?? {}));
175
+ }
package/src/types.ts CHANGED
@@ -162,3 +162,65 @@ export interface PresentGooglePayOptions {
162
162
  /** Optional override for the Google Pay merchant ID. */
163
163
  googlePayMerchantId?: string;
164
164
  }
165
+
166
+ /**
167
+ * Theming for Frame's reusable iOS components (checkout, cart, onboarding).
168
+ * iOS-only: on Android, Frame.setTheme() resolves immediately and has no effect
169
+ * until frame-android ships a matching theme API.
170
+ *
171
+ * Pass any subset — unspecified tokens fall back to SDK defaults.
172
+ */
173
+
174
+ /** Hex color: '#RGB', '#RRGGBB', or '#RRGGBBAA' (with or without leading '#'). */
175
+ export type FrameThemeColor = string;
176
+
177
+ /**
178
+ * Custom font reference. `name` must be a PostScript font name registered in
179
+ * the host app's Info.plist `UIAppFonts` and bundled as a resource. Pass
180
+ * `name: 'system'` to use the system font at the given size.
181
+ */
182
+ export interface FrameThemeFont {
183
+ name: string;
184
+ size: number;
185
+ }
186
+
187
+ export interface FrameThemeColors {
188
+ primaryButton?: FrameThemeColor;
189
+ primaryButtonText?: FrameThemeColor;
190
+ secondaryButton?: FrameThemeColor;
191
+ secondaryButtonText?: FrameThemeColor;
192
+ disabledButton?: FrameThemeColor;
193
+ disabledButtonStroke?: FrameThemeColor;
194
+ disabledButtonText?: FrameThemeColor;
195
+ surface?: FrameThemeColor;
196
+ surfaceStroke?: FrameThemeColor;
197
+ textPrimary?: FrameThemeColor;
198
+ textSecondary?: FrameThemeColor;
199
+ error?: FrameThemeColor;
200
+ onboardingHeaderBackground?: FrameThemeColor;
201
+ onboardingProgressFilledOnBrand?: FrameThemeColor;
202
+ onboardingProgressEmptyOnBrand?: FrameThemeColor;
203
+ }
204
+
205
+ export interface FrameThemeFonts {
206
+ title?: FrameThemeFont;
207
+ heading?: FrameThemeFont;
208
+ headline?: FrameThemeFont;
209
+ body?: FrameThemeFont;
210
+ bodySmall?: FrameThemeFont;
211
+ label?: FrameThemeFont;
212
+ caption?: FrameThemeFont;
213
+ button?: FrameThemeFont;
214
+ }
215
+
216
+ export interface FrameThemeRadii {
217
+ small?: number;
218
+ medium?: number;
219
+ large?: number;
220
+ }
221
+
222
+ export interface FrameTheme {
223
+ colors?: FrameThemeColors;
224
+ fonts?: FrameThemeFonts;
225
+ radii?: FrameThemeRadii;
226
+ }