MugunthKumar / MKStoreKit

The "Goto" In App Purchases Framework for iOS 8+
2.09k stars 430 forks source link

Release builds used via TestFlight will verify receipts against the wrong server #255

Open warpling opened 8 years ago

warpling commented 8 years ago

Release builds will attempt to verify receipts against the production verification server, but release builds distributed via TestFlight should validate against the Sandbox server. Verifying sandbox receipts against the production server results in the error:

Error Domain=com.mugunthkumar.mkstorekit Code=21007 "This receipt is from the test environment." UserInfo={NSLocalizedDescription=This receipt is from the test environment.}

Apparently Apple's recommended method of handling this is to first attempt to validate against the production server and if that fails with error code 21007, try the request again with the Sandbox server. This should definitely make it into a future release! :)

josueruiz7 commented 8 years ago

Hello @warpling

Do you have the code to fix it?

Regards

warpling commented 8 years ago

Yes!

Again the steps to handling the Sandbox are as follows: 1) Try to hit the production server 2) Fail, and recording when we get the 21007 error code 3) Try again with the sandbox server (and thereon out)

Tweaks to make

I first added the private ivar inSandbox to MKStoreKit

@interface MKStoreKit (/*Private Methods*/) <SKProductsRequestDelegate, SKPaymentTransactionObserver>
@property (readwrite) NSMutableDictionary *purchaseRecord;
@property (readwrite) BOOL inSandbox;
@end

Then in startValidatingAppStoreReceiptWithCompletionHandler I replaced the normal assignment to storeRequest with the following. This way future requests will immediately use the sandbox.

    NSMutableURLRequest *storeRequest;
    if (self.inSandbox) {
        storeRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kSandboxServer]];
    } else {
        storeRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kLiveServer]];
    }

Then I updated startValidatingReceiptsAndUpdateLocalStore like so. This way after the first failure we try again on the sandbox and continue to use the sandbox.

- (void)startValidatingReceiptsAndUpdateLocalStore {
    [self startValidatingAppStoreReceiptWithCompletionHandler:^(NSArray *receipts, NSError *error) {
        if (error) {
            // Receipt was from Sandbox
            if (error.code == 21007) {
                // Switch to using the Sandbox server here on out
                self.inSandbox = YES;
                // Restart validation
                [self startValidatingReceiptsAndUpdateLocalStore];
            }
            // If the error was anything else we'll treat it like an actual error/failure
            else {
                [[NSNotificationCenter defaultCenter] postNotificationName:kMKStoreKitReceiptValidationFailedNotification object:error];
            }
        } else {
josueruiz7 commented 8 years ago

it works perfect! Thank you!

warpling commented 8 years ago

😊 I hope this can help others. I'd make a PR but it seems like the project isn't getting any merge love these days.

~Ryan

On Jan 20, 2016, 9:53 AM -0800, josueruiz7notifications@github.com, wrote:

it works perfect! Thank you!

— Reply to this email directly orview it on GitHub(https://github.com/MugunthKumar/MKStoreKit/issues/255#issuecomment-173303301).

josueruiz7 commented 8 years ago

@warpling I have an issue, maybe you can help me, I am trying to validate:

if([[MKStoreKit sharedKit] isProductPurchased:ID]) { }

But it seems that store cache, because when I first log in with an appleid that already has buy the product , it works perfect, but when I log out and log in with other appleId that has not bought the product, THIS VALIDATION still return true.

Did you get this issue? :(

Thanks.

warpling commented 8 years ago

Yikes, I've run into lots of weird issues with the Sandbox I'm afraid. Have you tried reinstalling the app?

josueruiz7 commented 8 years ago

Yes , it works reinstalling the app, I figure out that the issues is on "appStoreReceiptURL", it seems to be cached or some thing like that. :S

NSURL receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSError receiptError; BOOL isPresent = [receiptURL checkResourceIsReachableAndReturnError:&receiptError]; if (!isPresent) { // No receipt - In App Purchase was never initiated completionHandler(nil, nil); return; }

warpling commented 8 years ago

I've always had to delete the app to reset purchases. Even then, sometimes when I reinstall they quickly get restored if I forget to log out of the sandbox account.

josueruiz7 commented 8 years ago

Hi @warpling I changed of library, I used the follow: https://github.com/robotmedia/RMStore RMStore seems better, use openssl. Regards

warpling commented 8 years ago

Ah neat! I'll look into using that.