diff --git a/README.md b/README.md
index e8f8d3b0284c15bde03d032e6f4e32277c9ed84f..0f03f76cf35fd9da108a70ba3d527031e992af0f 100644
--- a/README.md
+++ b/README.md
@@ -138,7 +138,7 @@ to [ios/Runner/Info.plist](./ios/Runner/Info.plist) and update the following cod
 
 ## Credits
 
-- Äž1 logo from duniter.org used in the card
+- Äž1 logos from duniter.org
 - undraw intro images: https://undraw.co/license
 - Chipcard https://commons.wikimedia.org/wiki/File:Chipcard.svg under the Creative Commons
   Attribution-Share Alike 3.0 Unported license.
diff --git a/assets/img/animated-bell.gif b/assets/img/animated-bell.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5b207828bf2f53c2f22ea7836cc3d2577fd7d21b
Binary files /dev/null and b/assets/img/animated-bell.gif differ
diff --git a/assets/img/coin.png b/assets/img/coin.png
new file mode 100644
index 0000000000000000000000000000000000000000..c964255f7fc3f2eae37ead131a7ceb80ad261037
Binary files /dev/null and b/assets/img/coin.png differ
diff --git a/assets/img/gbrevedot_color.svg b/assets/img/gbrevedot_color.svg
new file mode 100644
index 0000000000000000000000000000000000000000..737af2ceb634a363f2e9bffa95c56899eac956e7
--- /dev/null
+++ b/assets/img/gbrevedot_color.svg
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   viewBox="-0.72 -0.72 1.44 1.44"
+   version="1.1"
+   id="svg7"
+   sodipodi:docname="gbrevedot (copie).svg"
+   inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
+  <metadata
+     id="metadata13">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs11">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient893">
+      <stop
+         style="stop-color:#5096c8;stop-opacity:1"
+         offset="0"
+         id="stop885" />
+      <stop
+         style="stop-color:#40b2ff;stop-opacity:0.99607843"
+         offset="0.49135655"
+         id="stop887" />
+      <stop
+         style="stop-color:#ffd086;stop-opacity:1"
+         offset="0.70193791"
+         id="stop889" />
+      <stop
+         style="stop-color:#fabb37;stop-opacity:1"
+         offset="1"
+         id="stop891" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient893"
+       id="linearGradient863"
+       x1="0.034742367"
+       y1="-2.8491416"
+       x2="0.032584585"
+       y2="2.8229399"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient893"
+       id="linearGradient883"
+       gradientUnits="userSpaceOnUse"
+       x1="0.12357556"
+       y1="-2.7718253"
+       x2="0.11708657"
+       y2="1.8124492" />
+  </defs>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1007"
+     id="namedview9"
+     showgrid="false"
+     inkscape:zoom="0.51997208"
+     inkscape:cx="205.24387"
+     inkscape:cy="279.21503"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg7" />
+  <g
+     id="gbreve"
+     transform="scale(0.25)"
+     style="fill:url(#linearGradient863);fill-opacity:1.0">
+    <path
+       id="g"
+       d="M 0.7071,0.7071 A 1,1 0 1,1 0.866,-0.5 L 1.7321,-1 A 2,2 0 1,0 1.4142,1.4142 l 0.3536,0.3536 v -1.4142 h -1.4142 z"
+       fill="#000"
+       style="fill:url(#linearGradient883);fill-opacity:1.0" />
+    <path
+       id="breve"
+       d="M 1,0 h 1 A 2,2 0 0,0 -0.5176,-1.9319 L -0.2588,-0.9659 A 1,1 0 0,1 1,0 z"
+       transform="translate(0 -3.14159) scale(0.5) rotate(142.5)"
+       fill="#000"
+       style="fill:url(#linearGradient863);fill-opacity:1.0" />
+    <circle
+       cx="0"
+       cy="2.5"
+       r="0.3536"
+       fill="#000"
+       id="circle4"
+       style="fill:url(#linearGradient863);fill-opacity:1.0" />
+  </g>
+</svg>
diff --git a/assets/translations/en.json b/assets/translations/en.json
index 2c8e6d2adbf8edf15d3cbc12965f54f4f8462ea8..efc5818cff7f2b4257864fad6ac591bb2dbb9243 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -125,5 +125,14 @@
   "install_desktop": "Install Äž1nkgo in Desktop",
   "import_failed": "Wallet import failed",
   "select_file_to_import": "Select the wallet backup",
-  "You can't send money to yourself.": "You can't send money to yourself."
+  "You can't send money to yourself.": "You can't send money to yourself.",
+  "request_notifications_perms": "Get Notified with new payments",
+  "allow_notifications_desc": "Allow Äž1nkgo to notify you with new payments",
+  "allow_notifications_btn": "ALLOW",
+  "deny_notifications_btn": "DENY",
+  "notification_open": "OPEN",
+  "notification_new_payment_title": "New payment received",
+  "notification_new_payment_desc": "You have received a {amount} payment from {from}",
+  "notification_new_sent_title": "New payment sent",
+  "notification_new_sent_desc": "You have sent a {amount} to {to}"
 }
diff --git a/assets/translations/es.json b/assets/translations/es.json
index dff193b89bd031e9babc20067b566453b26ca3db..3b917f665dd85af5d1dc58714f8916a250864a7c 100644
--- a/assets/translations/es.json
+++ b/assets/translations/es.json
@@ -125,5 +125,14 @@
   "install_desktop": "Instalar Äž1nkgo en tu Desktop",
   "import_failed": "Error al importar monedero",
   "select_file_to_import": "Selecciona el monedero guadado",
