Pay

Vipaso Pay SDK makes it easy to add both online and offline payment capabilities via BLE to your Android app with simple user authentication, payment methods, 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 34 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_ADVERTISE"
    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_ADVERTISE 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.Pay)

/*
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 Pay 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: Using Dependency Injection (Koin)

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

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

Option 2: Using 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 VipasoPayModule {
    @Provides
    @Singleton
    fun provideVipasoPay(): VipasoPay {
        val vipasoPay by SdkKoinProvider.koin.inject<VipasoPay>()
        return vipasoPay
    }
}

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 vipasoPay = VipasoPayImpl()

Error Handling

When calling SDK methods, wrap them in try/catch blocks to handle exceptions properly. All SDK operations may throw appropriate exceptions when an unexpected error occurs. Implementing proper error handling ensures your application responds gracefully to network issues, BLE connection errors, or other errors.

try {
   val user = vipasoPay.managedAuth.fetchUser()
   // Process user data
} catch (e: Throwable) {
   // Handle unexpected errors
}

SDK Overview

The Vipaso Pay SDK is structured around a main interface called VipasoPay, which serves as the entry point to all SDK functionality. After initialization, you can access the main APIs through this interface:

// Obtain the VipasoPay instance through your preferred method
val vipasoPay: VipasoPay = ... 

// Access the main APIs
val managedAuthApi: PayManagedAuthApi = vipasoPay.managedAuth
val delegatedAuthApi: PayDelegatedAuthApi = vipasoPay.delegatedAuth
val userApi: PayUserApi = vipasoPay.user
val instrumentApi: PayInstrumentApi = vipasoPay.instrument
val paymentApi: PayPaymentApi = vipasoPay.payment

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.

PayManagedAuthApi

Handles user authentication when using Vipaso's identity provider and account management:

PayDelegatedAuthApi

Handles authentication when your app uses its own identity provider:

PayUserApi

Manages user profile information, login state and user-specific features:

PayInstrumentApi

Manages payment instruments like cards, MNO, and third-party instruments:

PayPaymentApi

Processes payments and maintains payment history:

Managed Authentication (PayManagedAuthApi)

The PayManagedAuthApi handles all authentication-related functionality when using Vipaso's identity provider, including user login, registration, and account recovery. Choose this approach when you want Vipaso to handle user authentication completely.

The PayManagedAuthApi handles all authentication-related functionality when using Vipaso's identity provider, including user login, registration, and account recovery. Choose this approach when you want Vipaso to handle user authentication completely.

User Authentication

Login

Once a user has an account, logging in is straightforward. You pass the phone number or email as the identifier along with a password. The SDK will store the session token securely, and the auth state listeners will be notified of the change.

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

vipasoPay.managedAuth.login(loginRequest)

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

Account Recovery

Password recovery consists of three steps, each requiring a network call.

Start Recovery Flow

First, initiate the recovery flow with the user's phone number. This will trigger an OTP code to be sent to that number.

// Request: Begin account recovery
val recoveryRequest = StartRecoveryRequest(phoneNumber = "+1234567890")
val recoveryResponse = vipasoPay.managedAuth.startRecoveryFlow(recoveryRequest)

// Response: Contains the flow ID needed for subsequent steps
StartRecoveryResponse(
    flowId = "recovery-flow-123"
)

Verify Recovery OTP

Next, verify the OTP code received by the user. Send the flow ID from the previous step along with the OTP code.

// Request: Verify OTP code during recovery
val recoveryOtpResponse = vipasoPay.managedAuth.verifyRecoveryOtp(
    flowId = "recovery-flow-123",
    otpCode = "123456"
)

// Response: Contains settings flow ID and session token needed for password update
RecoveryOtpResponse(
    settingsFlowId = "settings-flow-abc",
    sessionToken = "session-token-xyz"
)

Finalize Recovery Flow

Finally, complete the recovery process by updating the password. Use the settings flow ID and session token obtained in the previous step.

// Request: Complete recovery with new password
val passwordRequest = UpdatePasswordRequest(
    settingFlowId = "settings-flow-abc",
    sessionToken = "session-token-xyz",
    password = "new_secure_password"
)

vipasoPay.managedAuth.finalizeRecoveryFlow(passwordRequest)

// The login state will be updated if recovery is successful
// You can observe login state changes via getUserLoginState()

User Registration

The registration flow consists of three steps, each requiring a network call.

Start Signup Flow

First, you need to create a new registration flow with a phone number. This will trigger an OTP code to be sent to the user's phone number via SMS or WhatsApp.

// Request: Begin user registration with phone number
val signupRequest = StartSignupRequest(phoneNumber = "+1234567890")
val signupResponse = vipasoPay.managedAuth.startSignupFlow(signupRequest)

// Response: Contains the flow ID needed for subsequent steps
StartSignupResponse(
    flowId = "signup-flow-456"
)

Verify Signup OTP

Next, verify the phone number by sending the flow ID received in the previous step along with the OTP code that the user received.

// Request: Verify phone number during signup using OTP
val verifyRequest = VerifyPhoneNumberRequest(
    flowId = "signup-flow-456",
    otp = "123456" // The OTP code sent to the user's phone
)
val verifyResponse = vipasoPay.managedAuth.verifyPhoneNumber(verifyRequest)

// Response: Contains the flow ID for the final step
VerifyPhoneNumberResponse(
    flowId = "signup-flow-456"
)

Finish Signup Flow

Lastly, complete the signup by sending all the user data collected through the flow. The SDK will store the session token securely and the auth state listeners will be notified about the successful authentication.

// Request: Complete the signup process with user details
val finishRequest = FinishSignupRequest(
    flowId = "signup-flow-456",
    password = "secure_password",
    email = "[email protected]",
    name = "John Doe", // Deprecated, use firstName and lastName instead
    firstName = "John",
    lastName = "Doe",
    phoneNumber = "+1234567890"
)
val signupResult = vipasoPay.managedAuth.finalizeSignupFlow(finishRequest)

// Response: Contains the flow ID
FinishSignupResponse(
    flowId = "signup-flow-456"
)

// The auth state listener will be notified about the successful authentication
// You can observe login state changes via user.getUserLoginState()

User Information

Fetch User

// Request: Get current user details
val user = vipasoPay.user.fetchUser()

// Response: User profile information
VipasoUser(
    id = "user123",
    email = "[email protected]",
    phone = "+1234567890",
    firstName = "John",
    lastName = "Doe"
)

Delegated Authentication (PayDelegatedAuthApi)

The PayDelegatedAuthApi enables integration with your own authentication system. Choose this approach when your app already has user 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
vipasoPay.delegatedAuth.establishAuthentication(
    // The unique identifier for the user in your system
    userIdentifier = "user-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 (PayUserApi)

The PayUserApi manages user profile information, login state tracking, and user-specific features.

Logout

Simply calling the logout function will remove the stored session token and notify the auth state listeners of the change.

// Request: Logout current user
vipasoPay.user.logout()

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

Fetch Feature Flags

// Request: Get feature flags for the current user
val featureFlags = vipasoPay.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

// Request: Observe the user's login state
vipasoPay.user.getUserLoginState()
    .collect { loginState ->
        when (loginState) {
            LoginState.LoggedIn -> {
                // User is logged in, proceed with app functionality
            }
            LoginState.NotLoggedIn -> {
                // User is not logged in, show login screen
            }
        }
    }

Payment Instruments (PayInstrumentApi)

The PayInstrumentApi manages all payment instruments including cards, MNO (Mobile Network Operator), and third-party payment options.

Fetch Instruments

// Request: Get all available payment instruments
val instruments = vipasoPay.instrument.fetchInstruments()

// Response: List of payment instruments
InstrumentResponse(
    instruments = [
        VipasoInstrument(
            id = "instrument_id",
            type = InstrumentType.CARD,
            isDefault = true,
            details = CardDetails(
                last4 = "1111",
                cardType = "visa",
                expiryMonth = 12,
                expiryYear = 2025
            )
        ),
        VipasoInstrument(
            id = "instrument_id_2",
            type = InstrumentType.MNO,
            isDefault = false,
            details = MnoDetails(
                provider = "Provider Name",
                phoneNumber = "+1234567890"
            )
        )
    ]
)

Fetch Instrument

// Request: Get details for a specific instrument
val instrument = vipasoPay.instrument.fetchInstrument("instrument_id")

// Response: Detailed instrument information
VipasoInstrument(
    id = "instrument_id",
    type = InstrumentType.CARD,
    isDefault = true,
    details = CardDetails(
        last4 = "1111",
        cardType = "visa",
        expiryMonth = 12,
        expiryYear = 2025
    )
)

/*
Possible InstrumentType values:
- CARD: Credit or debit card instrument
- BANK_ACCOUNT: Direct bank account instrument
- MNO: Mobile Network Operator payment instrument
*/

