Skip to content

Commit

Permalink
Increase flexibility of ForageElement hierarchy
Browse files Browse the repository at this point in the history
This is the third step in the public ecommerce repo that needs to
take place in order to make sharing code between the private pos
repo and this repo pain free; all while allowing for both repos
to evolve independently.

This hierarchy will make it easy to keep ecom things in the
ecom-ForagePinEditText and pos things in the pos-ForagePINEditText

Signed-off-by: Devin Morgan <[email protected]>
  • Loading branch information
devinmorgan committed May 3, 2024
1 parent 6458699 commit 08b79c4
Show file tree
Hide file tree
Showing 17 changed files with 703 additions and 744 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.joinforage.forage.android.core

import com.joinforage.forage.android.core.services.forageapi.network.ForageApiResponse
import com.joinforage.forage.android.ecom.ui.ForagePANEditText
import com.joinforage.forage.android.ecom.ui.ForagePINEditText
import com.joinforage.forage.android.core.ui.element.ForagePanElement
import com.joinforage.forage.android.core.ui.element.ForagePinElement

/**
* An [Exception](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-exception/) thrown if a
Expand Down Expand Up @@ -98,7 +98,7 @@ internal interface ForageSDKInterface {
* to create multiple payments, set to true by default.
*/
data class TokenizeEBTCardParams(
val foragePanEditText: ForagePANEditText,
val foragePanEditText: ForagePanElement,
val customerId: String? = null,
val reusable: Boolean = true
)
Expand All @@ -120,7 +120,7 @@ data class TokenizeEBTCardParams(
* endpoint.
*/
data class CheckBalanceParams(
val foragePinEditText: ForagePINEditText,
val foragePinEditText: ForagePinElement,
val paymentMethodRef: String
)

Expand All @@ -138,7 +138,7 @@ data class CheckBalanceParams(
* [Create a `Payment`](https://docs.joinforage.app/reference/create-a-payment) endpoint.
*/
data class CapturePaymentParams(
val foragePinEditText: ForagePINEditText,
val foragePinEditText: ForagePinElement,
val paymentRef: String
)

Expand All @@ -163,6 +163,6 @@ data class CapturePaymentParams(
* [Create a `Payment`](https://docs.joinforage.app/reference/create-a-payment) endpoint.
*/
data class DeferPaymentCaptureParams(
val foragePinEditText: ForagePINEditText,
val foragePinEditText: ForagePinElement,
val paymentRef: String
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.joinforage.forage.android.core.services.vault

import android.content.Context
import com.joinforage.forage.android.core.services.launchdarkly.VaultType
import com.joinforage.forage.android.core.services.telemetry.Log
import com.joinforage.forage.android.core.services.telemetry.UserAction
import com.joinforage.forage.android.core.services.telemetry.VaultProxyResponseMonitor
Expand All @@ -11,9 +10,8 @@ import com.joinforage.forage.android.core.services.ForageConstants
import com.joinforage.forage.android.core.services.forageapi.network.ForageApiResponse
import com.joinforage.forage.android.core.services.forageapi.network.ForageError
import com.joinforage.forage.android.core.services.forageapi.network.UnknownErrorApiResponse
import com.joinforage.forage.android.ecom.services.vault.bt.BasisTheoryPinSubmitter
import com.joinforage.forage.android.ecom.ui.ForagePINEditText
import com.joinforage.forage.android.ecom.services.vault.vgs.VgsPinSubmitter
import com.joinforage.forage.android.core.services.VaultType
import com.joinforage.forage.android.core.ui.element.ForagePinElement

internal val IncompletePinError = ForageApiResponse.Failure.fromError(
ForageError(400, "user_error", "Invalid EBT Card PIN entered. Please enter your 4-digit PIN.")
Expand All @@ -31,16 +29,16 @@ internal open class VaultSubmitterParams(

internal interface VaultSubmitter {
suspend fun submit(params: VaultSubmitterParams): ForageApiResponse<String>

fun getVaultType(): VaultType
}

internal abstract class AbstractVaultSubmitter<VaultResponse>(
internal abstract class AbstractVaultSubmitter(
protected val context: Context,
protected val foragePinEditText: ForagePINEditText,
protected val foragePinEditText: ForagePinElement,
protected val logger: Log,
private val vaultType: VaultType
) : VaultSubmitter {

abstract val vaultType: VaultType

// interface methods
override suspend fun submit(params: VaultSubmitterParams): ForageApiResponse<String> {
logger.addAttribute("payment_method_ref", params.paymentMethod.ref)
Expand Down Expand Up @@ -99,28 +97,11 @@ internal abstract class AbstractVaultSubmitter<VaultResponse>(
return forageResponse
}

override fun getVaultType(): VaultType {
return vaultType
}

// abstract methods
internal abstract fun parseEncryptionKey(encryptionKeys: EncryptionKeys): String
internal abstract suspend fun submitProxyRequest(vaultProxyRequest: VaultProxyRequest): ForageApiResponse<String>
internal abstract fun getVaultToken(paymentMethod: PaymentMethod): String?

/**
* @return [UnknownErrorApiResponse] if the response is a vault error, or null if it is not
*/
internal abstract fun toVaultErrorOrNull(vaultResponse: VaultResponse): ForageApiResponse.Failure?
internal abstract fun toForageErrorOrNull(vaultResponse: VaultResponse): ForageApiResponse.Failure?
internal abstract fun toForageSuccessOrNull(vaultResponse: VaultResponse): ForageApiResponse.Success<String>?

/**
* @return A string containing the raw error details of the vault error.
* To be used for internal error reporting.
*/
internal abstract fun parseVaultErrorMessage(vaultResponse: VaultResponse): String

// concrete methods
protected open fun buildProxyRequest(
params: VaultSubmitterParams,
Expand All @@ -145,58 +126,13 @@ internal abstract class AbstractVaultSubmitter<VaultResponse>(
return tokensList[index]
}

protected fun vaultToForageResponse(vaultResponse: VaultResponse): ForageApiResponse<String> {
if (vaultResponse == null) {
logger.e("[$vaultType] Received null response from $vaultType")
return UnknownErrorApiResponse
}

val vaultError = toVaultErrorOrNull(vaultResponse)
if (vaultError != null) {
val rawVaultError = parseVaultErrorMessage(vaultResponse)
logger.e("[$vaultType] Received error from $vaultType: $rawVaultError")
return vaultError
}

val forageApiErrorResponse = toForageErrorOrNull(vaultResponse)
if (forageApiErrorResponse != null) {
val firstError = forageApiErrorResponse.errors[0]
logger.e("[$vaultType] Received ForageError from $vaultType: $firstError")
return forageApiErrorResponse
}

val forageApiSuccess = toForageSuccessOrNull(vaultResponse)
if (forageApiSuccess != null) {
logger.i("[$vaultType] Received successful response from $vaultType")
return forageApiSuccess
}
logger.e("[$vaultType] Received malformed response from $vaultType: $vaultResponse")

return UnknownErrorApiResponse
}

protected fun buildBaseRequestBody(vaultProxyRequest: VaultProxyRequest): HashMap<String, Any> {
return hashMapOf(
ForageConstants.RequestBody.CARD_NUMBER_TOKEN to vaultProxyRequest.vaultToken
)
}

internal companion object {
internal fun create(foragePinEditText: ForagePINEditText, logger: Log): VaultSubmitter {
val vaultType = foragePinEditText.getVaultType()
if (vaultType == VaultType.BT_VAULT_TYPE) {
return BasisTheoryPinSubmitter(
context = foragePinEditText.context,
foragePinEditText = foragePinEditText,
logger = logger
)
}
return VgsPinSubmitter(
context = foragePinEditText.context,
foragePinEditText = foragePinEditText,
logger = logger
)
}

const val TOKEN_DELIMITER = ","

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.joinforage.forage.android.core.ui

import android.content.Context
import android.graphics.Typeface
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import android.widget.FrameLayout
import com.joinforage.forage.android.core.services.VaultType
import com.joinforage.forage.android.core.services.vault.AbstractVaultSubmitter
import com.joinforage.forage.android.core.services.telemetry.Log
import com.joinforage.forage.android.core.ui.element.ForagePinElement
import com.joinforage.forage.android.core.ui.element.SimpleElementListener
import com.joinforage.forage.android.core.ui.element.StatefulElementListener
import com.joinforage.forage.android.core.ui.element.state.PinElementState
import com.joinforage.forage.android.core.ui.element.state.PinElementStateManager

internal abstract class VaultWrapper @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
abstract var typeface: Typeface?

// mutable references to event listeners. We use mutable
// references because the implementations of our vaults
// require that we are only able to ever pass a single
// monolithic event within init call. This is mutability
// allows us simulate setting and overwriting a listener
// with every set call
internal abstract val manager: PinElementStateManager
abstract val vaultType: VaultType

abstract fun clearText()

abstract fun setTextColor(textColor: Int)
abstract fun setTextSize(textSize: Float)
abstract fun setHint(hint: String)
abstract fun setHintTextColor(hintTextColor: Int)
abstract fun getTextElement(): View
abstract fun showKeyboard()

abstract fun getVaultSubmitter(
foragePinElement: ForagePinElement,
logger: Log
) : AbstractVaultSubmitter

fun getThemeAccentColor(context: Context): Int {
val outValue = TypedValue()
context.theme.resolveAttribute(android.R.attr.colorAccent, outValue, true)
return outValue.data
}

fun setOnFocusEventListener(l: SimpleElementListener) {
manager.setOnFocusEventListener(l)
}

fun setOnBlurEventListener(l: SimpleElementListener) {
manager.setOnBlurEventListener(l)
}

fun setOnChangeEventListener(l: StatefulElementListener<PinElementState>) {
manager.setOnChangeEventListener(l)
}
}
Loading

0 comments on commit 08b79c4

Please sign in to comment.