> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gx402.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Unity Integration

> How to integrate x402 payments in Unity using Gx402 middleware patterns.

# 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

<Tabs>
  <Tab title="EVM">
    ### EVM

    1. **Connect Wallet**
       * `Gx402Manager` triggers EVM wallet connect.
       * User approves in **MetaMask/Rabby**.
       * Address is saved for onchain verification.

    ***

    2. **Initiate Payment Request**
       * Player taps **Buy** / **Call API**.
       * Unity triggers `HandleApiCallOptimized()`.
       * Validates EVM address + fetches chain ID.

    ***

    3. **Build the Transaction**
       * Unity prepares an ERC-20 USDC transfer:

       * Resolve token contract for the chain.

       * Convert amount → token units (decimals).

       * ABI-encode transfer(recipient, amount).

       * Estimate gas + set to, data, value=0.

    ***

    4. **Sign and Send**

       * Wallet pops up with transaction details.

       * On approval, Unity sends the tx.

       * Network returns a tx hash (0x...).

       * Unity waits for receipt (status == 1).

    ***

    5. **Verify and Unlock (x402 Flow)**

       * Unity sends the txHash in the X-402-Payment header.

       * Backend checks ERC-20 Transfer event for correct from → to → amount.

       * On success, backend returns 200 OK.

       * Unity displays: ✅ “Payment Verified — Access Granted!”
  </Tab>

  <Tab title="Solana">
    ## Solana

    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.

    ***

    2. **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.

    ***

    3. **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.

    ***

    4. **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.

    ***

    5. **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!”*

    ***

    ## Unity WalletConnection (C#)

    ```csharp theme={null}
    // 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.");
        }
    }

    ```

    ***

    ## Unity CallApiButton (C#)

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

    ```csharp theme={null}
    // 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}");
        }
    }
    ```

    ***

    ## Express Server

    ```ts theme={null}

    // app/api/try/route.ts (or pages/api/try.ts)

    import { NextRequest, NextResponse } from 'next/server';
    import { X402PaymentHandler } from 'x402-solana/server';

    // ✅ Define CORS headers ONCE — include X-402-Payment!
    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, X-402-Payment',
    };

    const x402 = new X402PaymentHandler({
      network: 'solana-devnet',
      treasuryAddress: process.env.TREASURY_WALLET_ADDRESS!,
      facilitatorUrl: 'https://facilitator.payai.network', // ✅ No trailing spaces!
    });

    export async function OPTIONS() {
      // ✅ Return full CORS headers in preflight
      return new NextResponse(null, { status: 204, headers: corsHeaders });
    }

    export async function GET(req: NextRequest){
       try {
        const paymentHeader = x402.extractPayment(req.headers);

        const paymentRequirements = await x402.createPaymentRequirements({
          price: {
            amount: "2500000",
            asset: {
              address: "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr",
              decimals: 6,
            },
          },
          network: 'solana-devnet',
          config: {
            description: 'AI Chat Request Example',
            resource: `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:61664'}/api/try` as `${string}://${string}`,
          },
        });

        if (!paymentHeader) {
          const response = x402.create402Response(paymentRequirements);
          return NextResponse.json(response.body, {
            status: response.status,
            headers: corsHeaders, // ✅ Include CORS in 402 too
          });
        }

        const verified = await x402.verifyPayment(paymentHeader, paymentRequirements);
        if (!verified) {
          return NextResponse.json(
            { error: 'Invalid or unverified payment' },
            { status: 402, headers: corsHeaders }
          );
        }

        const body = await req.json().catch(() => ({}));
        const result = {
          message: '✅ Hello, you paid successfully!',
          receivedData: body,
        };

        await x402.settlePayment(paymentHeader, paymentRequirements);

        return NextResponse.json(result, { headers: corsHeaders });

      } catch (error: any) {
        console.error('API Error:', error);
        return NextResponse.json(
          { error: 'Internal server error' },
          { status: 500, headers: corsHeaders }
        );
      }
    }

    export async function POST(req: NextRequest) {
      try {
        const paymentHeader = x402.extractPayment(req.headers);

        const paymentRequirements = await x402.createPaymentRequirements({
          price: {
            amount: "2500000",
            asset: {
              address: "abcmasdmajwindojassmdjoaskmdass",
              decimals: 6,
            },
          },
          network: 'solana-devnet',
          config: {
            description: 'Request Example',
            resource: `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:61664'}/api/try` as `${string}://${string}`,
          },
        });

        if (!paymentHeader) {
          const response = x402.create402Response(paymentRequirements);
          return NextResponse.json(response.body, {
            status: response.status,
            headers: corsHeaders, // ✅ Include CORS in 402 too
          });
        }

        const verified = await x402.verifyPayment(paymentHeader, paymentRequirements);
        if (!verified) {
          return NextResponse.json(
            { error: 'Invalid or unverified payment' },
            { status: 402, headers: corsHeaders }
          );
        }

        const body = await req.json().catch(() => ({}));
        const result = {
          message: '✅ Hello, you paid successfully!',
          receivedData: body,
        };

        await x402.settlePayment(paymentHeader, paymentRequirements);

        return NextResponse.json(result, { headers: corsHeaders });

      } catch (error: any) {
        console.error('API Error:', error);
        return NextResponse.json(
          { error: 'Internal server error' },
          { status: 500, headers: corsHeaders }
        );
      }
    }

    ```
  </Tab>
</Tabs>
