itunes-iap icon indicating copy to clipboard operation
itunes-iap copied to clipboard

IndexError: list index out of range

Open aaksarin opened this issue 7 years ago • 7 comments

Hello. We got this problem with itunes-iap 2.5

[2017-11-08 15:00:20,934] [ERROR] views.py:3308 - root: list index out of range
Traceback (most recent call last):
  File "/home/django/atcontrol/releases/1510061996/mayak/api_v3/views.py", line 3243, in purchase_ios
    log.info(res.receipt.last_in_app)
  File "/home/django/atcontrol/releases/1510061996/venv/local/lib/python2.7/site-packages/itunesiap/receipt.py", line 245, in last_in_app	
    self.in_app, key=lambda x: x['original_purchase_date_ms'])[-1]
IndexError: list index out of range

part of view from our django rest framework api with problem line of code

@api_view(['POST'])
def purchase_ios(request):
    log.info('purchase_ios (%s) POST: %s' % (request.user, request.POST))
    try:
        res = itunesiap.verify(request.POST.get("receipt", None), settings.ITUNES_PASSWORD, verify_ssl=False,
                               use_production=True, use_sandbox=True)
        log.info(res.receipt.last_in_app)
...

Any ideas what's wrong?

aaksarin avatar Nov 08 '17 15:11 aaksarin

Hello. Do you have any expected result? I want to know there was actually no in_apps in the receipt or itunesiap failed to bring the data even if they were in receipts.

youknowone avatar Nov 29 '17 18:11 youknowone

I'm also experiencing a similar IndexError in one of my api views:

# ...
if sandbox:
    with itunesiap.env.sandbox:
        try:
            response = itunesiap.verify(raw_data, use_sandbox=True)
            return True, response
        except (ItunesServerNotAvailable, ItunesServerNotReachable):
            no_response = True
        except itunesiap.exc.InvalidReceipt as exc:
            error = exc.description

Sentry actually indicates the error/exception being raised when exiting the contextmanager:

itunesiap/environment.py in __exit__ at line 42
        self._ctx_id = len(self._stack)
        self.push()
        return self
    def __exit__(self, exc_type, exc_value, tb):
        self._stack.pop(self._ctx_id)  # this line

Any clues on what might be happening?

maccinza avatar Mar 05 '18 14:03 maccinza

I've come across the same issue. In my case it is caused because 'in_app' list can be empty.

    return response.receipt.last_in_app
  File "/home/vagrant/.local/share/virtualenvs/Server/lib/python3.6/site-packages/itunesiap/receipt.py", line 310, in last_in_app
    self.in_app, key=lambda x: x['original_purchase_date_ms'])[-1]
IndexError: list index out of range

https://stackoverflow.com/questions/36010595/itunes-reciept-validation-in-app-empty

An empty in_app array indicates that StoreKit has not recorded any transactions for that user yet. It may be that the application receipt has not yet been updated. When this happens, your app can inform the user that the receipt does not appear current and ask whether to refresh it. Upon user agreement, your app should use the SKReceiptRefreshRequest class to update the receipt. At this point, if StoreKit has recorded a purchase for the user, your app receipt will show it in in_app. See Refreshing the App Receipt for more information on how to update a receipt.

evgenybf avatar Mar 08 '19 11:03 evgenybf

It occurred to me, that the algo used to retrieve last_in_app is wrong. There can be several transactions having the same original_purchase_date. For example, it's true for subscriptions which share the same original_transaction_id:

     {
            "quantity": "1",
            "product_id": "com.subscription",
            "transaction_id": "1000000581056268",
            "original_transaction_id": "1000000506536546",
            "purchase_date": "2019-02-28 11:16:27 Etc/GMT",
            "purchase_date_ms": "1551352587000",
            "purchase_date_pst": "2019-02-28 03:16:27 America/Los_Angeles",
            "original_purchase_date": "2019-02-28 11:11:27 Etc/GMT",
            "original_purchase_date_ms": "1551352287000",
            "original_purchase_date_pst": "2019-02-28 03:11:27 America/Los_Angeles",
            "expires_date": "2019-02-28 11:21:27 Etc/GMT",
            "expires_date_ms": "1551352887000",
            "expires_date_pst": "2019-02-28 03:21:27 America/Los_Angeles",
            "web_order_line_item_id": "1000000042979542",
            "is_trial_period": "false",
            "is_in_intro_offer_period": "false"
        },
        {
            "quantity": "1",
            "product_id": "com.subscription",
            "transaction_id": "1000000581056278",
            "original_transaction_id": "1000000506536546",
            "purchase_date": "2019-02-28 11:21:27 Etc/GMT",
            "purchase_date_ms": "1551352887000",
            "purchase_date_pst": "2019-02-28 03:21:27 America/Los_Angeles",
            "original_purchase_date": "2019-02-28 11:11:27 Etc/GMT",
            "original_purchase_date_ms": "1551352287000",
            "original_purchase_date_pst": "2019-02-28 03:11:27 America/Los_Angeles",
            "expires_date": "2019-02-28 11:26:27 Etc/GMT",
            "expires_date_ms": "1551353187000",
            "expires_date_pst": "2019-02-28 03:26:27 America/Los_Angeles",
            "web_order_line_item_id": "1000000042979655",
            "is_trial_period": "false",
            "is_in_intro_offer_period": "false"
        },

So, the correct way to sort the transactionы is to sort them by "purchase_date_ms" instead. Actually, the key may need to be even more complex:

    @staticmethod
    def _get_transaction_date_ms(in_app):
        try:
            return in_app.cancellation_date_ms
        except MissingFieldError:
            pass
        try:
            return in_app.purchase_date_ms
        except MissingFieldError:
            pass
        return in_app.original_purchase_date_ms

evgenybf avatar Oct 25 '19 08:10 evgenybf

@maccinza @aaksarin Because I don't work for in-app-purchase anymore, it is hard to check issues. Can anyone share a full receipt with this error please?

@evgenybf I tried to reproduce the issue with your example but it passed tests. Would you share full receipt please?

A patch with test cases are also very appreciated :)

youknowone avatar Nov 02 '19 08:11 youknowone

I'm having the same issue - while it passes all sandbox tests, verifying some real-world subscription transactions returns an empty in_app array, which errors out when we try to access response.receipt.last_in_app.

I haven't been able to replicate the conditions under which we get the empty in_app.

It's an auto-renewing subscription, if that makes any difference - might be related to this: https://developer.apple.com/library/archive/technotes/tn2413/index.html#//apple_ref/doc/uid/DTS40016228-CH1-RECEIPT-MY_APP_VALIDATES_ITS_RECEIPT_WITH_THE_APP_STORE_VIA_PAYMENTQUEUE_UPDATEDTRANSACTIONS__AFTER_A_SUCCESSFUL_PURCHASE__HOWEVER__THE_RETURNED_RECEIPT_CONTAINS_AN_EMPTY_IN_APP_ARRAY_RATHER_THAN_THE_EXPECTED_PRODUCTS

datawaslost avatar Dec 10 '19 20:12 datawaslost

While it'd be good to get at the deeper issue, a partial solution would be to simply return None for last_in_app when the in_app list is empty.

datawaslost avatar Dec 10 '19 20:12 datawaslost