URI: 
       tDisplay and refresh the status of incoming payment requests: - All requests have an expiration date - Paid requests are automatically removed from the list - Unpaid, unconfirmed and expired requests are displayed - Fix a bug in get_payment_status, conf was off by one - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 8010123c0858bf9735cdfc6637899799ea6891f2
   DIR parent 336cf81a6def88ae803c11926c93b56de2bed1b8
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Wed, 21 Aug 2019 18:25:36 +0200
       
       Display and refresh the status of incoming payment requests:
        - All requests have an expiration date
        - Paid requests are automatically removed from the list
        - Unpaid, unconfirmed and expired requests are displayed
        - Fix a bug in get_payment_status, conf was off by one
       
       Diffstat:
         M electrum/commands.py                |      13 ++++---------
         M electrum/gui/kivy/uix/screens.py    |      48 ++++++++++++-------------------
         M electrum/gui/kivy/uix/ui_screens/r… |      21 +++++----------------
         M electrum/gui/qt/main_window.py      |      26 +++++++++++++++++++-------
         M electrum/gui/qt/request_list.py     |      41 +++++++++++++++++++------------
         M electrum/gui/qt/util.py             |       8 +-------
         M electrum/lnworker.py                |      42 ++++++++++++++++++-------------
         M electrum/util.py                    |      39 ++++++++++++++++++-------------
         M electrum/wallet.py                  |      23 +++++++++++++++++------
       
       9 files changed, 137 insertions(+), 124 deletions(-)
       ---
   DIR diff --git a/electrum/commands.py b/electrum/commands.py
       t@@ -670,14 +670,9 @@ class Commands:
                return decrypted.decode('utf-8')
        
            def _format_request(self, out):
       -        pr_str = {
       -            PR_UNKNOWN: 'Unknown',
       -            PR_UNPAID: 'Pending',
       -            PR_PAID: 'Paid',
       -            PR_EXPIRED: 'Expired',
       -        }
       +        from .util import get_request_status
                out['amount_BTC'] = format_satoshis(out.get('amount'))
       -        out['status'] = pr_str[out.get('status', PR_UNKNOWN)]
       +        out['status'] = get_request_status(out)
                return out
        
            @command('w')
       t@@ -850,9 +845,9 @@ class Commands:
                return await self.lnworker._pay(invoice, attempts=attempts)
        
            @command('wn')
       -    async def addinvoice(self, requested_amount, message):
       +    async def addinvoice(self, requested_amount, message, expiration=3600):
                # using requested_amount because it is documented in param_descriptions
       -        payment_hash = await self.lnworker._add_invoice_coro(satoshis(requested_amount), message)
       +        payment_hash = await self.lnworker._add_invoice_coro(satoshis(requested_amount), message, expiration)
                invoice, direction, is_paid = self.lnworker.invoices[bh2u(payment_hash)]
                return invoice
        
   DIR diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
       t@@ -2,7 +2,6 @@ import asyncio
        from weakref import ref
        from decimal import Decimal
        import re
       -import datetime
        import threading
        import traceback, sys
        from enum import Enum, auto
       t@@ -27,7 +26,7 @@ from electrum.util import profiler, parse_URI, format_time, InvalidPassword, Not
        from electrum import bitcoin, constants
        from electrum.transaction import TxOutput, Transaction, tx_from_str
        from electrum.util import send_exception_to_crash_reporter, parse_URI, InvalidBitcoinURI
       -from electrum.util import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED, TxMinedInfo, age
       +from electrum.util import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED, TxMinedInfo, get_request_status, pr_expiration_values
        from electrum.plugin import run_hook
        from electrum.wallet import InternalAddressCorruption
        from electrum import simple_config
       t@@ -404,12 +403,14 @@ class SendScreen(CScreen):
        class ReceiveScreen(CScreen):
        
            kvname = 'receive'
       -    cards = {}
        
            def __init__(self, **kwargs):
                super(ReceiveScreen, self).__init__(**kwargs)
                self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.do_delete)]
       -        self.expiration = self.app.electrum_config.get('request_expiration', 3600) # 1 hour
       +        Clock.schedule_interval(lambda dt: self.update(), 5)
       +
       +    def expiry(self):
       +        return self.app.electrum_config.get('request_expiry', 3600) # 1 hour
        
            def clear(self):
                self.screen.address = ''
       t@@ -452,9 +453,8 @@ class ReceiveScreen(CScreen):
                amount = self.screen.amount
                amount = self.app.get_amount(amount) if amount else 0
                message = self.screen.message
       -        expiration = self.expiration
                if lightning:
       -            payment_hash = self.app.wallet.lnworker.add_invoice(amount, message)
       +            payment_hash = self.app.wallet.lnworker.add_invoice(amount, message, self.expiry())
                    request, direction, is_paid = self.app.wallet.lnworker.invoices.get(payment_hash.hex())
                    key = payment_hash.hex()
                else:
       t@@ -463,40 +463,37 @@ class ReceiveScreen(CScreen):
                        self.app.show_info(_('No address available. Please remove some of your pending requests.'))
                        return
                    self.screen.address = addr
       -            req = self.app.wallet.make_payment_request(addr, amount, message, expiration)
       +            req = self.app.wallet.make_payment_request(addr, amount, message, self.expiry())
                    self.app.wallet.add_payment_request(req, self.app.electrum_config)
                    key = addr
       +        self.clear()
                self.update()
                self.app.show_request(lightning, key)
        
            def get_card(self, req):
                is_lightning = req.get('lightning', False)
       -        status = req['status']
       -        #if status != PR_UNPAID:
       -        #    continue
                if not is_lightning:
                    address = req['address']
                    key = address
                else:
                    key = req['rhash']
                    address = req['invoice']
       -        timestamp = req.get('time', 0)
                amount = req.get('amount')
                description = req.get('memo', '')
       -        ci = self.cards.get(key)
       -        if ci is None:
       -            ci = {}
       -            ci['address'] = address
       -            ci['is_lightning'] = is_lightning
       -            ci['key'] = key
       -            ci['screen'] = self
       -            self.cards[key] = ci
       +        ci = {}
       +        ci['screen'] = self
       +        ci['address'] = address
       +        ci['is_lightning'] = is_lightning
       +        ci['key'] = key
                ci['amount'] = self.app.format_amount_and_units(amount) if amount else ''
                ci['memo'] = description
       -        ci['status'] = age(timestamp)
       +        ci['status'] = get_request_status(req)
       +        ci['is_expired'] = req['status'] == PR_EXPIRED
                return ci
        
            def update(self):
       +        if not self.loaded:
       +            return
                _list = self.app.wallet.get_sorted_requests(self.app.electrum_config)
                requests_container = self.screen.ids.requests_container
                requests_container.data = [self.get_card(item) for item in _list if item.get('status') != PR_PAID]
       t@@ -507,16 +504,9 @@ class ReceiveScreen(CScreen):
        
            def expiration_dialog(self, obj):
                from .dialogs.choice_dialog import ChoiceDialog
       -        choices = {
       -            10*60: _('10 minutes'),
       -            60*60: _('1 hour'),
       -            24*60*60: _('1 day'),
       -            7*24*60*60: _('1 week')
       -        }
                def callback(c):
       -            self.expiration = c
       -            self.app.electrum_config.set_key('request_expiration', c)
       -        d = ChoiceDialog(_('Expiration date'), choices, self.expiration, callback)
       +            self.app.electrum_config.set_key('request_expiry', c)
       +        d = ChoiceDialog(_('Expiration date'), pr_expiration_values, self.expiry(), callback)
                d.open()
        
            def do_delete(self, req):
   DIR diff --git a/electrum/gui/kivy/uix/ui_screens/receive.kv b/electrum/gui/kivy/uix/ui_screens/receive.kv
       t@@ -13,29 +13,22 @@
            valign: 'top'
        
        <RequestItem@CardItem>
       +    is_expired: False
            address: ''
            memo: ''
            amount: ''
            status: ''
       -    date: ''
       -    icon: 'atlas://electrum/gui/kivy/theming/light/important'
       -    Image:
       -        id: icon
       -        source: root.icon
       -        size_hint: None, 1
       -        width: self.height *.54
       -        mipmap: True
            BoxLayout:
                spacing: '8dp'
                height: '32dp'
                orientation: 'vertical'
                Widget
                RequestLabel:
       -            text: root.address
       +            text: root.memo
                    shorten: True
                Widget
                RequestLabel:
       -            text: root.memo
       +            text: root.address
                    color: .699, .699, .699, 1
                    font_size: '13sp'
                    shorten: True
       t@@ -54,7 +47,7 @@
                    text: root.status
                    halign: 'right'
                    font_size: '13sp'
       -            color: .699, .699, .699, 1
       +            color: (1., .2, .2, 1) if root.is_expired else (.7, .7, .7, 1)
                Widget
        
        <RequestRecycleView>:
       t@@ -75,7 +68,6 @@ ReceiveScreen:
            message: ''
            status: ''
            is_lightning: False
       -    show_list: True
        
            BoxLayout
                padding: '12dp', '12dp', '12dp', '12dp'
       t@@ -100,7 +92,6 @@ ReceiveScreen:
                            text: _('Lightning') if root.is_lightning else (s.address if s.address else _('Bitcoin Address'))
                            shorten: True
                            on_release: root.is_lightning = not root.is_lightning
       -                    #on_release: Clock.schedule_once(lambda dt: app.addresses_dialog(s))
                    CardSeparator:
                        opacity: message_selection.opacity
                        color: blue_bottom.foreground_color
       t@@ -144,7 +135,7 @@ ReceiveScreen:
                        icon: 'atlas://electrum/gui/kivy/theming/light/list'
                        size_hint: 0.5, None
                        height: '48dp'
       -                on_release: root.show_list = not root.show_list
       +                on_release: Clock.schedule_once(lambda dt: app.addresses_dialog())
                    IconButton:
                        icon: 'atlas://electrum/gui/kivy/theming/light/clock1'
                        size_hint: 0.5, None
       t@@ -166,5 +157,3 @@ ReceiveScreen:
                    id: requests_container
                    scroll_type: ['bars', 'content']
                    bar_width: '25dp'
       -            opacity: 1 if root.show_list else 0
       -            disabled: not root.show_list
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -73,6 +73,7 @@ from electrum.exchange_rate import FxThread
        from electrum.simple_config import SimpleConfig
        from electrum.logging import Logger
        from electrum.paymentrequest import PR_PAID
       +from electrum.util import pr_expiration_values
        
        from .exception_window import Exception_Hook
        from .amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, FeerateEdit
       t@@ -83,7 +84,7 @@ from .fee_slider import FeeSlider
        from .util import (read_QIcon, ColorScheme, text_dialog, icon_path, WaitingDialog,
                           WindowModalDialog, ChoicesLayout, HelpLabel, FromList, Buttons,
                           OkButton, InfoButton, WWLabel, TaskThread, CancelButton,
       -                   CloseButton, HelpButton, MessageBoxMixin, EnterButton, expiration_values,
       +                   CloseButton, HelpButton, MessageBoxMixin, EnterButton,
                           ButtonsLineEdit, CopyCloseButton, import_meta_gui, export_meta_gui,
                           filename_field, address_field, char_width_in_lineedit, webopen)
        from .util import ButtonsTextEdit
       t@@ -753,6 +754,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                return fileName
        
            def timer_actions(self):
       +        self.request_list.refresh_status()
                # Note this runs in the GUI thread
                if self.need_update.is_set():
                    self.need_update.clear()
       t@@ -945,9 +947,20 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                self.connect_fields(self, self.receive_amount_e, self.fiat_receive_e, None)
        
                self.expires_combo = QComboBox()
       -        self.expires_combo.addItems([i[0] for i in expiration_values])
       -        self.expires_combo.setCurrentIndex(3)
       +        evl = sorted(pr_expiration_values.items())
       +        evl_keys = [i[0] for i in evl]
       +        evl_values = [i[1] for i in evl]
       +        default_expiry = self.config.get('request_expiry', 3600)
       +        try:
       +            i = evl_keys.index(default_expiry)
       +        except ValueError:
       +            i = 0
       +        self.expires_combo.addItems(evl_values)
       +        self.expires_combo.setCurrentIndex(i)
                self.expires_combo.setFixedWidth(self.receive_amount_e.width())
       +        def on_expiry(i):
       +            self.config.set_key('request_expiry', evl_keys[i])
       +        self.expires_combo.currentIndexChanged.connect(on_expiry)
                msg = ' '.join([
                    _('Expiration date of your request.'),
                    _('This information is seen by the recipient if you send them a signed payment request.'),
       t@@ -1057,13 +1070,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
            def create_invoice(self, is_lightning):
                amount = self.receive_amount_e.get_amount()
                message = self.receive_message_e.text()
       -        i = self.expires_combo.currentIndex()
       -        expiration = list(map(lambda x: x[1], expiration_values))[i]
       +        expiry = self.config.get('request_expiry', 3600)
                if is_lightning:
       -            payment_hash = self.wallet.lnworker.add_invoice(amount, message)
       +            payment_hash = self.wallet.lnworker.add_invoice(amount, message, expiry)
                    key = bh2u(payment_hash)
                else:
       -            key = self.create_bitcoin_request(amount, message, expiration)
       +            key = self.create_bitcoin_request(amount, message, expiry)
                    self.address_list.update()
                self.request_list.update()
                self.request_list.select_key(key)
   DIR diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py
       t@@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QMenu, QHeaderView
        from PyQt5.QtCore import Qt, QItemSelectionModel
        
        from electrum.i18n import _
       -from electrum.util import format_time, age
       +from electrum.util import format_time, age, get_request_status
        from electrum.util import PR_UNPAID, PR_EXPIRED, PR_PAID, PR_UNKNOWN, PR_INFLIGHT, pr_tooltips
        from electrum.lnutil import SENT, RECEIVED
        from electrum.plugin import run_hook
       t@@ -85,20 +85,28 @@ class RequestList(MyTreeView):
                item = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE))
                request_type = item.data(ROLE_REQUEST_TYPE)
                key = item.data(ROLE_RHASH_OR_ADDR)
       -        if request_type == REQUEST_TYPE_BITCOIN:
       -            req = self.wallet.receive_requests.get(key)
       -            if req is None:
       -                self.update()
       -                return
       -            req = self.wallet.get_request_URI(key)
       -        elif request_type == REQUEST_TYPE_LN:
       -            req, direction, is_paid = self.wallet.lnworker.invoices.get(key) or (None, None, None)
       -            if req is None:
       -                self.update()
       -                return
       -        else:
       -            raise Exception(f"unknown request type: {request_type}")
       -        self.parent.receive_address_e.setText(req)
       +        is_lightning = request_type == REQUEST_TYPE_LN
       +        req = self.wallet.get_request(key, is_lightning)
       +        if req is None:
       +            self.update()
       +            return
       +        text = req.get('invoice') if is_lightning else req.get('URI')
       +        self.parent.receive_address_e.setText(text)
       +
       +    def refresh_status(self):
       +        m = self.model()
       +        for r in range(m.rowCount()):
       +            idx = m.index(r, self.Columns.STATUS)
       +            date_idx = idx.sibling(idx.row(), self.Columns.DATE)
       +            date_item = m.itemFromIndex(date_idx)
       +            status_item = m.itemFromIndex(idx)
       +            key = date_item.data(ROLE_RHASH_OR_ADDR)
       +            is_lightning = date_item.data(ROLE_REQUEST_TYPE) == REQUEST_TYPE_LN
       +            req = self.wallet.get_request(key, is_lightning)
       +            if req:
       +                status_str = get_request_status(req)
       +                status_item.setText(status_str)
       +                status_item.setIcon(read_QIcon(pr_icons.get(req['status'])))
        
            def update(self):
                self.wallet = self.parent.wallet
       t@@ -116,7 +124,8 @@ class RequestList(MyTreeView):
                    message = req['memo']
                    date = format_time(timestamp)
                    amount_str = self.parent.format_amount(amount) if amount else ""
       -            labels = [date, message, amount_str, pr_tooltips.get(status,'')]
       +            status_str = get_request_status(req)
       +            labels = [date, message, amount_str, status_str]
                    items = [QStandardItem(e) for e in labels]
                    self.set_editability(items)
                    items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE)
   DIR diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py
       t@@ -45,16 +45,10 @@ pr_icons = {
            PR_UNPAID:"unpaid.png",
            PR_PAID:"confirmed.png",
            PR_EXPIRED:"expired.png",
       -    PR_INFLIGHT:"lightning.png",
       +    PR_INFLIGHT:"unconfirmed.png",
        }
        
        
       -expiration_values = [
       -    (_('1 hour'), 60*60),
       -    (_('1 day'), 24*60*60),
       -    (_('1 week'), 7*24*60*60),
       -    (_('Never'), None)
       -]
        
        
        class EnterButton(QPushButton):
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -868,8 +868,8 @@ class LNWallet(LNWorker):
                        raise PaymentFailure(_("No path found"))
                return route
        
       -    def add_invoice(self, amount_sat, message):
       -        coro = self._add_invoice_coro(amount_sat, message)
       +    def add_invoice(self, amount_sat, message, expiry):
       +        coro = self._add_invoice_coro(amount_sat, message, expiry)
                fut = asyncio.run_coroutine_threadsafe(coro, self.network.asyncio_loop)
                try:
                    return fut.result(timeout=5)
       t@@ -877,7 +877,7 @@ class LNWallet(LNWorker):
                    raise Exception(_("add_invoice timed out"))
        
            @log_exceptions
       -    async def _add_invoice_coro(self, amount_sat, message):
       +    async def _add_invoice_coro(self, amount_sat, message, expiry):
                payment_preimage = os.urandom(32)
                payment_hash = sha256(payment_preimage)
                amount_btc = amount_sat/Decimal(COIN) if amount_sat else None
       t@@ -887,7 +887,8 @@ class LNWallet(LNWorker):
                                     "Other clients will likely not be able to send to us.")
                invoice = lnencode(LnAddr(payment_hash, amount_btc,
                                          tags=[('d', message),
       -                                        ('c', MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE)]
       +                                        ('c', MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
       +                                        ('x', expiry)]
                                               + routing_hints),
                                   self.node_keypair.privkey)
                self.save_invoice(payment_hash, invoice, RECEIVED, is_paid=False)
       t@@ -933,26 +934,31 @@ class LNWallet(LNWorker):
                except KeyError as e:
                    raise UnknownPaymentHash(payment_hash) from e
        
       +    def get_request(self, key):
       +        invoice, direction, is_paid = self.invoices[key]
       +        status = self.get_invoice_status(key)
       +        lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
       +        amount_sat = lnaddr.amount*COIN if lnaddr.amount else None
       +        description = lnaddr.get_description()
       +        timestamp = lnaddr.date
       +        return {
       +            'lightning':True,
       +            'status':status,
       +            'amount':amount_sat,
       +            'time':timestamp,
       +            'exp':lnaddr.get_expiry(),
       +            'memo':description,
       +            'rhash':key,
       +            'invoice': invoice
       +        }
       +
            def get_invoices(self):
                items = self.invoices.items()
                out = []
                for key, (invoice, direction, is_paid) in items:
                    if direction == SENT:
                        continue
       -            status = self.get_invoice_status(key)
       -            lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
       -            amount_sat = lnaddr.amount*COIN if lnaddr.amount else None
       -            description = lnaddr.get_description()
       -            timestamp = lnaddr.date
       -            out.append({
       -                'lightning':True,
       -                'status':status,
       -                'amount':amount_sat,
       -                'time':timestamp,
       -                'memo':description,
       -                'rhash':key,
       -                'invoice': invoice
       -            })
       +            out.append(self.get_request(key))
                return out
        
            async def _calc_routing_hints_for_invoice(self, amount_sat):
   DIR diff --git a/electrum/util.py b/electrum/util.py
       t@@ -78,16 +78,34 @@ PR_UNPAID  = 0
        PR_EXPIRED = 1
        PR_UNKNOWN = 2     # sent but not propagated
        PR_PAID    = 3     # send and propagated
       -PR_INFLIGHT = 4    # lightning
       +PR_INFLIGHT = 4    # unconfirmed
        
        pr_tooltips = {
            PR_UNPAID:_('Pending'),
            PR_PAID:_('Paid'),
            PR_UNKNOWN:_('Unknown'),
            PR_EXPIRED:_('Expired'),
       -    PR_INFLIGHT:_('Inflight')
       +    PR_INFLIGHT:_('Paid (unconfirmed)')
        }
        
       +pr_expiration_values = {
       +    10*60: _('10 minutes'),
       +    60*60: _('1 hour'),
       +    24*60*60: _('1 day'),
       +    7*24*60*60: _('1 week')
       +}
       +
       +def get_request_status(req):
       +    status = req['status']
       +    status_str = pr_tooltips[status]
       +    if status == PR_UNPAID:
       +        if req.get('exp'):
       +            expiration = req['exp'] + req['time']
       +            status_str = _('Expires') + ' ' + age(expiration, include_seconds=True)
       +        else:
       +            status_str = _('Pending')
       +    return status_str
       +
        
        class UnknownBaseUnit(Exception): pass
        
       t@@ -638,22 +656,11 @@ def time_difference(distance_in_time, include_seconds):
            distance_in_seconds = int(round(abs(distance_in_time.days * 86400 + distance_in_time.seconds)))
            distance_in_minutes = int(round(distance_in_seconds/60))
        
       -    if distance_in_minutes <= 1:
       +    if distance_in_minutes == 0:
                if include_seconds:
       -            for remainder in [5, 10, 20]:
       -                if distance_in_seconds < remainder:
       -                    return "less than %s seconds" % remainder
       -            if distance_in_seconds < 40:
       -                return "half a minute"
       -            elif distance_in_seconds < 60:
       -                return "less than a minute"
       -            else:
       -                return "1 minute"
       +            return "%s seconds" % distance_in_seconds
                else:
       -            if distance_in_minutes == 0:
       -                return "less than a minute"
       -            else:
       -                return "1 minute"
       +            return "less than a minute"
            elif distance_in_minutes < 45:
                return "%s minutes" % distance_in_minutes
            elif distance_in_minutes < 90:
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -46,6 +46,7 @@ from .util import (NotEnoughFunds, UserCancelled, profiler,
                           WalletFileException, BitcoinException,
                           InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
                           Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex)
       +from .util import age
        from .simple_config import get_config
        from .bitcoin import (COIN, TYPE_ADDRESS, is_address, address_to_script,
                              is_minikey, relayfee, dust_threshold)
       t@@ -59,7 +60,7 @@ from .transaction import Transaction, TxOutput, TxOutputHwInfo
        from .plugin import run_hook
        from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
                                           TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE)
       -from .paymentrequest import (PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED,
       +from .paymentrequest import (PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_INFLIGHT,
                                     InvoiceStore)
        from .contacts import Contacts
        from .interface import NetworkException
       t@@ -1204,7 +1205,7 @@ class Abstract_Wallet(AddressSynchronizer):
                    txid, n = txo.split(':')
                    info = self.db.get_verified_tx(txid)
                    if info:
       -                conf = local_height - info.height
       +                conf = local_height - info.height + 1
                    else:
                        conf = 0
                    l.append((conf, v))
       t@@ -1282,13 +1283,23 @@ class Abstract_Wallet(AddressSynchronizer):
                expiration = r.get('exp')
                if expiration and type(expiration) != int:
                    expiration = 0
       -
                paid, conf = self.get_payment_status(address, amount)
       -        status = PR_PAID if paid else PR_UNPAID
       -        if status == PR_UNPAID and expiration is not None and time.time() > timestamp + expiration:
       -            status = PR_EXPIRED
       +        if not paid:
       +            if expiration is not None and time.time() > timestamp + expiration:
       +                status = PR_EXPIRED
       +            else:
       +                status = PR_UNPAID
       +        else:
       +            status = PR_INFLIGHT if conf <= 0 else PR_PAID
                return status, conf
        
       +    def get_request(self, key, is_lightning):
       +        if not is_lightning:
       +            req = self.get_payment_request(key, {})
       +        else:
       +            req = self.lnworker.get_request(key)
       +        return req
       +
            def receive_tx_callback(self, tx_hash, tx, tx_height):
                super().receive_tx_callback(tx_hash, tx, tx_height)
                for txo in tx.outputs():