> ## Documentation Index
> Fetch the complete documentation index at: https://walletconnect-pay-docs-tomiir-buyer-checkout-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# WalletConnect POS SDK - Kotlin Installation

> WalletConnect for POS is an SDK that lets merchants accept stablecoin payments with a checkout experience on par with NFC tap‑to‑pay. The SDK handles WalletConnect session lifecycles, building blockchain specific transactions, UI callbacks and transaction verification.

## User flow

1. Cashier selects "Pay with Crypto" on the POS.
2. POS shows a WalletConnect QR code with the payment intent.
3. Customer scans the QR code with their wallet.
4. Wallet displays the payment amount and the customer approves.
5. The transaction is broadcasted by the wallet.
6. POS shows confirmation when payment is successful.

## SDK Methods

The WalletConnect POS SDK exposes methods and callbacks to create smooth payment flows.

<Note>
  The methods here are shown in isolation, for a full guide on how to use them follow the step by step guide at the bottom.
</Note>

### Initialization

To initialize the SDK, call the `init` method with your API credentials:

```kotlin theme={null}
PosClient.init(
    apiKey = "YOUR_MERCHANT_API_KEY",
    merchantId = "YOUR_MERCHANT_ID",
    deviceId = "unique_device_identifier"
)
```

Where the initialization parameters are:

| Parameter  | Type   | Description                             |
| ---------- | ------ | --------------------------------------- |
| apiKey     | String | Your WalletConnect Pay merchant API key |
| merchantId | String | Your merchant identifier                |
| deviceId   | String | Unique identifier for this POS device   |

<Warning>
  All parameters are **required** and cannot be blank. The SDK will throw `IllegalStateException` if validation fails.
</Warning>

### Payment Intent

A payment intent specifies the amount to be paid and the currency. **Currently, only USD is supported**.

```kotlin theme={null}
PosClient.createPaymentIntent(
    amount = Pos.Amount(
        unit = "iso4217/USD", // ISO 4217 currency format
        value = "1000"        // Amount in minor units (cents)
    ),
    referenceId = "ORDER-12345" // Your internal order reference
)
```

Where the `Pos.Amount` has the following fields:

| Parameter   | Type   | Description                                                                                  |
| ----------- | ------ | -------------------------------------------------------------------------------------------- |
| unit        | String | Currency in ISO 4217 format: `"iso4217/{CURRENCY}"` (e.g., `"iso4217/USD"`, `"iso4217/EUR"`) |
| value       | String | Amount in **minor units** (e.g., `"1000"` = \$10.00 USD)                                     |
| referenceId | String | Merchant's reference ID for this payment (can be empty)                                      |

**What it does under the hood:**

* Creates the payment intent on the server
* Generates the connection URL and QR code
* Sends the connection proposal to the wallet
* Awaits the connection result
* Builds and sends the transaction to the wallet
* Awaits the transaction result from the wallet
* Polls the transaction status until completion

### Payment intent life cycle

The WalletConnect POS SDK emits events through a delegate that allow you to adapt the POS UI depending on the status of the payment.

```kotlin theme={null}
sealed interface PaymentEvent {
    // Payment created, QR code ready
    data class PaymentCreated(
        val uri: URI,         // URI for QR code generation
        val amount: Amount,   // Payment amount
        val paymentId: String // Unique payment identifier
    ) : PaymentEvent

    // Customer scanned QR and initiated payment
    data object PaymentRequested : PaymentEvent

    // Payment is being processed on-chain
    data object PaymentProcessing : PaymentEvent

    // Payment completed successfully
    data class PaymentSuccess(
        val paymentId: String
    ) : PaymentEvent

    // Payment failed
    sealed interface PaymentError : PaymentEvent {
        data class CreatePaymentFailed(val message: String) : PaymentError
        data class PaymentFailed(val message: String) : PaymentError
        data class PaymentNotFound(val message: String) : PaymentError
        data class PaymentExpired(val message: String) : PaymentError
        data class InvalidPaymentRequest(val message: String) : PaymentError
        data class Undefined(val message: String) : PaymentError
    }
}

fun interface POSDelegate {
    fun onEvent(event: Pos.PaymentEvent)
}

PosClient.setDelegate(POSDelegate)
```

