Skip to content
Merged
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Spreedly Checkout — Android Example

This sample app demonstrates the [Spreedly Android Checkout SDK](https://github.com/spreedly/checkout-android-sdk) at version **1.1.0** (tag `v1.1.0`).
This sample app demonstrates the [Spreedly Android Checkout SDK](https://github.com/spreedly/checkout-android-sdk) at version **1.2.0** (tag `v1.2.0`).

## Setup

Expand All @@ -20,10 +20,10 @@ gpr.key=YOUR_GITHUB_TOKEN
All SDK modules are resolved from GitHub Packages:

```kotlin
implementation("com.spreedly:checkout-paymentsheet:1.1.0")
implementation("com.spreedly:checkout-braintree-apm:1.1.0")
implementation("com.spreedly:checkout-stripe-apm:1.1.0")
implementation("com.spreedly:checkout-threeds:1.1.0")
implementation("com.spreedly:checkout-paymentsheet:1.2.0")
implementation("com.spreedly:checkout-braintree-apm:1.2.0")
implementation("com.spreedly:checkout-stripe-apm:1.2.0")
implementation("com.spreedly:checkout-threeds:1.2.0")
```

## SDK Documentation
Expand Down
13 changes: 8 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ android {
testOptions {
unitTests {
isIncludeAndroidResources = true
isReturnDefaultValues = true
}
}
}
Expand All @@ -143,11 +144,11 @@ kotlin {

dependencies {
// ✅ Use paymentsheet which includes payments-core and hosted-fields
implementation("com.spreedly:checkout-paymentsheet:1.1.0")
implementation("com.spreedly:checkout-braintree-apm:1.1.0")
implementation("com.spreedly:checkout-stripe-apm:1.1.0")
implementation("com.spreedly:checkout-stripe-radar:1.1.0")
implementation("com.spreedly:checkout-threeds:1.1.0")
implementation("com.spreedly:checkout-paymentsheet:1.2.0")
implementation("com.spreedly:checkout-braintree-apm:1.2.0")
implementation("com.spreedly:checkout-stripe-apm:1.2.0")
implementation("com.spreedly:checkout-stripe-radar:1.2.0")
implementation("com.spreedly:checkout-threeds:1.2.0")

implementation(libs.kotlinx.serialization.json)
implementation(platform(libs.androidx.compose.bom))
Expand All @@ -167,6 +168,8 @@ dependencies {
implementation(libs.androidx.constraintlayout)
implementation(libs.material)

implementation(libs.androidx.browser)

// Testing dependencies
testImplementation(libs.junit)
testImplementation(libs.mockk)
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/spreedly/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,7 @@ fun MainNavHost(bottomSheetViewModel: BottomSheetPaymentViewModel) {
onBackClick = { navController.popBackStack() },
)
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import com.spreedly.example.viewmodel.findActivityOrNull
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.spreedly.app.R
Expand Down Expand Up @@ -77,13 +79,17 @@ fun BankAccountScreen(
val showSheet by viewModel.showSheet.collectAsState()
val fieldConfig by viewModel.fieldConfig.collectAsState()
val uiConfig by viewModel.uiConfig.collectAsState()
val useCustomTheme by viewModel.useCustomTheme.collectAsState()

val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val activity = LocalContext.current.findActivityOrNull()
DisposableEffect(lifecycleOwner, showSheet) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_STOP -> {
if (showSheet) viewModel.onConfigurationChanging()
if (showSheet && activity?.isChangingConfigurations == true) {
viewModel.onConfigurationChanging()
}
}
Lifecycle.Event.ON_START -> {
viewModel.onConfigurationChangeComplete()
Expand Down Expand Up @@ -187,6 +193,8 @@ fun BankAccountScreen(
onFieldConfigChange = { viewModel.updateFieldConfig(it) },
uiConfig = uiConfig,
onUiConfigChange = { viewModel.updateUiConfig(it) },
useCustomTheme = useCustomTheme,
onUseCustomThemeChange = { viewModel.updateUseCustomTheme(it) },
)

Spacer(modifier = Modifier.height(Spacing.lg))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class BankAccountViewModel(private val context: Context) : ViewModel() {
private val _uiConfig = MutableStateFlow(CustomFieldsConfig())
val uiConfig: StateFlow<CustomFieldsConfig> = _uiConfig.asStateFlow()

private val _useCustomTheme = MutableStateFlow(false)
val useCustomTheme: StateFlow<Boolean> = _useCustomTheme.asStateFlow()

private val sdkSessionManager = SdkSessionManager(AuthService())
private val paymentMethodRepository = PaymentMethodRepository(context)
private val paymentResultHandler = PaymentResultHandler(paymentMethodRepository)
Expand Down Expand Up @@ -137,7 +140,6 @@ class BankAccountViewModel(private val context: Context) : ViewModel() {
_paymentToken.value = ""
viewModelScope.launch {
if (initializeForPayment()) {
_isProcessing.value = true
_showSheet.value = true
}
}
Expand Down Expand Up @@ -169,6 +171,13 @@ class BankAccountViewModel(private val context: Context) : ViewModel() {
_uiConfig.value = config
}

fun updateUseCustomTheme(enabled: Boolean) {
_useCustomTheme.value = enabled
if (!enabled) {
_uiConfig.value = CustomFieldsConfig()
}
}

private companion object {
private const val TAG = "BankAccountViewModel"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ fun SimpleInputField(
isRequired: Boolean = false,
isError: Boolean = false,
errorMessage: String? = null,
placeholder: String? = null,
showLabel: Boolean = true,
keyboardType: KeyboardType = KeyboardType.Text,
capitalization: KeyboardCapitalization = KeyboardCapitalization.Words,
imeAction: ImeAction = ImeAction.Default,
Expand All @@ -117,18 +119,20 @@ fun SimpleInputField(
val borderWidth = if (isFocused) 2.dp else 1.dp

Column(modifier = modifier) {
Row(modifier = Modifier.padding(bottom = Spacing.xxs)) {
Text(
text = label,
style = MaterialTheme.typography.labelMedium,
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface,
)
if (isRequired) {
if (showLabel) {
Row(modifier = Modifier.padding(bottom = Spacing.xxs)) {
Text(
text = "*",
text = label,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.error,
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface,
)
if (isRequired) {
Text(
text = "*",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.error,
)
}
}
}

Expand Down Expand Up @@ -167,7 +171,7 @@ fun SimpleInputField(
Box {
if (value.isEmpty()) {
Text(
text = "Enter $label",
text = placeholder ?: "Enter $label",
style = TextStyle(
fontSize = 16.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
Expand Down Expand Up @@ -263,6 +267,9 @@ fun BasicCheckoutScreen(
viewModel.startPaymentPolling()
}
is PaymentProcessingResult.ValidationFailed -> Unit
is PaymentProcessingResult.Rejected,
is PaymentProcessingResult.Failed,
-> Unit
}
}
}
Expand Down
Loading