omz / AppSales-Mobile

App Sales allows iPhone and Mac App Store developers to download and analyze their daily and weekly sales reports from iTunes Connect.
http://omz-software.com
1.89k stars 407 forks source link

Dealing with an account using two-factor authentication (and other download errors) #280

Open rmaddy opened 8 years ago

rmaddy commented 8 years ago

After installing the latest version of this app and deleting sales reports from October 26 and later I tried to redownload those reports. The app appeared like it was downloading and processing the reports but in the end, there were no bars for those days.

Then I noticed a bunch of errors in the console about the fact that my account uses two-factor authentication and I should create an app-specific app id.

So I guess this is an enhancement request that could help anyone complaining that this app isn't downloading any new reports. The app didn't indicate there was any error during the download. It would be nice if this app could present any download errors instead of silently ignoring them. Such an error message would have saved me some head scratching trying to figure out why I wasn't getting any new reports.

BTW - in my case I created app-specific app id and updated the password in this app with the new password and it started working again.

nicolasgomollon commented 8 years ago

Thanks for this! I ran into the same exact issue, and couldn’t figure out what was going on!

I just incorporated your idea into my copy of this project so I can be alerted about these kinds of things in the future:

In ReportDownloadOperation.m line 60:

NSMutableDictionary *errors = [[NSMutableDictionary alloc] init];

…and replaced line 177 in ReportDownloadOperation.m with:

// The message "Daily Reports are only available for past 365 days. Please enter a new date."
// just means that the report in question has not yet been released.
// We can safely ignore this error and move on.
if ([errorMessage rangeOfString:@"past 365 days"].location == NSNotFound) {
    NSLog(@"%@ -> %@", reportDateString, errorMessage);

    NSInteger year = [[reportDateString substringWithRange:NSMakeRange(0, 4)] intValue];
    NSInteger month = [[reportDateString substringWithRange:NSMakeRange(4, 2)] intValue];
    NSInteger day = [[reportDateString substringWithRange:NSMakeRange(6, 2)] intValue];

    NSDateComponents *components = [[NSDateComponents alloc] init];
    [components setYear:year];
    [components setMonth:month];
    [components setDay:day];

    NSDate *reportDate = [[NSCalendar currentCalendar] dateFromComponents:components];

    NSMutableDictionary *reportTypes = [[NSMutableDictionary alloc] initWithDictionary:errors[errorMessage]];

    NSMutableArray *reports = [[NSMutableArray alloc] initWithArray:reportTypes[dateType]];
    [reports addObject:reportDate];
    reportTypes[dateType] = reports;

    errors[errorMessage] = reportTypes;
}

…and right before line 225 in ReportDownloadOperation.m:

dispatch_async(dispatch_get_main_queue(), ^{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.timeStyle = NSDateFormatterNoStyle;
    dateFormatter.dateStyle = NSDateFormatterShortStyle;
    for (NSString *error in errors.allKeys) {
        NSString *message = error;

        NSDictionary *reportTypes = errors[error];
        for (NSString *reportType in reportTypes.allKeys) {
            message = [message stringByAppendingFormat:@"\n\n%@ Reports:", reportType];
            for (NSDate *reportDate in reportTypes[reportType]) {
                message = [message stringByAppendingFormat:@"\n%@", [dateFormatter stringFromDate:reportDate]];
            }
        }

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", nil)
                                                            message:message
                                                           delegate:nil
                                                  cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                                  otherButtonTitles:nil];
        [alertView show];
    }
});

This code generates a summarized error alert for each distinct error message encountered: img_2703

ddaddy commented 8 years ago

I've been seeing the error The report could not be downloaded, please try again later. but I don't think it's related to 2-factor auth. Refreshing again seems to pull the report down.

statcoder commented 8 years ago

Thanks for posting your solution to the problem. For those who might not be aware of the method of creating an app-specific password: https://support.apple.com/en-us/HT204397

Morpheus2002 commented 8 years ago

I'm getting an error after daily report is downloaded. Maybe it's payment download issue?

img_0253

ddaddy commented 8 years ago

Yes it's payments. I haven't had the time to look into it myself yet. iTunesConnect login page changed.

nicolasgomollon commented 8 years ago

I’ve just added UI to handle two-step verification and proper app-specific password support to my fork of AppSales, and submitted a pull request to @ddaddy's fork (https://github.com/ddaddy/AppSales-Mobile/pull/1). Go check it out if you’re interested.

