Skip to content

Commit

Permalink
Merge pull request #150 from breez/savage-notification-plugin
Browse files Browse the repository at this point in the history
Notification Plugin docs
  • Loading branch information
dangeross authored Apr 11, 2024
2 parents b29ccfe + 989e6b6 commit 38911fb
Show file tree
Hide file tree
Showing 18 changed files with 1,075 additions and 111 deletions.
23 changes: 20 additions & 3 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
- [Sending payments](guide/send_payment.md)
- [Sending spontaneous payments](guide/send_spontaneous_payment.md)
- [Listing payments](guide/list_payments.md)
- [Receiving payments via mobile notifications](guide/payment_notification.md)
- [iOS](guide/ios_notification_service_extension.md)
- [Android](guide/android_notification_foreground_service.md)
- [Adding and filtering for payment metadata](guide/payment_metadata.md)
- [Connecting to an LSP](guide/connecting_lsp.md)
- [Receiving an On-Chain Transaction](guide/receive_onchain.md)
Expand All @@ -28,3 +25,23 @@
- [Retrieving service status](guide/service_status.md)
- [Reporting payment failures](guide/failure_report.md)
- [Moving to production](guide/production.md)

# Notifications

- [Getting Started](notifications/getting_started.md)
- [Setup an NDS](notifications/setup_nds.md)
- [Register a webhook](notifications/register_webhook.md)
- [Project Integration](notifications/setup_plugin.md)
- [Android](notifications/android_setup.md)
- [Foreground Service](notifications/android_service.md)
- [Notification Plugin](notifications/android_plugin.md)
- [iOS](notifications/ios_setup.md)
- [Notification Service Extension](notifications/ios_service.md)
- [Notification Plugin](notifications/ios_plugin.md)
- [Logging](notifications/logging.md)
- [Service Configuration](notifications/service_configuration.md)
- [Changing Default Strings](notifications/changing_strings.md)
- [Custom Notification Handling](notifications/custom_notifications.md)

<!-- Hidden Links -->
[](guide/payment_notification.md)
116 changes: 8 additions & 108 deletions src/guide/payment_notification.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,14 @@
# Receiving payments via mobile notifications
# Introduction

