# Quickstart

> Before reading this document, make sure you have completed the [Integration Guide](/payments/docs/introduction/integrated-guide) and understand the [APIs for creating checkout sessions](/payments/apis/introduction/started).


The Android SDK provided by PayKKa allows you to natively embed multiple payment methods in your app with ease. You only need to pass a `sessionId` to the SDK to open the checkout page in your app, receive user payments, and customize and handle related payment callbacks. The latest SDK download is available in [Android SDK Release History and Changelog](/payments/docs/developer-resources/native-sdks/android-sdk/release-history).

## Step 1: Add the SDK to Your Project

After downloading the SDK, you will get a `.zip` file with a directory structure similar to the following:

```txt
.
├── libs
│   ├── alipayplusclient-2.2.0-release.aar
│   ├── cardinalmobilesdk-2.2.7-7.aar
│   ├── paykka-checkout-payments-1.1-Release.aar
│   └── ...
└── PayKKaCheckoutApp-Android
    ├── JavaCheckoutDemo
    │   ├── app
    │   ├── build.gradle
    │   ├── gradle
    │   ├── gradle.properties
    │   ├── gradlew
    │   ├── gradlew.bat
    │   └── settings.gradle
    └── KotlinCheckoutDemo
        ├── app
        ├── build.gradle.kts
        ├── gradle
        ├── gradle.properties
        ├── gradlew
        ├── gradlew.bat
        └── settings.gradle.kts
```

`PayKKaCheckoutApp-Android` contains sample Android projects written in Java and Kotlin. Developers can use the sample code to learn how to use the PayKKa SDK APIs.

The `paykka-checkout-payments-X.X-Release.aar` file in the `libs` folder is the core PayKKa SDK. Copy the `libs` folder from the downloaded zip file into the `app/` folder of your Android project, for example `YourAppProject/app/`. Then edit `app/build.gradle.kts` or `app/build.gradle` and add the following code to import and use the SDK.

Kotlin
```kotlin app/build.gradle.kts
dependencies {
    // Required: include all libraries and dependencies in the libs directory
    // paykka-checkout-payments-x.x-Release.aar: required, the core PayKKa SDK
    // 
    // alipayplusclient-x.x.x-release.aar: required for [Alipay+], provides Alipay+ capabilities
    // cardinalmobilesdk-x.x.x-x.aar: required for [BankCardPay], used for anti-fraud, risk control, and related 3DS flows
    implementation(
        fileTree(
            mapOf(
                "dir" to "libs",
                "include" to listOf("*.aar", "*.jar")
            )
        )
    )
    // Required: the SDK uses Gson for JSON serialization/deserialization
    implementation("com.google.code.gson:gson:2.13.2")
    // Required: related SDK UI styles and component implementations are based on this library
    implementation("com.google.android.material:material:1.12.0")
    // Required: related SDK API requests are based on okhttp
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    // Required: the SDK uses some WebKit features
    implementation("androidx.webkit:webkit:1.14.0")

    // Required for [GooglePay]: Google Pay native SDK
    implementation("com.google.android.gms:play-services-wallet:19.5.0")
    // Required for [BankCardPay, GooglePay]: used for anti-fraud, risk control, and related 3DS flows
    implementation("com.stripe:payments-core:22.6.1")
    // Required for [WeChatPay]: WeChat Pay native SDK
    implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.34")
}
```

Java
```gradle app/build.gradle
dependencies {
    // Required: include all libraries and dependencies in the libs directory
    // paykka-checkout-payments-x.x-Release.aar: required, the core PayKKa SDK
    // 
    // alipayplusclient-x.x.x-release.aar: required for [Alipay+], provides Alipay+ capabilities
    // cardinalmobilesdk-x.x.x-x.aar: required for [BankCardPay], used for anti-fraud, risk control, and related 3DS flows
    implementation(
        fileTree(
            mapOf(
                "dir" to "../../../libs",
                "include" to listOf("*.aar", "*.jar")
            )
        )
    )
    // Required: the SDK uses Gson for JSON serialization/deserialization
    implementation("com.google.code.gson:gson:2.13.2")
    // Required: related SDK UI styles and component implementations are based on this library
    implementation("com.google.android.material:material:1.12.0")
    // Required: related SDK API requests are based on okhttp
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    // Required: the SDK uses some WebKit features
    implementation("androidx.webkit:webkit:1.14.0")

    // Required for [GooglePay]: Google Pay native SDK
    implementation("com.google.android.gms:play-services-wallet:19.5.0")
    // Required for [BankCardPay, GooglePay]: used for anti-fraud, risk control, and related 3DS flows
    implementation("com.stripe:payments-core:22.6.1")
    // Required for [WeChatPay]: WeChat Pay native SDK
    implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.34")
}
```

## Step 2: Obtain AppCode

During initialization, the SDK verifies whether the package name and signing certificate hash of the host app match the information stored in the PayKKa database, in order to confirm that SDK calls are coming from the app officially packaged by the merchant. Therefore, you need to provide the following information about your app:

- App package name
- App signing certificate fingerprint


Tip
If your app has already been uploaded to Google Play, you can directly go to `Test and release -> App integrity -> Play app signing -> Settings` to view the certificate fingerprint.

