WinkPay POS Integration

WinkPay SDK - Integration Guide

Android SDK for integrating WinkPay biometric and card payments into your POS application.

Min SDK: 24  |  Compile SDK: 34  |  Namespace: com.winkpay.sdk


Table of Contents


Integration Methods

MethodDescriptionRequires
AARAdd the SDK AAR directly to your project as a local dependency.SDK AAR file
Companion AppAdd the SDK via the WinkPay companion app's bundled library dependency.WinkPay companion app installed on device

Both methods use the same WinkPay API (createPayFaceIntent, createPayPalmIntent, etc.) and receive results through ActivityResultContracts.StartActivityForResult().


Method 1: AAR (Direct SDK Dependency)

AAR Setup

  1. Copy winkpaysdk.aar into your project's libs/ directory.

  2. Add the dependency in your app's build.gradle.kts:

dependencies {
    implementation(files("libs/winkpaysdk.aar"))

    // Required transitive dependency
    implementation("org.json:json:20240303")
}

AAR Manifest Configuration

Add the <queries> block to your AndroidManifest.xml (required on Android 11+ for package visibility):

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <queries>
        <package android:name="com.wink.winkpay" />
        <intent>
            <action android:name="com.winkpay.sdk.REQUEST" />
        </intent>
    </queries>

    <!-- ... -->
</manifest>

Creating Payment Intents

Use the WinkPay object to create intents for each payment flow:

import com.winkpay.sdk.api.WinkPay

// General biometric payment (user chooses face or palm)
val payIntent = WinkPay.createPayIntent(
    amount = 2500,            // $25.00 in cents
    currency = "USD",
    orderId = "ORDER-123",
    tenderId = "TENDER-001"
)

// Face-only payment
val faceIntent = WinkPay.createPayFaceIntent(
    amount = 2500,
    currency = "USD",
    orderId = "ORDER-123",
    tenderId = "TENDER-001"
)

// Palm-only payment
val palmIntent = WinkPay.createPayPalmIntent(
    amount = 2500,
    currency = "USD",
    orderId = "ORDER-123",
    tenderId = "TENDER-001"
)

// Card entry only
val cardIntent = WinkPay.createPayCardIntent(
    amount = 2500,
    currency = "USD",
    orderId = "ORDER-123",
    tenderId = "TENDER-001"
)

Optional parameters available on all pay intents:

ParameterTypeDefaultDescription
ageRestrictedPurchaseBooleanfalseFlags the transaction for age verification
rotationAngleInt0Display rotation: 0, 90, 180, or 270
cameraIndexInt00 = back camera, 1 = front camera
targetPackageString"com.wink.winkpay"Override the WinkPay app package name

Launching Payments

Register an ActivityResultLauncher and launch the intent:

import com.winkpay.sdk.api.WinkPay
import com.winkpay.sdk.api.WinkPayResult

class PaymentActivity : AppCompatActivity() {

    private val winkPayLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { activityResult ->
        val result = WinkPay.parseResult(activityResult.resultCode, activityResult.data)

        when (result) {
            is WinkPayResult.Success -> {
                Log.d("Payment", "Approved! txnId=${result.transactionId} amount=${result.amount}")
                // result.winkTag and result.winkToken are optional biometric identifiers
            }
            is WinkPayResult.Failed -> {
                Log.e("Payment", "Failed: ${result.code} - ${result.message}")
            }
            is WinkPayResult.Cancelled -> {
                Log.d("Payment", "User cancelled")
            }
        }
    }

    private fun startPayment() {
        if (!WinkPay.isAppAvailable(this)) {
            Toast.makeText(this, "WinkPay app not installed", Toast.LENGTH_SHORT).show()
            return
        }

        val intent = WinkPay.createPayFaceIntent(
            amount = 2500,
            currency = "USD",
            orderId = "ORDER-${System.currentTimeMillis()}",
            tenderId = "TENDER-001",
            ageRestrictedPurchase = false
        )
        winkPayLauncher.launch(intent)
    }
}

Handling Results

WinkPay.parseResult() returns a sealed class with three outcomes:

sealed class WinkPayResult {
    data class Success(
        val requestId: String,
        val transactionId: String,
        val amount: Int,              // Amount in minor units (cents)
        val winkTag: String?,         // Optional biometric identifier
        val winkToken: String?        // Optional token
    )

