POS

Vipaso Pos SDK makes it easy to add both online and offline payment capabilities via BLE to your Android app with simple merchant authentication and transaction handling

Table of Contents

Prerequisites

SDK Version Requirements

  • Android Compile SDK: 34 (Your app App Compile SDK must be 34 or higher)
  • Android Target SDK = 34
  • Android Min SDK: 26 (Your app App Min SDK must be 26 or higher)

Required Permissions

The following permissions must be added to your app's AndroidManifest.xml file:

<!-- BLE feature requirement -->
<uses-feature
    android:name="android.hardware.bluetooth_le"
    android:required="true" />

<!-- Bluetooth permissions -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- Location permissions (for Android 10 and below) -->
<uses-permission
    android:name="android.permission.ACCESS_FINE_LOCATION"
    android:maxSdkVersion="30" />
<uses-permission
    android:name="android.permission.ACCESS_COARSE_LOCATION"
    android:maxSdkVersion="30" />

<!-- Bluetooth permissions for Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission
    android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation"
    tools:targetApi="s" />

<!-- Network permission -->
<uses-permission android:name="android.permission.INTERNET" />

For Android 12 (API level 31) and above, you will need to request runtime permissions for BLUETOOTH_SCAN and BLUETOOTH_CONNECT. For Android 11 and below, ACCESS_FINE_LOCATION permission is required for BLE scanning. Make sure to properly request these permissions in your app before using the BLE payment functionality.

Setup and Configuration

Initialize the SDK

The recommended way is to initialize the Vipaso SDK using the VipasoSdkInitializer singleton object. This will set up all necessary components and allow you to interact with the SDK features.

When initializing the SDK, you need to:

  1. First initialize the SDK with the appropriate mode
  2. Then set the configuration parameters
// Step 1: Initialize the SDK with the appropriate mode
VipasoSdkInitializer.initialize(context, VipasoSdkMode.Pos)

/*
Possible SDK modes:
- VipasoSdkMode.Pos: Pos mode (for merchants to request payments)
- VipasoSdkMode.Pay: Pay mode (for users to receive payments and pay)
- VipasoSdkMode.Full: Full mode (includes both Pos and Pay features)
*/

// Step 2: Create and set the SDK configuration
val sdkConfig = VipasoSdkConfig(
    baseUrl = "https://your.base.path",
    bleUuidService = UUID.fromString("your-service-uuid"),
    emailSupport = "[email protected]", // Optional
    timeoutPaymentStatusBackendPollingDuration = 60.seconds // Optional, defaults to 60 seconds
)

// Set the SDK configuration, you need to set this before using any of the SDK features
VipasoSdkInitializer.setSdkConfig(sdkConfig)

Accessing the SDK

The Vipaso POS SDK uses Koin for internal dependency injection. Depending on your application's DI framework, you can access the SDK in one of the following ways:

Option 1: With Dependency Injection (Koin)

As the SDK uses Koin for internal dependency injection, you can directly access SDK dependencies:

// Access VipasoPos directly from SDK's Koin container
val vipasoPos by SdkKoinProvider.koin.inject<VipasoPos>()

Option 2: With Dependency Injection (Dagger/Hilt)

If your application uses Hilt/Dagger for dependency injection, you need to re-provide the SDK dependencies:

// Re-providing dependencies from Koin DI to Hilt DI framework
// since the SDK uses Koin internally
@Module
@InstallIn(SingletonComponent::class)
class VipasoPosModule {
    @Provides
    @Singleton
    fun provideVipasoPos(): VipasoPos {
        val vipasoPos by SdkKoinProvider.koin.inject<VipasoPos>()
        return vipasoPos
    }
}

Option 3: Without Dependency Injection

If your app doesn't use dependency injection, you can initialize the SDK directly:

// it's possible to initialize SDK directly
val vipasoPos = VipasoPosImpl()

// Access the main APIs
val managedAuthApi: PosManagedAuthApi = vipasoPos.managedAuth
val delegatedAuthApi: PosDelegatedAuthApi = vipasoPos.delegatedAuth
val userApi: PosUserApi = vipasoPos.user
val paymentApi: PosPaymentApi = vipasoPos.payment

Error Handling

All SDK methods that can throw exceptions should be wrapped in try-catch blocks to handle potential errors:

