Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Right way to get the last receipt data using RMStore #148

Open
renatga opened this issue Aug 13, 2015 · 12 comments
Open

Right way to get the last receipt data using RMStore #148

renatga opened this issue Aug 13, 2015 · 12 comments

Comments

@renatga
Copy link

renatga commented Aug 13, 2015

I'm testing in-app-purchase functionality and stuck in receipt validation step. My receipt validation is customized and based on number of checks I proceed on server-side(calling my server API) and one them is condition for transactionID uniqueness to confirm payment. So, using RMStore I'm getting receipt successfully and addPayment function return unique transaction to success block. After that I'm running verifyTransaction and call receiptURL inside success block. Unfortunately, it looks like I'm always get the same receipt using receiptURL and when I send it to my server it responds me with error that transactionID already exists in DB and transactionID is not unique. This error indicates that I send the same receipt even I do new payment. Please, note I use consumable product.

Can someone show me the right chain of calls I provide below in order I do that in my code :

  1. [RMStore productRequest] - I call it right in init function.
  2. [RMStore addPayment] - It is start of consequence I need to understand.
  3. [[[RMStore defaultStore].transactionPersistor consumeProductOfIdentifier:pID]
    -- Do I need to call this function at all? Apple sends me unique transactinID each time I pay and calling this function has no influence.
  4. [[[RMStore] defaultStore].receiptVerificatore verifyTransaction:transaction]
    -- Do I need to call transaction verification?
  5. [RMStore receiptURL] or [[NSBundle mainBundle] appStoreReceiptURL]
    -- Are those ways to get receiptURL equal ? Both don't work as I expect.
  6. [NSData dataWithContentOfURL:receiptURL]
    -- Here is the main problem I'm experiencing. After different attempts with combination of functions I mentioned upper, this method always return the receipt with the same transactionID. This makes me think that there is a cash I need to refresh before run App crash on request products #5, Payment fail #6 to get the last receipt, but it is not clear how to do that.

So, RMStore wiki looks good, but I always expect that library will give me a chance do not fully understand what is going on under the hood. Unfortunately for RMStore I have to investigate how Apple in app purchase API works. This can be resolved if a consequence of functions calls will be provided for RMStore what I cannot find it anywhere(!)

Can someone review the list of functions I provided upper and help me with putting them in right order what let me fix my issue with getting the last receipt after the purchase I do?

@justvanbloom
Copy link

RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];

if (receipt != nil) {

    NSDateFormatter* localDateTime = [[NSDateFormatter alloc] init];
    //NSLog(@"%@", [NSTimeZone knownTimeZoneNames]);
    [localDateTime setTimeZone:[NSTimeZone timeZoneWithName:@"Europe/Berlin"]];

    [localDateTime setDateFormat:@"yyyy.MM.dd HH:mm:ss zzz"];



    for (RMAppReceiptIAP* purchase in receipt.inAppPurchases) {

        NSString* cancellationDate = nil;

        if (purchase.cancellationDate) {

            cancellationDate = [localDateTime stringFromDate:purchase.cancellationDate];

        }

        NSLog(@"Transaction: %@: product %@, original purchase date: %@, expiration date: %@, cancellation date: %@",

              purchase.originalTransactionIdentifier,

              purchase.productIdentifier,

              [localDateTime stringFromDate:purchase.originalPurchaseDate],

              [localDateTime stringFromDate:purchase.subscriptionExpirationDate],

              cancellationDate);

    }
}

@justvanbloom
Copy link

RMStoreAppReceiptVerificator *verificator = [RMStoreAppReceiptVerificator new];
[verificator verifyTransaction:transaction success:^(void){
//your code
} failure:^(NSError *error) {
//alert eg smtg
}]

but you need to make it accessable in the class first.

in RMStoreAppReceiptVerificator.h add

  • (void)verifyTransaction:(SKPaymentTransaction*)transaction
    success:(void (^)())successBlock
    failure:(void (^)(NSError *error))failureBlock;

@xiaosongshu
Copy link

hi @justvanbloom know it's been a while since you answered, but had a qq -

if we are dealing only with auto-renewables, do we always have to loop over the receipts? given I only care if one of the three subscriptions (part of same family) are active, couldn't I technically just just look at the last receipt? (given, any upgrade or cancellation will replace the previous receipt)

@justvanbloom
Copy link

Just look at the last recipe? Technically yes, BUT do you know it's there? download the last recipe if there is no and verify if it is a real one and look at the buytime eg.

@xiaosongshu
Copy link

xiaosongshu commented Feb 5, 2017

Sorry, I made a typo. I meant the last element in inAppPurchases in the receipt. Looks like the array is not ordered so I have to loop through all the elements. I did have one other question though, do you know if refreshing the receipt for autorenewables works just as well as restoring transactions? It's my question 3 in #203.

@justvanbloom
Copy link

Yep. The same way. I'll tmr at code and get you the lines. Nice Sunday.

@justvanbloom
Copy link

RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];

if (receipt != nil) {
NSDateFormatter* localDateTime = [[NSDateFormatter alloc] init];
[localDateTime setTimeZone:[NSTimeZone timeZoneWithName:@"Europe/Berlin"]];
[localDateTime setDateFormat:@"yyyy.MM.dd HH:mm:ss zzz"];
RMAppReceiptIAP *lastTransaction = nil;
NSTimeInterval lastInterval = 0;
for (RMAppReceiptIAP *iap in receipt.inAppPurchases)
{
NSTimeInterval thisInterval = [iap.originalPurchaseDate timeIntervalSince1970];
if (!lastTransaction || thisInterval > lastInterval)
{
lastTransaction = iap;
lastInterval = thisInterval;
}
}
NSLog(@"lastbuy:%@ %@",lastTransaction.productIdentifier,[localDateTime stringFromDate:lastTransaction.originalPurchaseDate])
}

@xiaosongshu
Copy link

Awesome, thanks for your help!

Apologies, one last question, but would you have any idea why when I call RMAppReceipt* appReceipt = [RMAppReceipt bundleReceipt]; I sometimes get a vastly different number elements in my inAppPurchases array (ie. 1 element becomes 14 elements)? It's my question 2 in the other post and I just can't understand why this is happening!

@justvanbloom
Copy link

Nslog the recipe and you will see :)

@xiaosongshu
Copy link

xiaosongshu commented Feb 7, 2017

Haha that's what I'm doing! Is there something I should be checking for? Receipt is magically changing somehow after leaving view and segueing again. All I'm doing is RMAppReceipt* appReceipt = [RMAppReceipt bundleReceipt]; and it's somehow a different receipt...

@justvanbloom
Copy link

Sort it by lastone or step trough all. Depends on usage.

@xiaosongshu
Copy link

xiaosongshu commented Feb 7, 2017

I think it was just a race condition, maybe because I changed the bundle id earlier. Tried it on a new phone and seems to be working so far. Here's to hoping things are good now. Thanks for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants