Repository: python-telegram-bot/python-telegram-bot

Issue:
Payment is not working if written AFTER a conversation handler
<!--
Thanks for reporting issues of python-telegram-bot!

Use this template to notify us if you found a bug, or if you want to request a new feature.
If you're looking for help with programming your bot using our library, feel free to ask your
questions in out telegram group at: https://t.me/pythontelegrambotgroup

To make it easier for us to help you please enter detailed information below.

Please note, we only support the latest version of python-telegram-bot and
master branch. Please make sure to upgrade & recreate the issue on the latest
version prior to opening an issue.
-->
### Steps to reproduce
1. Just make a test payment as shown in the official example https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/paymentbot.py

2. Add the payment handlers **AFTER a conversation handler**, like this:
```
conv_handler = ConversationHandler(
    ...
)

# Add command handler to start the payment invoice
dp.add_handler(CommandHandler("shipping", start_with_shipping_callback))
dp.add_handler(CommandHandler("noshipping", start_without_shipping_callback))

# Optional handler if your product requires shipping
dp.add_handler(ShippingQueryHandler(shipping_callback))

# Pre-checkout handler to final check
dp.add_handler(PreCheckoutQueryHandler(precheckout_callback))

# Success! Notify your user!
dp.add_handler(MessageHandler(Filters.successful_payment, successful_payment_callback))
```

3. Run it and try to make a payment (I used /noshipping).

### Expected behaviour
It should let you make the full payment successfully.

### Actual behaviour
It lets you type all payment information, but in the last step, when clicking the last "Pay" button, it crash (see [Logs](#logs)) and doesn't call precheckout_callback function throw PreCheckoutQueryHandler.

### Additional information

- It works perfectly if you add it BEFORE the ConversationHandler.
- It seems to be recognized as if payment handlers were located inside the ConversationHandler.
- I don't think it to affect this issue, but I'm using Stripe payment in testing mode.

### Configuration
I'm using the last version up to date. (e8fac19de31b3d246d0253ac0267f7253a22d526)

### Logs
```
2017-12-02 17:31:52,827 - telegram.ext.dispatcher - ERROR - An uncaught error was raised while processing the update
Traceback (most recent call last):
  File "C:\Users\Carlos\Documents\Programacion\TelegramBots\Pickatext\telegram\ext\dispatcher.py", line 278, in process_update
    for handler in (x for x in self.handlers[group] if x.check_update(update)):
  File "C:\Users\Carlos\Documents\Programacion\TelegramBots\Pickatext\telegram\ext\dispatcher.py", line 278, in <genexpr>
    for handler in (x for x in self.handlers[group] if x.check_update(update)):
  File "C:\Users\Carlos\Documents\Programacion\TelegramBots\Pickatext\telegram\ext\conversationhandler.py", line 215, in check_update
    key = self._get_key(update)
  File "C:\Users\Carlos\Documents\Programacion\TelegramBots\Pickatext\telegram\ext\conversationhandler.py", line 185, in _get_key
    key.append(chat.id)
AttributeError: 'NoneType' object has no attribute 'id'
```


Discussion:
I believe this was already resolved in chat. Can you confirm?

@Eldinnie It was given a temporary/fast solution by chat, the one which is indicated in the "**Additional information**" section:

> - It works perfectly if you add it BEFORE the ConversationHandler.

But it is still a bug that you can't add it after the ConversationHandler. I think it should be solved and/or clearly specified in the documentation. If I wouldn't have asked in chat, I'd "never" have be able to solve this issue.

I also opened this issue due to the @jh0ker recommendation.
Ok, I missed that. I will take a closer look soon. Thanks for your clarification.

Resolving patch:
diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py
--- a/telegram/ext/conversationhandler.py
+++ b/telegram/ext/conversationhandler.py
@@ -206,10 +206,11 @@ def check_update(self, update):
 
         """
         # Ignore messages in channels
-        if (not isinstance(update, Update) or update.channel_post or self.per_chat
-                and (update.inline_query or update.chosen_inline_result) or self.per_message
-                and not update.callback_query or update.callback_query and self.per_chat
-                and not update.callback_query.message):
+        if (not isinstance(update, Update) or
+                update.channel_post or
+                self.per_chat and not update.effective_chat or
+                self.per_message and not update.callback_query or
+                update.callback_query and self.per_chat and not update.callback_query.message):
             return False
 
         key = self._get_key(update)


Test patch:
diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py
--- a/tests/test_conversationhandler.py
+++ b/tests/test_conversationhandler.py
@@ -20,7 +20,8 @@
 
 import pytest
 
-from telegram import Update, Message, User, Chat, CallbackQuery
+from telegram import (CallbackQuery, Chat, ChosenInlineResult, InlineQuery, Message,
+                      PreCheckoutQuery, ShippingQuery, Update, User)
 from telegram.ext import (ConversationHandler, CommandHandler, CallbackQueryHandler)
 
 
@@ -277,3 +278,19 @@ def test_channel_message_without_chat(self, bot):
         message = Message(0, None, None, Chat(0, Chat.CHANNEL, 'Misses Test'), bot=bot)
         update = Update(0, message=message)
         assert not handler.check_update(update)
+
+    def test_all_update_types(self, dp, bot, user1):
+        handler = ConversationHandler(entry_points=[CommandHandler('start', self.start_end)],
+                                      states={}, fallbacks=[])
+        message = Message(0, user1, None, self.group, text='ignore', bot=bot)
+        callback_query = CallbackQuery(0, user1, None, message=message, data='data', bot=bot)
+        chosen_inline_result = ChosenInlineResult(0, user1, 'query', bot=bot)
+        inline_query = InlineQuery(0, user1, 'query', 0, bot=bot)
+        pre_checkout_query = PreCheckoutQuery(0, user1, 'USD', 100, [], bot=bot)
+        shipping_query = ShippingQuery(0, user1, [], None, bot=bot)
+        assert not handler.check_update(Update(0, callback_query=callback_query))
+        assert not handler.check_update(Update(0, chosen_inline_result=chosen_inline_result))
+        assert not handler.check_update(Update(0, inline_query=inline_query))
+        assert not handler.check_update(Update(0, message=message))
+        assert not handler.check_update(Update(0, pre_checkout_query=pre_checkout_query))
+        assert not handler.check_update(Update(0, shipping_query=shipping_query))