The app package name can be found directly in your app project. The signing certificate fingerprint can be obtained using [Android App: LibChecker](https://github.com/LibChecker/LibChecker) or by running the following commands:

```sh
# Export the certificate first
keytool -exportcert -alias {YourKeyAlias} -keystore {PathToYour}.keystore -rfc > cert.pem
# Calculate the md5 with openssl
openssl x509 -in cert.pem -noout -fingerprint -md5
```

OpenSSL will output a hash value like the following:

```
md5 Fingerprint=7A:3F:9C:10:B4:6D:2E:XX:XX:XX:CC:9A:3D:77:E8:21
```

Provide your app package name and signing certificate hash to our integration contact, and we will generate `PayKKaAppCode` for you.

Then add the following metadata to `AndroidManifest.xml` (replace `{YourPayKKaAppCode}` below with your actual PayKKaAppCode):

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
    <application ...>
        ...
        <meta-data android:name="paykka_appcode" android:value="{YourPayKKaAppCode}"/>
    </application>
</manifest>
```

## Step 3: Integrate and Use

After the SDK has been added and configured, you can start using PayKKa's payment SDK in your app. The SDK mainly provides two integration modes: Drop-In (an embedded checkout with a complete payment form popup component that includes multiple payment methods) or Component (a component-based checkout that includes only a single payment method).

For each payment method, some key Java/Kotlin code snippets are listed below. By calling the SDK methods, you can customize callbacks and launch payment forms or popups to guide users through the payment process.

### Define Your Merchant Configuration

Kotlin
```kotlin AppConf.kt
// ...
open class AppConf : YourMerchantConf() {
    // Optional. If not specified, the display language follows the device system setting.
    override fun getLocale(): String = "en-US"
}
open class YourMerchantConf : PayKKaConfiguration {
    // Required. Fill in your PayKKa merchant ID.
    override fun getGatewayMerchantId(): String = "{YOUR_GATEWAY_MERCHANT_ID}"
    // Required. Fill in your client key.
    override fun getClientKey(): String = "{YOUR_CLIENT_KEY}"
    // Optional. Your WeChat Open Platform App ID.
    override fun getWeChatPayAppId(): String = "{YOUR_WECHAT_PAY_APP_ID}"
    // Optional. Your `Participant ID` in Alipay+ Developer Center.
    override fun getAlipayPlusAcquirerId(): String = "{YOUR_ALIPAY_PARTICIPANT_ID}"
}
```

Java
```java AppConf.java
// ...
public class AppConf extends YourMerchantConf {
    // Optional. If not specified, the display language follows the device system setting.
    @Nullable
    @Override
    public String getLocale() {return "en-US";}
}
class YourMerchantConf implements PayKKaConfiguration {
    // Required. Fill in your PayKKa merchant ID.
    @Override
    public String getGatewayMerchantId() {return "{YOUR_GATEWAY_MERCHANT_ID}";}
    // Required. Fill in your client key.
    @Override
    public String getClientKey() {return "{YOUR_CLIENT_KEY}";}
    // Optional. Your WeChat Open Platform App ID.
    @Override
    public String getWeChatPayAppId() {return "{YOUR_WECHAT_PAY_APP_ID}";}
    // Optional. Your `Participant ID` in Alipay+ Developer Center.
    @Override
    public String getAlipayPlusAcquirerId() {return "{Your_ALIPAY_PARTICIPANT_ID}";}
}
```

### Option 1: Integrate in Drop-In Mode

The checkout integrated in Drop-In mode includes multiple payment methods (for example, Google Pay, bank card/credit card, and more), and the integration is relatively simple:

After your backend calls PayKKa's [Initialize Drop-In Checkout SessionID](/payments/apis/introduction/started) API, the app only needs to pass the SessionID to the SDK's payment form component to launch a popup that contains multiple PayKKa payment forms, guide users through payment, and complete the payment collection flow.

Paymentsheet examples
Kotlin
```kotlin DropInCheckoutConfirmOrderActivity.kt
package com.paykka.kotlincheckoutdemo

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PayKKaEnv
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.ui.component.PaymentBottomSheet
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.ConfirmOrderScreen

class DropInCheckoutConfirmOrderActivity : ComponentActivity() {
    val TAG = "DropInCheckoutConfirmOrderActivity"

    private var isPaying by mutableStateOf(false)

    // Initialize your merchant configuration
    private val APP_CONF = AppConf()

    private lateinit var paymentSheet: PaymentBottomSheet

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)

        // (2) Make sure to initialize the `PaymentBottomSheet` instance before Activity onCreate
        paymentSheet = PaymentBottomSheet(this, object : PaymentResultCallback {
            override fun onResult(paymentResult: PaymentResult) {
                // (3) Define your payment callback in onResult; this callback is triggered only after EXPIRED/ SUCCESS
                handlePaymentResult(paymentResult)
            }

            override fun onError(e: Throwable) {
                // (4) You can capture errors in the payment flow here
                // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
                // You can focus on your own error-handling logic in this callback
                Log.e(TAG, "onError: ", e)
                toast(e.message ?: "Unknown error")
                hideButtonLoading()
            }
        })

        setContent {
            KotlinCheckoutDemoTheme {
                Box(modifier = Modifier.fillMaxSize()) {
                    ConfirmOrderScreen {
                        openCheckout()
                    }

                    if (isPaying) {
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .background(Color.Black.copy(alpha = 0.25f)),
                            contentAlignment = Alignment.Center
                        ) {
                            CircularProgressIndicator(modifier = Modifier.size(44.dp))
                        }
                    }
                }
            }
        }
    }

    private fun openCheckout() {
        if (isPaying) return

        // Optional. Whether to display your merchant name in the form
        paymentSheet.showMerchantName()
        showButtonLoading()
        // (5) At the appropriate time (for example, when the user taps the "Pay Now" button),
        // call the popup form's init method to initialize it, then call show to display the popup
        paymentSheet
            .init(EXAMPLE_CHECKOUT_SESSION_ID)
            .show { _ ->
                // Callback after the form is fully displayed
                hideButtonLoading()
            }
    }

    private fun hideCheckout() {
        hideButtonLoading()
        // Manually close the form popup
        paymentSheet.hide()
    }

    private fun showButtonLoading() {
        isPaying = true
    }

    private fun hideButtonLoading() {
        isPaying = false
    }

    private fun handlePaymentResult(paymentResult: PaymentResult) {
        // Close BottomSheetDialog before leaving the page to avoid WindowLeaked
        if (this::paymentSheet.isInitialized && paymentSheet.isShowing) {
            paymentSheet.dismiss()
        }

        val intent = Intent(this, PaymentCompleteActivity::class.java)
        intent.putExtra("paymentResult", paymentResult.toString())
        startActivity(intent)
        finish()
    }

    override fun onDestroy() {
        // Close BottomSheetDialog before leaving the page to avoid WindowLeaked
        if (this::paymentSheet.isInitialized && paymentSheet.isShowing) {
            paymentSheet.dismiss()
        }
        super.onDestroy()
    }

    private fun toast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".DropInCheckoutConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

