PlaySuper LogoPlaySuper
Unity SDK

Transaction Sync

Synchronize purchase and refund transactions from PlaySuper to your game via the SDK

Transaction Sync

This guide explains how to receive and process purchase and refund transactions in your game using the PlaySuper SDK's transaction sync feature.

The Problem

When players make purchases or receive refunds through PlaySuper:

  1. Purchases: Player spends coins to buy a reward (e.g., gift card, coupon)
  2. Refunds: Player receives coins back if an order fails or is cancelled

Your game needs to know about these transactions to:

  • Update local currency displays
  • Show purchase/refund notifications
  • Trigger in-game rewards or achievements based on spending
  • Maintain accurate transaction history for the player

Without transaction sync, your game has no visibility into what happened on the PlaySuper platform.

The Solution

The SDK Transaction Sync system provides:

  1. Realtime Notifications: When a player makes a purchase in the store, the SDK is notified instantly via a URL scheme bridge — your game receives the transaction while the store is still open
  2. Automatic Fetching: Transactions are also fetched on SDK initialization, after authentication, and when the store closes (as a safety net)
  3. Event-Based Notifications: Your game receives an OnSdkTransactionsReceived event whenever new transactions are available
  4. Local Persistence: Transactions are stored locally until your game processes them
  5. Checkpoint System: Server tracks which transactions have been processed to avoid duplicates
  6. Deduplication: Transactions received via realtime notification and later fetched on store close are automatically deduplicated — your game never sees the same transaction twice
  7. Store Visit Optimization: Transaction syncing is only enabled for players who have visited the store, reducing unnecessary API calls

Transaction Types

SourceTypeDescription
PURCHASE_DEBITDEBITPlayer spent coins on a purchase
REFUND_CREDITCREDITPlayer received coins back from a refund

How It Works — Full Flow

1. App launch

PlaySuperUnitySDK.Initialize() checks for a previously authenticated session. If found, it fetches any transactions that happened while the app was closed and fires OnSdkTransactionsReceived with a List<SdkTransaction>.

2. Player opens the store

PlaySuperUnitySDK.OpenStore() opens the store and enables transaction syncing for this player.

3. Player makes a purchase

When a purchase completes inside the store, the SDK is notified immediately. It fetches the latest transactions and fires OnSdkTransactionsReceived with a List<SdkTransaction> — while the store is still open. The player can continue shopping, and your game receives each transaction as it happens.

4. Player closes the store

The SDK performs a final fetch to catch any transactions that may have been missed. Duplicates are automatically filtered — your callback only receives new transactions.

5. Next app session

Same as step 1 — any transactions that occurred between sessions are picked up on launch.

Backward compatible: Older SDK versions that don't support realtime notifications will still receive all transactions when the store closes.

Integration Guide

Step 1: Subscribe to Transaction Events

Subscribe to the OnSdkTransactionsReceived event to be notified when transactions are available:

using PlaySuperUnity;
using System.Collections.Generic;

void Start()
{
    // Subscribe — callback receives List<SdkTransaction> on every sync
    PlaySuperUnitySDK.OnSdkTransactionsReceived += HandleSdkTransactions;
}

void OnDestroy()
{
    // Always unsubscribe when the object is destroyed
    PlaySuperUnitySDK.OnSdkTransactionsReceived -= HandleSdkTransactions;
}

void HandleSdkTransactions(List<SdkTransaction> transactions)
{
    foreach (var txn in transactions)
    {
        Debug.Log($"Transaction: {txn.source} - {txn.amount} {txn.coinName}");

        if (txn.source == "PURCHASE_DEBIT")
        {
            // Player made a purchase - show notification, update UI, etc.
            ShowPurchaseNotification(txn);
        }
        else if (txn.source == "REFUND_CREDIT")
        {
            // Player received a refund - update balance display, show notification
            ShowRefundNotification(txn);
        }
    }

    // After processing all transactions, commit them
    CommitTransactions(transactions);
}

async void CommitTransactions(List<SdkTransaction> transactions)
{
    if (transactions.Count > 0)
    {
        // Commit all transactions at once
        bool success = await PlaySuperUnitySDK.CommitAllPendingSdkTransactions();

        if (success)
        {
            Debug.Log("Transactions committed successfully");
        }
        else
        {
            Debug.LogWarning("Failed to commit transactions - will retry on next fetch");
        }
    }
}

The OnSdkTransactionsReceived event fires automatically:

  • In realtime when the player makes a purchase while the store is open (via URL scheme bridge)
  • After player authentication
  • On SDK initialization if the player was previously logged in
  • When the store WebView closes (safety net to catch any missed transactions)

Step 2: Understanding the SdkTransaction Object

Each transaction contains the following fields:

FieldTypeDescription
idstringUnique transaction identifier
amountfloatTransaction amount (positive for credits, negative for debits)
typestringCREDIT or DEBIT
sourcestringPURCHASE_DEBIT or REFUND_CREDIT
coinIdstringID of the coin involved
coinNamestringDisplay name of the coin (e.g., "Gold Coins")
descriptionstringHuman-readable description (e.g., "Purchase: Amazon Gift Card")
createdAtstringISO 8601 timestamp when the transaction occurred

Step 3: Committing Transactions

After your game has processed the transactions, you must commit them to the server. This updates the checkpoint so you don't receive the same transactions again.

