App-to-app integration
Connect your Android POS app on the same device as the Peach Payments Payment App using the Intent API library (com.peach:intent_api).
For payment flows (sale, refund, void, recovery), see Terminal integration flows.
Integration modes
| Mode | When | How |
|---|---|---|
| Native Android | Kotlin or Java POS app | Use builders in com.peach.intentApi.builder |
| Web POS in WebView | React or other web UI in your Kotlin shell | Bridge WebView → Kotlin → Intent API; or use window.FinimoPay in Peach Payments web-wrapper pattern |
Amounts are always Long in minor currency units (cents). Sale amount must be > 0.
Installation
Maven repository
Add the Peach Payments GitLab package registry to your project settings.gradle - no access token required:
repositories {
maven {
url 'https://gitlab.com/api/v4/projects/61399867/packages/maven'
}
}Dependency
dependencies {
implementation 'com.peach:intent_api:2.0.8'
}Pin the version Peach Payments confirms for your integration. Refund and void require 2.0.6+.
Architecture
Your POS app
│
├─ Sale / Refund / Void / TransactionLookup builders
│
├─ [binder available] ──► AIDL to Payment App
│
└─ [no binder] ──► CommunicationActivity ──► Payment App
│
▼
ResultObject JSON ──► your listener callback (UI thread)The Payment App package follows com.finimo.paymentapp (variant suffixes per build).
Kotlin quick start
Sale.Builder(context)
.amount(1000L) // R 10.00
.disableReceiptingOnOutcome(true)
.setExternalPosData(mapOf("merchantTransactionId" to orderId))
.setListener(object : SaleTransactionListener {
override fun alertSaleTransactionResponse(response: PosTransactionSummary) {
val success = response.isApproved &&
(response.transactionType == PosTransactionType.SALE ||
response.transactionType == PosTransactionType.SALE_WITH_CASHBACK)
}
override fun alertError(error: IntentError) { /* see Intent error codes */ }
})
.startTransaction()Implement SaleTransactionListener, RefundTransactionListener, VoidTransactionListener, or TransactionLookupListener depending on the operation.
Java quick start
new Sale.Builder(context)
.amount(1000L)
.setExternalPosData(Collections.singletonMap("merchantTransactionId", orderId))
.setListener(new SaleTransactionListener() {
@Override
public void alertSaleTransactionResponse(PosTransactionSummary response) { }
@Override
public void alertError(IntentError error) { }
})
.startTransaction();Web POS (Kotlin shell + WebView)
Supported pattern for React or other web clients:
- Your Kotlin app hosts a
WebViewwith your POS URL. - Expose a
@JavascriptInterface(or replicate the web-wrapperPaymentInterfaceorFinimoPaybridge). - On payment request from JavaScript, call Intent API builders from Kotlin - do not call the Payment App directly from JavaScript.
In Peach Payments' web-wrapper sample, the bridge object is window.FinimoPay. Older samples may reference PaymentInterface.
| JavaScript method | Maps to |
|---|---|
FinimoPay.sale(amount, posData) | Sale builder |
FinimoPay.refund(amount, transactionId) | Refund builder |
FinimoPay.void(transactionId) | Void builder |
FinimoPay.lastTransaction() | Transaction lookup |
Your page implements callbacks (for example, onSaleComplete, onError) registered by the bridge.
Operations
| Operation | Builder entry | Notes |
|---|---|---|
| Sale | Sale.Builder → amount, startTransaction | Optional disableReceiptingOnOutcome, redirectText |
| Sale with cashback | SaleWithCashback.Builder | amount + cashbackAmount |
| Refund | Refund.Builder → refundTransaction(amount, transactionId) | transactionId = original sale UUID |
| Void | Void.Builder → performVoid(transactionId) | Returns VoidResponse |
| Last transaction | TransactionLookup.Builder → lastTransaction | Returns only the most recent transaction where the customer tapped a card - not pre-tap errors or cancels |
| Search | TransactionLookup.Builder → searchTransaction(params) | Filter by amount, time, RRN, and so on - use when lastTransaction is not enough |
| Prepare next | PrepareNextTransaction.Builder | Pre-warm Payment App (optional) |
Builder options (common)
| Method | Purpose |
|---|---|
setExternalPosData(map) | Pass merchantTransactionId and custom fields |
disableReceiptingOnOutcome(true) | Auto-return to POS after ~10s on outcome screen |
redirectText(text) | Custom label on outcome close button |
setListener(...) | Register operation-specific callback |
Lookup flags on results
| Field | Use |
|---|---|
isRefundable | Show refund action in POS |
isVoidable | Show void action in POS |
lastTransaction() vs searchTransaction
lastTransaction() vs searchTransactionlastTransaction()- returns only the most recent transaction where the customer tapped a card. Attempts that errored before card presentment are not returned.searchTransaction- use for broader recovery (amount, date range, RRN, and so on) whenlastTransaction()does not match your open order.
If lookup returns an error or no matching successful result, always treat the payment as unsuccessful in your POS - do not mark the order paid.
Callbacks and response models
| Listener method | When |
|---|---|
alertSaleTransactionResponse | Sale completed |
alertRefundTransactionResponse | Refund completed |
alertVoidResponse | Void completed (VoidResponse) |
alertTransactionLookupResponse | Lookup completed |
alertError | Error before or during handoff |
PosTransactionSummary (sale/refund) includes transactionId, transactionType, isApproved, transactionResult, posData, rrn, amount.
See Intent API data models for full field tables.
Error codes
See Intent error codes for all IntentErrorCode values and descriptions.
Next steps
- Terminal integration flows: end-to-end sale, refund, void, and recovery flows.
- Test your integration: Mock Payments App and UAT terminal testing.
- Request a UAT account and install the UAT Payment App before terminal testing.