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
- Method 1: AAR (Direct SDK Dependency)
- Method 2: WinkPay Companion App
- API Reference
- Input Validation Rules
- ProGuard
Integration Methods
| Method | Description | Requires |
|---|---|---|
| AAR | Add the SDK AAR directly to your project as a local dependency. | SDK AAR file |
| Companion App | Add 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
-
Copy
winkpaysdk.aarinto your project'slibs/directory. -
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:
| Parameter | Type | Default | Description |
|---|---|---|---|
ageRestrictedPurchase | Boolean | false | Flags the transaction for age verification |
rotationAngle | Int | 0 | Display rotation: 0, 90, 180, or 270 |
cameraIndex | Int | 0 | 0 = back camera, 1 = front camera |
targetPackage | String | "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
| Method | Returns | Description |
|---|---|---|
createPayIntent(...) | Intent | General biometric payment (user chooses face/palm) |
createPayFaceIntent(...) | Intent | Face authentication only |
createPayPalmIntent(...) | Intent | Palm authentication only |
createPayCardIntent(...) | Intent | Card entry only |
createRefundIntent(...) | Intent | Refund a previous transaction |
parseResult(resultCode, data) | WinkPayResult | Parse the activity result |
isAppAvailable(context) | Boolean | Check if WinkPay app is installed |
sendAgeRestrictionOverride(context) | Unit | Broadcast age-restriction manual override |
WinkPayResult
| Variant | Key Fields |
|---|---|
Success | requestId, transactionId, amount, winkTag?, winkToken? |
Failed | requestId?, code, message, transactionId?, amount, winkTag?, winkToken? |
Cancelled | requestId?, transactionId?, amount, winkTag?, winkToken? |
Request Parameters
| Parameter | Type | Constraints |
|---|---|---|
amount | Int | > 0, in minor currency units (cents) |
currency | String | 3 uppercase letters, ISO 4217 (e.g. "USD") |
orderId | String | 1-64 characters |
tenderId | String | 1-64 characters |
ageRestrictedPurchase | Boolean | Default false |
rotationAngle | Int | 0, 90, 180, or 270 (default 0) |
cameraIndex | Int | 0 (back) or 1 (front) (default 0) |
targetPackage | String | Default "com.wink.winkpay" |
transactionId | String | 1-128 chars, required for createRefundIntent |
Request Types
| Type | SDK Method | Description |
|---|---|---|
PAY | createPayIntent | General biometric (face or palm choice shown) |
PAY_FACE | createPayFaceIntent | Skips to face authentication |
PAY_PALM | createPayPalmIntent | Skips to palm authentication |
PAY_CARD | createPayCardIntent | Skips to card entry |
REFUND | createRefundIntent | Refund a previous transaction |
Input Validation Rules
The SDK validates all inputs at construction time and throws IllegalArgumentException for invalid values:
| Field | Rule |
|---|---|
amount | Must be > 0 |
currency | Regex: ^[A-Z]{3}$ |
orderId | 1-64 characters, non-blank |
tenderId | 1-64 characters, non-blank |
transactionId | 1-128 characters (when required) |
rotationAngle | Exactly 0, 90, 180, or 270 |
cameraIndex | 0 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.** { *; }Updated 2 days ago