// Option 1: Commit all pending transactions at once (recommended)
bool success = await PlaySuperUnitySDK.CommitAllPendingSdkTransactions();

// Option 2: Commit up to a specific transaction ID
bool success = await PlaySuperUnitySDK.CommitSdkTransactions(lastTransactionId);

Always commit transactions after processing them. If you don't commit, you'll receive the same transactions again on the next fetch.

Step 4: Manual Fetching (Optional)

While transactions are fetched automatically on login, you can also fetch them manually:

async void RefreshTransactions()
{
    var transactions = await PlaySuperUnitySDK.FetchSdkTransactions();

    if (transactions != null && transactions.Count > 0)
    {
        Debug.Log($"Fetched {transactions.Count} transactions");
        // The OnSdkTransactionsReceived event will also fire
    }
}

Step 5: Checking for Pending Transactions

You can check if there are unprocessed transactions stored locally:

// Check if there are pending transactions
if (PlaySuperUnitySDK.HasPendingSdkTransactions())
{
    // Get the pending transactions
    List<SdkTransaction> pending = PlaySuperUnitySDK.GetPendingSdkTransactions();
    Debug.Log($"There are {pending.Count} pending transactions");
}

Step 6: Clearing State on Logout

When the player logs out, clear the transaction sync state:

void OnPlayerLogout()
{
    // Clear SDK transaction sync state
    PlaySuperUnitySDK.ClearSdkTransactionSyncState();

    // ... other logout logic
}

Complete Example

Here's a complete example of integrating transaction sync in your game:

using UnityEngine;
using PlaySuperUnity;
using System.Collections.Generic;

public class PlaySuperTransactionHandler : MonoBehaviour
{
    [SerializeField] private TMPro.TextMeshProUGUI balanceText;
    [SerializeField] private GameObject purchaseNotificationPrefab;
    [SerializeField] private GameObject refundNotificationPrefab;

    void Start()
    {
        // Subscribe to transaction events
        PlaySuperUnitySDK.OnSdkTransactionsReceived += OnTransactionsReceived;
    }

    void OnDestroy()
    {
        PlaySuperUnitySDK.OnSdkTransactionsReceived -= OnTransactionsReceived;
    }

    void OnTransactionsReceived(List<SdkTransaction> transactions)
    {
        foreach (var txn in transactions)
        {
            ProcessTransaction(txn);
        }

        // Commit after processing
        _ = CommitAllTransactions();
    }

    void ProcessTransaction(SdkTransaction txn)
    {
        switch (txn.source)
        {
            case "PURCHASE_DEBIT":
                // Show purchase notification
                var purchaseNotif = Instantiate(purchaseNotificationPrefab);
                purchaseNotif.GetComponent<NotificationUI>().Setup(
                    $"Purchase Complete!",
                    $"You spent {Mathf.Abs(txn.amount)} {txn.coinName}",
                    txn.description
                );
                break;

            case "REFUND_CREDIT":
                // Show refund notification
                var refundNotif = Instantiate(refundNotificationPrefab);
                refundNotif.GetComponent<NotificationUI>().Setup(
                    $"Refund Received!",
                    $"You received {txn.amount} {txn.coinName} back",
                    txn.description
                );
                break;
        }

        // Refresh balance display
        RefreshBalance();
    }

    async void RefreshBalance()
    {
        var balances = await PlaySuperUnitySDK.Instance.GetBalance();
        if (balances != null && balances.Count > 0)
        {
            balanceText.text = $"{balances[0].amount} {balances[0].name}";
        }
    }

    async System.Threading.Tasks.Task CommitAllTransactions()
    {
        bool success = await PlaySuperUnitySDK.CommitAllPendingSdkTransactions();
        if (!success)
        {
            Debug.LogWarning("Failed to commit transactions - will retry later");
        }
    }
}

API Reference

Events

EventParametersDescription
OnSdkTransactionsReceivedList<SdkTransaction>Fired when new transactions are available

Methods

MethodReturnsDescription
FetchSdkTransactions()Task<List<SdkTransaction>>Manually fetch transactions from server
GetPendingSdkTransactions()List<SdkTransaction>Get locally stored pending transactions
HasPendingSdkTransactions()boolCheck if there are pending transactions
HasVisitedStore()boolCheck if player has opened the store
CommitSdkTransactions(string id)Task<bool>Commit transactions up to a specific ID
CommitAllPendingSdkTransactions()Task<bool>Commit all pending transactions
ClearSdkTransactionSyncState()voidClear all local sync state (call on logout)

Troubleshooting

Transactions not appearing

  1. Ensure the player is authenticated before fetching transactions
  2. Check that you're subscribing to OnSdkTransactionsReceived before the player logs in
  3. Verify the API key is correct and matches your game

Receiving duplicate transactions

  1. Make sure you're calling CommitSdkTransactions or CommitAllPendingSdkTransactions after processing
  2. Check that the commit call is succeeding (returns true)

Transactions not persisting across sessions

  1. Ensure you're not calling ClearSdkTransactionSyncState() during normal gameplay
  2. The sync state is stored in PlayerPrefs - verify PlayerPrefs are not being cleared

If you encounter issues, enable debug logging by checking the Unity Console for messages prefixed with [PlaySuper].