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.
- package/FrameReactNative.podspec +44 -0
- package/Package.swift +47 -0
- package/README.md +64 -33
- package/android/build.gradle +9 -3
- package/android/src/main/java/com/framepayments/reactnativeframe/FrameCheckoutActivity.kt +18 -3
- package/android/src/main/java/com/framepayments/reactnativeframe/FrameFlowActivity.kt +16 -3
- package/android/src/main/java/com/framepayments/reactnativeframe/FrameGooglePayActivity.kt +0 -3
- package/android/src/main/java/com/framepayments/reactnativeframe/FrameOnboardingActivity.kt +2 -5
- package/android/src/main/java/com/framepayments/reactnativeframe/FrameSDKModule.kt +13 -11
- package/ios/ApplePayPresenter.swift +1 -4
- package/ios/FramePreloader.h +23 -0
- package/ios/FrameSDKBridge.m +17 -17
- package/ios/FrameSDKBridge.swift +109 -54
- package/lib/native.d.ts +12 -2
- package/lib/native.d.ts.map +1 -1
- package/lib/native.js +18 -18
- package/lib/types.d.ts +12 -6
- package/lib/types.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/__tests__/native.test.ts +60 -74
- package/src/native.ts +37 -34
- package/src/types.ts +12 -6
- package/ios/FrameReactNative.podspec +0 -32
- package/lib/__tests__/native.test.d.ts +0 -32
- package/lib/__tests__/native.test.d.ts.map +0 -1
- package/lib/__tests__/native.test.js +0 -101
- package/lib/components/FrameApplePayButton.d.ts +0 -30
- package/lib/components/FrameApplePayButton.d.ts.map +0 -1
- package/lib/components/FrameApplePayButton.js +0 -42
- package/lib/components/FrameGooglePayButton.d.ts +0 -28
- package/lib/components/FrameGooglePayButton.d.ts.map +0 -1
- package/lib/components/FrameGooglePayButton.js +0 -24
- package/lib/components/index.d.ts +0 -5
- package/lib/components/index.d.ts.map +0 -1
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
####
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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. **
|
|
271
|
-
2. **
|
|
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
|
-
|
|
278
|
-
|
|
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 truth — every 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
|
-
**
|
|
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
|
|
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
|
-
|
|
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
|
|
package/android/build.gradle
CHANGED
|
@@ -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
|
|
35
|
-
implementation
|
|
36
|
-
implementation
|
|
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) {
|
|
72
|
-
|
|
73
|
-
|
|
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) {
|
|
79
|
-
|
|
80
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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
|