Skip to main content

Unity Integration

Gx402 shows how to integrate x402 payments into Unity projects using engine-native UI and wallet connectors (WalletConnect, browser wallets for WebGL). We provide example Unity scripts, a test scene, and an Express middleware example to handle server-side verification and x402 calls.

Prerequisites

  • Unity 2020.3 LTS or newer (2021+ recommended)

High-Level Flow

  1. Connect Wallet
    The player taps Connect Wallet in the Unity UI.
    • Gx402Manager calls ConnectWalletAsync() from the Solana SDK (e.g., MagicBlocks).
    • The user approves the prompt in Phantom or Solflare.
    • Once connected, Web3.Account stores the wallet address and updates the UI with the active wallet.

  1. Initiate Payment Request
    The player taps Buy (or Call API) to begin an x402-protected flow.
    • Unity triggers HandleApiCallOptimized().
    • The script validates the connected wallet (CheckWallet()).
    • The payer’s public key is retrieved to identify who’s making the payment.

  1. Build the Transaction
    Unity constructs a Solana USDC transaction via BuildUsdcTransaction().
    • Fetches a recent blockhash for transaction validity.
    • Derives payer and recipient token accounts (ATAs).
    • Bundles three key instructions:
      1. Create payer ATA (idempotent)
      2. Create recipient ATA (idempotent)
      3. Transfer required USDC amount
    • Finalizes and signs the transaction object with the payer as the fee payer.

  1. Sign and Send
    • The wallet prompts the player to approve and sign the transaction.
    • On approval, Unity sends it using SendTransactionAsync().
    • The Solana network returns a transaction signature (TX ID), confirming the payment on-chain.
    • ConfirmTransaction() ensures final settlement before proceeding.

  1. Verify and Unlock (x402 Flow)
    • Unity sends the transaction signature in the X-402-Payment header via a POST request to your backend.
    • The backend verifies the transaction (correct amount, mint, recipient).
    • Once confirmed, it unlocks the API endpoint or in-game reward.
    • Unity receives a 200 OK response and updates the UI to show:
      “Payment Verified — Access Granted!”

💡 Tip:
This flow allows any Unity-based Solana game to implement secure, non-custodial payments with a single, reusable Gx402Manager script — fully compliant with x402 payment standards.

Unity WalletConnection (C#)

// Unity - simple async flow (concept)
{
    Web3.OnWalletChangeState += OnWalletStateChanged;
    OnWalletStateChanged();
}

private void OnDestroy()
{
    Web3.OnWalletChangeState -= OnWalletStateChanged;
}

public void ConnectWallet()
{
    ConnectWalletAsync().Forget();
}

public void DisconnectWallet()
{
    if (Web3.Instance != null && Web3.Wallet != null)
    {
        Web3.Instance.Logout();
    }
}

private async UniTask ConnectWalletAsync()
{
    if (Web3.Instance == null)
    {
        Debug.LogError("Web3.Instance is not found.");
        UpdateStatus("Error: Web3 not initialized.");
        return;
    }

    try
    {
        UpdateStatus("Connecting...");
        Account connectedAccount = await Web3.Instance.LoginWalletAdapter();
        if (connectedAccount == null)
        {
            UpdateStatus("Connection cancelled.");
        }
    }
    catch (Exception e)
    {
        Debug.LogError($"Error connecting wallet: {e.Message}");
        UpdateStatus("Connection failed.");
    }
}

---


CallApiButton()

Now to call the API with Gx402 you just need to call HandleApi function from gx402 game manager

Unity CallApiButton (C#)

// Unity - simple async flow (concept)
public void CallApiButton()
{
    HandleApiCallOptimized().Forget();
}

private async UniTask HandleApiCallOptimized()
{
    if (!CheckWallet()) return;

    try
    {
        UpdateStatus("Preparing transaction...");
        PublicKey payerPublicKey = Web3.Account.PublicKey;

        var transaction = await BuildUsdcTransaction(
            payerPublicKey,
            new PublicKey(RECIPIENT_OWNER_ADDRESS),
            new PublicKey(USDC_MINT_DEVNET),
            AMOUNT_REQUIRED,
            TOKEN_DECIMALS
        );

        if (transaction == null)
        {
            UpdateStatus("Error: Could not build transaction.");
            return;
        }

        UpdateStatus("Please sign transaction...");
        var signedTx = await Web3.Wallet.SignTransaction(transaction);
        if (signedTx == null)
        {
            UpdateStatus("Error: Transaction signing failed.");
            return;
        }

        UpdateStatus("Sending transaction...");
        var sendResult = await Web3.Rpc.SendTransactionAsync(signedTx.Serialize());
        string signature = sendResult.WasSuccessful ? sendResult.Result : null;

        if (string.IsNullOrEmpty(signature))
        {
            UpdateStatus("Error: Failed to send transaction.");
            Debug.LogError($"[X402] Failed to send transaction: {sendResult.Reason}");
            return;
        }

        await Web3.Rpc.ConfirmTransaction(signature, Commitment.Confirmed);
        Debug.Log($"[X402] Transaction confirmed! Signature: {signature}");

        await PostWithSignature(signature, new RequestBody { someData = "hello from Unity" });
    }
    catch (Exception e)
    {
        Debug.LogError($"[X402] Optimized Flow Error: {e.Message}");
        UpdateStatus($"Error: {e.Message}");
    }
}


---