Create an Android Automotive Operating System (AAOS) app with payments via Samsung Checkout


Objective

Create a shopping app for Android Automotive OS (AAOS), which uses templates from AAOS and Ignite Store, and processes payments via the Ignite Payment SDK powered by Samsung Checkout.

Partnership Request

To use the Ignite Payment SDK and have access to development tools and resources, such as Ignite AAOS Emulators, you must become an official partner. Once done, you can fully utilize this Code Lab. You can learn more about the partnership process by visiting the Ignite Store Developer Portal.

Overview

Android Automotive OS

Android Automotive OS (AAOS) is a base Android platform that runs directly on the car and is deeply integrated with the underlying vehicle hardware. Unlike the Android Auto platform, users can download compatible apps with AAOS directly into their cars, without needing a phone, and utilize an interface specifically designed for the car screen.

AAOS can run both system and third-party Android applications. As AAOS is not a fork and shares the same codebase as Android for mobile devices, developers can easily adapt existing smartphone apps to function on AAOS.

The diagram below illustrates the architecture of AAOS:

  • At the Hardware Abstraction Layer (HAL) level, AAOS incorporates additional components such as the Vehicle HAL (VHAL), Exterior View System (EVS), and Broadcast Radio (BR) to handle vehicle properties and connectivity.

  • At the Framework level, Car Service and WebView modules are included.

  • At the Application level, the main system applications include Car System UI, Car Launcher, and Car Input Method Editor (IME). Additionally, Car Media and Automotive Host are incorporated as system apps.

  • Third-party apps are classified into three categories: Audio Media, Car Templated, and Parked.

Car Templates

The Car Templated Apps use templates specified by the Car App Library, which are rendered by the Automotive Host, customized by Original Equipment Manufacturers (OEM). The library consists of approximately 10 templates (List, Grid, Message, Pane, Navigation) and is utilized in both Android Auto (AA) and Android Automotive OS (AAOS) apps.

To target AAOS, you must incorporate an additional app-automotive library that injects the CarAppActivity into the app. The CarAppActivity needs to be included in the Manifest and can be set as distractionOptimized. Upon launching the application, the CarAppActivity provides a surface that is employed by the Automotive Host to render the template models.

Additionally, on the Harman Ignite Store, you can optionally integrate the ignite-car-app-lib, which adds supplementary templates such as Explore, ListDetails, RouteOverview, and PoiStreaming.

Harman Ignite Store

The Harman Ignite Store is a white-labeled and modular AAOS-based automotive app store. By connecting app developers with car manufacturers, Harman creates unique in-vehicle experiences. The Ignite Store has a rich app ecosystem with unique content, growth opportunities, and long-term partnerships. It facilitates future-proof monetization with a payments API powered by Samsung checkout.

After registering at the Ignite Store Developer Portal, developers can submit their apps for certification and testing by Harman. Upon approval from the OEM, the developer can proceed with publishing their app. Comprehensive developer documentation and tools are available to support app developers throughout the development process.

Payments API

The Ignite Store comes enabled with payment features empowering developers to monetize their apps. Developers are now able to offer their apps as paid apps. The Payment SDK exposes APIs for goods and services, in-app purchases, and subscriptions.

Developers can also integrate their own payment service providers (PSPs), to sell goods or services, and receive the money directly in their bank account. For a frictionless in-car payment experience, Ignite provides a dedicated digital wallet app for end-users to securely store their credit card information. The payment processor is powered by the industry proven Samsung Checkout.

The developer portal provides additional documentation to allow developers to access Ignite AAOS Emulators, VIM3, Tablet or Cuttlefish Ignite images, and additional guidelines.

Set up your environment

You will need the following:

  • Ignite AAOS system image (running on Android Emulator or on reference devices)

  • Android Studio (latest version recommended)

  • Java SE Development kit (JDK) 11 or later

Sample Code

Here is a sample code for you to start coding in this Code Lab. Download it and start your learning experience!

AAOS Ignite Shopping App Sample Code
(11.7 MB)

