Skip to content

Commit

Permalink
Merge pull request #1505 from Adyen/refactor/example_app_secrets
Browse files Browse the repository at this point in the history
Refactor example app build config keys
  • Loading branch information
jreij authored Mar 11, 2024
2 parents 89ea697 + 0b8aa4b commit 1fabf21
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 53 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ If you are upgrading from 4.x.x to a current release, check out our [migration g
If you use ProGuard or R8, you do not need to manually add any rules, as they are automatically embedded in the artifacts.
Please let us know if you find any issues.

## Development

For development and testing purposes the project is accompanied by a test app. See [here](example-app/README.md) how to set it up and run it.

## Support

If you have a feature request, or spotted a bug or a technical problem, [create an issue here][github.newIssue].
Expand Down
27 changes: 27 additions & 0 deletions example-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Example app

The `example-app` module is used for development and testing purposes. It should not be used as a template for your own integration. Check out the [docs](https://docs.adyen.com/online-payments/build-your-integration/) for best practices on integration.

## Running the app

Steps to run the example app:
1. Build a server that acts as a proxy between the app and the Adyen Checkout API.
* Your server should mirror the necessary endpoints for your flow (for example [/sessions](https://docs.adyen.com/api-explorer/Checkout/latest/post/sessions) for the sessions flow and [/paymentMethods](https://docs.adyen.com/api-explorer/Checkout/latest/post/paymentMethods), [/payments](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments) and [/payments/details](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments/details) for the advanced flow).
* The API key should be managed on the server.
2. Duplicate `example.local.gradle` and name it `local.gradle`. Make sure the file is placed in the `example-app` directory.
3. Replace the predefined values:
* `MERCHANT_SERVER_URL`: the URL to your server.
* `CLIENT_KEY`: your client key. Find out how to obtain it [here](https://docs.adyen.com/development-resources/client-side-authentication/#get-your-client-key).
* `MERCHANT_ACCOUNT`: your merchant account identifier.
* `AUTHORIZATION_HEADER_NAME`: the name of the authorization header as expected by your server. You can use an empty string if this is not applicable for you.
* `AUTHORIZATION_HEADER_VALUE`: the value for the authorization header. You can use an empty string if this is not applicable for you.
4. Sync the project.
5. Run on any device or emulator.

> [!WARNING]
> In case you don't have your own server you can connect to the Adyen Checkout API directly for testing purposes only. Be aware this could potentially leak your credentials, the market-ready application must never connect to Adyen API directly.
To connect to the Adyen Checkout API directly you can use the following values:
* `MERCHANT_SERVER_URL`: `https://checkout-test.adyen.com/{VERSION}/` (check [here](https://docs.adyen.com/api-explorer/Checkout/latest/overview) for the latest version).
* `AUTHORIZATION_HEADER_NAME`: `x-api-key`.
* `AUTHORIZATION_HEADER_VALUE`: your API key.
6 changes: 4 additions & 2 deletions example-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ apply from: "${rootDir}/config/gradle/ci.gradle"

if (file("local.gradle").exists()) {
apply from: "local.gradle"
} else if (System.getenv('CI')) {
// if building from CI use example file as it is to ensure the build passes
apply from: "example.local.gradle"
} else {
logger.lifecycle("File example-app/local.gradle not found. Falling back to default file with no values.")
apply from: "default.local.gradle"
throw new GradleException("File example-app/local.gradle not found. Check example-app/README.md for more instructions.")
}

// This runConnectedAndroidTest.gradle script is applied,
Expand Down
23 changes: 0 additions & 23 deletions example-app/default.local.gradle

This file was deleted.

22 changes: 22 additions & 0 deletions example-app/example.local.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Duplicate this file into "local.gradle" then replace the placeholder values with the correct
* parameters. You might need to escape some characters if you see an error.
*
* DO NOT commit the new file anywhere public, you might be exposing your secret credentials.
*/
android {
buildTypes {
debug {
buildConfigField "String", "MERCHANT_SERVER_URL", '"YOUR_SERVER_URL"'
buildConfigField "String", "CLIENT_KEY", '"YOUR_CLIENT_KEY"'
buildConfigField "String", "MERCHANT_ACCOUNT", '"YOUR_MERCHANT_ACCOUNT"'
buildConfigField "String", "AUTHORIZATION_HEADER_NAME", '"YOUR_AUTHORIZATION_HEADER_NAME"'
buildConfigField "String", "AUTHORIZATION_HEADER_VALUE", '"YOUR_AUTHORIZATION_HEADER_VALUE"'
}

release {
initWith debug
matchingFallbacks = ['debug']
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
package com.adyen.checkout.example.data.api

import com.adyen.checkout.components.core.PaymentMethodsApiResponse
import com.adyen.checkout.example.BuildConfig
import com.adyen.checkout.example.data.api.model.BalanceRequest
import com.adyen.checkout.example.data.api.model.CancelOrderRequest
import com.adyen.checkout.example.data.api.model.CreateOrderRequest
Expand All @@ -27,14 +26,6 @@ import retrofit2.http.Query

internal interface CheckoutApiService {

companion object {
private const val DEFAULT_GRADLE_SERVER_URL = "<YOUR_SERVER_URL>"

fun isRealUrlAvailable(): Boolean {
return BuildConfig.MERCHANT_SERVER_URL != DEFAULT_GRADLE_SERVER_URL
}
}

@POST("sessions")
suspend fun sessionsAsync(@Body sessionRequest: SessionRequest): SessionModel

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal class DefaultKeyValueStorage(
return sharedPreferences.getString(
appContext = appContext,
stringRes = R.string.shopper_reference_key,
defaultValue = BuildConfig.SHOPPER_REFERENCE,
defaultStringRes = R.string.preferences_default_shopper_reference,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,30 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object NetworkModule {

private val BASE_URL = if (CheckoutApiService.isRealUrlAvailable()) {
BuildConfig.MERCHANT_SERVER_URL
} else {
"http://myserver.com/my/endpoint/"
}

@Singleton
@Provides
internal fun provideOkHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder()

val authorizationHeader = (BuildConfig.AUTHORIZATION_HEADER_NAME to BuildConfig.AUTHORIZATION_HEADER_VALUE)
.takeIf { it.first.isNotBlank() }

if (BuildConfig.DEBUG) {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val interceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
if (authorizationHeader != null) redactHeader(authorizationHeader.first)
}
builder.addNetworkInterceptor(interceptor)
}

builder.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader(BuildConfig.API_KEY_HEADER_NAME, BuildConfig.CHECKOUT_API_KEY)
.build()
if (authorizationHeader != null) {
builder.addInterceptor { chain ->
val request = chain.request().newBuilder()
.header(authorizationHeader.first, authorizationHeader.second)
.build()

chain.proceed(request)
chain.proceed(request)
}
}

return builder.build()
Expand All @@ -63,7 +64,7 @@ object NetworkModule {
Moshi.Builder()
.add(JSONObjectAdapter())
.add(KotlinJsonAdapterFactory())
.build()
.build(),
)

@Singleton
Expand All @@ -74,7 +75,7 @@ object NetworkModule {
converterFactory: Converter.Factory,
): Retrofit =
Retrofit.Builder()
.baseUrl(BASE_URL)
.baseUrl(BuildConfig.MERCHANT_SERVER_URL)
.client(okHttpClient)
.addConverterFactory(converterFactory)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.DropDownPreference
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import com.adyen.checkout.example.R
import com.adyen.checkout.example.data.storage.KeyValueStorage
import com.adyen.checkout.example.databinding.ActivitySettingsBinding
import com.adyen.checkout.example.ui.theme.NightTheme
import com.adyen.checkout.example.ui.theme.NightThemeRepository
Expand All @@ -39,6 +41,9 @@ class ConfigurationActivity : AppCompatActivity() {
@AndroidEntryPoint
class ConfigurationFragment : PreferenceFragmentCompat() {

@Inject
lateinit var keyValueStorage: KeyValueStorage

@Inject
internal lateinit var nightThemeRepository: NightThemeRepository

Expand All @@ -49,12 +54,19 @@ class ConfigurationActivity : AppCompatActivity() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

preferenceManager.preferenceScreen
.findPreference<DropDownPreference>(requireContext().getString(R.string.night_theme_title))
preferenceManager
.findPreference<DropDownPreference>(requireContext().getString(R.string.night_theme_key))
?.setOnPreferenceChangeListener { _, newValue ->
nightThemeRepository.theme = NightTheme.findByPreferenceValue(newValue as String?)
true
}

/* This workaround is needed to display the default value of Merchant Account. We cannot set this value in
`preferences.xml` because it's only available in the code and there is no "clean" way to set the default
value programmatically. */
preferenceManager
.findPreference<EditTextPreference>(requireContext().getString(R.string.merchant_account_key))
?.text = keyValueStorage.getMerchantAccount()
}
}
}
1 change: 1 addition & 0 deletions example-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,6 @@
<string name="preferences_default_instant_payment_method">wechatpaySDK</string>
<string name="preferences_default_use_sessions">true</string>
<string name="preferences_default_analytics_level">ALL</string>
<string name="preferences_default_shopper_reference">test-android-components</string>

</resources>
3 changes: 2 additions & 1 deletion example-app/src/main/res/xml/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<PreferenceCategory android:title="@string/shopper_information">

<EditTextPreference
android:defaultValue="@string/preferences_default_shopper_reference"
android:key="@string/shopper_reference_key"
android:title="@string/shopper_reference_title"
app:useSimpleSummaryProvider="true" />
Expand Down Expand Up @@ -130,7 +131,7 @@
android:defaultValue="@string/night_theme_system"
android:entries="@array/night_theme_entries"
android:entryValues="@array/night_theme_values"
android:key="@string/night_theme_title"
android:key="@string/night_theme_key"
android:title="@string/night_theme_title"
app:useSimpleSummaryProvider="true" />

Expand Down

0 comments on commit 1fabf21

Please sign in to comment.