The Breez SDK provides users the ability to receive Lightning payments via mobile notifications. It uses a webhook that allows your application to be notified (via a pre-specified URL) when a payment is about to be received. To use this feature, you need to set up a webhook that an LSP can call when a payment is received and [register it in the Breez SDK](payment_notification.html#step-3-register-a-webhook). This webhook, a URL endpoint in your application, will handle incoming payment notifications. This URL should be capable of receiving POST requests. The payment received webhook payload is json formatted and contains the following structure:

<section>
<pre>
{
"template": "payment_received",
"data": {
"payment_hash": [payment hash]
}
}
</pre>
</section>

## Webhook integration for receiving payments while the app isn't running
You can use this webhook to allow mobile users to receive Lightning payments even if they aren't running the app at the time of the payment. This process involves using a Notification Delivery Service (NDS) acting as an intermediary. When a payment is made to a user, the LSP sends a notification to the NDS configured with a specific webhook URL. The NDS then processes this information and dispatches a push notification to the intended mobile device, ensuring the user receives timely updates about incoming payments. This architecture necessitates vendors setting up and maintaining their own NDS, tailored to handle and forward these notifications efficiently.
The Breez SDK Notification Plugin provides vendors the ability to receive events via mobile notifications even while the application is in the background or closed. This plugin processes several different notification types like receiving payments, LNURL-pay flows and swap confirmations. You can even extend the functionality to handle your own notification types within your application.

![receive via notifications_2](https://github.com/breez/breez-sdk-docs/assets/31890660/75e7cac6-4480-453d-823b-f52bd6757ce9)

### Step 1: Run your own NDS
You will need to run your own NDS because the NDS is configured to send push notifications to your app users and therefore should be configured with the required keys and certificates.

You can use our [reference NDS implementation](https://github.com/breez/notify).

Our NDS implementation expects URLs in the following format:
<section>
<pre>
https://your-nds-service.com/notify?platform=<ios|android>&token=[PUSH_TOKEN]
</pre>
</section>

Once the NDS has received such request it will send a push notification to the corresponding devices.

### Step 2: Obtain a mobile push token
Ensure that your mobile application is set up to receive push notifications and can generate a push token. This token uniquely identifies the device for push notifications.
* For iOS, use [Apple Push Notification Service (APNs)](https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns) to get the token.
* For Android, use [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging/manage-tokens) to obtain the token.

### Step 3: Register a webhook
Register the constructed URL with the Breez SDK By calling the register-payment-webook API as follows:

<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>

```rust,ignore
{{#include ../../snippets/rust/src/webhook.rs:register-payment-webook}}
```
</section>

<div slot="title">Swift</div>
<section>

```swift,ignore
{{#include ../../snippets/swift/BreezSDKExamples/Sources/Webhook.swift:register-payment-webook}}
```
</section>

<div slot="title">Kotlin</div>
<section>

```kotlin,ignore
{{#include ../../snippets/kotlin_mpp_lib/shared/src/commonMain/kotlin/com/example/kotlinmpplib/Webhook.kt:register-payment-webook}}
```
</section>

<div slot="title">React Native</div>
<section>

```typescript
{{#include ../../snippets/react-native/webhook.ts:register-payment-webook}}
```
</section>

<div slot="title">Dart</div>
<section>

```dart,ignore
{{#include ../../snippets/dart_snippets/lib/webhook.dart:register-payment-webook}}
```
</section>

<div slot="title">Python</div>
<section>

```python,ignore
{{#include ../../snippets/python/src/webhook.py:register-payment-webook}}
```
</section>

<div slot="title">Go</div>
<section>

```go,ignore
{{#include ../../snippets/go/webhook.go:register-payment-webook}}
```
</section>

<div slot="title">C#</div>
<section>

```cs,ignore
{{#include ../../snippets/csharp/Webhook.cs:register-payment-webook}}
```
</section>

</custom-tabs>
## How it works

### Step 4: Handling notifications when the app isn't running
To ensure that your mobile application can handle payment notifications even when it is not actively running, specific implementations are required for both iOS and Android platforms. This involves waking up the app upon receiving a push notification, connecting with the Breez SDK, and then waiting for the payment to be fully received.
* For iOS, please follow the steps in [iOS NotificationServiceExtension](ios_notification_service_extension.md).
This process involves using a Notification Delivery Service (NDS) acting as an intermediary host by the application developer. The NDS must provide a public facing webhook URL where a POST request can be sent to when a notification needs to be delivered to the application. The NDS then forwards the data sent in the webhook POST request via push notification to the application. When the application then receives the push notification then Breez SDK Notification Plugin can be used to process the event.

* For Android, please follow the steps in [Android foreground service](android_notification_foreground_service.md).
## Next Steps
- **[Setup an NDS](/notifications/setup_nds.md)** to receive webhook requests
- **[Register a webhook](/notifications/register_webhook.md)** in your main application
- **[Project integration](/notifications/setup_plugin.md)** using a notification service extension or foreground service
4 changes: 4 additions & 0 deletions src/guide/receive_onchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ The process of receiving funds via an on-chain address is trustless and uses a s

1. Either by a preimage that is exposed when the Lightning payment is completed - this is the positive case where the swap was successful.
2. Or by your node when the swap didn't complete within a certain timeout (216 blocks) - this is the negative case where your node will execute a refund (funds become refundable after 288 blocks). Refund will also be available in case the amount sent wasn't within the limits.
<div class="warning">
<h4>Developer note</h4>
Consider implementing the <a href="/notifications/getting_started.md">Notification Plugin</a> when using the Breez SDK in a mobile application. By registering a webhook the application can receive a transaction confirmation notification to redeem the swap in the background.
</div>

## Refund a Swap

Expand Down
4 changes: 4 additions & 0 deletions src/guide/receive_payment.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ The Breez SDK automatically connects your node to the LSP peer and you can now r
```
</section>
</custom-tabs>
<div class="warning">
<h4>Developer note</h4>
Consider implementing the <a href="/notifications/getting_started.md">Notification Plugin</a> when using the Breez SDK in a mobile application. By registering a webhook the application can receive a payment notification to process the payment in the background.
</div>

# Calculating fees

Expand Down
148 changes: 148 additions & 0 deletions src/notifications/android_plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Add the Android Notification Plugin

Add the `breez-sdk` dependency to your application's `build.gradle` file in the `app` directory.

```gradle
android {
defaultConfig {
// Add a build config field to read the Breez API key
// from a git ignored `gradle.properties` file
buildConfigField "String", "BREEZ_SDK_API_KEY", project.property('BREEZ_SDK_API_KEY')
}
// This might help building if duplicate libraries are found
packagingOptions {
pickFirst "lib/armeabi-v7a/libc++_shared.so"
pickFirst "lib/arm64-v8a/libc++_shared.so"
pickFirst "lib/x86/libc++_shared.so"
pickFirst "lib/x86_64/libc++_shared.so"
exclude "META-INF/*"
}
}
dependencies {
// Add the breez-sdk dependency
implementation "com.github.breez:breez-sdk"
}
```

## Integrate the Notification Plugin

You're ready to add some Kotlin code to implement the Notification Plugin in your application. In the example below, we are using the `FirebaseMessagingService` to receive the message intents. First, let's implement the Notification Plugin's `MessagingService` class along with `FirebaseMessagingService`.

```kotlin
package com.example.application

import android.annotation.SuppressLint
import android.content.Intent
import androidx.core.content.ContextCompat
import breez_sdk_notification.Constants
import breez_sdk_notification.Message
import breez_sdk_notification.MessagingService
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage

@SuppressLint("MissingFirebaseInstanceTokenRefresh")
class ExampleFcmService : MessagingService, FirebaseMessagingService() {
companion object {
private const val TAG = "FcmService"
}

// Override the `onMessageReceived` to handle the remote message
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)

// Check if the message is high priority and can be handled
if (remoteMessage.priority == RemoteMessage.PRIORITY_HIGH) {
remoteMessage.asMessage()?.also { message ->
// Call `startServiceIfNeeded` to check if the foreground
// service is needed depending on the message type and
// foreground state of the application
startServiceIfNeeded(applicationContext, message)
}
}
}

// A helper function the convert the `RemoteMessage`
// to a notification plugin 'Message'
private fun RemoteMessage.asMessage(): Message? {
return data[Constants.MESSAGE_DATA_TYPE]?.let {
Message(
data[Constants.MESSAGE_DATA_TYPE], data[Constants.MESSAGE_DATA_PAYLOAD]
)
}
}

// Override the `startForegroundService` function to start the foreground service
// using the `ExampleForegroundService` handler
override fun startForegroundService(message: Message) {
val intent = Intent(applicationContext, ExampleForegroundService::class.java)
intent.putExtra(Constants.EXTRA_REMOTE_MESSAGE, message)
ContextCompat.startForegroundService(applicationContext, intent)
}
}
```

Now lets add the foreground service implementation. This should implement the notification plugin `ForegroundService` class, which handles the incoming notification intent and processes the event. To properly implement this, your class needs to override the `onCreate`, `getConnectRequest` and `getServiceConfig` functions. The `getConnectRequest` function is called by the `ForegroundService` to get a BreezSDK `ConnectRequest` which contains the data necessary to connect the SDK to the node. This data includes the Breez API key, the `Config` with it's workingDir and the node seed.

<div class="warning">
<h4>Developer note</h4>
In Android reading from secured storage can vary a lot depending if it is a Kotlin, Flutter or React Native based application and the dependencies used to write to the secured storage. Consult the dependency used to write to the secured storage on how to read data back from them.
</div>

```kotlin
package com.example.application

import breez_sdk.ConnectRequest
import breez_sdk.EnvironmentType
import breez_sdk.GreenlightNodeConfig
import breez_sdk.NodeConfig
import breez_sdk.defaultConfig
import breez_sdk.mnemonicToSeed
import breez_sdk_notification.ForegroundService
import breez_sdk_notification.NotificationHelper.Companion.registerNotificationChannels
import breez_sdk_notification.ServiceConfig

class ExampleForegroundService : ForegroundService() {
companion object {
private const val TAG = "ForegroundService"
private const val ACCOUNT_MNEMONIC = "BREEZ_SDK_SEED_MNEMONIC"
}

// Override the `onCreate` function
override fun onCreate() {
super.onCreate()
// Register the default notification channels
registerNotificationChannels(applicationContext, DEFAULT_CLICK_ACTION)
}

// Override the `getConnectRequest` function
override fun getConnectRequest(): ConnectRequest? {
// Get the Breez API key from the build config
val apiKey = BuildConfig.BREEZ_SDK_API_KEY
val glNodeConf = GreenlightNodeConfig(null, null)
val nodeConf = NodeConfig.Greenlight(glNodeConf)
val config = defaultConfig(EnvironmentType.PRODUCTION, apiKey, nodeConf)

// Set the workingDir as the same directory as the main application
config.workingDir = "${applicationContext.applicationInfo.dataDir}/breezSdk"

// Get the mnemonic from secured storage using an implementation of
// `readSecuredValue` depending on how data is written to secured storage.
// See Developer Note
return readSecuredValue(applicationContext, ACCOUNT_MNEMONIC)
?.let { mnemonic ->
ConnectRequest(config, mnemonicToSeed(mnemonic))
}
}

// Override the `getServiceConfig` function
override fun getServiceConfig(): ServiceConfig? {
// For now just return the default config
return ServiceConfig.default()
}
}
```

## Reference implementation
For a complete reference, see how we implemented it in c-breez wallet: [BreezFcmService.kt](https://github.com/breez/c-breez/blob/main/android/app/src/main/kotlin/com/cBreez/client/BreezFcmService.kt).
37 changes: 37 additions & 0 deletions src/notifications/android_service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Android Foreground Service

In the `AndroidManifest.xml` file of the application's `app/src/main` directory, add the user permissions necessary to handle notifications `POST_NOTIFICATIONS` as a foreground service `FOREGROUND_SERVICE`. Then to your main application add two services, one to handle messaging events and one to handle the foreground service.

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

<!-- Add these permissions -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application
android:label="Custom App"
android:name="${applicationName}"
android:roundIcon="@mipmap/ic_launcher"
android:icon="@mipmap/ic_launcher">

<!-- Add a service to handle messaging events -->
<service
android:name=".ExampleFcmService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<!-- Add a service to handle the foreground service -->
<service
android:name=".ExampleForegroundService"
android:foregroundServiceType="shortService"
android:exported="false"
android:stopWithTask="false">
</service>
</application>
</manifest>
```
7 changes: 7 additions & 0 deletions src/notifications/android_setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Setup the Android Notification Plugin

In order to add the Notification Plugin to your Android application, first you need to setup the Foreground Service, then add the Notification Plugin dependency and integrate it:

## Next Steps
- **[Setup the Foreground Service](android_service.md)**
- **[Add the Notification Plugin](android_plugin.md)**
Loading

0 comments on commit 38911fb

Please sign in to comment.