-  "You can't send money to yourself.": "No puedes enviarte dinero a ti mismo/a."
+  "You can't send money to yourself.": "No puedes enviarte dinero a ti mismo/a.",
+  "request_notifications_perms": "Reciba notificaciones con nuevos pagos",
+  "allow_notifications_desc": "Permitir a Äž1nkgo notificarte de nuevos pagos",
+  "allow_notifications_btn": "PERMITIR",
+  "deny_notifications_btn": "DENEGAR",
+  "notification_open": "ABRIR",
+  "notification_new_payment_title": "Nuevo pago recibido",
+  "notification_new_payment_desc": "Has recibido un nuevo pago de {amount} de {from}",
+  "notification_new_sent_title": "Nuevo pago enviado",
+  "notification_new_sent_desc": "Has enviado un nuevo pago de {amount} a {to}"
 }
diff --git a/lib/data/models/transaction.dart b/lib/data/models/transaction.dart
index 1a4bbfb2293d1f2d434869e5844b8e0c57071417..266531deab7d1f3974827268a2f75f0ea1bb6cb4 100644
--- a/lib/data/models/transaction.dart
+++ b/lib/data/models/transaction.dart
@@ -67,20 +67,29 @@ class Transaction extends Equatable {
 @JsonSerializable()
 @CopyWith()
 class TransactionsAndBalanceState extends Equatable {
-  const TransactionsAndBalanceState({
-    required this.transactions,
-    required this.balance,
-    required this.lastChecked,
-  });
+  const TransactionsAndBalanceState(
+      {required this.transactions,
+      required this.balance,
+      required this.lastChecked,
+      this.lastSentNotification,
+      this.lastReceivedNotification});
 
   factory TransactionsAndBalanceState.fromJson(Map<String, dynamic> json) =>
       _$TransactionsAndBalanceStateFromJson(json);
   final List<Transaction> transactions;
   final double balance;
   final DateTime lastChecked;
+  final DateTime? lastSentNotification;
+  final DateTime? lastReceivedNotification;
 
   Map<String, dynamic> toJson() => _$TransactionsAndBalanceStateToJson(this);
 
   @override
-  List<Object?> get props => <dynamic>[transactions, balance, lastChecked];
+  List<Object?> get props => <dynamic>[
+        transactions,
+        balance,
+        lastChecked,
+        lastSentNotification,
+        lastReceivedNotification
+      ];
 }
diff --git a/lib/data/models/transaction.g.dart b/lib/data/models/transaction.g.dart
index 9442400adfcbe57bd654f92e99fb55f6ed6dad22..73042ccdfebd26a67b7fcc4328170e189f109605 100644
--- a/lib/data/models/transaction.g.dart
+++ b/lib/data/models/transaction.g.dart
@@ -161,6 +161,12 @@ abstract class _$TransactionsAndBalanceStateCWProxy {
 
   TransactionsAndBalanceState lastChecked(DateTime lastChecked);
 
+  TransactionsAndBalanceState lastSentNotification(
+      DateTime? lastSentNotification);
+
+  TransactionsAndBalanceState lastReceivedNotification(
+      DateTime? lastReceivedNotification);
+
   /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TransactionsAndBalanceState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
   ///
   /// Usage
@@ -171,6 +177,8 @@ abstract class _$TransactionsAndBalanceStateCWProxy {
     List<Transaction>? transactions,
     double? balance,
     DateTime? lastChecked,
+    DateTime? lastSentNotification,
+    DateTime? lastReceivedNotification,
   });
 }
 
@@ -192,6 +200,16 @@ class _$TransactionsAndBalanceStateCWProxyImpl
   TransactionsAndBalanceState lastChecked(DateTime lastChecked) =>
       this(lastChecked: lastChecked);
 
+  @override
+  TransactionsAndBalanceState lastSentNotification(
+          DateTime? lastSentNotification) =>
+      this(lastSentNotification: lastSentNotification);
+
+  @override
+  TransactionsAndBalanceState lastReceivedNotification(
+          DateTime? lastReceivedNotification) =>
+      this(lastReceivedNotification: lastReceivedNotification);
+
   @override
 
   /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TransactionsAndBalanceState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
@@ -204,6 +222,8 @@ class _$TransactionsAndBalanceStateCWProxyImpl
     Object? transactions = const $CopyWithPlaceholder(),
     Object? balance = const $CopyWithPlaceholder(),
     Object? lastChecked = const $CopyWithPlaceholder(),
+    Object? lastSentNotification = const $CopyWithPlaceholder(),
+    Object? lastReceivedNotification = const $CopyWithPlaceholder(),
   }) {
     return TransactionsAndBalanceState(
       transactions:
@@ -220,6 +240,15 @@ class _$TransactionsAndBalanceStateCWProxyImpl
               ? _value.lastChecked
               // ignore: cast_nullable_to_non_nullable
               : lastChecked as DateTime,
+      lastSentNotification: lastSentNotification == const $CopyWithPlaceholder()
+          ? _value.lastSentNotification
+          // ignore: cast_nullable_to_non_nullable
+          : lastSentNotification as DateTime?,
+      lastReceivedNotification:
+          lastReceivedNotification == const $CopyWithPlaceholder()
+              ? _value.lastReceivedNotification
+              // ignore: cast_nullable_to_non_nullable
+              : lastReceivedNotification as DateTime?,
     );
   }
 }
