Skip to content

checkout/frames-android

Repository files navigation

Frames Android SDK

CircleCI License: MIT

Start accepting online payments in just a few minutes with our Android SDK. It's quick and easy to integrate, accepts online payments from all major debit and credit cards, and is customizable to your brand.

Requirements

  • Android minimum SDK 21

Compatibility verified with targetSdk versions 21 to 34

Documentation

Frames for Android tokenises consumer data for use within Checkout.com's payment infrastructure. We want to abstract all the complexity in taking payments and building a payment screen from your Mobile Application and allow you to focus on your core business.

  • Integration: a guide for consuming our SDK in your Android app

  • Demo projects: We've created projects showcasing the range of functionality available in our SDKs while testing each distribution method offered

  • Get started: Start testing what you can achieve by presenting inside your Applications UI

  • Make it your own: Customising the UI to become a part of your app

  • Other features: How we help with Google Pay & 3D Secure Challenges

  • Make a hosted CVV payment: Make a compliant saved card payment with hosted CVV tokenisation

  • Migrating: If you have used 3.x.x version before

  • License

Complete information will be found in the Checkout Docs.

You can find the Frames API reference on our KDoc.

Integration

Add JitPack repository to the project level build.gradle file:

// project gradle file
allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
        maven { url 'https://maven.fpregistry.io/releases' }
    }
}

Add Frames SDK dependency to the module gradle file:

// module gradle file
dependencies {
    implementation 'com.github.checkout:frames-android:<latest_version>'
}

You can find more about the installation and available versions on

Please keep in mind that the Jitpack repository should to be added to the project gradle file while the dependency should be added in the module gradle file. (More about build configuration files is available here)

Demo projects

We've worked with Product and Design to create simple demo applications that showcase our SDKs, that you'll be able to run locally as soon as you clone the repository.

You will find our demo apps in the root of the repository, inside respective folders:

  • app - Java and Imperative UI. Covers default style and 3DS.
  • example_app_frames - Kotlin and Declarative UI. Covers: default and custom style, theming, 3DS and Google Pay.

Once running example_app_frames, you will find the home screen with a number of design options. We have tried to make them pretty contrasting to give your UI/UX teammates an idea of what can be achieved. We've also tried to write the code in the simplest way to track and understand how each UI flavour was created. Just start from HomeScreen.kt and follow the button actions in code for some examples on how we achieve those UIs.

Get started

If you got here, we'll either assume you've completed Integration or you're just curious. If none, then please complete Integration first.

Step 1: Create a callback for the Payment flow

val paymentFlowHandler = object : PaymentFlowHandler {
    override fun onSubmit() {
        // form submit initiated; you can potentially display a loader
    }
    override fun onSuccess(tokenDetails: TokenDetails) {
        // your token is here
    }
    override fun onFailure(errorMessage: String) {
        // token request error
    }
    override fun onBackPressed() {
        // the user decided to leave the payment page
    }
}

Step 2: Prepare your object responsible for the Frames configuration

/*
  Providing PrefillData is optional. However, using this can increase the speed of the checkout experience for your users by pre-filling non-sensitive information.
 */
val userPrefillData = PrefillData(
    cardHolderName = "Test Name",        // represent the cardHolderName in PaymentDetail form
    billingFormAddress = BillingFormAddress(
        name = "UserName",               // represent the cardHolderName in Billing form
        address = Address(
            addressLine1 = "Hill road",
            addressLine2 = "90 Tottenham Court Road",
            city = "London",
            state = "England",
            zip = "W1T 4PY",
            country = Country.from(iso3166Alpha2 = "GB")
        ),
        phone = Phone("7405987323", Country.UNITED_KINGDOM)
    )
)

// Create a configuration object for your Payment form
val paymentFormConfig = PaymentFormConfig(
    publicKey = PUBLIC_KEY,                     // set your public key
    context = context,                          // set context
    environment = Environment.SANDBOX,          // set the environment
    paymentFlowHandler = paymentFlowHandler,    // set the callback
    style = PaymentFormStyle(),                 // set the style
    supportedCardSchemeList = emptyList(),      // set supported card schemes, by default uses all schemes
    prefillData = userPrefillData               // set prefill data for user to improve their checkout experience
)

A note on cardHolderName and name in PrefillData

Please note that cardHolderName in the payment form and name in the billing form represent the same info, so you should only include this either in the payment or billing form, not both. If you do include it in both places, we will prioritise passing cardholderName in the Tokenisation request.

Additionally, if you hide these fields on the actual payment and billing forms, but include them in the PrefillData object, we will still pass them in the Tokenisation request. You may want to do this if you want to include this information in the Tokenisation request, but do not want the user to fill it out on the UI.

Step 3: Create a Payment mediator

val paymentFormMediator = PaymentFormMediator(paymentFormConfig)

Step 4: Use a Payment form mediator to get and show Payment form

With Compose UI

paymentFormMediator.PaymentForm()               // Compose function

