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
7 changes: 7 additions & 0 deletions .github/workflows/gradle-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ jobs:
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}

- name: Create local.properties
run: |
touch local.properties
echo "sandbox.components.public_key=${{ secrets.COMPONENTS_SANDBOX_PUBLIC_KEY }}" >> local.properties
echo "sandbox.components.secret_key=${{ secrets.COMPONENTS_SANDBOX_SECRET_KEY }}" >> local.properties

- name: Run ${{ inputs.task }} with Gradle
run: ./gradlew :${{ inputs.module }}:${{ inputs.task }}

Expand Down
16 changes: 16 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ android {
lint {
baseline = file('lint-baseline.xml')
}

def localProps = new Properties()
def localPropsFile = rootProject.file("local.properties")
if (localPropsFile.exists()) {
localProps.load(localPropsFile.newDataInputStream())
}

defaultConfig {
buildConfigField "String", "SANDBOX_PUBLIC_KEY", "\"${localProps.getProperty("sandbox.components.public_key") ?: ""}\""
buildConfigField "String", "SANDBOX_SECRET_KEY", "\"${localProps.getProperty("sandbox.components.secret_key") ?: ""}\""
buildConfigField "String", "SANDBOX_PROCESSING_CHANNEL_ID", "\"${localProps.getProperty("sandbox.components.processing_channel_id") ?: ""}\""

buildFeatures {
buildConfig true
}
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package checkout.checkout_android;

import static checkout.checkout_android.Constants.PUBLIC_KEY_CVV_TOKENIZATION;
import static checkout.checkout_android.Constants.REGIONAL_SUBDOMAIN;

import android.app.AlertDialog;
import android.os.Bundle;
Expand Down Expand Up @@ -51,7 +51,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
setupObservers();

// Create cvvComponentApi
CVVComponentApi cvvComponentApi = CVVComponentApiFactory.create(PUBLIC_KEY_CVV_TOKENIZATION, Environment.SANDBOX, this);
CVVComponentApi cvvComponentApi = CVVComponentApiFactory.create(BuildConfig.SANDBOX_PUBLIC_KEY, Environment.SANDBOX, this, REGIONAL_SUBDOMAIN);

// initialise CVVTokenizationResultHandler for tokenization
resultHandler = result -> {
Expand Down
16 changes: 4 additions & 12 deletions app/src/main/java/checkout/checkout_android/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,11 @@ public class Constants {
* Target platform environment
*/
public static final Environment ENVIRONMENT = Environment.SANDBOX;
/**
* Replace with public key from Hub in Sandbox Environment
*/
public static final String PUBLIC_KEY = "pk_test_b37b8b6b-fc9a-483f-a77e-3386b606f90e";

/**
* Replace with public key from Hub in Sandbox Environment, testing key for CVV Tokenization
*/
public static final String PUBLIC_KEY_CVV_TOKENIZATION = "pk_6b30805a-1f3b-4c63-8b75-eb3030109173";
/**
* Replace with Secret key from Hub in Sandbox Environment
*/
public static final String SECRET_KEY = "sk_test_568e6077-a08f-4692-9237-cc6c48dcf6aa";
/**
* Replace with subdomain value, testing key for base url regional subdomain prefix
*/
public static final String REGIONAL_SUBDOMAIN = "global";
/**
* Replace with Success/Failure Urls from Hub in Sandbox Environment
*/
Expand Down
12 changes: 10 additions & 2 deletions app/src/main/java/checkout/checkout_android/DemoActivity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package checkout.checkout_android;

import static checkout.checkout_android.Constants.REGIONAL_SUBDOMAIN;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.os.Bundle;
Expand All @@ -18,6 +20,8 @@
import com.checkout.threedsecure.model.ThreeDSResult;
import com.checkout.tokenization.model.TokenDetails;

import java.util.Collections;

import checkout.checkout_android.utils.PaymentUtil;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
Expand Down Expand Up @@ -89,11 +93,15 @@ public void onBackPressed() {

private PaymentFormConfig providePaymentFormConfig() {
return new PaymentFormConfig(
Constants.PUBLIC_KEY,
BuildConfig.SANDBOX_PUBLIC_KEY,
this,
Constants.ENVIRONMENT,
new PaymentFormStyle(),
mPaymentFlowHandler);
mPaymentFlowHandler,
Collections.emptyList(),
null,
REGIONAL_SUBDOMAIN
);
}

private void displayMessage(String title, String message, boolean exitScreen) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.io.IOException;

import checkout.checkout_android.BuildConfig;
import checkout.checkout_android.Constants;
import okhttp3.Call;
import okhttp3.MediaType;
Expand Down Expand Up @@ -47,7 +48,7 @@ public static void createPayment(String token, Callback callback) {
RequestBody requestBody = buildPaymentRequestBody(token);
Request paymentRequest = new Request.Builder()
.url("https://api.sandbox.checkout.com/payments")
.addHeader("Authorization", "Bearer "+Constants.SECRET_KEY)
.addHeader("Authorization", "Bearer "+ BuildConfig.SANDBOX_SECRET_KEY)
.post(requestBody)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.checkout.base.model.Environment
import com.checkout.logging.EventLoggerProvider
import com.checkout.logging.Logger
import com.checkout.logging.model.LoggingEvent
import com.checkout.logging.utils.toBaseUrl
import com.checkout.network.OkHttpProvider
import com.checkout.risk.FramesOptions
import com.checkout.threedsecure.Executor
Expand Down Expand Up @@ -40,6 +41,7 @@ public object CheckoutApiServiceFactory {
publicKey: String,
environment: Environment,
context: Context,
baseUrlPrefix: String? = null,
): CheckoutApiService {
val logger = EventLoggerProvider.provide()
logger.setup(context, environment)
Expand All @@ -51,7 +53,7 @@ public object CheckoutApiServiceFactory {
)

return CheckoutApiClient(
provideTokenRepository(context, publicKey, environment),
provideTokenRepository(context, publicKey, environment, baseUrlPrefix),
provideThreeDSExecutor(logger),
)
}
Expand All @@ -60,8 +62,9 @@ public object CheckoutApiServiceFactory {
context: Context,
publicKey: String,
environment: Environment,
baseUrlPrefix: String?,
): TokenRepository = TokenRepositoryImpl(
networkApiClient = provideNetworkApiClient(publicKey, environment.url),
networkApiClient = provideNetworkApiClient(publicKey, environment.toBaseUrl(baseUrlPrefix)),
cardToTokenRequestMapper = CardToTokenRequestMapper(),
cvvToTokenNetworkRequestMapper = CVVToTokenNetworkRequestMapper(),
cardTokenizationNetworkDataMapper = CardTokenizationNetworkDataMapper(),
Expand All @@ -77,7 +80,13 @@ public object CheckoutApiServiceFactory {
logger = TokenizationEventLogger(EventLoggerProvider.provide()),
publicKey = publicKey,
cvvTokenizationNetworkDataMapper = CVVTokenizationNetworkDataMapper(),
riskSdkUseCase = RiskSdkUseCase(environment, context, publicKey, riskSDKFramesOptions, RiskInstanceProvider),
riskSdkUseCase = RiskSdkUseCase(
environment,
context,
publicKey,
riskSDKFramesOptions,
RiskInstanceProvider,
),
)

private fun provideNetworkApiClient(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.checkout.base.model

import com.checkout.base.util.EnvironmentConstants

public enum class Environment(public val url: String) {
PRODUCTION(EnvironmentConstants.PRODUCTION_SERVER_URL),
SANDBOX(EnvironmentConstants.SANDBOX_SERVER_URL),
public enum class Environment {
PRODUCTION,
SANDBOX,
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ internal const val ZIP_LENGTH = 50
*/
internal const val PHONE_MIN_LENGTH = 6
internal const val PHONE_MAX_LENGTH = 25

/**
* Constants for environments
*/
internal const val HTTPS_PROTOCOL: String = "https://"
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ package com.checkout.base.util
internal object EnvironmentConstants {
internal const val SANDBOX_SERVER_URL = "https://api.sandbox.checkout.com/tokens"
internal const val PRODUCTION_SERVER_URL = "https://api.checkout.com/tokens"
internal const val PRODUCTION_LOGGING = "production"
internal const val SANDBOX_LOGGING = "sandbox"
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
package com.checkout.logging.utils

import com.checkout.base.model.Environment
import com.checkout.base.util.EnvironmentConstants.PRODUCTION_LOGGING
import com.checkout.base.util.EnvironmentConstants.PRODUCTION_SERVER_URL
import com.checkout.base.util.EnvironmentConstants.SANDBOX_LOGGING
import com.checkout.base.util.EnvironmentConstants.SANDBOX_SERVER_URL
import com.checkout.base.util.HTTPS_PROTOCOL

internal fun Environment.toBaseUrl(baseUrlPrefix: String? = null) = when (this) {
Environment.PRODUCTION -> PRODUCTION_SERVER_URL.applyPrefix(baseUrlPrefix)
Environment.SANDBOX -> SANDBOX_SERVER_URL.applyPrefix(baseUrlPrefix)
}
internal fun Environment.toLoggingEnvironment() = when (this) {
Environment.PRODUCTION -> com.checkout.eventlogger.Environment.PRODUCTION
Environment.SANDBOX -> com.checkout.eventlogger.Environment.SANDBOX
}

internal fun Environment.toLoggingName() = when (this) {
Environment.PRODUCTION -> "production"
Environment.SANDBOX -> "sandbox"
Environment.PRODUCTION -> PRODUCTION_LOGGING
Environment.SANDBOX -> SANDBOX_LOGGING
}

private fun String.applyPrefix(prefix: String?): String {
val validatedPrefix = prefix?.baseUrlPrefixValidator() ?: return this
return this.replace(HTTPS_PROTOCOL, "$HTTPS_PROTOCOL$validatedPrefix.")
}

private fun String?.baseUrlPrefixValidator() = this
?.takeIf { prefix ->
prefix.all { char -> char in 'a'..'z' || char in 'A'..'Z' || char in '0'..'9' } && prefix.isNotEmpty()
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class CheckoutApiServiceFactoryTest {
val mockEnvironment = Environment.SANDBOX

// When
CheckoutApiServiceFactory.create("", mockEnvironment, mockContext)
CheckoutApiServiceFactory.create("", mockEnvironment, mockContext, null)

// Then
verify { mockLogger.setup(eq(mockContext), eq(mockEnvironment)) }
Expand Down
93 changes: 93 additions & 0 deletions checkout/src/test/java/com/checkout/EnvironmentExtensionTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.checkout

import com.checkout.base.model.Environment
import com.checkout.base.util.EnvironmentConstants
import com.checkout.base.util.EnvironmentConstants.PRODUCTION_SERVER_URL
import com.checkout.logging.utils.toBaseUrl
import org.junit.Assert.assertEquals
import org.junit.Test

internal class EnvironmentExtensionTest {

@Test
fun `when toBaseUrl PRODUCTION with null subDomainPrefix returns production server URL`() {
val result = Environment.PRODUCTION.toBaseUrl(null)
assertEquals(EnvironmentConstants.PRODUCTION_SERVER_URL, result)
}

@Test
fun `when toBaseUrl PRODUCTION with no subDomainPrefix argument returns production server URL`() {
val result = Environment.PRODUCTION.toBaseUrl()
assertEquals(EnvironmentConstants.PRODUCTION_SERVER_URL, result)
}

@Test
fun `when toBaseUrl PRODUCTION with subDomainPrefix returns custom production URL`() {
val result = Environment.PRODUCTION.toBaseUrl("custom")
assertEquals("https://custom.api.checkout.com/tokens", result)
}

@Test
fun `when toBaseUrl PRODUCTION with alphanumeric subDomainPrefix returns custom production URL`() {
val result = Environment.PRODUCTION.toBaseUrl("mySubdomain123")
assertEquals("https://mySubdomain123.api.checkout.com/tokens", result)
}

@Test
fun `when toBaseUrl SANDBOX with null subDomainPrefix returns sandbox server URL`() {
val result = Environment.SANDBOX.toBaseUrl(null)
assertEquals(EnvironmentConstants.SANDBOX_SERVER_URL, result)
}

@Test
fun `when toBaseUrl SANDBOX with no subDomainPrefix argument returns sandbox server URL`() {
val result = Environment.SANDBOX.toBaseUrl()
assertEquals(EnvironmentConstants.SANDBOX_SERVER_URL, result)
}

@Test
fun `when toBaseUrl SANDBOX with subDomainPrefix returns custom sandbox URL`() {
val result = Environment.SANDBOX.toBaseUrl("custom")
assertEquals("https://custom.api.sandbox.checkout.com/tokens", result)
}

@Test
fun `when toBaseUrl SANDBOX with alphanumeric subDomainPrefix returns custom sandbox URL`() {
val result = Environment.SANDBOX.toBaseUrl("test99")
assertEquals("https://test99.api.sandbox.checkout.com/tokens", result)
}

@Test
fun `when toBaseUrl with empty string subDomainPrefix uses default URL for production`() {
val result = Environment.PRODUCTION.toBaseUrl("")
assertEquals("https://api.checkout.com/tokens", result)
}

@Test
fun `toBaseUrl PRODUCTION returns default URL for any non-alphanumeric prefix`() {
val invalidPrefixList = listOf(
"invalid_prefix",
"invalid-prefix",
"invalid.prefix",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the top 3 look like they could be valid prefixes - worth checking

"invalid@prefix",
"invalid!prefix",
"invalid#prefix",
"invalid\$prefix",
"invalid%prefix",
"invalid^prefix",
"invalid&prefix",
"invalid*prefix",
"invalid(prefix",
"invalid)prefix",
"invalid prefix",
)

invalidPrefixList.forEach { invalidPrefix ->
val result = Environment.PRODUCTION.toBaseUrl(invalidPrefix)
assertEquals(
PRODUCTION_SERVER_URL,
result,
)
}
}
}
26 changes: 26 additions & 0 deletions example_app_frames/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import java.util.Properties

plugins {
id("precompiled-android-app")
id("org.jetbrains.kotlin.plugin.compose")
Expand All @@ -9,6 +11,26 @@ android {
applicationId = ExampleAppFramesConfig.id
versionCode = ExampleAppFramesConfig.versionCode
versionName = ExampleAppFramesConfig.versionName

Properties().apply {
load(rootProject.file("local.properties").inputStream())
}.run {
buildConfigField(
"String",
"SANDBOX_PUBLIC_KEY",
"\"${this["sandbox.components.public_key"]}\"",
)
buildConfigField(
"String",
"SANDBOX_SECRET_KEY",
"\"${this["sandbox.components.secret_key"]}\"",
)
buildConfigField(
"String",
"SANDBOX_PROCESSING_CHANNEL_ID",
"\"${this["sandbox.components.processing_channel_id"]}\"",
)
}
}

buildTypes {
Expand All @@ -21,6 +43,10 @@ android {
}
}

buildFeatures {
buildConfig = true
}

packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand Down
Loading
Loading