CryptoOnramp SDK: Swapped Document Collection and T&C/TOS Bindings by mliberatore · Pull Request #2760 · stripe/stripe-react-native · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.WritableArray
import com.facebook.react.bridge.WritableMap
import com.reactnativestripesdk.utils.createCanceledError
import com.reactnativestripesdk.utils.getIntegerList
import com.reactnativestripesdk.utils.mapToPreferredNetworks
import com.stripe.android.core.model.CountryCode
Expand All @@ -19,6 +21,8 @@ import com.stripe.android.crypto.onramp.exception.SDKVersion
import com.stripe.android.crypto.onramp.model.IdType
import com.stripe.android.crypto.onramp.model.KycInfo
import com.stripe.android.crypto.onramp.model.OnrampConfiguration
import com.stripe.android.crypto.onramp.model.OnrampKycResult
import com.stripe.android.crypto.onramp.model.OnrampPartnerTermsResult
import com.stripe.android.crypto.onramp.model.PaymentMethodDisplayData
import com.stripe.android.crypto.onramp.model.PaymentMethodSelection
import com.stripe.android.crypto.onramp.model.WalletOwnershipChallenge
Expand Down Expand Up @@ -390,6 +394,24 @@ internal fun mapFromSubmitIdentifiersResult(
putArray("invalidIdentifiers", mapFromComplianceIdentifierTypes(result.invalidIdentifiers))
}

@SuppressLint("RestrictedApi")
internal fun mapFromOnrampKycResult(result: OnrampKycResult): WritableMap =
when (result) {
is OnrampKycResult.Submitted -> Arguments.createMap().apply { putString("status", "Submitted") }
is OnrampKycResult.PendingVerification -> Arguments.createMap().apply { putString("status", "PendingVerification") }
is OnrampKycResult.NotRequired -> Arguments.createMap().apply { putString("status", "NotRequired") }
is OnrampKycResult.Cancelled -> createCanceledError("KYC collection was canceled")
is OnrampKycResult.Failed -> createOnrampFailedError(result.error)
}

internal fun mapFromOnrampPartnerTermsResult(result: OnrampPartnerTermsResult): WritableMap =
when (result) {
is OnrampPartnerTermsResult.Accepted -> Arguments.createMap().apply { putString("status", "Accepted") }
is OnrampPartnerTermsResult.NotRequired -> Arguments.createMap().apply { putString("status", "NotRequired") }
is OnrampPartnerTermsResult.Cancelled -> createCanceledError("Partner terms were canceled")
is OnrampPartnerTermsResult.Failed -> createOnrampFailedError(result.error)
}