@@ -278,6 +307,12 @@ TransactionsAndBalanceState _$TransactionsAndBalanceStateFromJson(
           .toList(),
       balance: (json['balance'] as num).toDouble(),
       lastChecked: DateTime.parse(json['lastChecked'] as String),
+      lastSentNotification: json['lastSentNotification'] == null
+          ? null
+          : DateTime.parse(json['lastSentNotification'] as String),
+      lastReceivedNotification: json['lastReceivedNotification'] == null
+          ? null
+          : DateTime.parse(json['lastReceivedNotification'] as String),
     );
 
 Map<String, dynamic> _$TransactionsAndBalanceStateToJson(
@@ -286,4 +321,7 @@ Map<String, dynamic> _$TransactionsAndBalanceStateToJson(
       'transactions': instance.transactions,
       'balance': instance.balance,
       'lastChecked': instance.lastChecked.toIso8601String(),
+      'lastSentNotification': instance.lastSentNotification?.toIso8601String(),
+      'lastReceivedNotification':
+          instance.lastReceivedNotification?.toIso8601String(),
     };
diff --git a/lib/data/models/transaction_cubit.dart b/lib/data/models/transaction_cubit.dart
index fb54f6a35f3bb8a94d74f287b8e7f733885a0847..6ec5eecaa35de044e6af15c24e015bcb3a69787b 100644
--- a/lib/data/models/transaction_cubit.dart
+++ b/lib/data/models/transaction_cubit.dart
@@ -2,10 +2,14 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
 
 import '../../../g1/api.dart';
 import '../../../g1/transaction_parser.dart';
+import '../../notification_controller.dart';
 import '../../shared_prefs.dart';
+import '../../ui/contacts_cache.dart';
 import '../../ui/logger.dart';
+import 'contact.dart';
 import 'node_list_cubit.dart';
 import 'transaction.dart';
+import 'transaction_type.dart';
 
 class TransactionsCubit extends HydratedCubit<TransactionsAndBalanceState> {
   TransactionsCubit()
@@ -29,18 +33,46 @@ class TransactionsCubit extends HydratedCubit<TransactionsAndBalanceState> {
   }
 
   Future<void> fetchTransactions(NodeListCubit cubit) async {
-    logger('Loading transactions');
+    logger('Loading transactions --------------------');
     final Map<String, dynamic>? txData =
         await gvaHistoryAndBalance(SharedPreferencesHelper().getPubKey());
     if (txData == null) {
       logger('Failed to get transactions');
       return;
     }
-    final TransactionsAndBalanceState state = transactionsGvaParser(txData);
-    emit(state.copyWith(
-        transactions: state.transactions,
-        balance: state.balance,
-        lastChecked: state.lastChecked));
+    final TransactionsAndBalanceState newState =
+        transactionsGvaParser(txData, state);
+    // Notify
+    final DateTime lastReceivedNotification =
+        newState.lastReceivedNotification ?? DateTime.now();
+    final DateTime lastSentNotification =
+        newState.lastSentNotification ?? DateTime.now();
+    // Notify
+/*      logger(
+          'Last received: ${lastReceived.toIso8601String()}, last received notification: ${lastReceivedNotification.toIso8601String()}, compared ${lastReceived.compareTo(lastReceivedNotification)}');*/
+    emit(newState);
+    for (final Transaction tx in newState.transactions.reversed) {
+      if (tx.type == TransactionType.received &&
+          lastReceivedNotification.compareTo(tx.time) == -1) {
+        // Future
+        final Contact from = await ContactsCache().getContact(tx.from);
+        NotificationController.createNewNotification(
+            tx.time.millisecondsSinceEpoch.toString(),
+            amount: tx.amount / 100,
+            from: from.title);
+        emit(newState.copyWith(lastReceivedNotification: tx.time));
+      }
+      if (tx.type == TransactionType.sent &&
+          lastSentNotification.compareTo(tx.time) == -1) {
+        // Future
+        final Contact to = await ContactsCache().getContact(tx.from);
+        NotificationController.createNewNotification(
+            tx.time.millisecondsSinceEpoch.toString(),
+            amount: -tx.amount / 100,
+            to: to.title);
+        emit(newState.copyWith(lastSentNotification: tx.time));
+      }
+    }
   }
 
   @override
diff --git a/lib/g1/transaction_parser.dart b/lib/g1/transaction_parser.dart
index 656f208b8a5da88025b1cc1446004749d8a12587..ea53fef387cea00c34326965982a78c97079f1e6 100644
--- a/lib/g1/transaction_parser.dart
+++ b/lib/g1/transaction_parser.dart
@@ -52,7 +52,8 @@ TransactionsAndBalanceState transactionParser(String txData) {
       transactions: tx, balance: balance, lastChecked: DateTime.now());
 }
 
-TransactionsAndBalanceState transactionsGvaParser(Map<String, dynamic> txData) {
+TransactionsAndBalanceState transactionsGvaParser(
+    Map<String, dynamic> txData, TransactionsAndBalanceState state) {
   // Balance
   final dynamic rawBalance = txData['balance'];
   final double? amount;
@@ -89,7 +90,8 @@ TransactionsAndBalanceState transactionsGvaParser(Map<String, dynamic> txData) {
         sendingRaw as Map<String, dynamic>, TransactionType.sending);
     txs.insert(0, tx);
   }
-  return TransactionsAndBalanceState(
+
+  return state.copyWith(
       transactions: txs, balance: amount, lastChecked: DateTime.now());
 }
 
diff --git a/lib/main.dart b/lib/main.dart
index 07afe7e0a62650d3d669961236ec4a69696cd852..93224a7456cab416baf93be16ae17c64b3b86e8b 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,7 @@
 import 'dart:io';
 
 import 'package:connectivity_wrapper/connectivity_wrapper.dart';
+import 'package:cron/cron.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:filesystem_picker/filesystem_picker.dart';
 import 'package:flutter/foundation.dart';
@@ -32,6 +33,7 @@ import 'data/models/node_type.dart';
 import 'data/models/payment_cubit.dart';
 import 'data/models/transaction_cubit.dart';
 import 'g1/api.dart';
+import 'notification_controller.dart';
 import 'shared_prefs.dart';
 import 'ui/contacts_cache.dart';
 import 'ui/logger.dart';
@@ -40,6 +42,7 @@ import 'ui/ui_helpers.dart';
 
 void main() async {
   Bloc.observer = AppBlocObserver();
+  await NotificationController.initializeLocalNotifications();
 
   /// Initialize packages
   WidgetsFlutterBinding.ensureInitialized();
@@ -237,6 +240,10 @@ PageViewModel createPageViewModel(
 class GinkgoApp extends StatefulWidget {
   const GinkgoApp({super.key});
 
+  // The navigator key is necessary to navigate using static methods
+  static final GlobalKey<NavigatorState> navigatorKey =
+      GlobalKey<NavigatorState>();
+
   @override
   State<GinkgoApp> createState() => _GinkgoAppState();
 }
@@ -268,6 +275,24 @@ class _GinkgoAppState extends State<GinkgoApp> {
     super.initState();
     ContactsCache().init();
     NodeManager().loadFromCubit(context.read<NodeListCubit>());
+    // Only after at least the action method is set, the notification events are delivered
+    NotificationController.startListeningNotificationEvents();
+    final Cron cron = Cron();
+    cron.schedule(Schedule.parse(kReleaseMode ? '*/10 * * * *' : '*/2 * * * *'),
+        () async {
+      logger('---------- fetchTransactions via cron');
+      fetchTransactions(context);
+    });
+    Once.runHourly('load_nodes', callback: () {
+      logger('load nodes via once');
+      _loadNodes();
+    }, fallback: () {
+      _printNodeStatus(prefix: 'After once hourly having');
+    });
+    Once.runDaily('clear_errors', callback: () {
+      logger('clearErrors via once');
+      NodeManager().cleanErrorStats();
+    });
   }
 
   @override
@@ -280,18 +305,6 @@ class _GinkgoAppState extends State<GinkgoApp> {
   Widget build(BuildContext context) {
     return BlocBuilder<NodeListCubit, NodeListState>(
         builder: (BuildContext nodeContext, NodeListState state) {
-      Once.runHourly('load_nodes',
-          callback: () => _loadNodes(),
-          fallback: () {
-            _printNodeStatus(prefix: 'After once hourly having');
-          });
-      Once.runCustom('clear_errors', callback: () {
-        NodeManager().cleanErrorStats();
-      }, duration: const Duration(minutes: 90));
-      Once.runCustom('fetch_transactions', callback: () {
-        fetchTransactions(context);
-      }, duration: const Duration(minutes: 10));
-      fetchTransactions(context);
       return ConnectivityAppWrapper(
           app: FilesystemPickerDefaultOptions(
               fileTileSelectMode: FileTileSelectMode.wholeTile,
@@ -308,6 +321,8 @@ class _GinkgoAppState extends State<GinkgoApp> {
                 darkTheme:
                     ThemeData(useMaterial3: true, colorScheme: darkColorScheme),
 
+                navigatorKey: GinkgoApp.navigatorKey,
+
                 /// Theme stuff
 
                 /// Localization stuff
@@ -319,6 +334,7 @@ class _GinkgoAppState extends State<GinkgoApp> {
                     ? const SkeletonScreen()
                     : const AppIntro(),
                 builder: (BuildContext buildContext, Widget? widget) {
+                  NotificationController.locale = context.locale;
                   return ResponsiveWrapper.builder(
                     BouncingScrollWrapper.builder(
                         context,
diff --git a/lib/notification_controller.dart b/lib/notification_controller.dart
new file mode 100644
index 0000000000000000000000000000000000000000..03ee23fd6f12d58ca02d464a2efe6042fa4b615d
--- /dev/null
+++ b/lib/notification_controller.dart
@@ -0,0 +1,251 @@
+import 'package:awesome_notifications/awesome_notifications.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+import 'config/theme.dart';
+import 'main.dart';
+import 'ui/logger.dart';
+import 'ui/ui_helpers.dart';
+
+// ignore: avoid_classes_with_only_static_members
+///  *********************************************
+///     NOTIFICATION CONTROLLER
+///  *********************************************
+///
+class NotificationController {
+  static ReceivedAction? initialAction;
+  static Locale locale = const Locale('en', 'UK');
+
+  ///  *********************************************
+  ///     INITIALIZATIONS
+  ///  *********************************************
+  ///
+  static Future<void> initializeLocalNotifications() async {
+    await AwesomeNotifications().initialize(
+        null, //'resource://drawable/res_app_icon',//
+        <NotificationChannel>[
+          NotificationChannel(
+              channelKey: 'alerts',
+              channelName: 'Alerts',
+              channelDescription: 'Notification tests as alerts',
+              playSound: true,
+              onlyAlertOnce: true,
+              groupAlertBehavior: GroupAlertBehavior.Children,
+              importance: NotificationImportance.High,
+              defaultPrivacy: NotificationPrivacy.Private,
+              defaultColor: lightColorScheme.primary,
+              ledColor: lightColorScheme.primary)
+        ],
+        debug: true);
+
+    // Get initial notification action is optional
+    initialAction = await AwesomeNotifications().getInitialNotificationAction();
+  }
+
+  ///  *********************************************
+  ///     NOTIFICATION EVENTS LISTENER
+  ///  *********************************************
+  ///  Notifications events are only delivered after call this method
+  static Future<void> startListeningNotificationEvents() async {
+    AwesomeNotifications()
+        .setListeners(onActionReceivedMethod: onActionReceivedMethod);
+  }
+
+  ///  *********************************************
+  ///     NOTIFICATION EVENTS
+  ///  *********************************************
+  ///
+  @pragma('vm:entry-point')
+  static Future<void> onActionReceivedMethod(
+      ReceivedAction receivedAction) async {
+    if (receivedAction.actionType == ActionType.SilentAction ||
+        receivedAction.actionType == ActionType.SilentBackgroundAction) {
+      // For background actions, you must hold the execution until the end
+      logger(
+          'Message sent via notification input: "${receivedAction.buttonKeyInput}"');
+      // await executeLongTaskInBackground();
+    } else {
+      // FIXME (vjrj): go to transactions tab
+      GinkgoApp.navigatorKey.currentState?.pushNamedAndRemoveUntil(
+          '/notification-page',
+          (Route<dynamic> route) =>
+              (route.settings.name != '/notification-page') || route.isFirst,
+          arguments: receivedAction);
+    }
+  }
+
+  ///  *********************************************
+  ///     REQUESTING NOTIFICATION PERMISSIONS
+  ///  *********************************************
+  ///
+  static Future<bool> displayNotificationRationale() async {
+    bool userAuthorized = false;
+    final BuildContext context = GinkgoApp.navigatorKey.currentContext!;
+    await showDialog(
+        context: context,
+        builder: (BuildContext ctx) {
+          return AlertDialog(
+            title: Text(tr('request_notifications_perms'),
+                style: Theme.of(context).textTheme.titleLarge),
+            content: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: <Widget>[
+                Row(
+                  children: <Widget>[
+                    Expanded(
+                      child: Image.asset(
+                        'assets/img/animated-bell.gif',
+                        height: MediaQuery.of(context).size.height * 0.3,
+                        fit: BoxFit.fitWidth,
+                      ),
+                    ),
+                  ],
+                ),
+                const SizedBox(height: 20),
+                Text(tr('allow_notifications_desc')),
+              ],
+            ),
+            actions: <Widget>[
+              TextButton(
+                  onPressed: () {
+                    Navigator.of(ctx).pop();
+                  },
+                  child: Text(
+                    tr('deny_notifications_btn'),
+                    style: Theme.of(context)
+                        .textTheme
+                        .titleLarge
+                        ?.copyWith(color: Colors.red),
+                  )),
+              TextButton(
+                onPressed: () async {
+                  userAuthorized = true;
+                  Navigator.of(ctx).pop();
+                },
+                child: Text(tr('allow_notifications_btn'),
+                    style: Theme.of(context)
+                        .textTheme
+                        .titleLarge
+                        ?.copyWith(color: lightColorScheme.primary)),
+              ),
+            ],
+          );
+        });
+    return userAuthorized &&
+        await AwesomeNotifications().requestPermissionToSendNotifications();
+  }
+
+  ///  *********************************************
+  ///     NOTIFICATION CREATION METHODS
+  ///  *********************************************
+  ///
+  static Future<void> createNewNotification(String id,
+      {required double amount, String? to, String? from}) async {
+    final String title = from != null
+        ? tr('notification_new_payment_title')
+        : tr('notification_new_sent_title');
+    final String desc = from != null
+        ? tr('notification_new_payment_desc', namedArgs: <String, String>{
+            'amount': formatAmountWithLocale(locale.languageCode, amount),
+            'from': from,
+          })
+        : tr('notification_new_sent_desc', namedArgs: <String, String>{
+            'amount': formatAmountWithLocale(locale.languageCode, amount),
+            'to': to!,
+          });
+    if (kIsWeb) {
+      // dart:html cannot be used in Android
+      /* if (html.Notification.permission != 'granted') {
+        await html.Notification.requestPermission();
+      }
+      if (html.Notification.permission == 'granted') {
+        final html.Notification notification = html.Notification(
+          title, body: desc,
+          // icon:
+        );
+        // html.Notification.show();
+      } */
+    } else {
+      bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
+      if (!isAllowed) {
+        isAllowed = await displayNotificationRationale();
+      }
+      if (!isAllowed) {
+        return;
+      }
+
+      await AwesomeNotifications().createNotification(
+          content: NotificationContent(
+              id: -1,
+              // -1 is replaced by a random number
+              channelKey: 'alerts',
+              title: title,
+              body: desc,
+              largeIcon:
+                  'https://git.duniter.org/vjrj/ginkgo/-/raw/master/assets/img/coin.png',
+              bigPicture: 'https://git.duniter.org/vjrj/ginkgo/-/raw/master/assets/img/gbrevedot_color.svg',
+              //'asset://assets/images/balloons-in-sky.jpg',
+              notificationLayout: NotificationLayout.BigPicture,
+              payload: <String, String>{'notificationId': id}),
+          actionButtons: <NotificationActionButton>[
+            NotificationActionButton(
+                key: 'notification_open', label: tr('notification_open')),
+            /* NotificationActionButton(
+              key: 'REPLY',
+              label: 'Reply Message',
+              requireInputText: true,
+              actionType: ActionType.SilentAction), */
+            /* NotificationActionButton(
+              key: 'DISMISS',
+              label: 'Dismiss',
+              actionType: ActionType.DismissAction,
+              isDangerousOption: true) */
+          ]);
+    }
+  }
+
+  static Future<void> scheduleNewNotification() async {
+    bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
+    if (!isAllowed) {
+      isAllowed = await displayNotificationRationale();
+    }
+    if (!isAllowed) {
+      return;
+    }
+
+    await AwesomeNotifications().createNotification(
+        content: NotificationContent(
+            id: -1,
+            // -1 is replaced by a random number
+            channelKey: 'alerts',
+            title: 'Huston! The eagle has landed!',
+            body:
+                "A small step for a man, but a giant leap to Flutter's community!",
+            bigPicture: 'https://storage.googleapis.com/cms-storage-bucket/d406c736e7c4c57f5f61.png',
+            largeIcon: 'https://storage.googleapis.com/cms-storage-bucket/0dbfcc7a59cd1cf16282.png',
+            //'asset://assets/images/balloons-in-sky.jpg',
+            notificationLayout: NotificationLayout.BigPicture,
+            payload: <String, String>{
+              'notificationId': '1234567890'
+            }),
+        actionButtons: <NotificationActionButton>[
+          NotificationActionButton(key: 'REDIRECT', label: 'Redirect'),
+          NotificationActionButton(
+              key: 'DISMISS',
+              label: 'Dismiss',
+              actionType: ActionType.DismissAction,
+              isDangerousOption: true)
+        ],
+        schedule: NotificationCalendar.fromDate(
+            date: DateTime.now().add(const Duration(seconds: 10))));
+  }
+
+  static Future<void> resetBadgeCounter() async {
+    await AwesomeNotifications().resetGlobalBadge();
+  }
+
+  static Future<void> cancelNotifications() async {
+    await AwesomeNotifications().cancelAll();
+  }
+}
diff --git a/lib/ui/screens/fifth_screen.dart b/lib/ui/screens/fifth_screen.dart
index a641ec63702923cb4f7a946a71adf9318ad67255..7567c6c2125a278b0bcc998acdfae23b784bc6d3 100644
--- a/lib/ui/screens/fifth_screen.dart
+++ b/lib/ui/screens/fifth_screen.dart
@@ -7,6 +7,7 @@ import 'package:share_plus/share_plus.dart';
 import '../../data/models/app_cubit.dart';
 import '../../data/models/app_state.dart';
 import '../../data/models/node_type.dart';
+import '../../notification_controller.dart';
 import '../../shared_prefs.dart';
 import '../ui_helpers.dart';
 import '../widgets/bottom_widget.dart';
@@ -25,8 +26,7 @@ class FifthScreen extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return BlocBuilder<AppCubit, AppState>(
-        builder: (BuildContext context, AppState state) =>
-            Scaffold(
+        builder: (BuildContext context, AppState state) => Scaffold(
               appBar: AppBar(title: Text(tr('bottom_nav_fifth'))),
               drawer: const CardDrawer(),
               body: ListView(
@@ -43,6 +43,7 @@ class FifthScreen extends StatelessWidget {
                       ),
                       onChanged: (Locale? newLocale) {
                         context.setLocale(newLocale!);
+                        NotificationController.locale = newLocale;
                       },
                       items: const <DropdownMenuItem<Locale>>[
                         DropdownMenuItem<Locale>(
@@ -89,9 +90,8 @@ class FifthScreen extends StatelessWidget {
                             GridItem(
                                 title: 'share_your_key',
                                 icon: Icons.share,
-                                onTap: () =>
-                                    Share.share(
-                                        SharedPreferencesHelper().getPubKey())),
+                                onTap: () => Share.share(
+                                    SharedPreferencesHelper().getPubKey())),
                           GridItem(
                             title: 'copy_your_key',
                             icon: Icons.copy,
@@ -140,7 +140,6 @@ class FifthScreen extends StatelessWidget {
                                   },
                                 );
                               }),
-
                         ]),
                     if (state.expertMode)
                       const TextDivider(text: 'technical_info_title'),
@@ -155,7 +154,7 @@ class FifthScreen extends StatelessWidget {
                           title: 'code_card_title',
                           icon: Icons.code_rounded,
                           url:
-                          Uri.parse('https://git.duniter.org/vjrj/ginkgo')),
+                              Uri.parse('https://git.duniter.org/vjrj/ginkgo')),
                     const BottomWidget(),
                     SwitchListTile(
                       title: Text(tr('expert_mode')),
diff --git a/lib/ui/screens/first_screen.dart b/lib/ui/screens/first_screen.dart
index edac8f14a3ad9dce37f846a95f604ad354bc6f31..85f05a73bb2e5f2f7d2c309b28e245799f84a20c 100644
--- a/lib/ui/screens/first_screen.dart
+++ b/lib/ui/screens/first_screen.dart
@@ -10,7 +10,7 @@ import '../widgets/bottom_widget.dart';
 import '../widgets/card_drawer.dart';
 import '../widgets/first_screen/credit_card.dart';
 import '../widgets/first_screen/pay_contact_search_button.dart';
-import 'pay_form.dart';
+import '../widgets/first_screen/pay_form.dart';
 
 class FirstScreen extends StatefulWidget {
   const FirstScreen({super.key});
diff --git a/lib/ui/ui_helpers.dart b/lib/ui/ui_helpers.dart
index 930c10af193518a775ce73c1fd29b33b084d3842..1ecbc266581856106173070a161bd3bb88cef5c7 100644
--- a/lib/ui/ui_helpers.dart
+++ b/lib/ui/ui_helpers.dart
@@ -36,6 +36,11 @@ void showTooltip(BuildContext context, String title, String message) {
 }
 
 void copyPublicKeyToClipboard(BuildContext context) {
+  /* final DataWriterItem item = DataWriterItem();
+  item.add(Formats.plainText(SharedPreferencesHelper().getPubKey()));
+  ClipboardWriter.instance.write(<DataWriterItem>[item]).then((dynamic value) =>
+      ScaffoldMessenger.of(context).showSnackBar(
+          SnackBar(content: Text(tr('key_copied_to_clipboard'))))); */
   FlutterClipboard.copy(SharedPreferencesHelper().getPubKey()).then(
       (dynamic value) => ScaffoldMessenger.of(context).showSnackBar(
           SnackBar(content: Text(tr('key_copied_to_clipboard')))));
@@ -121,10 +126,15 @@ bool smallScreen(BuildContext context) =>
     MediaQuery.of(context).size.width <= smallScreenWidth;
 
 String formatAmount(BuildContext context, double amount) {
+  return formatAmountWithLocale(
+      Localizations.localeOf(context).toString(), amount);
+}
+
+String formatAmountWithLocale(String locale, double amount) {
   final NumberFormat currencyFormatter = NumberFormat.currency(
     // in English $10 is G110 ... confusing
     symbol: 'Äž1 ',
-    locale: Localizations.localeOf(context).toString(),
+    locale: locale,
     decimalDigits: 2,
   );
   return currencyFormatter.format(amount);
@@ -221,3 +231,10 @@ Future<Directory?> getAppSpecificExternalFilesDirectory(
   }
   return getExternalStorageDirectory();
 }
+
+ImageIcon get g1nkgoIcon => ImageIcon(
+      AssetImage(ginkgoIconLocation),
+      size: 24,
+    );
+
+String get ginkgoIconLocation => assets('img/favicon.png');
diff --git a/lib/ui/widgets/card_drawer.dart b/lib/ui/widgets/card_drawer.dart
index 6fe4a51f0f566ef8c47d3c74c4fb110ff08d191f..9f6041829e6bcfa4c94ffcb3a2e8d2d433033c2b 100644
--- a/lib/ui/widgets/card_drawer.dart
+++ b/lib/ui/widgets/card_drawer.dart
@@ -14,10 +14,7 @@ class CardDrawer extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final List<CesiumCard> cards = SharedPreferencesHelper().cesiumCards;
-    final ImageIcon g1nkgoIcon = ImageIcon(
-      AssetImage(assets('img/favicon.png')),
-      size: 24,
-    );
+
     return FutureBuilder<PackageInfo>(
       future: PackageInfo.fromPlatform(),
       builder: (BuildContext context, AsyncSnapshot<PackageInfo> snapshot) {
diff --git a/lib/ui/screens/g1_textfield.dart b/lib/ui/widgets/first_screen/g1_textfield.dart
similarity index 96%
rename from lib/ui/screens/g1_textfield.dart
rename to lib/ui/widgets/first_screen/g1_textfield.dart
index e3db739e9d83cc1ab62bf5e0e31eb7fc9c1f7b65..a2cd1aff3e1068f6423b9c81922209305c9838df 100644
--- a/lib/ui/screens/g1_textfield.dart
+++ b/lib/ui/widgets/first_screen/g1_textfield.dart
@@ -3,9 +3,9 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_svg/flutter_svg.dart';
 
-import '../../data/models/payment_cubit.dart';
-import '../../data/models/payment_state.dart';
-import '../ui_helpers.dart';
+import '../../../data/models/payment_cubit.dart';
+import '../../../data/models/payment_state.dart';
+import '../../ui_helpers.dart';
 
 class G1PayAmountField extends StatefulWidget {
   const G1PayAmountField({super.key});
diff --git a/lib/ui/screens/pay_form.dart b/lib/ui/widgets/first_screen/pay_form.dart
similarity index 95%
rename from lib/ui/screens/pay_form.dart
rename to lib/ui/widgets/first_screen/pay_form.dart
index 8d4ea40a87452f6d37bb1cd6cb4c252045296c2b..1512436e792c49dae0a5a539aaf10ce58e623f09 100644
--- a/lib/ui/screens/pay_form.dart
+++ b/lib/ui/widgets/first_screen/pay_form.dart
@@ -5,14 +5,14 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../data/models/node_list_cubit.dart';
-import '../../data/models/node_type.dart';
-import '../../data/models/payment_cubit.dart';
-import '../../data/models/payment_state.dart';
-import '../../data/models/transaction_cubit.dart';
-import '../../g1/api.dart';
-import '../logger.dart';
-import '../ui_helpers.dart';
+import '../../../data/models/node_list_cubit.dart';
+import '../../../data/models/node_type.dart';
+import '../../../data/models/payment_cubit.dart';
+import '../../../data/models/payment_state.dart';
+import '../../../data/models/transaction_cubit.dart';
+import '../../../g1/api.dart';
+import '../../logger.dart';
+import '../../ui_helpers.dart';
 import 'g1_textfield.dart';
 
 class PayForm extends StatefulWidget {
diff --git a/lib/ui/widgets/fourth_screen/transaction_page.dart b/lib/ui/widgets/fourth_screen/transaction_page.dart
index aa26228d101b86765ef883b64e43f295c231e97d..be5fc90a3317e8e4ba6021094bdf21ded53ecb82 100644
--- a/lib/ui/widgets/fourth_screen/transaction_page.dart
+++ b/lib/ui/widgets/fourth_screen/transaction_page.dart
@@ -83,6 +83,7 @@ class _TransactionsAndBalanceWidgetState
                   builder: (BuildContext lContext,
                           BoxConstraints constraints) =>
                       IconButton(
+                          // icon: const Icon(Icons.account_balance_wallet),
                           icon: const Icon(Icons.savings),
                           onPressed: () {
                             if (Backdrop.of(lContext).isBackLayerConcealed) {
diff --git a/lib/ui/widgets/third_screen/contacts_page.dart b/lib/ui/widgets/third_screen/contacts_page.dart
index c36f7fb4ff0520a4c717875f7b329fe3d8f5c9b2..128aca994993fa12bf7e470e914e63759cd6ab9c 100644
--- a/lib/ui/widgets/third_screen/contacts_page.dart
+++ b/lib/ui/widgets/third_screen/contacts_page.dart
@@ -88,6 +88,16 @@ class _ContactsPageState extends State<ContactsPage> {
                               icon: Icons.delete,
                               label: tr('delete_contact'),
                             ),
+                            if (showShare())
+                              SlidableAction(
+                                onPressed: (BuildContext c) =>
+                                    Share.share(contact.pubKey),
+                                backgroundColor:
+                                    Theme.of(context).secondaryHeaderColor,
+                                foregroundColor: Theme.of(context).primaryColor,
+                                icon: Icons.share,
+                                label: tr('share_this_key'),
+                              ),
                             /*  SlidableAction(
                             onPressed: (BuildContext c) {},
                             backgroundColor: const Color(0xFF21B7CA),
@@ -120,16 +130,6 @@ class _ContactsPageState extends State<ContactsPage> {
                               icon: Icons.copy,
                               label: tr('copy_contact_key'),
                             ),
-                            if (showShare())
-                              SlidableAction(
-                                onPressed: (BuildContext c) =>
-                                    Share.share(contact.pubKey),
-                                backgroundColor:
-                                    Theme.of(context).secondaryHeaderColor,
-                                foregroundColor: Theme.of(context).primaryColor,
-                                icon: Icons.share,
-                                label: tr('share_this_key'),
-                              ),
                             SlidableAction(
                               onPressed: (BuildContext c) {
                                 onSent(c, contact);
diff --git a/pubspec.lock b/pubspec.lock
index 83f8c8cc96feea6a539d1bac7f31ba29bb076dc0..5a1edf2bfea59cb6e26a9786781269d9d4ecc835 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -57,6 +57,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.10.0"
+  awesome_notifications:
+    dependency: "direct main"
+    description:
+      name: awesome_notifications
+      sha256: "2b430c75cc879d6cfd52bb6eb2b5c1591ed425347816408cdcbd3f6916bba14c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.7.4+1"
   backdrop:
     dependency: "direct main"
     description:
@@ -265,6 +273,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "5.0.2"
+  cron:
+    dependency: "direct main"
+    description:
+      name: cron
+      sha256: d98aa8cdad0cccdb6b098e6a1fb89339c180d8a229145fa4cd8c6fc538f0e35f
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.5.1"
   cross_file:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 6e6fd1c340241ac47faabb780a0ec59577156b79..d952124a7683354939212e3b6e6b67434696fef5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 0.0.14-SNAPSHOT
+version: 0.0.15
 
 environment:
   sdk: ">=2.17.1 <3.0.0"
@@ -71,6 +71,8 @@ dependencies:
   filesystem_picker: ^3.1.0
   path: ^1.8.2
   path_provider: ^2.0.14
+  awesome_notifications: ^0.7.4+1
+  cron: ^0.5.1
 
 dev_dependencies:
   flutter_test:
@@ -115,6 +117,7 @@ flutter:
     - assets/img/logo-cesium.png
     - assets/tx.json
     - assets/gva-tx.json
+    - assets/img/animated-bell.gif
 
   fonts:
     - family: Nunito
diff --git a/test/transactions_test.dart b/test/transactions_test.dart
index 5cad3a18e26053f1991bc9324410edb010e88760..ad223b29b87001dc7cec5fe313e3698249b7be0d 100644
--- a/test/transactions_test.dart
+++ b/test/transactions_test.dart
@@ -7,6 +7,11 @@ import 'package:ginkgo/data/models/transaction_type.dart';
 import 'package:ginkgo/g1/transaction_parser.dart';
 
 void main() {
+  final TransactionsAndBalanceState emptyState = TransactionsAndBalanceState(
+      transactions: const <Transaction>[],
+      balance: 0,
+      lastChecked: DateTime(1970));
+
   test('Test parsing', () async {
     TestWidgetsFlutterBinding.ensureInitialized();
     final String txData = await rootBundle.loadString('assets/tx.json');
@@ -29,7 +34,8 @@ void main() {
     final String txData = await rootBundle.loadString('assets/gva-tx.json');
     final TransactionsAndBalanceState result = transactionsGvaParser(
         (jsonDecode(txData) as Map<String, dynamic>)['data']
-            as Map<String, dynamic>);
+            as Map<String, dynamic>,
+        emptyState);
     expect(result.balance, equals(3));
     final List<Transaction> txs = result.transactions;
     for (final Transaction tx in txs) {
@@ -79,7 +85,8 @@ void main() {
 }''';
     final TransactionsAndBalanceState emptyResult = transactionsGvaParser(
         (jsonDecode(emptyTx) as Map<String, dynamic>)['data']
-            as Map<String, dynamic>);
+            as Map<String, dynamic>,
+        emptyState);
     expect(emptyResult.balance, equals(0));
     final List<Transaction> emptyTxs = emptyResult.transactions;
     expect(emptyTxs.length, equals(0));