    data class Failed(
        val requestId: String?,
        val code: String,             // Error code (e.g., "PAYMENT_DECLINED")
        val message: String,          // Human-readable error message
        val transactionId: String?,
        val amount: Int,
        val winkTag: String?,
        val winkToken: String?
    )

    data class Cancelled(
        val requestId: String?,
        val transactionId: String?,
        val amount: Int,
        val winkTag: String?,
        val winkToken: String?
    )
}

If the result intent is null and resultCode is RESULT_CANCELED, the SDK returns Cancelled. If the result intent is null with any other result code, it returns Failed with code "NO_RESULT_DATA".

Refunds

val refundIntent = WinkPay.createRefundIntent(
    transactionId = "txn_abc123",    // From the original Success result
    amount = 2500,
    currency = "USD",
    orderId = "ORDER-123",
    tenderId = "TENDER-001"
)
winkPayLauncher.launch(refundIntent)

Handle the result the same way as payments -- WinkPay.parseResult() returns Success, Failed, or Cancelled.

Age-Restricted Purchases

Pass ageRestrictedPurchase = true when creating the intent:

val intent = WinkPay.createPayFaceIntent(
    amount = 2500,
    currency = "USD",
    orderId = "ORDER-123",
    tenderId = "TENDER-001",
    ageRestrictedPurchase = true
)

The WinkPay app may block the transaction pending manual merchant verification. To signal the merchant has approved the age check:

WinkPay.sendAgeRestrictionOverride(context)

Checking App Availability

if (WinkPay.isAppAvailable(context)) {
    // WinkPay companion app is installed and can handle requests
}

Method 2: WinkPay Companion App

When the WinkPay companion app is installed on the device, your app can depend on the SDK library it bundles. This avoids shipping the AAR yourself.

Companion App Setup

Add the WinkPay SDK as a dependency from the companion app's exposed library. In your app's build.gradle.kts:

dependencies {
    implementation("com.winkpay.sdk:winkpaysdk:<version>")
}

The WinkPay companion app (com.wink.winkpay) must be installed on the target device.

Companion App Manifest Configuration

Add the <queries> block to your AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <queries>
        <package android:name="com.wink.winkpay" />
        <intent>
            <action android:name="com.winkpay.sdk.REQUEST" />
        </intent>
    </queries>

    <!-- ... -->
</manifest>

Companion App Creating Payment Intents

The API is identical to the AAR method. Use the WinkPay object:

import com.winkpay.sdk.api.WinkPay

// Face payment
val faceIntent = WinkPay.createPayFaceIntent(
    amount = 1500,            // $15.00
    currency = "USD",
    orderId = "ORDER-456",
    tenderId = "TENDER-001"
)

// Palm payment
val palmIntent = WinkPay.createPayPalmIntent(
    amount = 1500,
    currency = "USD",
    orderId = "ORDER-456",
    tenderId = "TENDER-001",
    rotationAngle = 270,
    cameraIndex = 1
)

// General biometric (user picks face or palm)
val payIntent = WinkPay.createPayIntent(
    amount = 1500,
    currency = "USD",
    orderId = "ORDER-456",
    tenderId = "TENDER-001",
    rotationAngle = 90,
    cameraIndex = 1
)

// Card entry
val cardIntent = WinkPay.createPayCardIntent(
    amount = 1500,
    currency = "USD",
    orderId = "ORDER-456",
    tenderId = "TENDER-001"
)

Companion App Launching Payments

import com.winkpay.sdk.api.WinkPay
import com.winkpay.sdk.api.WinkPayResult

class CheckoutActivity : AppCompatActivity() {

    private val winkPayLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { activityResult ->
        val result = WinkPay.parseResult(activityResult.resultCode, activityResult.data)
        handleResult(result)
    }

    private fun payWithFace(amountCents: Int, orderId: String) {
        if (!WinkPay.isAppAvailable(this)) {
            Toast.makeText(this, "WinkPay app not installed", Toast.LENGTH_SHORT).show()
            return
        }

        winkPayLauncher.launch(
            WinkPay.createPayFaceIntent(
                amount = amountCents,
                currency = "USD",
                orderId = orderId,
                tenderId = "TENDER-001"
            )
        )
    }