Java
```java DropInCheckoutConfirmOrderActivity.java
package com.paykka.javacheckoutdemo;

import static com.paykka.android.checkout.api.CheckoutApi.CCSInfoResponse;
import static com.paykka.javacheckoutdemo.AppConf.EXAMPLE_CHECKOUT_SESSION_ID;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.paykka.android.checkout.PayKKa;
import com.paykka.android.checkout.PayKKaConfiguration;
import com.paykka.android.checkout.PayKKaEnv;
import com.paykka.android.checkout.PaymentResult;
import com.paykka.android.checkout.PaymentResultCallback;
import com.paykka.android.checkout.ui.component.PaymentBottomSheet;

public class DropInCheckoutConfirmOrderActivity extends AppCompatActivity {
    public static final String TAG = "DropInCheckoutConfirmOrderActivity";
    private Button btnPay;
    private FrameLayout checkoutPayLoadingOverlay;
    // Initialize your merchant configuration
    private final PayKKaConfiguration APP_CONF = new AppConf();
    private PaymentBottomSheet paymentSheet;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_embed_checkout_confirm_order);

        btnPay = findViewById(R.id.btn_pay);
        btnPay.setOnClickListener(this::openCheckout);

        checkoutPayLoadingOverlay = findViewById(R.id.checkoutPayLoadingOverlay);

        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX);

        // (2) Make sure to initialize the `PaymentBottomSheet` instance before Activity onCreate
        paymentSheet = new PaymentBottomSheet(this, new PaymentResultCallback() {
            @Override
            public void onResult(PaymentResult paymentResult) {
                // (3) Define your payment callback in onResult; this callback is triggered only after EXPIRED/ SUCCESS
                handlePaymentResult(paymentResult);
            }

            @Override
            public void onError(Throwable e) {
                // (4) You can capture errors in the payment flow here
                // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
                // You can focus on your own error-handling logic in this callback
                Log.e(TAG, "onError: ", e);
                toast(e.getMessage());
                hideButtonLoading();
            }
        });
    }

    private void openCheckout(View v) {
        // Optional. Whether to display your merchant name in the form
        paymentSheet.showMerchantName();
        showButtonLoading();
        // (5) At the appropriate time (for example, when the user taps the "Pay Now" button),
        // call the popup form's init method to initialize it, then call show to display the popup
        paymentSheet.init(EXAMPLE_CHECKOUT_SESSION_ID).show(this::hideButtonLoading);
    }
    private void hideCheckout(View v) {
        hideButtonLoading();
        // Manually close the form popup
        paymentSheet.hide();
    }
    private void showButtonLoading() {
        btnPay.setEnabled(false);
        checkoutPayLoadingOverlay.setVisibility(View.VISIBLE);
    }
    private void hideButtonLoading(@Nullable CCSInfoResponse res) {
        btnPay.setEnabled(true);
        checkoutPayLoadingOverlay.setVisibility(View.GONE);
    }
    private void hideButtonLoading() {
        hideButtonLoading(null);
    }
    private void handlePaymentResult(PaymentResult paymentResult) {
        // Close BottomSheetDialog before leaving the page to avoid WindowLeaked
        if (paymentSheet != null && paymentSheet.isShowing()) {
            paymentSheet.dismiss(); // or paymentSheet.hide();
        }
        Intent intent = new Intent(this, PaymentCompleteActivity.class);
        intent.putExtra("paymentResult", paymentResult.toString());
        startActivity(intent);
        finish();
    }
    @Override
    protected void onDestroy() {
        // Ensure the dialog no longer holds the window no matter how the page exits
        if (paymentSheet != null && paymentSheet.isShowing()) {
            paymentSheet.dismiss();
        }
        super.onDestroy();
    }
    private void toast(String message) {
        Toast.makeText(DropInCheckoutConfirmOrderActivity.this, message, Toast.LENGTH_SHORT).show();
    }
}
```

```xml res/values/themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Theme.JavaCheckoutDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>
    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".DropInCheckoutConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

### Option 2: Integrate in Component Mode

In Component mode, PayKKa currently provides multiple options such as the card payment component, Google Pay, and WeChat Pay component. You can embed each payment method component independently anywhere in your app, with a high degree of customization:

After your backend calls PayKKa's [Initialize Component Checkout SessionID](/payments/apis/introduction/started) API, the app passes the SessionID to the SDK's single payment component to complete the payment collection flow.

#### Card Payment

Card payment form examples
Kotlin
1. Declare the card payment form layout


```kotlin views/BankCardPayConfirmOrderScreen.kt
package com.paykka.kotlincheckoutdemo.views

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.card.CardPaymentForm


@Composable
fun BankCardPayConfirmOrderScreen(
    isPayButtonEnabled: Boolean,
    onPayClick: () -> Unit,
    onFormCreated: (CardPaymentForm) -> Unit
) {
    Scaffold(
        bottomBar = {
            Surface(shadowElevation = 8.dp) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 12.dp, vertical = 10.dp),
                    contentAlignment = Alignment.Center
                ) {
                    Button(
                        onClick = onPayClick,
                        enabled = isPayButtonEnabled,
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        Text(text = "Pay Now", fontWeight = FontWeight.Bold)
                    }
                }
            }
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
                .verticalScroll(rememberScrollState())
                .fillMaxSize()
                .padding(vertical = 12.dp)
        ) {
            OrderCardView()

            AndroidView(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 12.dp)
                    .padding(horizontal = 12.dp),
                factory = { context ->
                    // Import and declare the card payment form layout
                    CardPaymentForm(context).also { onFormCreated(it) }
                }
            )
        }
    }
}
```

1. Initialize and integrate BankCardPaymentSDK


```kotlin BankCardPayConfirmOrderActivity.kt
package com.paykka.kotlincheckoutdemo

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PayKKaEnv
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.api.CheckoutApi.CCSInfoResponse
import com.paykka.android.checkout.model.dto.enums.CheckoutStatus
import com.paykka.android.checkout.payments.card.BankCardPayment
import com.paykka.android.checkout.payments.card.BankCardPayment.CardPaymentFormInitCallback
import com.paykka.android.checkout.ui.component.card.CardPaymentForm
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.BankCardPayConfirmOrderScreen

class BankCardPayConfirmOrderActivity : ComponentActivity() {
    val TAG = "BankCardPayConfirmOrderActivity"

    private var isPaying by mutableStateOf(false)
    private var isPayEnabled by mutableStateOf(false)
    // Initialize your merchant configuration
    private val APP_CONF = AppConf()

    private lateinit var cardPaymentForm: CardPaymentForm
    private lateinit var bankCardPayment: BankCardPayment

    private val payCallback = object : PaymentResultCallback {
        override fun onResult(paymentResult: PaymentResult) {
            // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
            handlePaymentResult(paymentResult)
        }

        override fun onError(e: Throwable) {
            // You can capture errors in the payment flow here
            // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
            // You can focus on your own error-handling logic in this callback
            Log.e(TAG, "onError: ", e)
            toast(e.message ?: "Unknown error")
            hideLoadingOverlay()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)

        setContent {
            KotlinCheckoutDemoTheme {
                Box(modifier = Modifier.fillMaxSize()) {
                    BankCardPayConfirmOrderScreen(
                        isPayButtonEnabled = isPayEnabled && !isPaying,
                        onPayClick = { onClickBtnPay() },
                        onFormCreated = { form ->
                            cardPaymentForm = form
                            initCardPaymentForm(form)
                            initBankCardPayment(form)
                        }
                    )

                    if (isPaying) {
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .background(Color.Black.copy(alpha = 0.25f)),
                            contentAlignment = Alignment.Center
                        ) {
                            CircularProgressIndicator(modifier = Modifier.size(44.dp))
                        }
                    }
                }
            }
        }
    }

    private fun initCardPaymentForm(form: CardPaymentForm) {
        // (2) Initialize the card component and set form validation callbacks
        form.init(this).setInputValidationListener(object : CardPaymentForm.InputValidationListener {
            override fun onAllFieldsValid() {
                // Triggered when all form fields are filled in correctly
                isPayEnabled = true
                Log.i(TAG, "All form fields are valid")
            }

            override fun onFieldInputInvalid(throwable: Throwable) {
                // Triggered when any form field is filled in incorrectly
                isPayEnabled = false
                Log.e(TAG, "Some form field(s) invalid")
            }
        })
        form.isEnabled = false
    }

    private fun initBankCardPayment(form: CardPaymentForm) {
        // (3) Initialize the card component PaymentSDK
        // At this stage, the form structure and form field interactions are initialized based on the checkout configuration
        bankCardPayment = BankCardPayment(this).init(
            form,
            YourMerchantConf.EXAMPLE_CHECKOUT_SESSION_ID,
            object : CardPaymentFormInitCallback {
                override fun onComplete(ccsInfoResponse: CCSInfoResponse?) {
                    // Triggered when initialization is complete
                    if (ccsInfoResponse?.data?.status == CheckoutStatus.EXPIRED) {
                        toast("Expired")
                        return
                    }
                    form.isEnabled = true
                }

                override fun onFailed(t: Throwable) {
                    // Triggered when initialization fails
                    toast(t.message!!)
                }
            }
        )
    }

    private fun onClickBtnPay() {
        if (isPaying) return
        if (!this::bankCardPayment.isInitialized || !this::cardPaymentForm.isInitialized) return

        showLoadingOverlay()
        cardPaymentForm.hideKeyboard()
        cardPaymentForm.clearAllInputFocus()
        // (4) Initiate payment
        bankCardPayment.requestPayment(payCallback)
    }

    private fun showLoadingOverlay() {
        isPaying = true
    }

    private fun hideLoadingOverlay() {
        isPaying = false
    }

    private fun handlePaymentResult(paymentResult: PaymentResult) {
        val intent = Intent(this, PaymentCompleteActivity::class.java)
        intent.putExtra("paymentResult", paymentResult.toString())
        startActivity(intent)
        finish()
    }

    private fun toast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".BankCardPayConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