Remove Instrument

// Request: Delete a payment instrument
vipasoPay.instrument.removeInstrument("instrument_id")

Create Card Instrument

Add a new card payment method to the user's account. The card will be validated and stored securely:

// Request: Add a new card payment method
val cardRequest = CreateCardInstrumentRequest(
    cardNumber = "4111111111111111",
    expiryMonth = 12,
    expiryYear = 2025,
    cvv = "123",
    cardholderName = "John Doe"
)
val cardResponse = vipasoPay.instrument.createCardInstrument(cardRequest)

// Response: Created card instrument details
CardInstrumentResponse(
    instrumentId = "card-123",
    last4 = "1111",
    cardType = "visa",
    expiryMonth = 12,
    expiryYear = 2025,
    cardholderName = "John Doe",
    isDefault = true
)

/*
Possible VipasoInstrumentStatus values:
- IN_PROGRESS: Instrument creation is being processed
- VERIFIED: Instrument has been successfully verified and is ready for use
- FAILED: Instrument creation or verification failed
*/

Create MNO Instrument

Add a mobile network operator payment method to the user's account:

// Request: Add a mobile network operator payment method
val mnoRequest = CreateMnoInstrumentRequest(
    phoneNumber = "+1234567890",
    provider = "Provider Name"
)
val mnoResponse = vipasoPay.instrument.createMnoInstrument(mnoRequest)