With Activity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    paymentFormMediator.setActivityContent(this)
}

With Fragment

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View = paymentFormMediator.provideFragmentContent(this)

Make it your own

!!! Any customisation needs to be done before creating Payment form mediator. Once mediator is created, any changes done on style will not be reflected in the UI !!!

We have been working hard to provide a selection of ways to make it your own. In order of complexity we'll start with:

Modify Default

In our Get started example we have used Default Style to get something working quickly. If that was mostly what you were looking for, then modify individual properties should be the best option for you.

val paymentFormStyle = PaymentFormStyle()
val customInputComponentStyle = DefaultLightStyle.inputComponentStyle(
    titleTextId = R.string.custom_card_number_title,
    withLeadingIcon = true,
    margin = Margin(bottom = 16)
)
paymentFormStyle.paymentDetailsStyle.cardNumberStyle.inputStyle = customInputComponentStyle

To disable cardHolderName and BillingFormAddress in the Payment Form, pass a null value to each respective style property in PaymentDetailsStyle

val paymentFormStyle = PaymentFormStyle(
    paymentDetailsStyle =  PaymentDetailsStyle(
        addressSummaryStyle = null,
        cardHolderNameStyle = null
    )
)

Use Theme

Detailed implementation of this approach can be found in CustomPaymentFormTheme.kt. With the Theme, we are aiming to give you a design system that you can use to create the full UI style by providing a small number of properties that we will share across to sub components. Since you might not fully agree with our mapping, you can still individually change each component afterwards (as in the Modify Default example).

val theme = PaymentFormTheme(
    paymentFormThemeColors = paymentFormThemeColors,
    /**
     * option 1: Use combination of default and custom components
     */
    paymentFormComponents = DefaultPaymentFormTheme.providePaymentFormComponents(
        cardNumber = cardNumber,
        addressLineOne = addressLineOne,
        addressLineTwo = addressLineTwo,
        addBillingSummaryButton = addBillingSummaryButton,
        editBillingSummaryButton = editBillingSummaryButton
    ),
    /**
     * option 2: Use default components
     * paymentFormComponents = DefaultPaymentFormTheme.provideComponents()
     */
    paymentFormShape = PaymentFormShape(
        inputFieldShape = Shape.RoundCorner,
        addressSummaryShape = Shape.Rectangle, buttonShape = Shape.Circle
    ),
    paymentFormCornerRadius = PaymentFormCornerRadius(
        inputFieldCornerRadius = CornerRadius(INPUT_FIELD_CORNER_RADIUS),
        addressSummaryCornerRadius = CornerRadius(INPUT_FIELD_CORNER_RADIUS)
    )
)
val style = PaymentFormStyleProvider.provide(theme)

We think this approach should hit a good balance between great control of UI & simple, concise code. However if you still find the mapping to need excessive customisation, our final approach may be more to your liking.

Declare all components

This is by no means the easy way, but it is absolutely the way to fully customise every property, and discover the full extent of customisability as you navigate through CustomPaymentDetailsStyle.kt.

val paymentDetailsStyle: PaymentDetailsStyle = PaymentDetailsStyle(
    paymentDetailsHeaderStyle = ScreenHeaderStyle(..),
    cardSchemeStyle = CardSchemeComponentStyle(..),
    cardNumberStyle = CardNumberComponentStyle(..),
    expiryDateStyle = ExpiryDateComponentStyle(..),
    cvvStyle = CvvComponentStyle(..),
    addressSummaryStyle = AddressSummaryComponentStyle(..),
    payButtonStyle = PayButtonComponentStyle(..),
    fieldsContainerStyle = ContainerStyle(..)
)
val billingFormStyle: BillingFormStyle = BillingFormStyle(..)
val paymentFormStyle = PaymentFormStyle(paymentDetailsStyle, billingFormStyle)

To use some of our default component styles during custom style creation please take a look on: DefaultTextStyle.kt, DefaultButtonStyle.kt, DefaultImageStyle.kt,DefaultTextLabelStyle.kt,DefaultTextStyle.kt,DefaultLightStyle.kt,....

Other features

Handle 3D Secure

When you send a 3D secure charge request from your server you will get back a 3D Secure URL. This is available from _links.redirect.href within the JSON response. You can then pass the 3D Secure URL to a PaymentFormMediator in order to handle the verification.

The redirection URLs (success_url and failure_url) are set in the Checkout.com Hub, but they can be overwritten in the charge request sent from your server. It is important to provide the correct URLs to ensure a successful payment flow. Example of 3DS handling can be found in demo app, DemoActivity.java.

Step 1: Create 3DS result handler

