Notifications (Android)
Push notifications
Notifications are messages that can be sent directly to a user's device. Unlike in-app messages, notifications can be displayed on the lock screen or at the top section of the device. An app can send a notification only if the user has installed the app and enabled notifications (note that the app must first request permission). Notifications are very effective at keeping users engaged or re-engaging them with an app they haven't opened in a while.
Paylisher delivers these messages through Firebase Cloud Messaging (FCM). The SDK supports three delivery types:
| Type | Description |
|---|---|
| PUSH | Standard OS-level push notification shown in the notification tray. |
| IN-APP | A custom overlay rendered inside the app (banner, modal, fullscreen, carousel). |
| ACTION_BASED | Push with interactive action buttons. |
The SDK routes each payload to the correct renderer based on the type field in the notification data.
This guide targets Paylisher
paylisher-sdk-android-lite1.1.3. For the iOS counterpart, see the iOS Notification Integration guide. Huawei devices are supported through HMS in addition to FCM — follow your HMS setup documentation for those devices.
Prerequisites
Dependencies
Add the Paylisher SDK and Firebase Messaging to your app-level build.gradle.kts:
dependencies {
// Paylisher SDK (core module comes transitively)
implementation("com.paylisher:paylisher-sdk-android-lite:1.1.3")
// Firebase (use the BoM for version alignment)
implementation(platform("com.google.firebase:firebase-bom:33.5.1"))
implementation("com.google.firebase:firebase-messaging")
}
Google Services plugin
Apply the Google Services plugin. In your project-level build.gradle.kts:
plugins {
id("com.google.gms.google-services") version "4.4.2" apply false
}
And in your app-level build.gradle.kts:
plugins {
id("com.google.gms.google-services")
}
Firebase project setup
For the full, screenshot-by-screenshot walkthrough see Getting FCM Certificate. In short:
- Go to the Firebase Console → create or select a project.
- Add an Android app with your package name.
- Download
google-services.jsonand place it in theapp/directory. - In Firebase Console → Project Settings → Cloud Messaging, note your Server Key.
- In the Paylisher dashboard, enter this Server Key under Settings → Push Notifications → Android.
AndroidManifest.xml
The SDK automatically registers its FCM service and notification receivers via manifest merger — no action is needed for these. You only need to declare the runtime notification permission for Android 13+:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required for Android 13+ (API 33+) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application ...>
<!-- Your activities -->
</application>
</manifest>
For reference, the SDK's own manifest (merged automatically) declares:
<!-- Automatically included via SDK manifest merger — no action needed -->
<service
android:name="com.paylisher.android.notification.FcmMessagingService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver android:name="com.paylisher.android.notification.NotificationDismissReceiver" ... />
<receiver android:name="com.paylisher.android.notification.NotificationCopyReceiver" ... />
<receiver android:name="com.paylisher.android.notification.NotificationOpenReceiver" ... />
Custom notification icon
To control the small icon shown in the status bar (for example when the app is in the background or closed), declare a default_notification_icon meta-data in your app manifest:
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/custom_notification_icon"
tools:replace="android:resource" />
See Set Small Notification Icon for details.
SDK initialization
Create a custom Application class and initialize the SDK in onCreate():
// MyApplication.kt
import android.app.Application
import com.paylisher.android.PaylisherAndroid
import com.paylisher.android.PaylisherAndroidConfig
import com.paylisher.android.notification.FcmMessagingService
import com.paylisher.Paylisher
import com.google.firebase.messaging.FirebaseMessaging
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 1. Build configuration
val config = PaylisherAndroidConfig(
apiKey = "YOUR_API_KEY", // from the Paylisher dashboard
host = "https://us.i.paylisher.com"
).apply {
debug = false // true during development
captureApplicationLifecycleEvents = true
captureScreenViews = true
}
// 2. Initialize the SDK
PaylisherAndroid.setup(this, config)
// 3. Tell the SDK which Activity to open when a notification is tapped
FcmMessagingService.setNotificationIntentClass(MainActivity::class.java)
// 4. (Optional) register the initial FCM token. Token refreshes are handled
// automatically by the SDK's FcmMessagingService.
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (task.isSuccessful) {
task.result?.takeIf { it.isNotBlank() }?.let { token ->
Paylisher.capture("FCM", userProperties = mapOf("token" to token))
}
}
}
}
}
Register your Application class in AndroidManifest.xml:
<application
android:name=".MyApplication"
... >
Permission request (Android 13+)
In your MainActivity, request the POST_NOTIFICATIONS permission at runtime:
// MainActivity.kt
import android.Manifest
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
class MainActivity : AppCompatActivity() {
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
// Inform the user why notifications are useful if denied
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
}
FCM token registration
The SDK's built-in FcmMessagingService handles token refresh automatically — on every onNewToken it registers the token with Paylisher (captured as an FCM event with the token and locale user properties). No extra code is required for the default setup.
If you maintain your own
FirebaseMessagingService, see Coexisting with your own FirebaseMessagingService below.
Activity setup for in-app messages
The SDK needs a reference to the currently active Activity to display in-app overlays. Provide it in onResume:
// MainActivity.kt
import com.paylisher.android.notification.FcmMessagingService
class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
// Required: lets the SDK know which Activity to attach in-app messages to
FcmMessagingService.setMainActivity(this)
}
}
Multi-Activity apps: call
FcmMessagingService.setMainActivity(this)in theonResumeof every Activity where in-app messages should appear.
Identifying the user with their token
After a user logs in, identify them and attach their FCM token and device ID:
import com.paylisher.Paylisher
import com.google.firebase.messaging.FirebaseMessaging
fun loginUser(userId: String) {
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
Paylisher.identify(
distinctId = userId,
userProperties = mapOf(
"token" to token,
"platform" to "android",
"deviceID" to getDeviceId() // your device ID helper
)
)
}
}
To log out:
Paylisher.reset()
Coexisting with your own FirebaseMessagingService
FCM allows only one service to handle the com.google.firebase.MESSAGING_EVENT action. If your app declares its own FirebaseMessagingService (common in banking and enterprise apps), Android's manifest merger gives the app's service priority and the SDK's service is disabled. As a result, Paylisher messages reach your service but are never forwarded to the SDK — so in-app messages don't render in the foreground and tokens stop reaching Paylisher.
Every Paylisher push carries source: "Paylisher" in its clear-text data payload, so you can detect and forward Paylisher messages with an early return. Pick the pattern that matches your app.
Pattern A — Single push SDK (Extend) — SDK 1.1.0+
If Paylisher is the only push SDK you use, extend the SDK's FcmMessagingService:
import com.paylisher.android.notification.FcmMessagingService
import com.google.firebase.messaging.RemoteMessage
class BankFcmService : FcmMessagingService() { // ← superclass changed
override fun onNewToken(token: String) {
super.onNewToken(token) // forwards the token to Paylisher
bankBackend.registerDeviceToken(token) // your existing logic
}
override fun onMessageReceived(message: RemoteMessage) {
if (message.data["source"] == "Paylisher") {
super.onMessageReceived(message) // SDK handler renders it
return // your code does not run for Paylisher messages
}
// Your existing push handling — unchanged
super.onMessageReceived(message)
showBankNotification(message)
}
}
Pattern B — Multiple push SDKs (Static forward) — SDK 1.1.1+
If you use more than one push SDK, you cannot extend two services at once (Kotlin/Java single inheritance). Forward each SDK with static calls instead:
import com.paylisher.android.notification.FcmMessagingService
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class BankFcmService : FirebaseMessagingService() { // ← generic base class
override fun onNewToken(token: String) {
super.onNewToken(token)
FcmMessagingService.handleNewToken(token) // forward to Paylisher
// OtherSdk.setPushToken(token)
bankBackend.registerDeviceToken(token)
}
override fun onMessageReceived(message: RemoteMessage) {
when (message.data["source"]) {
"Paylisher" -> {
FcmMessagingService.handleRemoteMessage(this, message)
return
}
else -> {
super.onMessageReceived(message)
showBankNotification(message)
}
}
}
}
You do not need to touch
AndroidManifest.xml. As long as your service class name is unchanged, the manifest entry stays the same.
Encrypted payloads
If your own pushes are encrypted, run the Paylisher check before decryption — the Paylisher payload arrives in a clear-text data field, so message.data["source"] is always readable:
override fun onMessageReceived(message: RemoteMessage) {
// 1. Paylisher check first (clear-text)
if (message.data["source"] == "Paylisher") {
FcmMessagingService.handleRemoteMessage(this, message)
return
}
// 2. Then your decrypt + handling code
val decrypted = decryptPayload(message)
showBankNotification(decrypted)
}
Because each forward block ends with return, your existing push flow, notification templates, channels, encryption, deep links, and backend token registration all keep working unchanged.
Reference
Notification types
type value | Platform | Description |
|---|---|---|
PUSH | Android / iOS | Standard push notification shown in the tray/banner. |
IN-APP | Android / iOS | Custom in-app overlay rendered inside the app. |
ACTION-BASED | Android | Push with interactive action buttons. |
Payload fields
| Field | Type | Description |
|---|---|---|
type | String | PUSH, IN-APP, ACTION-BASED. |
source | String | Always "Paylisher". |
title | JSON String | Localized title map, e.g. {"en": "Hello", "tr": "Merhaba"}. |
message | JSON String | Localized body map. |
imageUrl | String | URL of the notification image. |
iconUrl | String | URL of the notification icon (Android). |
action | String | Deep link URL opened on tap. |
silent | Boolean | true = no sound, low priority. |
buttons | JSON Array | Action buttons (label, action, url). |
defaultLang | String | Fallback language code, e.g. "en". |
condition | JSON String | Targeting conditions (see below). |
Condition object
{
"target": "HomeScreen,ProfileScreen", // screen name(s), empty = all screens
"displayTime": 1710000000000, // Unix ms, show after this time
"expireDate": 1710086400000, // Unix ms, do not show after this
"delay": 3 // seconds to delay display
}
On Android, condition.target is matched against the active Activity's localClassName.
In-app layout types
| Layout type | Description |
|---|---|
banner | Horizontal bar at the top, center, or bottom of the screen. |
modal | Centered dialog with an overlay backdrop. |
fullscreen | Full-screen takeover. |
modal-carousel | Centered multi-slide carousel. |
fullscreen-carousel | Full-screen multi-slide carousel. |
Layout blocks
Each layout is composed of ordered blocks:
| Block | Description |
|---|---|
text | Localized text with font size, color, and alignment. |
image | Remote image with an aspect ratio. |
buttonGroup | 1–3 action buttons (open URL, dismiss, copy text). |
spacer | Fixed or flexible vertical space. |
Banner positioning
Set style.verticalPosition in the layout: top, center (default), or bottom.
Analytics events
The SDK automatically captures these events — they appear in your Paylisher dashboard:
| Event | Trigger |
|---|---|
notificationOpen | User taps a push notification. |
notificationDismiss | User dismisses a push notification. |
inappMessageRead | An in-app message is displayed. |
FCM | FCM token registered or refreshed (carries the token user property). |
You can also capture custom events:
Paylisher.capture("button_clicked", properties = mapOf("screen" to "home"))
API reference
The key public methods used for notifications. These static helpers live on com.paylisher.android.notification.FcmMessagingService.
| Method | What it does |
|---|---|
FcmMessagingService.setNotificationIntentClass(activityClass: Class<*>) | Sets the Activity that opens when the user taps a Paylisher notification. Call once in Application.onCreate(). |
FcmMessagingService.setMainActivity(activity: Activity) | Tells the SDK which Activity to render in-app overlays on. Call from onResume() of every Activity that should display in-app messages. |
FcmMessagingService.handleRemoteMessage(context: Context, remoteMessage: RemoteMessage) | Forwards a Paylisher FCM message to the SDK's handler. Use from your own FirebaseMessagingService (static forward pattern). Equivalent to the SDK's internal onMessageReceived. |
FcmMessagingService.handleNewToken(token: String) | Forwards a refreshed FCM token to Paylisher (stored as a People property so token rotation doesn't break push delivery). Use from your own service's onNewToken. |
Event-tracking, identity, and feature-flag methods (
Paylisher.capture,identify,reset,getFeatureFlag, …) are documented in Getting Started and Identify User.
Troubleshooting
Push notifications not received
google-services.jsonis in theapp/directory.POST_NOTIFICATIONSpermission is granted (Android 13+).- The Server Key is entered in the Paylisher dashboard.
- The device has an internet connection.
In-app messages not appearing
- Ensure
FcmMessagingService.setMainActivity(this)is called in theonResume()of the active Activity. - Check that
captureScreenViews = true, or that a screen navigation triggers the in-app evaluation. - Verify
condition.targetmatches the current Activity'slocalClassName. - In-app messages are deduplicated (by
pushId) in the local Room database.
Foreground messages not showing (app has its own FirebaseMessagingService)
This is the manifest-merger conflict described in Coexisting with your own FirebaseMessagingService. Add the forward block to your service. When wired correctly, a Paylisher push produces this Logcat chain:
BankFCM Paylisher message detected → forward to SDK
FCM Notification InApp Data: {...}
FCM DB insert IN_APP OK
FCM | InAppTaskWorker doWork OK | layoutType=native
FCM | InApp In-App sent!
Filter the logs with:
adb logcat | grep -iE "BankFCM|FCM |FCM \||InApp|Paylisher"
Duplicate notifications
The SDK deduplicates by gcm.message_id (push) and pushId (in-app) in the local Room database. If duplicates still appear, ensure your server is not sending the same gcm.message_id twice.
Related
- Getting FCM Certificate — full Firebase project setup.
- Set Small Notification Icon — customize the status-bar icon.