**Event Flow:**

PaymentCreated → PaymentRequested → PaymentProcessing → PaymentSuccess
↘ PaymentError

## How to Integrate the POS SDK

Below are the steps to integrate the Kotlin POS SDK into your project.

### Prerequisites

To use the POS SDK you will need the following:

* **API Key and Merchant ID** from WalletConnect Pay
* **Latest SDK** (WalletConnect Kotlin): `com.walletconnect:pos:0.0.1`
* **Requirements:** Android min SDK 23

### Integration Steps

<Steps>
  <Step title="Add the Kotlin POS SDK dependencies" titleSize="h3">
    First, add the dependencies to your project's `build.gradle.kts` file as shown below:

    ```kotlin /app/build.gradle.kts theme={null}
    dependencies {
        implementation("com.walletconnect:pos:0.0.1")
    }
    ```
  </Step>

  <Step title="Initialize the POS client" titleSize="h3">
    Initialize the POS client in your `Application` class:

    ```kotlin theme={null}
    class MyApp : Application() {
        override fun onCreate() {
            super.onCreate()

            PosClient.init(
                apiKey = "YOUR_MERCHANT_API_KEY",
                merchantId = "YOUR_MERCHANT_ID",
                deviceId = "unique_device_identifier"
            )
        }
    }
    ```
  </Step>

  <Step title="Set up the delegate" titleSize="h3">
    It's important that you register the delegate before triggering a payment, or you risk missing early events like `PaymentCreated`.

    Create a delegate to receive payment events:

    ```kotlin theme={null}
    object MyPaymentDelegate : POSDelegate {
        private val scope = CoroutineScope(
            SupervisorJob() + Dispatchers.IO
        )
        private val _paymentEvents = MutableSharedFlow<Pos.PaymentEvent>()
        val paymentEvents = _paymentEvents.asSharedFlow()

        override fun onEvent(event: Pos.PaymentEvent) {
            scope.launch {
                _paymentEvents.emit(event)
            }
        }
    }
    ```

    Register the delegate after initialization:

    ```kotlin theme={null}
    PosClient.setDelegate(MyPaymentDelegate)
    ```
  </Step>

  <Step title="Initiate the payment" titleSize="h3">
    Now, you can create a payment intent and initiate the payment:

    ```kotlin theme={null}
    PosClient.createPaymentIntent(
        amount = Pos.Amount(
            unit = "iso4217/USD",
            value = "1000" // $10.00 USD in cents
        ),
        referenceId = "ORDER-12345"
    )
    ```
  </Step>

  <Step title="Render QR code and handle events" titleSize="h3">
    Handle the `PaymentCreated` event to display the QR code. You can use the `uri` from the event to generate a QR code. On successful connection, you'll receive `PaymentRequested` which you can use to show a loading spinner indicating that the payment request is being processed.

    **Projects need to create their own UI.**

    Example event handling in a ViewModel:

    ```kotlin theme={null}
    class PaymentViewModel : ViewModel() {
        init {
            viewModelScope.launch {
                MyPaymentDelegate.paymentEvents.collect { event ->
                    handlePaymentEvent(event)
                }
            }
        }

        private fun handlePaymentEvent(event: Pos.PaymentEvent) {
            when (event) {
                is Pos.PaymentEvent.PaymentCreated -> {
                    // Display QR code using event.uri
                    // Show amount: event.amount.format() → "10.00 USD"
                    // Store payment ID: event.paymentId
                }
                is Pos.PaymentEvent.PaymentRequested -> {
                    // Customer scanned QR - update UI to show "Processing..."
                }
                is Pos.PaymentEvent.PaymentProcessing -> {
                    // Transaction submitted on-chain - show "Confirming..."
                }
                is Pos.PaymentEvent.PaymentSuccess -> {
                    // Payment complete! Show success screen
                }
                is Pos.PaymentEvent.PaymentError -> {
                    handleError(event)
                }
            }
        }
    }
    ```
  </Step>

  <Step title="Observe payment status" titleSize="h3">
    The SDK automatically polls for payment status and emits events through the delegate:

    * `PaymentRequested` → Customer scanned QR and initiated payment
    * `PaymentProcessing` → Transaction submitted on-chain
    * `PaymentSuccess` → Payment confirmed and completed
    * `PaymentError` → Payment failed (see Error Handling below)

    Depending on the different status, it's recommended to adapt the UI to show feedback to the user.
  </Step>