try {
    vipasoPos.payment.requestPaymentViaBle(amount, currency, merchantId, operatorId)
} catch (throwable: Throwable) {
    // Handle the error
    Log.e("VipasoError", "Error processing payment", throwable)
}

SDK Overview

The Vipaso POS SDK exposes its functionality through a set of specialized APIs, each handling a specific domain:

Authentication APIs

The SDK provides two different authentication approaches:

  1. Managed Authentication: When your app wants to use Vipaso's authentication system and identity provider.
  2. Delegated Authentication: When your app uses its own authentication system and identity provider.

PosManagedAuthApi

Handles merchant authentication when using Vipaso's identity provider:

  • login() - Authenticate and login a merchant operator

PosDelegatedAuthApi

Handles authentication when your app uses its own identity provider:

PosUserApi

Manages merchant information and login state:

PosPaymentApi

Processes payments and maintains payment history:

Managed Authentication (PosManagedAuthApi)

The PosManagedAuthApi handles merchant operator authentication when using Vipaso's identity provider. Choose this approach when you want Vipaso to handle merchant authentication completely.

Login

Authenticate and login a merchant operator:

// Request: Login a merchant operator
val loginRequest = LoginRequest(
    userIdentifer = "[email protected]", // Can be email or phone number
    password = "secure_password"
)

vipasoPos.managedAuth.login(loginRequest)

// You can observe login state changes via user.getUserLoginState()

Delegated Authentication (PosDelegatedAuthApi)

The PosDelegatedAuthApi enables integration with your own authentication system. Choose this approach when your app already has merchant accounts and you want to maintain your existing identity management while using Vipaso's payment features.

Establish Authentication

// Request: Establish authentication between your own identity provider and Vipaso SDK
vipasoPos.delegatedAuth.establishAuthentication(
    // The unique identifier for the merchant operator in your system
    userIdentifier = "merchant-op-123",
    
    // A callback that receives a JWK (provided by Vipaso SDK) as input and outputs a string (you get from your backend)
    connect = { jwk ->
        // 1. Send the JWK to a dedicated backend endpoint provided on your domain
        // 2. And then return the output string received from your backend
    }
)

// Once this call returns successfully then you can use all the SDK features

User Management (PosUserApi)

The PosUserApi manages merchant profile information, login state tracking, and merchant-specific features.

Logout

Log out the current merchant operator:

// Request: Logout current operator
vipasoPos.user.logout()

// You can observe login state changes via getUserLoginState()

Fetch Merchant

Retrieve information about the merchant associated with the current operator:

// Request: Get merchant information
val merchant = vipasoPos.user.fetchMerchant()

// Response: Merchant information
VipasoMerchant(
    id = "merchant-123",
    name = "Coffee Shop",
    operatorId = "operator-456",
    currency = "USD"
)

Fetch Feature Flags

// Request: Get feature flags for the current merchant
val featureFlags = vipasoPos.user.fetchFeatureFlags()

// Response: Set of feature flags with their states
Set<VipasoFeatureFlagWithState>(
    VipasoFeatureFlagWithState(
        featureFlag = "feature_name_1",
        isEnabled = true
    ),
    VipasoFeatureFlagWithState(
        featureFlag = "feature_name_2",
        isEnabled = false
    )
)

Get User Login State

Get the current login state as a Flow that emits updates when the login state changes:

// Request: Observe login state changes
val loginStateFlow = vipasoPos.user.getUserLoginState()

// Collect login state changes
lifecycleScope.launch {
    loginStateFlow.collect { loginState ->
        when (loginState) {
            LoginState.LoggedIn -> {
                // User is logged in, show main content
                showMainContent()
            }
            LoginState.NotLoggedIn -> {
                // User is not logged in, show login screen
                showLoginScreen()
            }
        }
    }
}

Payment Processing (PosPaymentApi)

The PosPaymentApi handles all payment-related operations for POS terminals.

Fetch Payments

Retrieve a paginated list of payment transactions with optional filtering by status:

// Request: Get list of payment transactions
val payments = vipasoPos.payment.fetchPayments(
    page = 0,
    status = VipasoPaymentStatus.COMPLETED
)