// Response: Created MNO instrument details
MnoInstrumentResponse(
    instrumentId = "mno-456",
    phoneNumber = "+1234567890",
    provider = "Provider Name",
    isDefault = false,
    verificationRequired = true,
    verificationUrl = "https://verify.example.com/mno/456"
)

Create Third-Party Instrument

Create an instrument with an existing third-party ID from your own system:

// Request: Add an external payment provider
val thirdPartyRequest = CreateThirdPartyInstrumentRequest(
    provider = "Provider Name",
    accountId = "account_id"
)
val thirdPartyResponse = vipasoPay.instrument.createThirdPartyInstrument(thirdPartyRequest)

// Response: Created third-party instrument details
ThirdPartyInstrumentResponse(
    instrumentId = "tp-789",
    provider = "PayPal",
    accountId = "[email protected]",
    isDefault = false,
    redirectUrl = "https://connect.example.com/thirdparty/789"
)

Pre-Authorize Instrument (Experimental)

Certain instruments can be pre-authorized with an amount of money to enable users to pay in offline mode. This is an EXPERIMENTAL functionality.

// Request: Pre-authorize funds on an instrument
val preAuthRequest = PreAuthorizationRequest(
    instrumentId = "instrument_id",
    amount = 100.0,
    currency = "USD"
)
val preAuth = vipasoPay.instrument.preAuthorizeInstrument(preAuthRequest)

// Response: Pre-authorization results
VipasoPreAuthorization(
    id = "preauth-123",
    instrumentId = "instrument_id",
    amount = 30.0,
    currency = "USD",
    expiryTime = 1623498765432
)

Top Up Instrument

Top up allows funds to be transferred from a mobile phone number to an instrument. This results in a response containing a URL which points to the web flow start page for the top-up process.

// Request: Add funds to a payment instrument
val topUpRequest = TopUpRequest(
    instrumentId = "instrument_id",
    amount = 50.0,
    currency = "USD"
)
val topUpResponse = vipasoPay.instrument.topUpInstrument(topUpRequest)

// Response: Contains URL to complete the top-up process in a web flow
TopUpResponse(
    webFlowUrl = "https://pay.vipaso.io/topup/flow/abc123"
)

Withdraw From Instrument

Withdrawal allows funds to be transferred from an instrument to a supplied phone number. The response contains an ID and a status.

// Request: Transfer funds from an instrument
val withdrawalRequest = WithdrawalRequest(
    instrumentId = "instrument_id",
    amount = 25.0,
    currency = "USD"
)
val withdrawalResponse = vipasoPay.instrument.withdrawFromInstrument(withdrawalRequest)

// Response: Withdrawal status information
WithdrawalResponse(
    id = "withdrawal-789",
    status = WithdrawalStatus.IN_PROGRESS // Other possible values: INITIATED, COMPLETED, CANCELLED, FAILED
)