Prepare your Ignite AAOS Emulator

  1. Add the Ignite AAOS Emulator to your Android Studio by following the guide provided in the Ignite Store Developer Console.
  2. Open the Device Manager and start the emulator.

  1. Configure the emulator to use fake data for payments, as instructed in the Ignite Store Developer Console under the Offline mode tab in the Payment SDK section.

  2. The sample app requires navigation from point A (starting location) to point B (destination location). The destination addresses are predefined and near San Jose McEnery Convention Center. To shorten the distance between two locations, follow the steps below to set the starting location:

    a. Open the Extended Controls in the emulator panel.

    b. Go to Location, search for a location near the destination location, and click Set Location.

  1. Next, in the emulator, go to the Ignite Navigation app's settings and enable the following:

    • Enable Navigation simulation

    • Enable Mock Location provider

  2. Go to Settings > System > Developer options > Location and set Ignite Navigation as mock location app.

Start your project

After downloading the sample code containing the project files, open your Android Studio and click Open to open an existing project.

Locate the downloaded Android Project (igniteautomotivepaymentssdc202488) from the directory and click OK.

Check the Ignite Payment SDK dependency

Verify that the Ignite Payment SDK library is included in the dependencies section of the module's build.gradle file.

dependencies { 
    implementation files('libs/ignite-payment-sdk-3.13.0.24030417-0-SNAPSHOT.aar') 
}

Add payment permission

Next, go to the manifests folder and, in the AndroidManifest.xml file, include the PAYMENT_REQUEST permission to perform in-app purchases.

<uses-permission android:name="com.harman.ignite.permission.PAYMENT_REQUEST" /> 

This ensures that the app has the necessary permissions to perform transactions and handle sensitive financial data.

Show the Payment Screen

When an item is added to the cart, the Shopping Cart screen displays the Select Store button, selected pickup store address, total amount to pay, and details of each item added. The screen also includes the Pay button.

Go to kotlin + java > com.harman.ignite.pickupstore > screens > ShoppingCartScreen.kt.

In the doCheckout() function, use the Car App's ScreenManager to navigate to the Payment screen from the Shopping Cart screen after clicking the Pay button.

getScreenManager().push(PaymentScreen(carContext, session)) 

Instantiate the Ignite Payment Client

The Ignite Payment API provides a singleton class called IgnitePaymentClientSingleton, which enables performing and tracking transactions.

Navigate to the PaymentScreen.kt file and instantiate the Ignite Payment Client.

private val mIpc = IgnitePaymentClientSingleton.getInstance(carContext) 

Define the Ignite Payment transaction callback

The Ignite Payment transaction provides three callback methods: onSuccess, onCanceled, and onFailure. After each callback, make sure to:

  • set the isPaymentFailed variable to track whether a payment is successful or not.

  • update the session (which owns the Shopping Cart screen) to reflect the status of the payment transaction.

  • call the updateTemplate() function to invalidate the current template and create a new one with updated information.

private val mIpctcb = object : IIgnitePaymentClientTransactionCallback { 
    override fun onSuccess(requestUuid: String?, sessionId: String?, successMessage: String?, paymentAdditionalProperties: HashMap<String, String>?) { 
        Log.d(TAG, LOG_PREFIX + "onSuccess  rID: $requestUuid, sId: $sessionId, sM: $successMessage") 
        CarToast.makeText(carContext, "Payment successful", CarToast.LENGTH_SHORT).show() 
        isPaymentFailed = false 
        session.paymentDone(requestUuid, sessionId, successMessage) 
        updateTemplate() 
    } 
 
    override fun onCanceled(requestUuid: String?, sessionId: String?) { 
        Log.d(TAG, LOG_PREFIX + "onCanceled  rID: $requestUuid, sId: $sessionId") 
        CarToast.makeText(carContext, "Payment canceled", CarToast.LENGTH_LONG).show() 
        isPaymentFailed = true 
        session.paymentError(requestUuid, sessionId, null) 
        updateTemplate() 
    } 
 
    override fun onFailure(requestUuid: String?, sessionId: String?, wallerErrorCode: Int, errorMessage: String) { 
        Log.d(TAG, LOG_PREFIX + "onFailure rID: $requestUuid, sId: $sessionId, wEc: $wallerErrorCode, eM: $errorMessage") 
        CarToast.makeText(carContext, "Payment failed", CarToast.LENGTH_LONG).show() 
        isPaymentFailed = true 
        session.paymentError(requestUuid, sessionId, errorMessage) 
        updateTemplate() 
    } 
}

Define the Ignite Payment Client connection callback

The Ignite Payment Client needs to be connected in order to perform a payment request.

