Skip to main content

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:

TypeDescription
PUSHStandard OS-level push notification shown in the notification tray.
IN-APPA custom overlay rendered inside the app (banner, modal, fullscreen, carousel).
ACTION_BASEDPush 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-lite 1.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:

  1. Go to the Firebase Console → create or select a project.
  2. Add an Android app with your package name.
  3. Download google-services.json and place it in the app/ directory.
  4. In Firebase Console → Project Settings → Cloud Messaging, note your Server Key.
  5. 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 the onResume of 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 valuePlatformDescription
PUSHAndroid / iOSStandard push notification shown in the tray/banner.
IN-APPAndroid / iOSCustom in-app overlay rendered inside the app.
ACTION-BASEDAndroidPush with interactive action buttons.

Payload fields

FieldTypeDescription
typeStringPUSH, IN-APP, ACTION-BASED.
sourceStringAlways "Paylisher".
titleJSON StringLocalized title map, e.g. {"en": "Hello", "tr": "Merhaba"}.
messageJSON StringLocalized body map.
imageUrlStringURL of the notification image.
iconUrlStringURL of the notification icon (Android).
actionStringDeep link URL opened on tap.
silentBooleantrue = no sound, low priority.
buttonsJSON ArrayAction buttons (label, action, url).
defaultLangStringFallback language code, e.g. "en".
conditionJSON StringTargeting 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 typeDescription
bannerHorizontal bar at the top, center, or bottom of the screen.
modalCentered dialog with an overlay backdrop.
fullscreenFull-screen takeover.
modal-carouselCentered multi-slide carousel.
fullscreen-carouselFull-screen multi-slide carousel.

Layout blocks

Each layout is composed of ordered blocks:

BlockDescription
textLocalized text with font size, color, and alignment.
imageRemote image with an aspect ratio.
buttonGroup1–3 action buttons (open URL, dismiss, copy text).
spacerFixed or flexible vertical space.

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:

EventTrigger
notificationOpenUser taps a push notification.
notificationDismissUser dismisses a push notification.
inappMessageReadAn in-app message is displayed.
FCMFCM 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.

MethodWhat 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.json is in the app/ directory.
  • POST_NOTIFICATIONS permission 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 the onResume() of the active Activity.
  • Check that captureScreenViews = true, or that a screen navigation triggers the in-app evaluation.
  • Verify condition.target matches the current Activity's localClassName.
  • 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.