framepayments-react-native 2.2.1 → 3.0.1

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.
Files changed (35) hide show
  1. package/FrameReactNative.podspec +44 -0
  2. package/Package.swift +47 -0
  3. package/README.md +64 -33
  4. package/android/build.gradle +9 -3
  5. package/android/src/main/java/com/framepayments/reactnativeframe/FrameCheckoutActivity.kt +18 -3
  6. package/android/src/main/java/com/framepayments/reactnativeframe/FrameFlowActivity.kt +16 -3
  7. package/android/src/main/java/com/framepayments/reactnativeframe/FrameGooglePayActivity.kt +0 -3
  8. package/android/src/main/java/com/framepayments/reactnativeframe/FrameOnboardingActivity.kt +2 -5
  9. package/android/src/main/java/com/framepayments/reactnativeframe/FrameSDKModule.kt +13 -11
  10. package/ios/ApplePayPresenter.swift +1 -4
  11. package/ios/FramePreloader.h +23 -0
  12. package/ios/FrameSDKBridge.m +17 -17
  13. package/ios/FrameSDKBridge.swift +109 -54
  14. package/lib/native.d.ts +12 -2
  15. package/lib/native.d.ts.map +1 -1
  16. package/lib/native.js +18 -18
  17. package/lib/types.d.ts +12 -6
  18. package/lib/types.d.ts.map +1 -1
  19. package/package.json +8 -2
  20. package/src/__tests__/native.test.ts +60 -74
  21. package/src/native.ts +37 -34
  22. package/src/types.ts +12 -6
  23. package/ios/FrameReactNative.podspec +0 -32
  24. package/lib/__tests__/native.test.d.ts +0 -32
  25. package/lib/__tests__/native.test.d.ts.map +0 -1
  26. package/lib/__tests__/native.test.js +0 -101
  27. package/lib/components/FrameApplePayButton.d.ts +0 -30
  28. package/lib/components/FrameApplePayButton.d.ts.map +0 -1
  29. package/lib/components/FrameApplePayButton.js +0 -42
  30. package/lib/components/FrameGooglePayButton.d.ts +0 -28
  31. package/lib/components/FrameGooglePayButton.d.ts.map +0 -1
  32. package/lib/components/FrameGooglePayButton.js +0 -24
  33. package/lib/components/index.d.ts +0 -5
  34. package/lib/components/index.d.ts.map +0 -1
  35. package/lib/components/index.js +0 -2
