Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.pay.*;
import com.badlogic.gdx.utils.Timer;
import libcore.io.Base64;
import org.robovm.apple.foundation.*;
import org.robovm.apple.storekit.*;
Expand Down Expand Up @@ -52,6 +53,7 @@ public class PurchaseManageriOSApple implements PurchaseManager {
private NSArray<SKProduct> products;

private final List<Transaction> restoredTransactions = new ArrayList<Transaction>();
private Timer.Task restoreTimerTask;

@Override
public String storeName () {
Expand Down Expand Up @@ -107,6 +109,9 @@ public boolean installed () {
@Override
public void dispose () {
if (appleObserver != null) {
// Cancel any pending restore delivery timer.
cancelRestoreTimer();

// Remove and null our apple transaction observer.

SKPaymentQueue defaultQueue = SKPaymentQueue.getDefaultQueue();
Expand Down Expand Up @@ -151,6 +156,8 @@ public void purchase (String identifier) {
public void purchaseRestore () {
log(LOGTYPELOG, "Restoring purchases...");

// Cancel any pending restore delivery timer.
cancelRestoreTimer();
// Clear previously restored transactions.
restoredTransactions.clear();
// Start the restore flow.
Expand Down Expand Up @@ -483,16 +490,27 @@ else if (error.getCode() == SKErrorCode.PaymentCancelled.value()) {

@Override
public void restoreCompletedTransactionsFinished (SKPaymentQueue queue) {
// All products have been restored.
log(LOGTYPELOG, "All transactions have been restored!");

observer.handleRestore(restoredTransactions.toArray(new Transaction[restoredTransactions.size()]));
restoredTransactions.clear();
// On iOS 26.4+, restoreCompletedTransactionsFinished may be called
// before all restored transactions are delivered via updatedTransactions.
// Defer delivery to allow any pending restored transactions to be processed.
// 500ms is chosen to safely exceed the observed ~60ms gap between callbacks.
cancelRestoreTimer();
restoreTimerTask = Timer.schedule(new Timer.Task() {
@Override
public void run() {
restoreTimerTask = null;
observer.handleRestore(restoredTransactions.toArray(new Transaction[restoredTransactions.size()]));
restoredTransactions.clear();
}
}, 0.5f);
}

@Override
public void restoreCompletedTransactionsFailed (SKPaymentQueue queue, NSError error) {
// Restoration failed.
cancelRestoreTimer();

// Decide if user cancelled or transaction failed.
if (error.getCode() == SKErrorCode.PaymentCancelled.value()) {
Expand All @@ -505,6 +523,13 @@ public void restoreCompletedTransactionsFailed (SKPaymentQueue queue, NSError er
}
}

private void cancelRestoreTimer() {
if (restoreTimerTask != null) {
restoreTimerTask.cancel();
restoreTimerTask = null;
}
}

void log (final int type, final String message) {
log(type, message, null);
}
Expand Down
Loading