SKReceiptRefreshRequest zickt rum

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • SKReceiptRefreshRequest zickt rum

    Hallo Leute,

    Im Moment bringe ich meiner macOS Applikation gerade In-App-Purchases bei. Soweit funktioniert es ganz gut, aber ich kämpfe mit der Möglichkeit, Käufe wiederherzustellen und bin ziemlich verwirrt...

    Es handelt sich bei dem Produkt um ein nicht-konsumierbares Feature. Mit anderen Worten, für die Wiederherstellung reicht es aus, das aktuelle Receipt neu anzufordern. Dies mache ich so

    Quellcode

    1. self.receiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
    2. self.receiptRequest.delegate = self;
    3. [self.receiptRequest start];
    Leider wird nach dem Request (in der Sandbox, ausserhalb von Xcode) immer die Delegate-Methode - (void)request:(SKRequest *)request didFailWithError:(NSError *)error aufgerufen, wobei das Error-Objekt nil ist.

    Da das Vorgehen eigentlich sehr "straight forward" ist, fällt mir einfach keine Fehlerquelle ein. Konkret bin ich noch mehr verwirrt, denn
    • ... in welchem Szenario sollte ein Benutzer eigentlich Einkäufe wiederherstellen wollen? Er "verliert" sie ja nur, wenn er das Receipt löscht, dass sich im App-Bundle befindet ... also die ganze App. Dann kann er diese ja neu laden (oder aus einem Backup restoren) und beim ersten Start wird das Receipt inkl. Käufe wieder aus dem App-Store geladen
    • ... die Klasse SKReceiptRefreshRequest gibt es erst ab macOS 10.9, während die anderen Teile des StoreKits seit 10.7 verfügbar sind.
    Mir scheint diese Funktion (in meinem Fall) vollkommen überflüssig zu sein und ich würde sie gerne weglassen. Aber war es nicht so, dass Apple diese bei IAP zwingend fordert? Oder gilt dies nur für iOS-Apps?

    Wer auch immer schon mit IAP unter macOS gearbeitet hat: Bitte schubs' mich in die richtige Richtung ?(
    Ratlos, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    ... in welchem Szenario sollte ein Benutzer eigentlich Einkäufe wiederherstellen wollen?
    Der Benutzer kann die Anwendung ja auch auf anderen Macs installiert haben und dort sollen die gekauften In-App-Produkte ja auch zur Verfügung stehen.

    Ich arbeite auch gerade an einer macOS-Anwendung mit In-App-Käufen (non-renewing subscriptions) und habe bisher StoreKit auch nicht via SKReceiptRefreshRequest bewegen können, das Receipt zu aktualisieren. Ich lasse jetzt zum Wiederherstellen der In-App-Käufe die Anwendung einfach mit Return Code 173 beenden (mit erklärendem Alert), um das Receipt zu aktualisieren. Mal sehen, ob das so durch den Review kommt.
  • Auf einem neuen Gerät würde meine Receipt Validation schon mit 173 beenden und so ein neues Zertifikat anfordern, aber danke für diesen Ansatz:

    Eigentlich müsste doch auch ein Restore der Transaktionen ein neues Receipt liefern. Dies versuche ich zuerst (obwohl ich eigentlich diese gar nicht detailliert brauche). Exit 173 ist dann Plan B :D

    Danke, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Okay, um meine Fragen mal selber zu beantworten:
    • Krudes Szenario, aber ein Benutzer spielt vielleicht ein Backup von einem Zeitpunkt vor dem IAP ein. Dann ist das Receipt gültig und passiert den Check beim Applikations-Start, aber beinhaltet den getätigten Kauf nicht. Hier macht "Wiederherstellen" Sinn, die Alternative wäre ein erneutes Laden aus dem Store ... aber der Fall kann eben eintreten.
    • Der Plan mit dem Restore der Transaktionen war gut: [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; ruft die AppStore-Authorisierung auf und feuert im Transaction-Observer die entsprechende Methode nach Erhalt eines neuen Receipts. Dieses auf den IAP überprüfen und fertig.
    Danke für's Mitdenken!

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Hi guys!

    Sorry for English, but this was pretty much the single place in all of Internet that mentions my problem :)

    I used some Google Translate magic to get ahold of the discussion, but I did not completely understand how the problem was solved.

    In my scenario I enable/disable IAP features based on the contents of the receipt. Everything works very well except the Restore Purchases functionality. As I understand there are 3 ways to restore purchases:
    1) restoreCompletedTransactions "plays back" purchases, but does not actually add anything to the receipt. Seems very useless unless you store the state of IAPs in UserDefaults, which seems very insecure. Any user could easily "unlock" your app by changing the file in Finder.
    2) SKReceiptRefreshRequest seems to be just the thing I need. It should download the receipt with all previous purchases. However no matter what I do it always fails with nil error like mentioned in the first post. It does not even try to ask for credentials. Just fails almost instantly.
    3) Third way as I understand is to just repurchase products. You won't be charged for it according to Apple docs. This works in sandbox, but I have not tried in production.

    3rd option is fine for me, but Apple clearly states that there should be a Restore Purchases button that should use either option 1 or 2. First one works, but is not useful, second does not. Any good ideas?
  • mlap schrieb:

    1) restoreCompletedTransactions "plays back" purchases, but does not actually add anything to the receipt. Seems very useless unless you store the state of IAPs in UserDefaults, which seems very insecure. Any user could easily "unlock" your app by changing the file in Finder.
    2) SKReceiptRefreshRequest seems to be just the thing I need. It should download the receipt with all previous purchases. However no matter what I do it always fails with nil error like mentioned in the first post. It does not even try to ask for credentials. Just fails almost instantly.
    3) Third way as I understand is to just repurchase products. You won't be charged for it according to Apple docs. This works in sandbox, but I have not tried in production.
    Hi!

    I'd suggest to go with restoreCompletedTransactions for the following reasons
    1. SKReceidtRefreshRequest always failed with an error in my code, but unfortunately the error object was nil and I couldn't resolve this issue.
    2. In addition this class requires OS X 10.9 or higher, limiting its usage
    3. Your 3rd way is not very user friendly. Although customers could delete and re-purchase the app (w/o being charged), they might loose data like user preferences. And most users won't recognize repurchasing the IAP as a way to restore.
    Just make sure your SKPaymentTransactionObserver is handling the corresponding transaction

    Quellcode

    1. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
    2. {
    3. if (!preferenceController)
    4. {
    5. preferenceController = [[STBPreferenceController alloc] init];
    6. }
    7. for (SKPaymentTransaction *transaction in transactions)
    8. {
    9. switch (transaction.transactionState)
    10. {
    11. case SKPaymentTransactionStatePurchasing:
    12. preferenceController.proPurchaseState = PURCHASE_INPROGRESS;
    13. NSLog(@"Transaction in progress: %@", @(transaction.transactionState));
    14. break;
    15. case SKPaymentTransactionStateDeferred:
    16. preferenceController.proPurchaseState = PURCHASE_PENDING;
    17. NSLog(@"Transaction deferred: %@", @(transaction.transactionState));
    18. break;
    19. case SKPaymentTransactionStateFailed:
    20. {
    21. preferenceController.proPurchaseState = PURCHASE_AVAILABLE;
    22. NSLog(@"Transaction failed: %@ (%@)", @(transaction.transactionState), transaction.error);
    23. NSAlert *errorAlert = [NSAlert alertWithError:transaction.error];
    24. dispatch_async(dispatch_get_main_queue(), ^{
    25. [errorAlert runModal];
    26. });
    27. break;
    28. }
    29. case SKPaymentTransactionStatePurchased:
    30. {
    31. preferenceController.proPurchaseState = PURCHASE_DONE;
    32. [queue finishTransaction:transaction];
    33. NSLog(@"Transaction done %@", @(transaction.transactionState));
    34. break;
    35. }
    36. case SKPaymentTransactionStateRestored:
    37. preferenceController.proPurchaseState = PURCHASE_DONE;
    38. [queue finishTransaction:transaction];
    39. NSLog(@"Transaction restored %@", @(transaction.transactionState));
    40. break;
    41. default:
    42. NSLog(@"Unexpected transaction state %@", @(transaction.transactionState));
    43. break;
    44. }
    45. }
    46. [preferenceController checkPurchase];
    47. }
    Alles anzeigen
    I'll post a similar reply in Apple's Developer forum... :D

    Take care, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • plasmatron schrieb:

    Ich arbeite auch gerade an einer macOS-Anwendung mit In-App-Käufen (non-renewing subscriptions) und habe bisher StoreKit auch nicht via SKReceiptRefreshRequest bewegen können, das Receipt zu aktualisieren. Ich lasse jetzt zum Wiederherstellen der In-App-Käufe die Anwendung einfach mit Return Code 173 beenden (mit erklärendem Alert), um das Receipt zu aktualisieren. Mal sehen, ob das so durch den Review kommt.
    Nur der Vollständigkeit halber: Die Mac-App ist jetzt im Store und kam ohne Geningel und Gebarme so durch den Review. Wie geplant wird einfach mit Exit Code 173 beendet, was das Refetchen des Receipts triggert. So habe ich dann auch alle non-renewing subscriptions schön brav im Receipt, die mir via restoreCompletedTransactions gar nicht geliefert werden. Hier das Endergebnis:
    itunes.apple.com/app/importer-…s/id1125811461?l=en&mt=12
  • MyMattes schrieb:

    mlap schrieb:

    1) restoreCompletedTransactions "plays back" purchases, but does not actually add anything to the receipt. Seems very useless unless you store the state of IAPs in UserDefaults, which seems very insecure. Any user could easily "unlock" your app by changing the file in Finder.
    2) SKReceiptRefreshRequest seems to be just the thing I need. It should download the receipt with all previous purchases. However no matter what I do it always fails with nil error like mentioned in the first post. It does not even try to ask for credentials. Just fails almost instantly.
    3) Third way as I understand is to just repurchase products. You won't be charged for it according to Apple docs. This works in sandbox, but I have not tried in production.
    Hi!
    I'd suggest to go with restoreCompletedTransactions for the following reasons
    1. SKReceidtRefreshRequest always failed with an error in my code, but unfortunately the error object was nil and I couldn't resolve this issue.
    2. In addition this class requires OS X 10.9 or higher, limiting its usage
    3. Your 3rd way is not very user friendly. Although customers could delete and re-purchase the app (w/o being charged), they might loose data like user preferences. And most users won't recognize repurchasing the IAP as a way to restore.
    Just make sure your SKPaymentTransactionObserver is handling the corresponding transaction

    Quellcode

    1. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
    2. {
    3. if (!preferenceController)
    4. {
    5. preferenceController = [[STBPreferenceController alloc] init];
    6. }
    7. for (SKPaymentTransaction *transaction in transactions)
    8. {
    9. switch (transaction.transactionState)
    10. {
    11. case SKPaymentTransactionStatePurchasing:
    12. preferenceController.proPurchaseState = PURCHASE_INPROGRESS;
    13. NSLog(@"Transaction in progress: %@", @(transaction.transactionState));
    14. break;
    15. case SKPaymentTransactionStateDeferred:
    16. preferenceController.proPurchaseState = PURCHASE_PENDING;
    17. NSLog(@"Transaction deferred: %@", @(transaction.transactionState));
    18. break;
    19. case SKPaymentTransactionStateFailed:
    20. {
    21. preferenceController.proPurchaseState = PURCHASE_AVAILABLE;
    22. NSLog(@"Transaction failed: %@ (%@)", @(transaction.transactionState), transaction.error);
    23. NSAlert *errorAlert = [NSAlert alertWithError:transaction.error];
    24. dispatch_async(dispatch_get_main_queue(), ^{
    25. [errorAlert runModal];
    26. });
    27. break;
    28. }
    29. case SKPaymentTransactionStatePurchased:
    30. {
    31. preferenceController.proPurchaseState = PURCHASE_DONE;
    32. [queue finishTransaction:transaction];
    33. NSLog(@"Transaction done %@", @(transaction.transactionState));
    34. break;
    35. }
    36. case SKPaymentTransactionStateRestored:
    37. preferenceController.proPurchaseState = PURCHASE_DONE;
    38. [queue finishTransaction:transaction];
    39. NSLog(@"Transaction restored %@", @(transaction.transactionState));
    40. break;
    41. default:
    42. NSLog(@"Unexpected transaction state %@", @(transaction.transactionState));
    43. break;
    44. }
    45. }
    46. [preferenceController checkPurchase];
    47. }
    Alles anzeigen
    I'll post a similar reply in Apple's Developer forum... :D

    Take care, Mattes
    Yep that's what I am doing, but receipt is not added no matter what I do. It goes through all stages, I finish the transaction, then paymentQueue:removedTransactions: is called and then paymentQueueRestoreCompletedTransactionsFinished, but receipt is not put to the bundle. Tried different certificates, tried launching from Xcode and from Finder and from Terminal and exiting with 173 and all possible ways but nothing except re-purchasing the product generates the receipt. Same seems to be happening in production. One of my users reported that Restore Purchases is not working.
    I am beginning to think if this could be Sierra issue...
  • So restoreCompletedTransactions still does not work, but I was able to get the 173 status working. Turns out you have to be using Development certificate for that.
    I'll probably then go with this solution, since looking at Importer for Contacts it works and passes the review.

    Thank you for your help!