Morpheus2002 commented 8 years ago

@nicolasgomollon does it fix payments download error?

BTW, is there any option in ITC to download payments manually to import them to App Sales?

ddaddy commented 8 years ago

@nicolasgomollon Thanks for your additions. I do apologise, I haven't had the time to take a look yet, but it is on my todo list.

@Morpheus2002 No it won't fix the payments, however that is also on my todo list to take a look at. There's no way to export/import payments as they are literally scraped from the web page.

Morpheus2002 commented 8 years ago

@ddaddy thanks for your answer! I took a look at ITC, but couldn't find any fix for this issue.

ddaddy commented 8 years ago

The payment issue, will be down to the new login view for iTC.

nicolasgomollon commented 8 years ago

@Morpheus2002 @ddaddy My fork of AppSales does indeed fix the Payments download issue (among other minor issues). I also added in some UI to handle Two-Step Verification code input, since developer accounts with this enabled are now required to verify their account on untrusted devices, so my fork supports these accounts as well.

The problem was initially due to the changed iTunes Connect login page.

The way I accomplished this was by performing the login via the Member Center (which has a much more simplified page structure, and is much less hacky), and then visiting the Payments and Financial Reports page in iTunes Connect. This works because both the Member Center and iTunes Connect use the same cookies to recognize a logged in user.

I’ve made so many other changes to my fork since fixing this, so you might have to dig in to my commit history to find the right commits, or you can download a copy of my fork and give it a try for yourself.


EDIT: Here are the highlight commits that address this issue:

ddaddy commented 8 years ago

@nicolasgomollon that's awesome, thanks. It just moved higher up my todo list.

pupasani commented 8 years ago

@nicolasgomollon still getting same payments error (indicated parsing error of login page) on your latest commit

nicolasgomollon commented 8 years ago

@pupasani Do you mind providing additional details about the error?

The page structure might be a bit different for accounts that only have one vendor ID (and mine happens to have two), which may be the cause of your issue. Could you provide a snippet of the source code of your Payments and Financial Reports page, specifically the part where the vendor ID is shown? (See example snippet below.)

    <form name="mainForm" enctype="multipart/form-data" method="post" action="/WebObjects/iTunesConnect.woa/wo/25.0.0.11.5.0.9.3.1">
        <table width="980" border="0" cellpadding="0" cellspacing="0">
            <tr>
                <td width="8"><img src="https://itc.mzstatic.com/itc/images/wrapper_topl.png" width="8" height="51" alt=""></td>
                <td width="*" background="https://itc.mzstatic.com/itc/images/wrapper_top.png" height="51" align="center" class="header_text">

                    <div class="vendor-id-container">
                        <select onMouseDown="toggleVendorIdWidth()" style='top: 10px' onBlur="this.style.width = '250px'" onChange="this.form.submit();" class="vendor-id-select" id="vendor-id" name="0.0.11.5.0.9.3.1.5.1">
                            <option value="0">Vendor Name One - 8XXXXXXX</option>
                            <option selected="selected" value="1">Vendor Name Two - 8XXXXXXX</option>
                        </select>
                    </div>

                    <p id="header-txt">Payments and Financial Reports</p>
                </td>
ddaddy commented 8 years ago

@nicolasgomollon I have cherry picked some of your changes for my branch thanks. I noticed on a couple of occasions during testing, if I deleted the payments from my 2 accounts, it would download the same batch of payments for both.

pupasani commented 8 years ago

@nicolasgomollon my page does not have the vendor-id-container div at all. Here's the snippet:

```

Payments and Financial Reports

```
pupasani commented 8 years ago

@daddy, been using your fork for a long time, just switch to @nicolasgomollon fork to try it out. That fixed a long-term issue I have been having with not being able to download reviews. Also love the Touch ID integration! Hope you incorporated those changes as well :-)

nicolasgomollon commented 8 years ago

@pupasani That’s strange. Does your vendor ID show up anywhere on that page?

pupasani commented 8 years ago

No it doesn't - my guess is its not used when there is a single vendor id (like in my case).

nicolasgomollon commented 8 years ago