private fun mapFromComplianceIdentifierRequirementsList(
requirements: List<ComplianceIdentifierRequirement>,
): WritableArray =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.SavedStateHandle
import com.stripe.android.core.model.CountryCode
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.Promise
Expand All @@ -31,6 +30,7 @@ import com.reactnativestripesdk.utils.createResult
import com.reactnativestripesdk.utils.getStringList
import com.reactnativestripesdk.utils.getValOr
import com.reactnativestripesdk.utils.mapToPaymentSheetAddress
import com.stripe.android.core.model.CountryCode
import com.stripe.android.crypto.onramp.ExperimentalCryptoOnramp
import com.stripe.android.crypto.onramp.OnrampCoordinator
import com.stripe.android.crypto.onramp.exception.SDKVersion
Expand All @@ -46,7 +46,6 @@ import com.stripe.android.crypto.onramp.model.OnrampConfigurationResult
import com.stripe.android.crypto.onramp.model.OnrampCreateCryptoPaymentTokenResult
import com.stripe.android.crypto.onramp.model.OnrampDeleteWalletAddressResult
import com.stripe.android.crypto.onramp.model.OnrampGetWalletOwnershipChallengeResult
import com.stripe.android.crypto.onramp.model.OnrampUserAttestationResult
import com.stripe.android.crypto.onramp.model.OnrampHasLinkAccountResult
import com.stripe.android.crypto.onramp.model.OnrampLogOutResult
import com.stripe.android.crypto.onramp.model.OnrampRegisterLinkUserResult
Expand All @@ -56,6 +55,7 @@ import com.stripe.android.crypto.onramp.model.OnrampSubmitIdentifiersResult
import com.stripe.android.crypto.onramp.model.OnrampSubmitWalletOwnershipSignatureResult
import com.stripe.android.crypto.onramp.model.OnrampTokenAuthenticationResult
import com.stripe.android.crypto.onramp.model.OnrampUpdatePhoneNumberResult
import com.stripe.android.crypto.onramp.model.OnrampUserAttestationResult
import com.stripe.android.crypto.onramp.model.OnrampVerifyIdentityResult
import com.stripe.android.crypto.onramp.model.OnrampVerifyKycInfoResult
import com.stripe.android.link.LinkController.PaymentMethodPreview
Expand Down Expand Up @@ -106,6 +106,9 @@ class OnrampSdkModule(
private var checkoutPromise: Promise? = null
private var verifyKycPromise: Promise? = null
private var userAttestationPromise: Promise? = null
private var fulfillKycRequirementsPromise: Promise? = null
private var termsAndConditionsPromise: Promise? = null
private var termsOfServicePromise: Promise? = null
private var samsungPayAvailability = CompletableDeferred(false)

private var checkoutClientSecretDeferred: CompletableDeferred<String>? = null
Expand Down Expand Up @@ -185,34 +188,7 @@ class OnrampSdkModule(
return
}

val onrampCallbacks =
OnrampCallbacks()
.verifyIdentityCallback { result ->
handleOnrampIdentityVerificationResult(result, identityVerificationPromise!!)
}.collectPaymentCallback { result ->
handleOnrampCollectPaymentResult(result, collectPaymentPromise!!)
}.authorizeCallback { result ->
handleOnrampAuthorizationResult(result, authorizePromise!!)
}.checkoutCallback { result ->
handleOnrampCheckoutResult(result, checkoutPromise!!)
}.verifyKycCallback { result ->
handleOnrampKycVerificationResult(result, verifyKycPromise!!)
}.userAttestationCallback { result ->
userAttestationPromise?.let {
handleUserAttestationResult(result, it)
}
}.samsungPayIsReadyCallback { isReady, _ ->
samsungPayAvailability.complete(isReady)
}.onrampSessionClientSecretProvider { sessionId ->
checkoutClientSecretDeferred = CompletableDeferred()

val params = Arguments.createMap()
params.putString("onrampSessionId", sessionId)

eventEmitterCompat.emitOnCheckoutClientSecretRequested(params)

checkoutClientSecretDeferred!!.await()
}
val onrampCallbacks = createOnrampCallbacks()

this.onrampCallbacks = onrampCallbacks

Expand All @@ -239,6 +215,44 @@ class OnrampSdkModule(
}
}

private fun createOnrampCallbacks(): OnrampCallbacks =
OnrampCallbacks()
.verifyIdentityCallback { result ->
handleOnrampIdentityVerificationResult(result, identityVerificationPromise!!)
}.collectPaymentCallback { result ->
handleOnrampCollectPaymentResult(result, collectPaymentPromise!!)
}.authorizeCallback { result ->
handleOnrampAuthorizationResult(result, authorizePromise!!)
}.checkoutCallback { result ->
handleOnrampCheckoutResult(result, checkoutPromise!!)
}.verifyKycCallback { result ->
handleOnrampKycVerificationResult(result, verifyKycPromise!!)
}.userAttestationCallback { result ->
userAttestationPromise?.let {
handleUserAttestationResult(result, it)
}
}.kycCallback { result ->
fulfillKycRequirementsPromise?.resolve(mapFromOnrampKycResult(result))
fulfillKycRequirementsPromise = null
}.termsAndConditionsCallback { result ->
termsAndConditionsPromise?.resolve(mapFromOnrampPartnerTermsResult(result))
termsAndConditionsPromise = null
}.termsOfServiceCallback { result ->
termsOfServicePromise?.resolve(mapFromOnrampPartnerTermsResult(result))
termsOfServicePromise = null
}.samsungPayIsReadyCallback { isReady, _ ->
samsungPayAvailability.complete(isReady)
}.onrampSessionClientSecretProvider { sessionId ->
checkoutClientSecretDeferred = CompletableDeferred()

val params = Arguments.createMap()
params.putString("onrampSessionId", sessionId)

eventEmitterCompat.emitOnCheckoutClientSecretRequested(params)

checkoutClientSecretDeferred!!.await()
}