/*
Possible WithdrawalStatus values:
- INITIATED: Withdrawal request has been created but processing hasn't started
- IN_PROGRESS: Withdrawal is currently being processed
- COMPLETED: Withdrawal has been successfully completed
- CANCELLED: Withdrawal was cancelled by the user or system
- FAILED: Withdrawal processing failed due to an error
*/

Payment Processing (PayPaymentApi)

The PayPaymentApi handles all payment-related operations including initiating payments, checking payment status, and processing BLE payments.

Payment History

Fetch Payments

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

// Request: Get list of payment transactions
val payments = vipasoPay.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"
            )
        ),
        VipasoPayment(
            paymentId = "payment-124",
            amount = 75.00,
            currency = "USD",
            unifiedStatus = VipasoPaymentStatus.PENDING,
            timestamp = 1623412345678,
            instrumentId = "mno-456",
            instrumentType = "MNO",
            description = "Phone accessories",
            lastUpdate = "2025-06-03T09:24:12Z",
            merchant = VipasoMerchant(
                merchantId = "merchant-789",
                name = "Electronics Store"
            ),
            consumer = VipasoConsumer(
                id = "user-123",
                name = "John Doe"
            )
        )
    ],
    totalCount = 15,
    hasMore = true
)

/*
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 Details

Fetch the complete details of a specific payment by its ID:

// Request: Get details for a specific payment
val paymentDetails = vipasoPay.payment.fetchPayment(
    request = FetchPaymentDetailsRequest(paymentId = "payment_id")
)

// Response: Detailed payment information
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
*/

BLE Payment

The BLE Payment functionality allows receiving payments and payment events from Vipaso PoS Devices. The SDK provides a Flow-based approach to listen for and process BLE payment events.

Listen for incoming payments via BLE

Start detecting and receiving payment requests via Bluetooth Low Energy from Vipaso PoS Devices. This returns a Flow that emits payment state changes as they occur:

// Request: Start detecting Bluetooth payment requests from Vipaso PoS Devices
val blePaymentsFlow = vipasoPay.payment.listenForBlePayments()

// Response: Flow of VipasoWalletPaymentState values
// Collect and handle payment states:
scope.launch {
    blePaymentsFlow.collect { state ->
        when (state) {
            is VipasoWalletPaymentState.ReceivedPaymentRequest -> {
                // Request to pay received from terminal
                val amount = state.amount
                val currency = state.currency
                val merchantId = state.merchantId
                val merchantName = state.merchantName
                // You can accept or decline the payment request
            }
            is VipasoWalletPaymentState.PaymentSuccessful -> {
                // Payment was successful, show confirmation
            }
            is VipasoWalletPaymentState.PaymentCancelled -> {
                // Payment was cancelled
            }
            is VipasoWalletPaymentState.AnotherUserPaid -> {
                // Another user paid instead
            }
            is VipasoWalletPaymentState.PaymentFailed -> {
                // Payment failed, show error message
            }
        }
    }
}

Accept Payment

When an incoming payment request arrives via BLE, users can decide if they accept it. In case the user accepts the payment, the following method sends the acceptance to the PoS Terminal:

// Request: Accept a payment from a BLE payment request

vipasoPay.payment.acceptPayment(
    paymentId = "payment-id-from-request",
    instrumentId = "selected-instrument-id", // ID of the payment instrument to use
    amount = 50.00,
    tip = 5.00, // Optional
    currency = "USD"
)

// The payment result will be delivered through the listenForBlePayments() flow
// as a PaymentSuccessful, PaymentFailed, or other state

Cancel Payment

In case the user doesn't accept a payment, the following method has to be called to notify the PoS Terminal that the payment is not accepted:

// Request: Reject or cancel a payment operation

vipasoPay.payment.cancelPayment("payment_id")

// The payment state will be updated in the BLE payment flow
// You'll receive a PaymentCancelled state in the listenForBlePayments flow

Stop BLE

When your app no longer needs to listen for BLE payments (for example, when the user navigates away from the payment screen) or when closing the app, call this method to clean up resources and stop the BLE detection:

// Request: Stop listening for BLE payments

vipasoPay.payment.stopBle()

// After calling this, you'll no longer receive payment requests or states
// You can call listenForBlePayments() again to start listening for new payments