@@ -0,0 +1,44 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'FrameReactNative'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.homepage = package['homepage']
10
+ s.license = package['license']
11
+ s.authors = package['author']
12
+ s.platforms = { :ios => '17.0' }
13
+ s.source = { :git => package['repository']['url'], :tag => "#{s.version}" }
14
+ s.source_files = 'ios/**/*.{h,m,mm,swift}'
15
+ s.requires_arc = true
16
+
17
+ # static_framework exposes the pod's generated `FrameReactNative-Swift.h` to
18
+ # consumers — required so host AppDelegate.mm can `#import <FrameReactNative/FrameReactNative-Swift.h>`
19
+ # to reach `FramePreloader`.
20
+ s.static_framework = true
21
+
22
+ s.dependency 'React-Core'
23
+
24
+ # Autolink Frame-iOS (SPM-only) via RN 0.81+'s Podfile SPM hook. spm.rb injects
25
+ # XCRemoteSwiftPackageReferences into Pods.xcodeproj at react_native_post_install;
26
+ # consumers get Frame-iOS + Frame-Onboarding resolved by `pod install` alone.
27
+ #
28
+ # The respond_to? guards use `include_private: true` because both helpers are
29
+ # top-level Ruby `def`s in react_native_pods.rb — i.e. private methods of Object.
30
+ # The guards exist not for RN-version compatibility (peer dep is >= 0.81) but
31
+ # because the RN CLI loads this podspec STANDALONE for autolinking discovery,
32
+ # outside the Podfile's `require 'react_native_pods.rb'` context where the
33
+ # helpers are defined. Without guards, `npx react-native config` would crash
34
+ # and return `ios: null`, breaking `use_native_modules!`.
35
+ if respond_to?(:spm_dependency, true)
36
+ spm_dependency(s,
37
+ url: 'https://github.com/Frame-Payments/frame-ios',
38
+ requirement: { kind: 'upToNextMajorVersion', minimumVersion: package['frameNativeVersions']['ios'] },
39
+ products: ['Frame-iOS', 'Frame-Onboarding']
40
+ )
41
+ end
42
+
43
+ install_modules_dependencies(s) if respond_to?(:install_modules_dependencies, true)
44
+ end
package/Package.swift ADDED
@@ -0,0 +1,47 @@
1
+ // swift-tools-version: 5.9
2
+ // Distribution paths:
3
+ // • Pure-SPM consumers: add framepayments-react-native via Swift Package Manager;
4
+ // frame-ios resolves transitively from this manifest.
5
+ // • CocoaPods consumers (the common RN setup): the podspec at
6
+ // ios/FrameReactNative.podspec declares the same frame-ios SPM dependency via
7
+ // RN 0.81+'s `spm_dependency` Podfile hook, so `pod install` resolves it
8
+ // automatically — no Xcode "Add Package Dependencies" step required.
9
+ //
10
+ // IMPORTANT: keep the frame-ios version below in sync with
11
+ // package.json:frameNativeVersions.ios. Swift Package manifests cannot read JSON,
12
+ // so this is the one duplicated version pin. See MAINTAINING.md.
13
+
14
+ import PackageDescription
15
+
16
+ let package = Package(
17
+ name: "framepayments-react-native",
18
+ platforms: [
19
+ .iOS(.v17)
20
+ ],
21
+ products: [
22
+ .library(
23
+ name: "framepayments-react-native",
24
+ targets: ["framepayments-react-native"]
25
+ )
26
+ ],
27
+ dependencies: [
28
+ .package(
29
+ url: "https://github.com/Frame-Payments/frame-ios",
30
+ from: "2.2.3" // Keep in sync with package.json:frameNativeVersions.ios
31
+ )
32
+ ],
33
+ targets: [
34
+ .target(
35
+ name: "framepayments-react-native",
36
+ dependencies: [
37
+ .product(name: "Frame-iOS", package: "frame-ios"),
38
+ .product(name: "Frame-Onboarding", package: "frame-ios")
39
+ ],
40
+ path: "ios",
41
+ publicHeadersPath: ".",
42
+ cSettings: [
43
+ .headerSearchPath(".")
44
+ ]
45
+ )
46
+ ]
47
+ )
package/README.md CHANGED
@@ -4,7 +4,7 @@ React Native SDK for [Frame Payments](https://framepayments.com). Bridges the na
4
4
 
5
5
  ## Requirements
6
6
 
7
- - React Native >= 0.74
7
+ - React Native >= 0.81 (autolinking on iOS depends on RN's `spm_dependency` Podfile hook, which stabilized in 0.81)
8
8
  - iOS 17+ / Android 8.0+ (API 26+)
9
9
  - A [Frame](https://framepayments.com) account and API key
10
10
 
@@ -18,19 +18,13 @@ yarn add framepayments-react-native
18
18
 
19
19
  ### iOS setup
20
20
 
21
- #### 1. Add the Frame iOS SDK via Swift Package Manager
22
-
23
- The React Native SDK's native layer depends on the Frame iOS SDK, which must be added manually via SPM — CocoaPods cannot pull it in automatically.
24
-
25
- In Xcode: **File → Add Package Dependencies**, enter:
26
-
27
- ```
28
- https://github.com/Frame-Payments/frame-ios
21
+ ```bash
22
+ cd ios && pod install && cd ..
29
23
  ```
30
24
 
31
- Add the **Frame-iOS** package and select the version you need.
25
+ That's it. `pod install` autolinks the React Native bridge **and** resolves the underlying Frame iOS SDK (Frame-iOS + Frame-Onboarding) via Swift Package Manager — no Xcode "Add Package Dependencies" step required.
32
26
 
33
- #### 2. Preload Frame on the main thread
27
+ #### Preload Frame on the main thread
34
28
 
35
29
  Add this to your `AppDelegate.m` or `AppDelegate.mm` **before** `[super application:didFinishLaunchingWithOptions:]`:
36
30
 
@@ -48,15 +42,11 @@ This prevents the **"Helpers are not supported by the default hub"** crash, whic
48
42
 
49
43
  > If your app module name isn't the default (i.e., not matching the generated `-Swift.h` header), set `FRAME_SWIFT_HEADER=YourApp-Swift.h` in your target's **Preprocessor Macros** in Xcode build settings.
50
44
 
51
- #### 3. Install pods
52
-
53
- ```bash
54
- cd ios && pod install && cd ..
55
- ```
45
+ > Using Swift Package Manager directly instead of CocoaPods? The package's `Package.swift` resolves `frame-ios` transitively, so just add `framepayments-react-native` via **File → Add Package Dependencies** in Xcode.
56
46
 
57
47
  ### Android setup
58
48
 
59
- No extra steps required. Autolinking handles the native module automatically.
49
+ No extra steps required. Autolinking handles the native module automatically and pulls in `com.framepayments:framesdk*` from Maven Central.
60
50
 
61
51
  ---
62
52
 
@@ -101,6 +91,8 @@ Initializes the native SDK. Must be called before any `present*` method. Call on
101
91
  await Frame.initialize({
102
92
  secretKey: 'sk_sandbox_...', // your Frame secret key
103
93
  publishableKey: 'pk_sandbox_...', // your Frame publishable key
94
+ applePayMerchantId: 'merchant.com.yourapp', // optional — see Apple Pay section
95
+ googlePayMerchantId: 'BCR2DN4T...', // optional — see Google Pay section
104
96
  debugMode: false, // set true in development to enable native debug logging
105
97
  });
106
98
  ```
@@ -109,6 +101,8 @@ await Frame.initialize({
109
101
  |---|---|---|---|
110
102
  | `secretKey` | `string` | Yes | Your Frame secret key (`sk_…`). Used for server-style operations. |
111
103
  | `publishableKey` | `string` | Yes | Your Frame publishable key (`pk_…`). Used for client-side operations like wallet payments. |
104
+ | `applePayMerchantId` | `string` | No | Apple Pay merchant identifier (`merchant.com.…`). Single source of truth for every Apple Pay surface — `presentApplePay`, the bundled checkout's wallet row, the onboarding wallet attach button. iOS-only; ignored on Android. |
105
+ | `googlePayMerchantId` | `string` | No | Google Pay merchant identifier from the Google Pay & Wallet Console. Single source of truth for every Google Pay surface — `presentGooglePay`, the bundled checkout's wallet row, the onboarding wallet attach button. Android-only; ignored on iOS. |
112
106
  | `debugMode` | `boolean` | No | Enables native debug logging and routes wallet flows through sandbox/test environments. Default: `false`. |
113
107
 
114
108
  ---
@@ -194,7 +188,8 @@ if (result.status === 'completed') {
194
188
  |---|---|---|---|
195
189
  | `accountId` | `string` | No | The Frame account to onboard |
196
190
  | `capabilities` | `OnboardingCapability[]` | No | Which onboarding steps to include (see below) |
197
- | `applePayMerchantId` | `string` | No | Apple Pay merchant ID. When set, the onboarding flow includes an Apple Pay setup step. iOS only — ignored on Android. Same prerequisites as `presentApplePay`. |
191
+
192
+ The Apple Pay / Google Pay wallet attach steps are rendered automatically when the corresponding merchant ID was passed to `Frame.initialize`. No per-call merchant params here.
198
193
 
199
194
  **`capabilities` values:**
200
195
 
@@ -227,6 +222,8 @@ if (result.status === 'completed') {
227
222
 
228
223
  Launches the native Apple Pay sheet, creates a Frame payment method from the authorized payment, and creates a charge against the owner. Resolves with the resulting resource's id string. Render your own button — Apple's `PKPaymentButton`, a community wrapper, or your own design-system component — and call this from its `onPress`.
229
224
 
225
+ The Apple Pay merchant ID is configured **once** at `Frame.initialize({ applePayMerchantId })`; there is no per-call merchant parameter.
226
+
230
227
  The `owner` determines which downstream resource is created:
231
228
 
232
229
  - `owner.type === 'customer'` → creates a `ChargeIntent` against the customer; resolves with the ChargeIntent's `id`.
@@ -242,7 +239,6 @@ const transferId = await Frame.presentApplePay({
242
239
  amount: 15000,
243
240
  currency: 'usd',
244
241
  owner: { type: 'account', id: 'acct_xxx' },
245
- merchantId: 'merchant.com.yourapp',
246
242
  });
247
243
 
248
244
  // Customer → ChargeIntent
@@ -250,7 +246,6 @@ const chargeIntentId = await Frame.presentApplePay({
250
246
  amount: 15000,
251
247
  currency: 'usd',
252
248
  owner: { type: 'customer', id: 'cus_xxx' },
253
- merchantId: 'merchant.com.yourapp',
254
249
  });
255
250
  ```
256
251
 
@@ -259,23 +254,42 @@ const chargeIntentId = await Frame.presentApplePay({
259
254
  | `amount` | `number` | Yes | Payment amount in cents |
260
255
  | `currency` | `string` | No | ISO 4217 currency code. Default `'usd'` |
261
256
  | `owner` | `{ type: 'customer' \| 'account', id: string }` | Yes | Customer or account that owns the resulting payment method and charge |
262
- | `merchantId` | `string` | Yes | Apple Pay merchant ID configured in your Apple Developer account |
263
257
 
264
258
  **Returns:** `Promise<string>` — the created `ChargeIntent`'s `id` (for customer owners) or the `Transfer`'s `id` (for account owners).
265
259
 
266
- The promise rejects with `code: 'USER_CANCELED'` when the user dismisses the sheet, `'INVALID_OWNER'` if the owner is missing or malformed, `'APPLE_PAY_UNAVAILABLE'` if the device cannot make Apple Pay payments, `'NOT_ATTESTED'` if device attestation has not completed yet (try again in a moment), `'PAYMENT_METHOD_FAILED'` when the wallet payment method could not be persisted, and `'PAYMENT_FAILED'` when the downstream ChargeIntent or Transfer could not be created.
260
+ The promise rejects with `code: 'USER_CANCELED'` when the user dismisses the sheet, `'INVALID_OWNER'` if the owner is missing or malformed, `'INVALID_MERCHANT_ID'` if `applePayMerchantId` was not configured at `Frame.initialize`, `'APPLE_PAY_UNAVAILABLE'` if the device cannot make Apple Pay payments, `'NOT_ATTESTED'` if device attestation has not completed yet (try again in a moment), `'PAYMENT_METHOD_FAILED'` when the wallet payment method could not be persisted, and `'PAYMENT_FAILED'` when the downstream ChargeIntent or Transfer could not be created.
261
+
262
+ #### Required iOS setup
267
263
 
268
- **Required iOS setup:**
264
+ Apple Pay setup is a four-part process: create a merchant ID with Apple, configure your Xcode project, pass the merchant ID to `Frame.initialize`, then **contact Frame** to enable the feature on your account.
269
265
 
270
- 1. **Apple Pay capability + merchant ID.** Add Apple Pay in your target's *Signing & Capabilities*, and register a merchant ID in your Apple Developer account.
271
- 2. **App Attest entitlement.** The Frame iOS SDK uses Apple's App Attest for device attestation on every Apple Pay payment. Add to your `.entitlements`:
266
+ 1. **Create a merchant identifier.** Sign in to [developer.apple.com Certificates, Identifiers & Profiles Identifiers Merchant IDs](https://developer.apple.com/account/resources/identifiers/list/merchant), click **+**, choose **Merchant IDs**, and create one in reverse-DNS form (e.g. `merchant.com.yourapp`).
267
+ 2. **Add the Apple Pay capability in Xcode.** Open your project, select your **app target**, go to **Signing & Capabilities** **+ Capability** → **Apple Pay**, and add the merchant ID you created in step 1. Xcode writes it into your entitlements file:
268
+ ```xml
269
+ <key>com.apple.developer.in-app-payments</key>
270
+ <array>
271
+ <string>merchant.com.yourapp</string>
272
+ </array>
273
+ ```
274
+ 3. **App Attest entitlement.** Frame uses Apple's App Attest for device attestation on every Apple Pay payment. Add to your `.entitlements`:
272
275
  ```xml
273
276
  <key>com.apple.developer.devicecheck.appattest-environment</key>
274
277
  <string>development</string>
275
278
  ```
276
- Use `production` for App Store builds.
277
- 3. **Real device required.** App Attest does not work in the simulator.
278
- 4. **Frame dashboard configuration.** In your Frame dashboard, go to **Settings → Device Attestation** and set your Apple Team ID and the Bundle ID of your iOS app. These must exactly match the app you're running the backend computes `SHA256("<TeamID>.<BundleID>")` and compares it to the hash signed by the device. If they don't match, payment-method creation fails with `App ID verification failed`.
279
+ Use `production` for App Store builds. App Attest does not work in the simulator — Apple Pay requires a real device.
280
+ 4. **Frame dashboard — device attestation.** In your Frame dashboard, **Settings Device Attestation**, set your Apple Team ID and the Bundle ID of your iOS app. These must exactly match the app you're running — the backend computes `SHA256("<TeamID>.<BundleID>")` and compares it to the hash signed by the device. If they don't match, payment-method creation fails with `App ID verification failed`.
281
+ 5. **Pass the merchant ID to `Frame.initialize`.** This is the single source of truthevery Frame Apple Pay surface reads it from here.
282
+ ```ts
283
+ await Frame.initialize({
284
+ secretKey: 'sk_...',
285
+ publishableKey: 'pk_...',
286
+ applePayMerchantId: 'merchant.com.yourapp',
287
+ });
288
+ ```
289
+
290
+ #### Enabling Apple Pay on your account
291
+
292
+ Once the steps above are complete, contact Frame at [support@framepayments.com](mailto:support@framepayments.com) (or via your [Frame dashboard](https://framepayments.com)) and we'll enable Apple Pay on your account. Apple Pay charges won't succeed until this is done on our side.
279
293
 
280
294
  On non-iOS platforms `Frame.presentApplePay` rejects synchronously with a not-supported error.
281
295
 
@@ -285,6 +299,8 @@ On non-iOS platforms `Frame.presentApplePay` rejects synchronously with a not-su
285
299
 
286
300
  Launches the native Google Pay sheet, creates a Frame payment method from the wallet token, and creates a charge against the owner. Resolves with the resulting resource's id string. Render your own button (Google's `PayButton` from `play-services-pay`, a community wrapper, or your own component) and call this from its `onPress`.
287
301
 
302
+ The Google Pay merchant ID is configured **once** at `Frame.initialize({ googlePayMerchantId })`; there is no per-call merchant parameter.
303
+
288
304
  The `owner` mirrors `presentApplePay` and determines which downstream resource is created:
289
305
 
290
306
  - `owner.type === 'customer'` → creates a `ChargeIntent` against the customer; resolves with the ChargeIntent's `id`.
@@ -312,21 +328,36 @@ const chargeIntentId = await Frame.presentGooglePay({
312
328
  | `amountCents` | `number` | Yes | Payment amount in cents |
313
329
  | `owner` | `{ type: 'customer' \| 'account', id: string }` | Yes | Customer or account that owns the resulting payment method and charge |
314
330
  | `currencyCode` | `string` | No | ISO 4217 currency code. Default `'USD'` |
315
- | `googlePayMerchantId` | `string` | No | Google Pay merchant ID override |
316
331
 
317
332
  **Returns:** `Promise<string>` — the created `ChargeIntent`'s `id` (for customer owners) or the `Transfer`'s `id` (for account owners).
318
333
 
319
- The promise rejects with `code: 'USER_CANCELED'` when the user dismisses the sheet, `'INVALID_OWNER'` if the owner is missing or malformed, `'GOOGLE_PAY_UNAVAILABLE'` if Google Pay is not ready on the device (no signed-in account, no test card, or Wallet API disabled), and `'PAYMENT_FAILED'` for backend failures.
334
+ The promise rejects with `code: 'USER_CANCELED'` when the user dismisses the sheet, `'INVALID_OWNER'` if the owner is missing or malformed, `'GOOGLE_PAY_UNAVAILABLE'` if Google Pay is not ready on the device (no signed-in account, no test card, no `googlePayMerchantId` configured at `Frame.initialize`, or Wallet API disabled in the manifest), and `'PAYMENT_FAILED'` for backend failures.
335
+
336
+ #### Required Android setup
320
337
 
321
- **Required Android setup:**
338
+ Google Pay setup is a four-part process: get a merchant ID from Google, declare the wallet capability in your manifest, pass the merchant ID to `Frame.initialize`, then **contact Frame** to enable the feature on your account.
322
339
 
323
- 1. **Google Pay metadata.** Add to your app's `AndroidManifest.xml` inside `<application>`:
340
+ 1. **Obtain a Google Pay merchant ID.** Sign up for a [Google Pay & Wallet Console](https://pay.google.com/business/console/) account, complete the business profile, and accept the Google Pay API Terms of Service. Your **Merchant ID** (looks like `BCR2DN4T…`) appears on the Business Console home page once approved.
341
+ 2. **Declare the wallet capability in `AndroidManifest.xml`.** Inside `<application>`:
324
342
  ```xml
325
343
  <meta-data
326
344
  android:name="com.google.android.gms.wallet.api.enabled"
327
345
  android:value="true" />
328
346
  ```
329
- 2. **Test environment.** When the SDK is initialized with `debugMode: true`, Google Pay runs in `ENVIRONMENT_TEST`; otherwise it uses `ENVIRONMENT_PRODUCTION`.
347
+ Without this entry the Google Pay button stays hidden the Wallet API is opted-out by default.
348
+ 3. **Test environment.** When the SDK is initialized with `debugMode: true`, Google Pay runs in `ENVIRONMENT_TEST`; otherwise it uses `ENVIRONMENT_PRODUCTION`.
349
+ 4. **Pass the merchant ID to `Frame.initialize`.** This is the single source of truth — every Frame Google Pay surface reads it from here.
350
+ ```ts
351
+ await Frame.initialize({
352
+ secretKey: 'sk_...',
353
+ publishableKey: 'pk_...',
354
+ googlePayMerchantId: 'BCR2DN4T...',
355
+ });
356
+ ```
357
+
358
+ #### Enabling Google Pay on your account
359
+
360
+ Once the steps above are complete, contact Frame at [support@framepayments.com](mailto:support@framepayments.com) (or via your [Frame dashboard](https://framepayments.com)) and we'll enable Google Pay on your account. Google Pay charges won't succeed until this is done on our side.
330
361
 
331
362
  On non-Android platforms `Frame.presentGooglePay` rejects synchronously with a not-supported error.
332
363
 
@@ -1,3 +1,5 @@
1
+ import groovy.json.JsonSlurper
2
+
1
3
  apply plugin: 'com.android.library'
2
4
  apply plugin: 'kotlin-android'
3
5
  apply plugin: 'org.jetbrains.kotlin.plugin.compose'
@@ -6,6 +8,10 @@ def safeExtGet(prop, fallback) {
6
8
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
7
9
  }
8
10
 
11
+ // Single source of truth for Frame native SDK versions — see package.json:frameNativeVersions.
12
+ def frameNativeVersions = new JsonSlurper().parse(file("$projectDir/../package.json")).frameNativeVersions
13
+ def frameAndroidVersion = frameNativeVersions.android
14
+
9
15
  android {
10
16
  namespace "com.framepayments.reactnativeframe"
11
17
  compileSdk safeExtGet('compileSdkVersion', 36)
@@ -31,9 +37,9 @@ android {
31
37
  dependencies {
32
38
  implementation 'com.facebook.react:react-native:+'
33
39
  implementation "org.jetbrains.kotlin:kotlin-stdlib:2.1.0"
34
- implementation 'com.framepayments:framesdk:2.0.7'
35
- implementation 'com.framepayments:framesdk_ui:2.0.7'
36
- implementation 'com.framepayments:framesdk_onboarding:2.0.7'
40
+ implementation "com.framepayments:framesdk:${frameAndroidVersion}"
41
+ implementation "com.framepayments:framesdk_ui:${frameAndroidVersion}"
42
+ implementation "com.framepayments:framesdk_onboarding:${frameAndroidVersion}"
37
43
  // Required by FrameGooglePayButton (PaymentsClient, WalletConstants).
38
44
  // framesdk_ui declares this as `implementation`, so it is not exposed transitively.
39
45
  implementation 'com.google.android.gms:play-services-wallet:19.4.0'
@@ -7,6 +7,7 @@ import android.os.Looper
7
7
  import android.widget.FrameLayout
8
8
  import androidx.appcompat.app.AppCompatActivity
9
9
  import com.framepayments.framesdk.FrameNetworking
10
+ import com.framepayments.framesdk.FrameResult
10
11
  import com.framepayments.framesdk_ui.FrameCheckoutView
11
12
 
12
13
  class FrameCheckoutActivity : AppCompatActivity() {
@@ -68,9 +69,23 @@ class FrameCheckoutActivity : AppCompatActivity() {
68
69
  private fun addCheckoutView(container: FrameLayout, accountId: String, amount: Int) {
69
70
  val checkoutView = FrameCheckoutView(this)
70
71
  FrameRNTheme.current?.let { checkoutView.setTheme(it) }
71
- checkoutView.configure(accountId, amount) { transferId ->
72
- setResult(RESULT_OK, Intent().putExtra(EXTRA_TRANSFER_ID, transferId))
73
- finish()
72
+ checkoutView.configure(accountId, amount) { result ->
73
+ when (result) {
74
+ is FrameResult.Completed -> {
75
+ setResult(RESULT_OK, Intent().putExtra(EXTRA_TRANSFER_ID, result.id))
76
+ finish()
77
+ }
78
+ FrameResult.Cancelled -> {
79
+ setResult(RESULT_CANCELED)
80
+ finish()
81
+ }
82
+ is FrameResult.Failed -> {
83
+ // Surfaced as USER_CANCELED at the JS layer (RESULT_CANCELED maps to it in FrameSDKModule).
84
+ // Per-error toast UI is already shown by the native FrameCheckoutView before this fires.
85
+ setResult(RESULT_CANCELED)
86
+ finish()
87
+ }
88
+ }
74
89
  }
75
90
  container.addView(checkoutView)
76
91
  }
@@ -4,6 +4,7 @@ import android.content.Intent
4
4
  import android.os.Bundle
5
5
  import android.widget.FrameLayout
6
6
  import androidx.appcompat.app.AppCompatActivity
7
+ import com.framepayments.framesdk.FrameResult
7
8
  import com.framepayments.framesdk_ui.FrameCartItem
8
9
  import com.framepayments.framesdk_ui.FrameCartView
9
10
  import com.framepayments.framesdk_ui.FrameCheckoutView
@@ -75,9 +76,21 @@ class FrameFlowActivity : AppCompatActivity() {
75
76
  container.removeAllViews()
76
77
  checkoutView = FrameCheckoutView(this).apply {
77
78
  FrameRNTheme.current?.let { setTheme(it) }
78
- configure(accountId, amount) { transferId ->
79
- setResult(RESULT_OK, Intent().putExtra(EXTRA_TRANSFER_ID, transferId))
80
- finish()
79
+ configure(accountId, amount) { result ->
80
+ when (result) {
81
+ is FrameResult.Completed -> {
82
+ setResult(RESULT_OK, Intent().putExtra(EXTRA_TRANSFER_ID, result.id))
83
+ finish()
84
+ }
85
+ FrameResult.Cancelled -> {
86
+ setResult(RESULT_CANCELED)
87
+ finish()
88
+ }
89
+ is FrameResult.Failed -> {
90
+ setResult(RESULT_CANCELED)
91
+ finish()
92
+ }
93
+ }
81
94
  }
82
95
  }
83
96
  container.addView(checkoutView)
@@ -40,7 +40,6 @@ class FrameGooglePayActivity : AppCompatActivity() {
40
40
  val ownerType = intent.getStringExtra(EXTRA_OWNER_TYPE)
41
41
  val ownerId = intent.getStringExtra(EXTRA_OWNER_ID)
42
42
  val currencyCode = intent.getStringExtra(EXTRA_CURRENCY) ?: "USD"
43
- val googlePayMerchantId = intent.getStringExtra(EXTRA_MERCHANT_ID)
44
43
 
45
44
  if (amountCents <= 0) {
46
45
  deliverFailure("Invalid amountCents")
@@ -68,7 +67,6 @@ class FrameGooglePayActivity : AppCompatActivity() {
68
67
  amountCents = amountCents,
69
68
  owner = owner,
70
69
  currencyCode = currencyCode,
71
- googlePayMerchantId = googlePayMerchantId,
72
70
  onResult = { result -> handleResult(result) },
73
71
  onReadinessChanged = { isReady -> handleReadiness(isReady) }
74
72
  )
@@ -153,7 +151,6 @@ class FrameGooglePayActivity : AppCompatActivity() {
153
151
  const val EXTRA_OWNER_TYPE = "owner_type"
154
152
  const val EXTRA_OWNER_ID = "owner_id"
155
153
  const val EXTRA_CURRENCY = "currency"
156
- const val EXTRA_MERCHANT_ID = "merchant_id"
157
154
  /**
158
155
  * The id of the resource created by the wallet flow. Holds a Transfer id when
159
156
  * the owner was an account, or a ChargeIntent id when the owner was a customer.
@@ -19,12 +19,10 @@ class FrameOnboardingActivity : ComponentActivity() {
19
19
  val accountId = intent.getStringExtra(EXTRA_ACCOUNT_ID)
20
20
  val capabilitiesJson = intent.getStringExtra(EXTRA_CAPABILITIES_JSON) ?: "[]"
21
21
  val capabilities = parseCapabilities(capabilitiesJson)
22
- val googlePayMerchantId = intent.getStringExtra(EXTRA_GOOGLE_PAY_MERCHANT_ID)
23
22
 
24
23
  val config = OnboardingConfig(
25
24
  accountId = accountId,
26
- requiredCapabilities = capabilities,
27
- googlePayMerchantId = googlePayMerchantId
25
+ requiredCapabilities = capabilities
28
26
  )
29
27
 
30
28
  val themeOverride = FrameRNTheme.current
@@ -44,7 +42,7 @@ class FrameOnboardingActivity : ComponentActivity() {
44
42
  setResult(RESULT_CANCELED)
45
43
  finish()
46
44
  }
47
- is OnboardingResult.Error -> {
45
+ is OnboardingResult.Failed -> {
48
46
  setResult(RESULT_CANCELED)
49
47
  finish()
50
48
  }
@@ -69,7 +67,6 @@ class FrameOnboardingActivity : ComponentActivity() {
69
67
  companion object {
70
68
  const val EXTRA_ACCOUNT_ID = "account_id"
71
69
  const val EXTRA_CAPABILITIES_JSON = "capabilities_json"
72
- const val EXTRA_GOOGLE_PAY_MERCHANT_ID = "google_pay_merchant_id"
73
70
  const val EXTRA_PAYMENT_METHOD_ID = "payment_method_id"
74
71
  const val REQUEST_CODE = 9003
75
72
  }
@@ -7,9 +7,10 @@ import com.facebook.react.bridge.Promise
7
7
  import com.facebook.react.bridge.ReactApplicationContext
8
8
  import com.facebook.react.bridge.ReactContextBaseJavaModule
9
9
  import com.facebook.react.bridge.ReactMethod
10
+ import com.facebook.react.bridge.ReadableArray
11
+ import com.facebook.react.bridge.ReadableMap
10
12
  import com.facebook.react.bridge.WritableNativeMap
11
13
  import com.framepayments.framesdk.FrameNetworking
12
- import org.json.JSONObject
13
14
 
14
15
  class FrameSDKModule(reactContext: ReactApplicationContext) :
15
16
  ReactContextBaseJavaModule(reactContext), ActivityEventListener {
@@ -35,12 +36,17 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
35
36
  secretKey: String,
36
37
  publishableKey: String,
37
38
  debugMode: Boolean,
38
- theme: com.facebook.react.bridge.ReadableMap?,
39
+ applePayMerchantId: String?,
40
+ googlePayMerchantId: String?,
41
+ theme: ReadableMap?,
39
42
  promise: Promise
40
43
  ) {
41
44
  try {
42
45
  val ctx = reactApplicationContext.applicationContext
43
- FrameNetworking.initializeWithAPIKey(ctx, secretKey, publishableKey, debugMode)
46
+ // applePayMerchantId is iOS-only; accepted in the bridge signature so the JS Frame.initialize()
47
+ // API stays cross-platform, but ignored here. frame-android has no Apple Pay surface.
48
+ @Suppress("UNUSED_PARAMETER") val ignoredApplePayMerchantId = applePayMerchantId
49
+ FrameNetworking.initializeWithAPIKey(ctx, secretKey, publishableKey, googlePayMerchantId, debugMode)
44
50
  FrameRNTheme.current = theme?.takeIf { it.keySetIterator().hasNextKey() }?.let {
45
51
  FrameRNTheme.parse(ctx, it)
46
52
  }
@@ -69,7 +75,7 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
69
75
  @ReactMethod
70
76
  fun presentCart(
71
77
  accountId: String?,
72
- items: com.facebook.react.bridge.ReadableArray,
78
+ items: ReadableArray,
73
79
  shippingAmountInCents: Double,
74
80
  promise: Promise
75
81
  ) {
@@ -98,7 +104,6 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
98
104
  ownerType: String?,
99
105
  ownerId: String?,
100
106
  currencyCode: String?,
101
- googlePayMerchantId: String?,
102
107
  promise: Promise
103
108
  ) {
104
109
  val activity = reactApplicationContext.currentActivity ?: run {
@@ -128,7 +133,6 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
128
133
  putExtra(FrameGooglePayActivity.EXTRA_OWNER_TYPE, ownerType)
129
134
  putExtra(FrameGooglePayActivity.EXTRA_OWNER_ID, ownerId)
130
135
  putExtra(FrameGooglePayActivity.EXTRA_CURRENCY, currencyCode ?: "USD")
131
- putExtra(FrameGooglePayActivity.EXTRA_MERCHANT_ID, googlePayMerchantId)
132
136
  }
133
137
  activity.startActivity(intent)
134
138
  }
@@ -137,8 +141,7 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
137
141
  @ReactMethod
138
142
  fun presentOnboarding(
139
143
  accountId: String?,
140
- capabilities: com.facebook.react.bridge.ReadableArray,
141
- googlePayMerchantId: String?,
144
+ capabilities: ReadableArray,
142
145
  promise: Promise
143
146
  ) {
144
147
  val activity = reactApplicationContext.currentActivity ?: run {
@@ -151,13 +154,12 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
151
154
  val intent = Intent(activity, FrameOnboardingActivity::class.java).apply {
152
155
  putExtra(FrameOnboardingActivity.EXTRA_ACCOUNT_ID, accountId)
153
156
  putExtra(FrameOnboardingActivity.EXTRA_CAPABILITIES_JSON, capabilitiesJson)
154
- putExtra(FrameOnboardingActivity.EXTRA_GOOGLE_PAY_MERCHANT_ID, googlePayMerchantId)
155
157
  }
156
158
  activity.startActivityForResult(intent, FrameOnboardingActivity.REQUEST_CODE)
157
159
  }
158
160
  }
159
161
 
160
- private fun readableArrayToJson(items: com.facebook.react.bridge.ReadableArray): String? {
162
+ private fun readableArrayToJson(items: ReadableArray): String? {
161
163
  val arr = org.json.JSONArray()
162
164
  for (i in 0 until items.size()) {
163
165
  val item = items.getMap(i) ?: return null
@@ -171,7 +173,7 @@ class FrameSDKModule(reactContext: ReactApplicationContext) :
171
173
  return arr.toString()
172
174
  }
173
175
 
174
- private fun readableArrayToJsonArray(arr: com.facebook.react.bridge.ReadableArray): String {
176
+ private fun readableArrayToJsonArray(arr: ReadableArray): String {
175
177
  val jsonArr = org.json.JSONArray()
176
178
  for (i in 0 until arr.size()) {
177
179
  arr.getString(i)?.let { jsonArr.put(it) }
@@ -28,7 +28,6 @@ final class ApplePayPresenter: NSObject, PKPaymentAuthorizationControllerDelegat
28
28
  private let amount: Int
29
29
  private let currency: String
30
30
  private let owner: Owner
31
- private let merchantId: String
32
31
  private let resolve: (Any?) -> Void
33
32
  private let reject: (String, String, Error?) -> Void
34
33
 
@@ -46,13 +45,11 @@ final class ApplePayPresenter: NSObject, PKPaymentAuthorizationControllerDelegat
46
45
  init(amount: Int,
47
46
  currency: String,
48
47
  owner: Owner,
49
- merchantId: String,
50
48
  resolve: @escaping (Any?) -> Void,
51
49
  reject: @escaping (String, String, Error?) -> Void) {
52
50
  self.amount = amount
53
51
  self.currency = currency
54
52
  self.owner = owner
55
- self.merchantId = merchantId
56
53
  self.resolve = resolve
57
54
  self.reject = reject
58
55
  }
@@ -63,7 +60,7 @@ final class ApplePayPresenter: NSObject, PKPaymentAuthorizationControllerDelegat
63
60
 
64
61
  func present() {
65
62
  let request = PKPaymentRequest()
66
- request.merchantIdentifier = merchantId
63
+ request.merchantIdentifier = FrameNetworking.shared.applePayMerchantId ?? ""
67
64
  request.supportedNetworks = Self.supportedNetworks
68
65
  request.merchantCapabilities = .threeDSecure
69
66
  request.countryCode = "US"
@@ -0,0 +1,23 @@
1
+ //
2
+ // FramePreloader.h
3
+ // FrameReactNative
4
+ //
5
+ // Public Obj-C interface for the Swift `FramePreloader` class. Lets consumers
6
+ // reach `+[FramePreloader preloadOnMainThread]` from AppDelegate.mm (or any
7
+ // .m/.mm file) via `#import "FramePreloader.h"`, without needing modules
8
+ // enabled in C++ context.
9
+ //
10
+
11
+ #import <Foundation/Foundation.h>
12
+
13
+ NS_ASSUME_NONNULL_BEGIN
14
+
15
+ @interface FramePreloader : NSObject
16
+
17
+ /// Call from AppDelegate before [super application:didFinishLaunchingWithOptions:].
18
+ /// Forces Frame (and its deps: Evervault, Sift) to load on the main thread.
19
+ + (void)preloadOnMainThread;
20
+
21
+ @end
22
+
23
+ NS_ASSUME_NONNULL_END