Java
1. Declare the card payment form layout


```xml activity_bankcardpay_confirm_order.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
    <!-- Scroll content -->
    <ScrollView ...>
        <LinearLayout ...>
            ...
            <LinearLayout ...>
               <!-- Import and declare the card payment form -->
                <com.paykka.android.checkout.ui.component.card.CardPaymentForm
                    android:id="@+id/paykkaCardPaymentForm"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginVertical="12dp"/>
            </LinearLayout>
        </LinearLayout>
    </ScrollView>

    <!-- Bottom pay button -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="12dp">
            <Button
                android:id="@+id/btn_card_pay"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Card Payment ¥1118.83"
                android:textStyle="bold"
                android:gravity="center"
                android:textColor="?attr/colorOnPrimary"/>
            <!-- Top loading overlay -->
            <FrameLayout
                android:id="@+id/bankcardPayLoadingOverlay"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#66000000"
                android:visibility="gone"
                android:clickable="true"
                android:focusable="true">
                <ProgressBar
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:indeterminate="true" />
            </FrameLayout>
        </FrameLayout>
    </LinearLayout>
</LinearLayout>
```

1. Initialize and integrate BankCardPaymentSDK


```java BankCardPayConfirmOrderActivity.java
package com.paykka.javacheckoutdemo;

import static com.paykka.javacheckoutdemo.AppConf.EXAMPLE_CHECKOUT_SESSION_ID;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.paykka.android.checkout.PayKKa;
import com.paykka.android.checkout.PayKKaConfiguration;
import com.paykka.android.checkout.PayKKaEnv;
import com.paykka.android.checkout.PaymentResult;
import com.paykka.android.checkout.PaymentResultCallback;
import com.paykka.android.checkout.api.CheckoutApi;
import com.paykka.android.checkout.api.CheckoutApi.CCSInfoResponse;
import com.paykka.android.checkout.model.dto.enums.CheckoutStatus;
import com.paykka.android.checkout.model.dto.enums.OrderStatus;
import com.paykka.android.checkout.payments.card.BankCardPayment;
import com.paykka.android.checkout.payments.card.BankCardPayment.CardPaymentFormInitCallback;
import com.paykka.android.checkout.ui.component.card.CardPaymentForm;

public class BankCardPayConfirmOrderActivity extends AppCompatActivity {
    public static final String TAG = "GooglePayConfirmOrderActivity";
    private CardPaymentForm cardPaymentForm;
    private BankCardPayment bankCardPayment;
    private Button btnPay;
    private View bankcardPayLoadingOverlay;
    // Initialize your merchant configuration
    private final PayKKaConfiguration APP_CONF = new AppConf();
    // Customize your PaymentResult callback
    private final PaymentResultCallback payCallback = new PaymentResultCallback() {
        @Override
        public void onResult(PaymentResult paymentResult) {
            // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
            handlePaymentResult(paymentResult);
        }

        @Override
        public void onError(Throwable e) {
            // You can capture errors in the payment flow here
            // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
            // You can focus on your own error-handling logic in this callback
            Log.e(TAG, "onError: ", e);
            toast(e.getMessage());
            hideLoadingOverlay();
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bankcardpay_confirm_order);
        btnPay = findViewById(R.id.btn_card_pay);
        btnPay.setOnClickListener(this::onClickBtnPay);
        btnPay.setEnabled(false);
        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX);

        cardPaymentForm = findViewById(com.paykka.android.checkout.R.id.paykkaCardPaymentForm);
        // (2) Initialize the card component and set form validation callbacks
        cardPaymentForm.init(this).setInputValidationListener(new CardPaymentForm.InputValidationListener() {
            @Override
            public void onAllFieldsValid() {
                // Triggered when all form fields are filled in correctly
                btnPay.setEnabled(true);
                Log.i(TAG, "All form fields are valid");
            }
            @Override
            public void onFieldInputInvalid(Throwable throwable) {
                // Triggered when any form field is filled in incorrectly
                btnPay.setEnabled(false);
                Log.e(TAG, "Some form field(s) invalid");
            }
        });

        bankcardPayLoadingOverlay = findViewById(R.id.bankcardPayLoadingOverlay);
        cardPaymentForm.setEnabled(false);
        // (3) Initialize the card component PaymentSDK
        // At this stage, the form structure and form field interactions are initialized based on the checkout configuration
        bankCardPayment = new BankCardPayment(this).init(cardPaymentForm, EXAMPLE_CHECKOUT_SESSION_ID, new CardPaymentFormInitCallback() {
            @Override
            public void onComplete(CCSInfoResponse ccsInfoResponse) {
                // Triggered when initialization is complete
                if (ccsInfoResponse.data.status == CheckoutStatus.EXPIRED) {
                    toast("Expired");
                    return;
                }
                cardPaymentForm.setEnabled(true);
            }

            @Override
            public void onFailed(Throwable t) {
                // Triggered when initialization fails
                toast(t.getMessage());
            }
        });
    }
    private void onClickBtnPay(View view) {
        showLoadingOverlay();
        cardPaymentForm.hideKeyboard();
        cardPaymentForm.clearAllInputFocus();
        // (4) Initiate payment
        bankCardPayment.requestPayment(payCallback);
    }
    private void showLoadingOverlay() {
        btnPay.setEnabled(false);
        bankcardPayLoadingOverlay.setVisibility(View.VISIBLE);
    }
    private void hideLoadingOverlay() {
        btnPay.setEnabled(true);
        bankcardPayLoadingOverlay.setVisibility(View.GONE);
    }
    private void handlePaymentResult(PaymentResult paymentResult) {
        Intent intent = new Intent(this, PaymentCompleteActivity.class);
        intent.putExtra("paymentResult", paymentResult.toString());
        startActivity(intent);
        finish();
    }
    private void toast(String message) {
        Toast.makeText(BankCardPayConfirmOrderActivity.this, message, Toast.LENGTH_SHORT).show();
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".BankCardPayConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

#### Google Pay

Google payment form examples
Kotlin
1. Declare the Google Pay form layout


```kotlin views/GooglePayConfirmOrderScreen.kt
package com.paykka.kotlincheckoutdemo.views

