diff --git a/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt b/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt index c6cced3e11..8b5ff910ae 100644 --- a/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +++ b/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.os.Bundle import android.util.Log import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity import androidx.browser.customtabs.CustomTabsIntent import androidx.fragment.app.FragmentActivity import com.facebook.react.ReactActivity @@ -19,6 +20,7 @@ import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.WritableNativeMap import com.facebook.react.module.annotations.ReactModule import com.reactnativestripesdk.addresssheet.AddressLauncherManager import com.reactnativestripesdk.customersheet.CustomerSheetManager @@ -63,6 +65,7 @@ import com.stripe.android.model.ConfirmPaymentIntentParams import com.stripe.android.model.ConfirmSetupIntentParams import com.stripe.android.model.PaymentIntent import com.stripe.android.model.PaymentMethod +import com.stripe.android.model.RadarSession import com.stripe.android.model.SetupIntent import com.stripe.android.model.Token import com.stripe.android.payments.bankaccount.CollectBankAccountConfiguration @@ -1293,6 +1296,31 @@ class StripeSdkModule( } } + @ReactMethod + override fun createRadarSession(promise: Promise) { + if (!::stripe.isInitialized) { + promise.resolve(createMissingInitError()) + return + } + + stripe.createRadarSession( + stripeAccountId = stripeAccountId, + callback = + object : com.stripe.android.ApiResultCallback { + override fun onSuccess(session: RadarSession) { + val result = WritableNativeMap() + result.putString("id", session.id) + promise.resolve(result) + } + + override fun onError(e: Exception) { + promise.resolve(createError(ErrorType.Failed.toString(), e)) + } + }, + activity = getCurrentActivityOrResolveWithError(promise) as? AppCompatActivity, + ) + } + @ReactMethod override fun createEmbeddedPaymentElement( intentConfig: ReadableMap, diff --git a/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java b/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java index d298aba01b..6562f0cebb 100644 --- a/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java +++ b/android/src/oldarch/java/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java @@ -279,4 +279,8 @@ private void invoke(String eventName) { @ReactMethod @DoNotStrip public abstract void removeListeners(double count); + + @ReactMethod + @DoNotStrip + public abstract void createRadarSession(Promise promise); } diff --git a/ios/StripeSdk.mm b/ios/StripeSdk.mm index 24fdc13d20..f4b9bb809b 100644 --- a/ios/StripeSdk.mm +++ b/ios/StripeSdk.mm @@ -443,6 +443,12 @@ - (instancetype)init [StripeSdkImpl.shared customPaymentMethodResultCallback:result resolver:resolve rejecter:reject]; } +RCT_EXPORT_METHOD(createRadarSession:(nonnull RCTPromiseResolveBlock)resolve + reject:(nonnull RCTPromiseRejectBlock)reject) +{ + [StripeSdkImpl.shared createRadarSession:resolve rejecter:reject]; +} + /* clang-format on */ #ifdef RCT_NEW_ARCH_ENABLED diff --git a/ios/StripeSdkImpl.swift b/ios/StripeSdkImpl.swift index f8561007c6..cd42736982 100644 --- a/ios/StripeSdkImpl.swift +++ b/ios/StripeSdkImpl.swift @@ -4,6 +4,7 @@ import PassKit @_spi(DashboardOnly) @_spi(STP) import Stripe import StripeFinancialConnections @_spi(STP) @_spi(ConfirmationTokensPublicPreview) import StripePayments +import StripePaymentsUI #if canImport(StripeCryptoOnramp) @_spi(STP) import StripeCryptoOnramp @@ -1189,6 +1190,26 @@ public class StripeSdkImpl: NSObject, UIAdaptivePresentationControllerDelegate { #endif } + @objc(createRadarSession:rejecter:) + public func createRadarSession( + resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock + ) { + STPAPIClient.shared.createRadarSession { (session, error) in + if let error = error as NSError? { + resolve(Errors.createError(ErrorType.Failed, error)) + return + } + + guard let session else { + resolve(Errors.createError(ErrorType.Unknown, "Radar session not available")) + return + } + + resolve(["id": session.id]) + } + } + #if canImport(StripeCryptoOnramp) @objc(configureOnramp:resolver:rejecter:) public func configureOnramp( diff --git a/src/functions.ts b/src/functions.ts index 6ff0031282..8c3d9606bc 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -31,6 +31,7 @@ import type { CanAddCardToWalletResult, FinancialConnections, PlatformPay, + CreateRadarSessionResult, } from './types'; import { Platform, EventSubscription } from 'react-native'; import type { CollectFinancialConnectionsAccountsParams } from './types/FinancialConnections'; @@ -910,6 +911,15 @@ export const openPlatformPaySetup = async (): Promise => { } }; +/** + * Creates a [Radar session](https://docs.stripe.com/radar/radar-session). + * @returns A promise that resolves to a Radar session id, or an error. + */ +export const createRadarSession = + async (): Promise => { + return await NativeStripeSdk.createRadarSession(); + }; + export const setFinancialConnectionsForceNativeFlow = async ( enabled: boolean ): Promise => { diff --git a/src/hooks/useStripe.tsx b/src/hooks/useStripe.tsx index 0a66860160..7e4c775567 100644 --- a/src/hooks/useStripe.tsx +++ b/src/hooks/useStripe.tsx @@ -27,6 +27,7 @@ import type { FinancialConnections, PlatformPay, PlatformPayError, + CreateRadarSessionResult, } from '../types'; import { useCallback } from 'react'; import { @@ -59,6 +60,7 @@ import { createPlatformPayToken, updatePlatformPaySheet, openPlatformPaySetup, + createRadarSession, } from '../functions'; import type { CollectBankAccountTokenParams } from '../types/PaymentMethod'; import type { CollectFinancialConnectionsAccountsParams } from '../types/FinancialConnections'; @@ -320,6 +322,11 @@ export function useStripe() { return openPlatformPaySetup(); }, []); + const _createRadarSession = + useCallback(async (): Promise => { + return createRadarSession(); + }, []); + return { retrievePaymentIntent: _retrievePaymentIntent, retrieveSetupIntent: _retrieveSetupIntent, @@ -355,5 +362,6 @@ export function useStripe() { createPlatformPayToken: _createPlatformPayToken, updatePlatformPaySheet: _updatePlatformPaySheet, openPlatformPaySetup: _openPlatformPaySetup, + createRadarSession: _createRadarSession, }; } diff --git a/src/specs/NativeStripeSdkModule.ts b/src/specs/NativeStripeSdkModule.ts index 31b9d684fc..729c378ada 100644 --- a/src/specs/NativeStripeSdkModule.ts +++ b/src/specs/NativeStripeSdkModule.ts @@ -34,6 +34,7 @@ import type { StripeError, Token, VerifyMicrodepositsParams, + CreateRadarSessionResult, } from '../types'; import type { EmbeddedPaymentElementConfiguration, @@ -193,6 +194,7 @@ export interface Spec extends TurboModule { intentConfig: UnsafeObject ): Promise; clearEmbeddedPaymentOption(viewTag: Int32): Promise; + createRadarSession(): Promise; setFinancialConnectionsForceNativeFlow(enabled: boolean): Promise; diff --git a/src/types/Errors.ts b/src/types/Errors.ts index 2c5affeb44..2d57a4ac35 100644 --- a/src/types/Errors.ts +++ b/src/types/Errors.ts @@ -107,3 +107,8 @@ export enum PlatformPayError { Failed = 'Failed', Unknown = 'Unknown', } + +export enum RadarError { + Failed = 'Failed', + Unknown = 'Unknown', +} diff --git a/src/types/index.ts b/src/types/index.ts index fd15926720..103ee46a36 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -12,6 +12,7 @@ import type { StripeError, VerifyMicrodepositsError, CollectBankAccountError, + RadarError, } from './Errors'; import * as ApplePay from './ApplePay'; import * as PaymentIntent from './PaymentIntent'; @@ -176,6 +177,16 @@ export type ConfirmPaymentSheetPaymentResult = { error?: StripeError; }; +export type CreateRadarSessionResult = + | { + id: string; + error?: undefined; + } + | { + id?: undefined; + error: StripeError; + }; + export type ApplePayResult = | { paymentMethod: PaymentMethod.Result;