</Steps>

## Error Handling

Handle payment errors by checking the `PaymentError` event type:

```kotlin theme={null}
private fun handleError(error: Pos.PaymentEvent.PaymentError) {
    val message = when (error) {
        is Pos.PaymentEvent.PaymentError.CreatePaymentFailed ->
            "Failed to create payment: ${error.message}"
        is Pos.PaymentEvent.PaymentError.PaymentFailed ->
            "Payment failed: ${error.message}"
        is Pos.PaymentEvent.PaymentError.PaymentNotFound ->
            "Payment not found: ${error.message}"
        is Pos.PaymentEvent.PaymentError.PaymentExpired ->
            "Payment expired - please try again"
        is Pos.PaymentEvent.PaymentError.InvalidPaymentRequest ->
            "Invalid request: ${error.message}"
        is Pos.PaymentEvent.PaymentError.Undefined ->
            "Unexpected error: ${error.message}"
    }
    showErrorToUser(message)
}
```

## Additional Methods

### Cancel an Active Payment

Call when user cancels the payment flow:

```kotlin theme={null}
PosClient.cancelPayment()
```

This stops any active polling and allows starting a new payment.

### Check Payment Status

For checking status without polling (one-off check):

```kotlin theme={null}
viewModelScope.launch {
    val status = PosClient.checkPaymentStatus(paymentId)
    when (status) {
        is Pos.PaymentEvent.PaymentSuccess -> {
            /* Payment successful */
        }
        is Pos.PaymentEvent.PaymentProcessing -> {
            /* Payment processing */
        }
        is Pos.PaymentEvent.PaymentError -> {
            /* Handle error */
        }
    }
}
```

### Amount Formatting

The `Pos.Amount` class provides a formatting helper:

```kotlin theme={null}
val amount = Pos.Amount(unit = "iso4217/USD", value = "1050")
amount.format() // Returns: "10.50 USD"
```

### Shutdown SDK

Call when SDK is no longer needed (e.g., app termination):

```kotlin theme={null}
PosClient.shutdown()
```

## API Reference Summary

| Method                                               | Description                                          |
| ---------------------------------------------------- | ---------------------------------------------------- |
| `PosClient.init(apiKey, merchantId, deviceId)`       | Initialize the SDK (required before any other calls) |
| `PosClient.setDelegate(delegate)`                    | Set delegate for receiving payment events            |
| `PosClient.createPaymentIntent(amount, referenceId)` | Create payment and start polling                     |
| `PosClient.cancelPayment()`                          | Cancel active payment/polling                        |
| `PosClient.checkPaymentStatus(paymentId)`            | One-off status check (suspend function)              |
| `PosClient.shutdown()`                               | Release all SDK resources                            |

## Important Notes

1. **Initialize once** - Call `init()` only once, typically in `Application.onCreate()`

2. **Set delegate early** - Set delegate before creating payments to avoid missing events

3. **Minor units** - Amount value is in minor units (cents for USD, not dollars)

4. **Cancel before new payment** - Call `cancelPayment()` if user exits payment flow

5. **Thread safety** - Events are delivered on IO dispatcher; update UI on main thread