import android.view.ViewGroup
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.googlepay.GooglePaymentForm


@Composable
fun GooglePayConfirmOrderScreen(
    googlePaymentForm: GooglePaymentForm
) {
    Scaffold(
        bottomBar = {
            Surface(shadowElevation = 8.dp) {
                AndroidView(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(12.dp),
                    // Import and declare the Google Pay form layout
                    factory = { googlePaymentForm },
                    update = { view ->
                        val parent = view.parent
                        if (parent is ViewGroup) {
                            parent.removeView(view)
                        }
                    }
                )
            }
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
                .verticalScroll(rememberScrollState())
                .fillMaxSize()
        ) {
            OrderCardView()
        }
    }
}
```

1. Initialize and integrate GooglePaymentSDK


```kotlin GooglePayConfirmOrderActivity.kt
package com.paykka.kotlincheckoutdemo

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PayKKaConfiguration
import com.paykka.android.checkout.PayKKaEnv
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.payments.PaymentBase
import com.paykka.android.checkout.payments.googlepay.GooglePayment
import com.paykka.android.checkout.ui.component.googlepay.GooglePaymentForm
import com.paykka.android.utils.gson.JsonObject
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.GooglePayConfirmOrderScreen

class GooglePayConfirmOrderActivity : ComponentActivity() {
    val TAG = "GooglePayConfirmOrderActivity"

    private lateinit var googlePayment: GooglePayment
    private lateinit var googlePaymentForm: GooglePaymentForm
    // Initialize your merchant configuration
    private val APP_CONF = AppConf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)

        // (2) Initialize Google Pay
        googlePaymentForm = GooglePaymentForm(this)
        // Payment must be initialized before Lifecycle enters STARTED
        googlePayment = GooglePayment(this, googlePaymentForm, this::requestPayment)

        setContent {
            KotlinCheckoutDemoTheme {
                GooglePayConfirmOrderScreen(googlePaymentForm = googlePaymentForm)
            }
        }
    }

    private fun requestPayment(@Suppress("UNUSED_PARAMETER") view: View) {
        googlePaymentForm.showLoadingOverlay()
        // (3) Initiate payment and launch the Google Pay popup
        googlePayment.requestPayment(EXAMPLE_CHECKOUT_SESSION_ID, object : PaymentResultCallback {
            override fun onResult(paymentResult: PaymentResult) {
                // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
                googlePaymentForm.hideLoadingOverlay()
                handlePaymentResult(paymentResult)
            }

            override fun onError(e: Throwable) {
                // You can capture errors in the payment flow here
                // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
                // You can focus on your own error-handling logic in this callback
                googlePaymentForm.hideLoadingOverlay()
                Log.e(TAG, "onError: ", e)
                toast(e.message ?: "Unknown error")
            }

            override fun onUserCanceled(paymentSource: PaymentBase?, extraData: JsonObject?) {
                // The user canceled the payment and closed the Google Pay popup
                googlePaymentForm.hideLoadingOverlay()
                toast("Google Payment: CANCELED")
            }
        })
    }

    private fun handlePaymentResult(paymentResult: PaymentResult) {
        val intent = Intent(this, PaymentCompleteActivity::class.java)
        intent.putExtra("paymentResult", paymentResult.toString())
        startActivity(intent)
        finish()
    }

    private fun toast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".GooglePayConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

Java
1. Declare the Google Pay form layout


```xml activity_googlepay_confirm_order.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
    ...
    <!-- Bottom pay button -->
    <LinearLayout ...>

      <!-- Import and declare the Google Pay form -->
        <com.paykka.android.checkout.ui.component.googlepay.GooglePaymentForm
            android:id="@+id/googlePaymentForm"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />

    </LinearLayout>

</LinearLayout>
```

1. Initialize and integrate GooglePaymentSDK


```java GooglePayConfirmOrderActivity.java
package com.paykka.javacheckoutdemo;

import static com.paykka.javacheckoutdemo.AppConf.EXAMPLE_CHECKOUT_SESSION_ID;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.paykka.android.checkout.PayKKa;
import com.paykka.android.checkout.PayKKaConfiguration;
import com.paykka.android.checkout.PayKKaEnv;
import com.paykka.android.checkout.PaymentResult;
import com.paykka.android.checkout.PaymentResultCallback;
import com.paykka.android.checkout.payments.PaymentBase;
import com.paykka.android.checkout.payments.googlepay.GooglePayment;
import com.paykka.android.checkout.ui.component.googlepay.GooglePaymentForm;
import com.paykka.android.utils.gson.JsonObject;

public class GooglePayConfirmOrderActivity extends AppCompatActivity {
    public static final String TAG = "GooglePayConfirmOrderActivity";
    private GooglePayment googlePayment;
    private GooglePaymentForm googlePaymentForm;
    // Initialize your merchant configuration
    private final PayKKaConfiguration APP_CONF = new AppConf();
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_googlepay_confirm_order);
        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX);
        // Initialize UI. Make sure initialization is completed before Activity onCreate
        initializeUi();
    }
    private void initializeUi() {
        googlePaymentForm = findViewById(R.id.googlePaymentForm);
        // (2) Initialize Google Pay
        // Payment must be initialized before Lifecycle enters STARTED
        googlePayment = new GooglePayment(this, googlePaymentForm, this::requestPayment);
    }
    public void requestPayment(View view) {
        googlePaymentForm.showLoadingOverlay();
        // (3) Initiate payment and launch the Google Pay popup
        googlePayment.requestPayment(EXAMPLE_CHECKOUT_SESSION_ID, new PaymentResultCallback() {
            @Override
            public void onResult(PaymentResult paymentResult) {
                // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
                googlePaymentForm.hideLoadingOverlay();
                handlePaymentResult(paymentResult);
            }

            @Override
            public void onError(Throwable e) {
                // You can capture errors in the payment flow here
                // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
                // You can focus on your own error-handling logic in this callback
                googlePaymentForm.hideLoadingOverlay();
                Log.e(TAG, "onError: ", e);
                toast(e.getMessage());
            }

            @Override
            public void onUserCanceled(@Nullable PaymentBase paymentSource, @Nullable JsonObject extraData) {
                // The user canceled the payment and closed the Google Pay popup
                googlePaymentForm.hideLoadingOverlay();
                toast("Google Payment: CANCELED");
            }
        });
    }
    private void handlePaymentResult(PaymentResult paymentResult) {
        Intent intent = new Intent(this, PaymentCompleteActivity.class);
        intent.putExtra("paymentResult", paymentResult.toString());
        startActivity(intent);
        finish();
    }
    private void toast(String message) {
        Toast.makeText(GooglePayConfirmOrderActivity.this, message, Toast.LENGTH_SHORT).show();
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".GooglePayConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

#### WeChat Pay

WeChat payment form examples
Kotlin
1. Declare the WeChat Pay form layout (used to display user-friendly messages)


```kotlin views/WeChatPayConfirmOrderScreen.kt
package com.paykka.kotlincheckoutdemo.views

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.wechatpay.WeChatPaymentForm