val threeDSResultHandler: ThreeDSResultHandler = { threeDSResult: ThreeDSResult ->
    when (threeDSResult) {
        // Used when [ProcessThreeDSRequest.redirectUrl] contains [ThreeDSRequest.successUrl]
        is ThreeDSResult.Success -> { 
            /* Handle success result */
            threeDSResult.token
        }
        // Used when [ProcessThreeDSRequest.redirectUrl] contains [ThreeDSRequest.successUrl]
        is ThreeDSResult.Failure -> { /* Handle failure result */ }
        // Used when [ProcessThreeDSRequest.redirectUrl] can't be parsed or when error received from 3DS WebView
        is ThreeDSResult.Error -> { 
            /* Handle success result */
            threeDSResult.error
        }
    }
}

Step 2: Handle 3DS

val request = ThreeDSRequest(
    container = findViewById(android.R.id.content), // Provide a ViewGroup container for 3DS WebView
    challengeUrl = redirectUrl,                     // Provide a 3D Secure URL
    successUrl = "http://example.com/success",
    failureUrl = "http://example.com/failure",
    resultHandler = threeDSResultHandler
)
paymentFormMediator.handleThreeDS(request)

Handle GooglePay

Handle a Google Pay token payload and retrieve a token, that can be used to create a charge from your backend.

Step 1: Create CheckoutApiService

val checkoutApiClient = CheckoutApiServiceFactory.create(
    publicKey = PUBLIC_KEY,                     // set your public key
    environment = Environment.SANDBOX,          // set context
    context = context                           // set the environment
)

Step 2: Create a token for GooglePay

val request = GooglePayTokenRequest(
    tokenJsonPayload = tokenJsonPayload,
    onSuccess = { tokenDetails ->
       /* Handle success result */
        tokenDetails.token
    },
    onFailure = { errorMessage ->
        /* Handle failure result */
    }
)
checkoutApiClient.createToken(request)

Make a payment with a hosted CVV

Use our CVV component to make a compliant saved card payment in regions where sending a CVV is always mandatory.

Within this flow, we will securely tokenise the CVV and return a CVV token to your application layer, which you can then use to continue the payment flow with.

Step 1: Create the CVVComponentApi

// Create one time cvvComponentApi
val cvvComponentApi = CVVComponentApiFactory.create(
    publicKey = "PUBLIC_KEY",                     // set your public key
    environment = Environment.SANDBOX,           // set the environment
    context = context                           // set context
)

Step 2: Create the CVVComponentMediator using CVVComponentConfig

Prepare the object responsible for the CVV component configuration.

Here you should inject the card scheme in order for the SDK to validate the length of the CVV using isEnteredCVVValid. If you don't pass a scheme, we will treat it as UNKNOWN. Within CVVComponentConfig, you can also configure the cvvComponentStyle, which has all the styling attributes of our existing UIs.

If the CVV is length 0, the SDK will throw a validation error when calling createToken.

val cvvComponentConfig = CVVComponentConfig(
    cardScheme = CardScheme.fromString(cardSchemeValue = "your card scheme"),         // set your card scheme (optional)
    onCVVValueChange = { isEnteredCVVValid ->                                         // update your listener from the onCVVValueChange 
        enteredVisaCVVUpdated.value = isEnteredCVVValid
    },
    cvvInputFieldStyle = inputFieldStyle,                                            // set your cvvInputFieldStyle
)

Once you've prepared the CVV component's config, then use the CVVComponentMediator.

val cvvComponentMediator = cvvComponentApi.createComponentMediator(cvvComponentConfig)

Step 3: Load the CVV component

For Compose based UIs:

cvvComponentMediator.CVVComponent()

For XML-based UIs:

// Access the LinearLayout by its Resource ID
val linearLayout = findViewById<LinearLayout>(R.id.linearLayout)
// Get the cvvComponentView 
val cvvComponentView = cvvComponentMediator.provideCvvComponentContent(linearLayout)
// Add the cvvComponentView into parent Layout
linearLayout.addView(cvvComponentView)

Step 4: Create a CVV token

 cvvComponentMediator.createToken { result ->
    when (result) {
        is CVVTokenizationResultHandler.Success -> {
            /* Handle success result */ result.tokenDetails
        }
        is CVVTokenizationResultHandler.Failure -> {
            /* Handle failure result */ result.errorMessage
        } 
    }
}

You can then continue the payment flow with this token by passing into a field name as cvv in the payment request.

Migrating

3DS and GooglePay processing remain unaffected so using them should still work the same.

If you're using Frames Android from versions prior to v4 (we would strongly recommend using the latest release), this update will bring breaking changes that'll require a little development time.

Because of our efforts to greatly improve the UI of the Frames and enabling you to customise it to such extents, the approach required is very different. This will require you to:

  • remove usage of Frames older version from your code base. This may be an opportunity to remove a screen as well, or a few other objects that were created only to support older Frames integration!
  • Get started

We would like to point out the great benefits that we think v4+ brings to our SDK, like:

  • customisable UI focussed on enabling your users to seamlessly transition through the payment flow
  • updated and improved validation logic, in line with our supported card payment methods
  • using our updated UIs provides added security benefits to your customers

License

Frames Android is released under the MIT license.