// Response: List of payment transactions
FetchPaymentsResponse(
    payments = [
        VipasoPayment(
            paymentId = "payment-123",
            amount = 25.50,
            currency = "USD",
            unifiedStatus = VipasoPaymentStatus.COMPLETED,
            timestamp = 1623412345678,
            instrumentId = "card-123",
            instrumentType = "CARD",
            description = "Coffee and sandwich",
            lastUpdate = "2025-06-03T09:25:48Z",
            merchant = VipasoMerchant(
                merchantId = "merchant-456",
                name = "Coffee Shop"
            ),
            consumer = VipasoConsumer(
                id = "user-123",
                name = "John Doe"
            )
        ),
        // Additional payments...
    ]
)

/*
Possible VipasoPaymentStatus values:
- INITIATED: Payment has been created but processing hasn't started
- IN_PROGRESS: Payment is currently being processed
- COMPLETED: Payment has been successfully completed and funds transferred
- FAILED: Payment processing failed due to an error
- CANCELLED: Payment was cancelled by the user, merchant, or system
*/

Fetch Payment

Retrieve details for a specific payment transaction:

// Request: Get details for a specific payment
val paymentDetails = vipasoPos.payment.fetchPayment(
    FetchPaymentDetailsRequest("payment-123")
)

// Response: Payment details
FetchPaymentDetailsResponse(
    payment = VipasoPayment(
        paymentId = "payment-123",
        amount = 25.50,
        currency = "USD",
        unifiedStatus = VipasoPaymentStatus.COMPLETED,
        timestamp = 1623412345678,
        instrumentId = "card-123",
        instrumentType = "CARD",
        description = "Coffee and sandwich",
        lastUpdate = "2025-06-03T09:25:48Z",
        merchant = VipasoMerchant(
            merchantId = "merchant-456",
            name = "Coffee Shop"
        ),
        consumer = VipasoConsumer(
            id = "user-123",
            name = "John Doe"
        )
    )
)

/*
Possible VipasoPaymentStatus values:
- INITIATED: Payment has been created but processing hasn't started
- IN_PROGRESS: Payment is currently being processed
- COMPLETED: Payment has been successfully completed and funds transferred
- FAILED: Payment processing failed due to an error
- CANCELLED: Payment was cancelled by the user, merchant, or system
*/

Request Payment via BLE

Initiate a payment request via BLE to nearby Vipaso Pay users. This method sends a payment request to all potential payers in range and returns when the first payer successfully accepts the payment:

You can get merchantId and operatorId from the fetchMerchant()

// Request: Initiate a payment request via BLE
try {
    val paymentResult = vipasoPos.payment.requestPaymentViaBle(
        amount = "25.50",
        currency = "USD",
        merchantId = "merchant-123",
        operatorId = "operator-456"
    )
    
    // Process the payment result based on its type
    when (paymentResult) {
        is VipasoPosPaymentResult.PaymentSuccessful -> {
            // Payment was successfully processed
            val amount = paymentResult.amount // "25.50"
            val tip = paymentResult.tip      // "2.50"
            val currency = paymentResult.currency // "USD"
            val paymentId = paymentResult.paymentId
            
            // Update UI with successful payment
            displayPaymentSuccess(amount, tip, currency, paymentId)
        }
        is VipasoPosPaymentResult.PaymentCancelled -> {
            // Payment was cancelled by the operator or payer
            val paymentId = paymentResult.paymentId
            
            // Update UI to show payment was cancelled
            displayPaymentCancelled(paymentId)
        }
        is VipasoPosPaymentResult.PaymentFailed -> {
            // Payment processing failed
            val paymentId = paymentResult.paymentId
            val error = paymentResult.error
            
            // Handle payment failure, display error message
            displayPaymentError(paymentId, error)
        }
    }
} catch (e: Exception) {
    // Handle exceptions during payment initialization
    Log.e("PaymentError", "Failed to initiate payment", e)
}

Cancel Payment Request

Cancel an ongoing payment operation that was initiated via BLE:

// Request: Cancel an ongoing payment
vipasoPos.payment.cancelPayment()

// The payment will be terminated and any Pay Devices that were already connected will receive a PaymentCancelled state.

Stop BLE

When you not interested in request payments anymore or when closing the app, it's recommended to stop the BLE payment service and clean up resources:

// Request: Terminate all BLE payment operations immediately and clean up resources
vipasoPos.payment.stopBle()