@Composable
fun WeChatPayConfirmOrderScreen(
    isPayButtonEnabled: Boolean,
    onPayClick: () -> Unit,
    onFormCreated: (WeChatPaymentForm) -> Unit
) {
    Scaffold(
        bottomBar = {
            Surface(shadowElevation = 8.dp) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 10.dp)
                ) {
                    AndroidView(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 12.dp),
                        factory = { context ->
                            WeChatPaymentForm(context).also { onFormCreated(it) }
                        }
                    )

                    Box(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 12.dp, vertical = 10.dp),
                        contentAlignment = Alignment.Center
                    ) {
                        Button(
                            onClick = onPayClick,
                            enabled = isPayButtonEnabled,
                            modifier = Modifier.fillMaxWidth()
                        ) {
                            Text(text = "WeChat Pay ¥1118.83", fontWeight = FontWeight.Bold)
                        }
                    }
                }
            }
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
                .verticalScroll(rememberScrollState())
                .fillMaxSize()
                .padding(vertical = 12.dp)
        ) {
            OrderCardView()
        }
    }
}
```

1. Initialize and integrate WeChatPaymentSDK


```kotlin WeChatPayConfirmOrderActivity.kt
package com.paykka.kotlincheckoutdemo

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.payments.PaymentBase
import com.paykka.android.checkout.payments.wechatpay.WeChatPayment
import com.paykka.android.checkout.ui.component.wechatpay.WeChatPaymentForm
import com.paykka.android.utils.gson.JsonObject
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.WeChatPayConfirmOrderScreen

class WeChatPayConfirmOrderActivity : ComponentActivity() {
    val TAG = "WeChatPayConfirmOrderActivity"

    private var isPaying by mutableStateOf(false)
    private var isPayEnabled by mutableStateOf(false)

    private lateinit var weChatPaymentForm: WeChatPaymentForm
    private lateinit var weChatPayment: WeChatPayment

    // Initialize your merchant configuration
    private val APP_CONF = AppConf()

    private val payCallback = object : PaymentResultCallback {
        override fun onResult(paymentResult: PaymentResult) {
            // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
            hideLoadingOverlay()
            handlePaymentResult(paymentResult)
        }

        override fun onError(e: Throwable) {
            // You can capture errors in the payment flow here
            // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
            // You can focus on your own error-handling logic in this callback
            Log.e(TAG, "onError: ", e)
            hideLoadingOverlay()
            toast(e.message ?: "Unknown error")
        }

        override fun onUserCanceled(paymentSource: PaymentBase?, extraData: JsonObject?) {
            // The user canceled the payment
            hideLoadingOverlay()
            toast("WeChat Payment: CANCELED")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, AppConf.ENVIRONMENT)

        setContent {
            KotlinCheckoutDemoTheme {
                Box(modifier = Modifier.fillMaxSize()) {
                    WeChatPayConfirmOrderScreen(
                        isPayButtonEnabled = isPayEnabled && !isPaying,
                        onPayClick = { onClickBtnPay() },
                        onFormCreated = { form ->
                            weChatPaymentForm = form
                            initWeChatPayment(form)
                        }
                    )

                    if (isPaying) {
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .background(Color.Black.copy(alpha = 0.25f)),
                            contentAlignment = Alignment.Center
                        ) {
                            CircularProgressIndicator()
                        }
                    }
                }
            }
        }
    }

    private fun initWeChatPayment(form: WeChatPaymentForm) {
        if (this::weChatPayment.isInitialized) return

        // (2) Initialize WeChat PaymentSDK
        weChatPayment = WeChatPayment(this).init(form, EXAMPLE_CHECKOUT_SESSION_ID) {
            // Callback when initialization is complete
            isPayEnabled = true
        }
    }

    private fun onClickBtnPay() {
        if (isPaying) return
        if (!this::weChatPayment.isInitialized) return

        showLoadingOverlay()
        // (3) Initiate payment
        weChatPayment.requestPayment(payCallback)
    }

    private fun showLoadingOverlay() {
        isPaying = true
    }

    private fun hideLoadingOverlay() {
        isPaying = false
    }

    private fun handlePaymentResult(paymentResult: PaymentResult) {
        val intent = Intent(this, PaymentCompleteActivity::class.java)
        intent.putExtra("paymentResult", paymentResult.toString())
        startActivity(intent)
        finish()
    }

    private fun toast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".WeChatPayConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

Java
1. Declare the WeChat Pay form layout (used to display user-friendly messages)


```xml activity_wechatpay_confirm_order.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
    ...

    <!-- Import and declare the WeChat Pay form, used to display error messages and more -->
    <com.paykka.android.checkout.ui.component.wechatpay.WeChatPaymentForm
        android:id="@+id/weChatPaymentForm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingHorizontal="12dp"/>

    </LinearLayout>

</LinearLayout>
```

1. Initialize and integrate WeChatPaymentSDK