Once the client connects successfully, retrieve the names of the shopping cart items and use them to create an order summary. Afterwards, construct an Ignite Payment Request containing the total amount, currency code, merchant ID, and details of the order summary.

Then, initiate the payment process by invoking the readyToPay() function of the Ignite Payment Client API.

private val mIpccb = IIgnitePaymentClientConnectionCallback { connected -> 
    Log.d(TAG, LOG_PREFIX + "onPaymentClientConnected: $connected") 
    if (connected) { 
        val textSummary = session.shoppingCart.cartItems.joinToString(", ") { item -> item.name } 
        val ipr = IgnitePaymentRequest.Builder() 
            .setAmount(session.shoppingCart.getTotal() * 100) 
            .setCurrencyCode(CurrencyCode.USD) 
            .setPaymentOperation(PaymentOperation.Purchase) 
            .setMerchantId(Constants.MERCHANT_ID) 
            .setOrderSummary(OrderSummary.Builder() 
                .setOrderSummaryBitmapImage(BitmapFactory.decodeResource(carContext.resources, session.shoppingCart.store.largeIcon)) 
                .setOrderSummaryLabel1("${carContext.getString(R.string.pickupstore_app_title)} ${session.shoppingCart.store.title}") 
                .setOrderSummarySubLabel1(session.shoppingCart.store.address) 
                .setOrderSummaryLabel2(textSummary) 
                .setOrderSummarySubLabel2(carContext.getString(R.string.pickupstore_payment_sublabel2)) 
                .build() 
            ) 
            .build() 
        try { 
            mIpc.readyToPay(ipr, mIpctcb) 
        } catch (e: Exception) { 
            Log.d(TAG, LOG_PREFIX + "payment exception: $e") 
            e.printStackTrace() 
        } catch (e: Error) { 
            Log.d(TAG, LOG_PREFIX + "payment error: $e") 
            e.printStackTrace() 
        } 
    } 
}

Start the payment process and go back to previous screen after the transaction

Next, in the startPayment() function, connect the Ignite Payment Client and the connection callback to start the payment process.

mIpc.connect(mIpccb) 

After the transaction is completed, the updateTemplate() function refreshes the template used in the Payment screen before calling the scheduleGoBack() function.

Modify the scheduleGoBack() function to navigate back to the previous template screen (Shopping Cart). You can use the pop method of the ScreenManager.

screenManager.pop()

Start the navigation to the store to collect the paid pickup

The Shopping Cart screen shows the pickup store location, details of the order, and Go To Store button after a successful payment.

Go to kotlin + java > com.harman.ignite.pickupstore > PickupStoreSession.kt.

Modify the navigateToStore(geofence: Geofence) function to trigger the navigation to the pickup store when the Go To Store button is clicked. You can use the intent CarContext.ACTION_NAVIGATE with geo schema (RFC 5879) data, containing latitude and longitude (e.g., geo:12.345, 14.8767).

To send the intent, use the carContext.startCarApp() API call.

val geoUriString = "geo:${geofence.latitude},${geofence.longitude}" 
val uri = Uri.parse(geoUriString) 
val navIntent = Intent(CarContext.ACTION_NAVIGATE, uri) 
try { 
    carContext.startCarApp(navIntent) 
} catch (e: Exception) { 
    Log.e(TAG, LOG_PREFIX + "navigateToStore: exception starting navigation") 
    e.printStackTrace() 
    CarToast.makeText(carContext, "Failure Starting Navigation", CarToast.LENGTH_SHORT).show() 
}

Run the app on Ignite AAOS Emulator

  1. Run the pickup-store-app on Ignite AAOS Emulator.

  1. When the app starts for the first time, it requests for user permissions. Click Grant Permissions.

  1. Choose Allow all the time for location permission and click the return button.4

  1. Browse the pickup store catalog and add items to shopping cart.

  1. Open the shopping cart and click Pay. You can also change the pickup store by clicking Select Store.

  1. Check the order summary and billing information. Then, click Confirm and Pay to process payment.

  1. After a successful payment, the app returns to Shopping Cart screen with the updated transaction information. Click Go To Store to start the navigation to the store.

  1. The app displays a notification when the car is near the store. Click the notification to show a reference QR code to present to the store upon pick up.

You're done!

Congratulations! You have successfully achieved the goal of this Code Lab topic.

Now, you can create an AAOS templated app, which supports payments by yourself!

Learn more by going to the developer console section of the Ignite Store Developer Portal.