@ReactMethod
override fun isSamsungPaySupported(promise: Promise) {
rnScope.launch {
Expand Down Expand Up @@ -599,6 +613,30 @@ class OnrampSdkModule(
}
}

@ReactMethod
override fun fulfillKycRequirements(promise: Promise) {
withOnrampPresenter(promise) { presenter ->
fulfillKycRequirementsPromise = promise
presenter.fulfillKycRequirements()
}
}

@ReactMethod
override fun presentTermsAndConditionsIfNeeded(promise: Promise) {
withOnrampPresenter(promise) { presenter ->
termsAndConditionsPromise = promise
presenter.presentTermsAndConditionsIfNeeded()
}
}

@ReactMethod
override fun presentTermsOfServiceIfNeeded(promise: Promise) {
withOnrampPresenter(promise) { presenter ->
termsOfServicePromise = promise
presenter.presentTermsOfServiceIfNeeded()
}
}

@ReactMethod
override fun updatePhoneNumber(
phone: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import com.reactnativestripesdk.mapAppearance
import com.reactnativestripesdk.mapConfig
import com.reactnativestripesdk.mapFromCryptoConsumerWallet
import com.reactnativestripesdk.mapFromKycInfo
import com.reactnativestripesdk.mapFromOnrampKycResult
import com.reactnativestripesdk.mapFromOnrampPartnerTermsResult
import com.reactnativestripesdk.mapFromWalletOwnershipChallenge
import com.reactnativestripesdk.mapGooglePayConfig
import com.reactnativestripesdk.mapOnrampPaymentMethodSelection
Expand All @@ -24,6 +26,8 @@ import com.stripe.android.crypto.onramp.ExperimentalCryptoOnramp
import com.stripe.android.crypto.onramp.model.CryptoNetwork
import com.stripe.android.crypto.onramp.model.IdType
import com.stripe.android.crypto.onramp.model.KycInfo
import com.stripe.android.crypto.onramp.model.OnrampKycResult
import com.stripe.android.crypto.onramp.model.OnrampPartnerTermsResult
import com.stripe.android.crypto.onramp.model.PaymentMethodDisplayData
import com.stripe.android.crypto.onramp.model.PaymentMethodSelection
import com.stripe.android.googlepaylauncher.GooglePayEnvironment
Expand Down Expand Up @@ -926,4 +930,62 @@ class OnrampMappersTest {
error.message,
)
}

@Test
fun mapFromOnrampKycResult_preservesOutcomes() {
val submitted = mapFromOnrampKycResult(nativeResult(OnrampKycResult.Submitted::class.java))
assertEquals("Submitted", submitted.getString("status"))
assertFalse(submitted.hasKey("error"))

val pendingVerification = mapFromOnrampKycResult(
nativeResult(OnrampKycResult.PendingVerification::class.java),
)
assertEquals("PendingVerification", pendingVerification.getString("status"))
assertFalse(pendingVerification.hasKey("error"))

val notRequired = mapFromOnrampKycResult(nativeResult(OnrampKycResult.NotRequired::class.java))
assertEquals("NotRequired", notRequired.getString("status"))
assertFalse(notRequired.hasKey("error"))

val canceled = mapFromOnrampKycResult(nativeResult(OnrampKycResult.Cancelled::class.java))
assertEquals("Canceled", canceled.getMap("error")!!.getString("code"))
assertFalse(canceled.hasKey("status"))

val failed = mapFromOnrampKycResult(
nativeResult(OnrampKycResult.Failed::class.java, IllegalStateException("Upload failed")),
)
assertEquals("Failed", failed.getMap("error")!!.getString("code"))
assertEquals("Upload failed", failed.getMap("error")!!.getString("message"))
assertFalse(failed.hasKey("status"))
}

@Test
fun mapFromOnrampPartnerTermsResult_preservesOutcomes() {
val accepted = mapFromOnrampPartnerTermsResult(nativeResult(OnrampPartnerTermsResult.Accepted::class.java))
assertEquals("Accepted", accepted.getString("status"))
assertFalse(accepted.hasKey("error"))

val notRequired = mapFromOnrampPartnerTermsResult(
nativeResult(OnrampPartnerTermsResult.NotRequired::class.java),
)
assertEquals("NotRequired", notRequired.getString("status"))
assertFalse(notRequired.hasKey("error"))

val canceled = mapFromOnrampPartnerTermsResult(nativeResult(OnrampPartnerTermsResult.Cancelled::class.java))
assertEquals("Canceled", canceled.getMap("error")!!.getString("code"))
assertFalse(canceled.hasKey("status"))

val failed = mapFromOnrampPartnerTermsResult(
nativeResult(OnrampPartnerTermsResult.Failed::class.java, IllegalStateException("Acceptance failed")),
)
assertEquals("Failed", failed.getMap("error")!!.getString("code"))
assertEquals("Acceptance failed", failed.getMap("error")!!.getString("message"))
assertFalse(failed.hasKey("status"))
}

@Suppress("UNCHECKED_CAST")
private fun <T> nativeResult(type: Class<T>, vararg arguments: Any): T =
type.declaredConstructors.single { !it.isSynthetic && it.parameterCount == arguments.size }
.apply { isAccessible = true }
.newInstance(*arguments) as T
}
25 changes: 25 additions & 0 deletions etc/stripe-react-native.api.md
Loading
Loading