```java WeChatPayConfirmOrderActivity.java
package com.paykka.javacheckoutdemo;

import static com.paykka.javacheckoutdemo.AppConf.ENVIRONMENT;
import static com.paykka.javacheckoutdemo.YourMerchantConf.EXAMPLE_CHECKOUT_SESSION_ID;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.paykka.android.checkout.PayKKa;
import com.paykka.android.checkout.PayKKaConfiguration;
import com.paykka.android.checkout.PaymentResult;
import com.paykka.android.checkout.PaymentResultCallback;
import com.paykka.android.checkout.payments.PaymentBase;
import com.paykka.android.checkout.payments.wechatpay.WeChatPayment;
import com.paykka.android.checkout.ui.component.wechatpay.WeChatPaymentForm;
import com.paykka.android.utils.gson.JsonObject;

public class WeChatPayConfirmOrderActivity extends AppCompatActivity {
    public static final String TAG = "WeChatPayConfirmOrderActivity";
    private View weChatPayLoadingOverlay;
    private WeChatPaymentForm weChatPaymentForm;
    private Button btnPay;
    private WeChatPayment weChatPayment;
    // Initialize your merchant configuration
    private final PayKKaConfiguration APP_CONF = new AppConf();
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_wechatpay_confirm_order);
        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, ENVIRONMENT);

        weChatPayLoadingOverlay = findViewById(R.id.wechatPayLoadingOverlay);
        weChatPaymentForm = findViewById(R.id.weChatPaymentForm);
        btnPay = findViewById(R.id.btn_wechat_pay);
        btnPay.setOnClickListener(this::onClickBtnPay);
        btnPay.setEnabled(false);
        // (2) Initialize WeChat PaymentSDK
        weChatPayment = new WeChatPayment(this).init(weChatPaymentForm, EXAMPLE_CHECKOUT_SESSION_ID, () -> {
            // Callback when initialization is complete
            btnPay.setEnabled(true);
        });
    }
    private void onClickBtnPay(View view) {
        showLoadingOverlay();
        // (3) Initiate payment
        weChatPayment.requestPayment(new PaymentResultCallback() {
            @Override
            public void onResult(PaymentResult paymentResult) {
                // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
                handlePaymentResult(paymentResult);
            }

            @Override
            public void onError(Throwable e) {
                // You can capture errors in the payment flow here
                // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
                // You can focus on your own error-handling logic in this callback
                Log.e(TAG, "onError: ", e);
                hideLoadingOverlay();
                toast(e.getMessage());
            }

            @Override
            public void onUserCanceled(@Nullable PaymentBase paymentSource, @Nullable JsonObject extraData) {
                // The user canceled the payment
                toast("WeChat Payment: CANCELED");
            }
        });
    }

    private void handlePaymentResult(PaymentResult paymentResult) {
        Intent intent = new Intent(this, PaymentCompleteActivity.class);
        intent.putExtra("paymentResult", paymentResult.toString());
        startActivity(intent);
        finish();
    }

    private void showLoadingOverlay() {
        btnPay.setEnabled(false);
        weChatPayLoadingOverlay.setVisibility(View.VISIBLE);
    }
    private void hideLoadingOverlay() {
        btnPay.setEnabled(true);
        weChatPayLoadingOverlay.setVisibility(View.GONE);
    }
    private void toast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".WeChatPayConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

#### Alipay+

Alipay+ payment form examples
Kotlin
1. Declare the Alipay+ payment form layout (used to display user-friendly messages)


```kotlin views/AlipayPlusConfirmOrderScreen.kt
package com.paykka.kotlincheckoutdemo.views

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.alipay.AlipayPlusPaymentForm

@Composable
fun AlipayPlusConfirmOrderScreen(
    isPayButtonEnabled: Boolean,
    onPayClick: () -> Unit,
    onFormCreated: (AlipayPlusPaymentForm) -> Unit
) {
    Scaffold(
        bottomBar = {
            Surface(shadowElevation = 8.dp) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 10.dp)
                ) {
                    AndroidView(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 12.dp),
                        factory = { context ->
                            AlipayPlusPaymentForm(context).also { onFormCreated(it) }
                        }
                    )

                    Box(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 12.dp, vertical = 10.dp),
                        contentAlignment = Alignment.Center
                    ) {
                        Button(
                            onClick = onPayClick,
                            enabled = isPayButtonEnabled,
                            modifier = Modifier.fillMaxWidth()
                        ) {
                            Text(text = "Alipay+ Payment ¥1118.83", fontWeight = FontWeight.Bold)
                        }
                    }
                }
            }
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
                .verticalScroll(rememberScrollState())
                .fillMaxSize()
                .padding(vertical = 12.dp)
        ) {
            OrderCardView()
        }
    }
}
```

1. Initialize and integrate AlipayPlusPaymentSDK


```kotlin AlipayPlusConfirmOrderActivity.kt
package com.paykka.kotlincheckoutdemo

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.payments.PaymentBase
import com.paykka.android.checkout.payments.alipay.AlipayPlusPayment
import com.paykka.android.checkout.ui.component.alipay.AlipayPlusPaymentForm
import com.paykka.android.utils.gson.JsonObject
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.AlipayPlusConfirmOrderScreen

class AlipayPlusConfirmOrderActivity : ComponentActivity() {
    val TAG = "AlipayPlusConfirmOrderActivity"

    private var isPaying by mutableStateOf(false)
    private var isPayEnabled by mutableStateOf(false)

    private lateinit var alipayPlusPaymentForm: AlipayPlusPaymentForm
    private lateinit var alipayPlusPayment: AlipayPlusPayment

    // Initialize your merchant configuration
    private val APP_CONF = AppConf()

    private val payCallback = object : PaymentResultCallback {
        override fun onResult(paymentResult: PaymentResult) {
            // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
            hideLoadingOverlay()
            handlePaymentResult(paymentResult)
        }

        override fun onError(e: Throwable) {
            // You can capture errors in the payment flow here
            // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
            // You can focus on your own error-handling logic in this callback
            Log.e(TAG, "onError: ", e)
            hideLoadingOverlay()
            toast(e.message ?: "Unknown error")
        }

        override fun onUserCanceled(paymentSource: PaymentBase?, extraData: JsonObject?) {
            // The user canceled the payment and closed the Alipay+ payment popup
            hideLoadingOverlay()
            toast("AlipayPlus Payment: CANCELED")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, AppConf.ENVIRONMENT)

        setContent {
            KotlinCheckoutDemoTheme {
                Box(modifier = Modifier.fillMaxSize()) {
                    AlipayPlusConfirmOrderScreen(
                        isPayButtonEnabled = isPayEnabled && !isPaying,
                        onPayClick = { onClickBtnPay() },
                        onFormCreated = { form ->
                            alipayPlusPaymentForm = form
                            initAlipayPlusPayment(form)
                        }
                    )

                    if (isPaying) {
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .background(Color.Black.copy(alpha = 0.25f)),
                            contentAlignment = Alignment.Center
                        ) {
                            CircularProgressIndicator()
                        }
                    }
                }
            }
        }
    }

    private fun initAlipayPlusPayment(form: AlipayPlusPaymentForm) {
        if (this::alipayPlusPayment.isInitialized) return

        // (2) Initialize Alipay+ PaymentSDK
        alipayPlusPayment = AlipayPlusPayment(this).init(form, EXAMPLE_CHECKOUT_SESSION_ID) {
            isPayEnabled = true
        }
    }

    private fun onClickBtnPay() {
        if (isPaying) return
        if (!this::alipayPlusPayment.isInitialized) return

        showLoadingOverlay()
        // (3) Initiate payment
        alipayPlusPayment.requestPayment(payCallback)
    }

    private fun showLoadingOverlay() {
        isPaying = true
    }

    private fun hideLoadingOverlay() {
        isPaying = false
    }

    private fun handlePaymentResult(paymentResult: PaymentResult) {
        val intent = Intent(this, PaymentCompleteActivity::class.java)
        intent.putExtra("paymentResult", paymentResult.toString())
        startActivity(intent)
        finish()
    }

    private fun toast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".AlipayPlusConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