@pupasani I think I just nailed this bug and fixed Auto-Fill Vendor ID in my latest commit (https://github.com/nicolasgomollon/AppSales-Mobile/commit/8523402bcecb33f015b387033d6221bacfc3e5ee) by using a different method. Try it out and let me know how it goes!

pupasani commented 8 years ago

@nicolasgomollon looks like its working now thanks! One minor issue, it downloaded the last 23 payments and added them to the existing payments instead of replacing them. I deleted all the payments and then re-downloaded, so now the numbers look fine (even with repeated downloads). Unfortunately in this process I lost my payment history from before 23 months ago :-) Do you know any way to manually input those numbers?

nicolasgomollon commented 8 years ago

@pupasani Sorry about that! That’s actually because I fixed a bug that’s been in the code for quite a long time (https://github.com/nicolasgomollon/AppSales-Mobile/commit/2d393d14e52180a3d03d3db68faf594d778c48ef), where the vendor ID was never saved with each payment, so those payments are now duplicated because they’re technically not the same as the payments saved without it.

Nevertheless, I ran into the same issue, and ended up manually adding my missing payments using some SQLite editor app.

First you’ll need to download the AppSales app container:

  1. Make sure that AppSales is closed on your device!
  2. Connect your device.
  3. Open Xcode.
  4. Go to Window → Devices.
  5. Select your device in the left pane.
  6. Click on AppSales in the right.
  7. Click on the little gear below the Installed Apps section, and “Download Container…”

Once you download the container, I would make and keep a copy of it (just in case), and then:

  1. Right-click the container and “Show Package Contents”.
  2. Navigate to AppData → Library → Application Support.
  3. The AppSales.sqlite file is the database that stores all of your AppSales data.

For this next step, you’ll need to use some SQLite editor app (I used MesaSQLite, but any app will get the job done):

  1. In the ZPAYMENT table, insert a new row.
  2. Ensure the Z_PK field is the next consecutive integer.
  3. Set Z_ENT and Z_OPT with the same values as the other rows.
  4. ZMONTH and ZYEAR are the payment month and year, respectively.
  5. The ZACCOUNT field is equal to the Z_PK field for your account in the ZACCOUNT table.
  6. ZAMOUNT is a float of the payment amount.
  7. ZCURRENCY is your three-character currency code (e.g. USD).
  8. ZVENDORID is the vendor ID for your account.
  9. Repeat steps 1-8 until you’ve added all missing payments.
  10. In the Z_PRIMARYKEY table, edit the Z_MAX field for “Payment” to equal the Z_PK field for your last inserted payment.
  11. Save and close the database.

Now we need to replace the app container with our edited one:

  1. Make sure that AppSales is closed on your device!
  2. Connect your device.
  3. Open Xcode.
  4. Go to Window → Devices.
  5. Select your device in the left pane.
  6. Click on AppSales in the right.
  7. Click on the little gear below the Installed Apps section, and “Replace Container…”
  8. Select your edited container, and once it’s copied over to your device, boom, you’re done.

Open the AppSales app on your device once you’re done, and make sure that everything works as expected. Let me know if you run into any trouble along the way!

nicolasgomollon commented 8 years ago

@pupasani Since you mentioned that you use the reviews feature, I would suggest that you try out my latest commit, as I’ve greatly improved the reliability of the feature and grouped reviews by app version.

pupasani commented 8 years ago

Thanks @nicolasgomollon, reviews feature indeed looks good. But two minor issues there: firstly I miss the ability to download reviews for all my products at once, have to download each product's reviews separately. Secondly, for some reason I have a bunch of reviews without a version set (think these were downloaded previously) and the app crashes anytime I try to open one of those.

nicolasgomollon commented 8 years ago

@pupasani That feature’s not too difficult to add back in, so I’ll look into doing that next. As for the other issue, I recommend deleting all previous reviews from the database and then downloading them again within the app (they should all be redownloaded again—they were for me). If not, I’ll try adding in the ability to delete reviews individually within the app.

ddaddy commented 8 years ago

@nicolasgomollon I'm thinking it might be worth using your fork now instead of mine as you have done a lot of tremendous work in it. I started cherry picking some of your updates, but it's seems pointless. Can you enable issues in the settings of your fork please? Thanks

nicolasgomollon commented 8 years ago

@ddaddy I hadn’t realized that feature was disabled! It should be on now. Thanks!

ddaddy commented 8 years ago

Yeh me neither. It must be a new thing. Thanks