    private fun payWithPalm(amountCents: Int, orderId: String) {
        winkPayLauncher.launch(
            WinkPay.createPayPalmIntent(
                amount = amountCents,
                currency = "USD",
                orderId = orderId,
                tenderId = "TENDER-001",
                rotationAngle = 270,
                cameraIndex = 1
            )
        )
    }

    private fun payWithCard(amountCents: Int, orderId: String) {
        winkPayLauncher.launch(
            WinkPay.createPayCardIntent(
                amount = amountCents,
                currency = "USD",
                orderId = orderId,
                tenderId = "TENDER-001"
            )
        )
    }

    private fun handleResult(result: WinkPayResult) {
        when (result) {
            is WinkPayResult.Success -> {
                // Payment approved
                // result.transactionId - use for refunds
                // result.amount - confirmed amount in cents
                // result.winkTag - optional biometric ID
            }
            is WinkPayResult.Failed -> {
                // Payment failed
                // result.code - error code
                // result.message - human-readable message
            }
            is WinkPayResult.Cancelled -> {
                // User cancelled
            }
        }
    }
}

Companion App Handling Results

Result handling is identical to the AAR method. See Handling Results above.

Companion App Refunds

val refundIntent = WinkPay.createRefundIntent(
    transactionId = "txn_abc123",
    amount = 1500,
    currency = "USD",
    orderId = "ORDER-456",
    tenderId = "TENDER-001"
)
winkPayLauncher.launch(refundIntent)

Companion App Age-Restricted Purchases

// Flag the purchase
val intent = WinkPay.createPayFaceIntent(
    amount = 1500,
    currency = "USD",
    orderId = "ORDER-456",
    tenderId = "TENDER-001",
    ageRestrictedPurchase = true
)
winkPayLauncher.launch(intent)

// Later, when merchant manually approves the age check:
WinkPay.sendAgeRestrictionOverride(context)

API Reference

WinkPay

MethodReturnsDescription
createPayIntent(...)IntentGeneral biometric payment (user chooses face/palm)
createPayFaceIntent(...)IntentFace authentication only
createPayPalmIntent(...)IntentPalm authentication only
createPayCardIntent(...)IntentCard entry only
createRefundIntent(...)IntentRefund a previous transaction
parseResult(resultCode, data)WinkPayResultParse the activity result
isAppAvailable(context)BooleanCheck if WinkPay app is installed
sendAgeRestrictionOverride(context)UnitBroadcast age-restriction manual override

WinkPayResult

VariantKey Fields
SuccessrequestId, transactionId, amount, winkTag?, winkToken?
FailedrequestId?, code, message, transactionId?, amount, winkTag?, winkToken?
CancelledrequestId?, transactionId?, amount, winkTag?, winkToken?

Request Parameters

ParameterTypeConstraints
amountInt> 0, in minor currency units (cents)
currencyString3 uppercase letters, ISO 4217 (e.g. "USD")
orderIdString1-64 characters
tenderIdString1-64 characters
ageRestrictedPurchaseBooleanDefault false
rotationAngleInt0, 90, 180, or 270 (default 0)
cameraIndexInt0 (back) or 1 (front) (default 0)
targetPackageStringDefault "com.wink.winkpay"
transactionIdString1-128 chars, required for createRefundIntent

Request Types

TypeSDK MethodDescription
PAYcreatePayIntentGeneral biometric (face or palm choice shown)
PAY_FACEcreatePayFaceIntentSkips to face authentication
PAY_PALMcreatePayPalmIntentSkips to palm authentication
PAY_CARDcreatePayCardIntentSkips to card entry
REFUNDcreateRefundIntentRefund a previous transaction

Input Validation Rules

The SDK validates all inputs at construction time and throws IllegalArgumentException for invalid values:

FieldRule
amountMust be > 0
currencyRegex: ^[A-Z]{3}$
orderId1-64 characters, non-blank
tenderId1-64 characters, non-blank
transactionId1-128 characters (when required)
rotationAngleExactly 0, 90, 180, or 270
cameraIndex0 or 1

ProGuard

The SDK ships with consumer ProGuard rules. No additional configuration is needed. The following rule is applied automatically:

-keep class com.winkpay.sdk.** { *; }