Java
1. Declare the Alipay+ payment form layout (used to display user-friendly messages)


```xml activity_alipay_plus_confirm_order.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
    ...

    <!-- Import and declare the Alipay+ payment form, used to display error messages and more -->
    <com.paykka.android.checkout.ui.component.alipay.AlipayPlusPaymentForm
        android:id="@+id/alipayPlusPaymentForm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingHorizontal="12dp"/>

    </LinearLayout>

</LinearLayout>
```

1. Initialize and integrate AlipayPlusPaymentSDK


```java AlipayPlusConfirmOrderActivity.java
package com.paykka.javacheckoutdemo;

import static com.paykka.javacheckoutdemo.AppConf.ENVIRONMENT;
import static com.paykka.javacheckoutdemo.YourMerchantConf.EXAMPLE_CHECKOUT_SESSION_ID;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.paykka.android.checkout.PayKKa;
import com.paykka.android.checkout.PayKKaConfiguration;
import com.paykka.android.checkout.PaymentResult;
import com.paykka.android.checkout.PaymentResultCallback;
import com.paykka.android.checkout.payments.PaymentBase;
import com.paykka.android.checkout.payments.alipay.AlipayPlusPayment;
import com.paykka.android.checkout.ui.component.alipay.AlipayPlusPaymentForm;
import com.paykka.android.utils.gson.JsonObject;

public class AlipayPlusConfirmOrderActivity extends AppCompatActivity {
    public static final String TAG = "alipayPlusConfirmOrderActivity";
    private View alipayPlusLoadingOverlay;
    private AlipayPlusPaymentForm alipayPlusPaymentForm;
    private Button btnPay;
    private AlipayPlusPayment alipayPlusPayment;
    // Initialize your merchant configuration
    private final PayKKaConfiguration APP_CONF = new AppConf();
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_alipay_plus_confirm_order);
        // (1) You must initialize the PayKKa SDK in advance
        PayKKa.init(this, APP_CONF, ENVIRONMENT);

        alipayPlusLoadingOverlay = findViewById(R.id.alipayPlusLoadingOverlay);
        alipayPlusPaymentForm = findViewById(R.id.alipayPlusPaymentForm);
        btnPay = findViewById(R.id.btn_alipay_plus);
        btnPay.setOnClickListener(this::onClickBtnPay);
        btnPay.setEnabled(false);

        // (2) Initialize Alipay+ PaymentSDK
        alipayPlusPayment = new AlipayPlusPayment(this).init(alipayPlusPaymentForm, EXAMPLE_CHECKOUT_SESSION_ID, () -> {
            btnPay.setEnabled(true);
        });
    }
    private void onClickBtnPay(View view) {
        showLoadingOverlay();
        // (3) Initiate payment
        alipayPlusPayment.requestPayment(new PaymentResultCallback() {
            @Override
            public void onResult(PaymentResult paymentResult) {
                // Callback after payment is completed; triggered only after EXPIRED/ SUCCESS
                handlePaymentResult(paymentResult);
            }

            @Override
            public void onError(Throwable e) {
                // You can capture errors in the payment flow here
                // By default, the SDK prints errors in the UI and shows a user-friendly message explaining the cause
                // You can focus on your own error-handling logic in this callback
                Log.e(TAG, "onError: ", e);
                hideLoadingOverlay();
                toast(e.getMessage());
            }

            @Override
            public void onUserCanceled(@Nullable PaymentBase paymentSource, @Nullable JsonObject extraData) {
                // The user canceled the payment and closed the Alipay+ payment popup
                toast("AlipayPlus Payment: CANCELED");
            }
        });
    }

    private void handlePaymentResult(PaymentResult paymentResult) {
        Intent intent = new Intent(this, PaymentCompleteActivity.class);
        intent.putExtra("paymentResult", paymentResult.toString());
        startActivity(intent);
        finish();
    }

    private void showLoadingOverlay() {
        btnPay.setEnabled(false);
        alipayPlusLoadingOverlay.setVisibility(View.VISIBLE);
    }
    private void hideLoadingOverlay() {
        btnPay.setEnabled(true);
        alipayPlusLoadingOverlay.setVisibility(View.GONE);
    }
    private void toast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}
```

```xml res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.KotlinCheckoutDemo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
      ...
    </style>

    <style name="Theme.DefaultNoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
    </style>

    <!-- Note: various SDK components rely on AppCompat/Material, so make sure the outermost Activity theme inherits from MaterialComponents -->
    <style name="Theme.ConfirmOrder" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>

    <style name="Theme.PaymentComplete" parent="Theme.DefaultNoActionBar">
        <item name="android:windowFullscreen">false</item>
    </style>
</resources>
```

```xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.paykka.javacheckoutdemo">

    ...
    <application ...>
        <!-- Make sure the outer Activity theme inherits from MaterialComponents -->
        <activity android:name=".AlipayPlusConfirmOrderActivity" android:theme="@style/Theme.ConfirmOrder" android:exported="false" />
        ...
    </application>

</manifest>
```

## Step 4: Test Payments

You can use test cards in the PayKKa sandbox environment (`SANDBOX`) to test payments, and then switch to the `PROD` environment when packaging the release version of your app.

Kotlin
```kotlin
// You can specify the payment environment to use in the init method
PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)

/// SANDBOX (default)
PayKKa.useEnv(PayKKaEnv.SANDBOX)
/// Switch to the EU production environment
PayKKa.useEnv(PayKKaEnv.PROD_EU)
/// Switch to the HK production environment
PayKKa.useEnv(PayKKaEnv.PROD_HK)
```

Java
```java
// You can specify the payment environment to use in the init method
PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX);

/// SANDBOX (default)
PayKKa.useEnv(PayKKaEnv.SANDBOX);
/// Switch to the EU production environment
PayKKa.useEnv(PayKKaEnv.PROD_EU);
/// Switch to the HK production environment
PayKKa.useEnv(PayKKaEnv.PROD_HK);
```

## Notes

### Google Pay

#### 1. How do I use test cards for payment testing?

Please refer to: [Payment Method - Google Pay](/payments/docs/payment-method/google-pay/google-pay#testing-google-pay).

#### 2. Google Pay cannot accept payments in the PROD environment and shows "OR_BIBED_11"

Google Pay uses the test environment and mock data in the sandbox; however, after going live, it processes real user payment card information. To protect the funds and privacy of both users and merchants, Google requires:

- A supported **payment gateway** (for example, PayKKa), or
- A PCI DSS-compliant (Payment Card Industry Data Security Standard) environment in order to apply for production access. This ensures that you are capable of securely handling sensitive payment information, rather than operating only in development and test mode.


Therefore, you also need to complete the [Google Pay - Publish your integration](https://developers.google.com/pay/api/android/guides/test-and-deploy/publish-your-integration) steps.