# Indie Exchange Unity SDK 0.3.0

## Setup

1. On the website, add your games in **My games** and copy the publisher game's **Game ID**.
2. In **Ads**, create an ad for a game and choose its format. Import store artwork or upload a checked creative. Activate the ad when ready.
3. In **My games → Advertising settings**, set the overall Indie Exchange share for each format, the public/personal split, and the personal ads and weights. Choose the banner size and position there too. New games start at 0% until configured.
4. Import `IndieExchange-0.3.0.unitypackage`. Open **Tools → Indie Exchange → Setup**, create settings, paste **Game ID**, and leave **Test Mode** on while checking the integration.
5. Initialize once after your consent flow permits requests. Keep your existing ad provider ready independently.

```csharp
using IndieExchange;

// Once, after consent. Uses the Game ID / Test Mode settings asset.
IndieAds.Initialize();
// Alternatively, configure by code:
// IndieAds.Initialize("YOUR_GAME_ID", testMode: false);

// At a natural interstitial break (await before resuming gameplay):
if (!await IndieAds.TryShowInterstitialAsync())
    ShowMyNormalInterstitial();

// When your game wants a banner:
if (!IndieAds.TryShowBanner())
    ShowMyNormalBanner();

// When leaving that screen:
IndieAds.HideBanner();
```

Call on Unity's main thread. Do not display both providers' banners together. Hide your normal banner before displaying an exchange banner. Hide banners before fullscreen ads and restore your chosen provider afterward. The host owns its normal provider's loading, readiness and failure handling.

`true` from the interstitial task completes after the exchange ad closes. `false` completes immediately, without a network request from the show call. Repeated calls while an interstitial is open share the same task. Your normal provider may itself have no ad; this package cannot guarantee its fill.

## Website-controlled launch settings

Live mode fetches a verified configuration **once per game per app launch**, then preloads eligible creatives in the background. Calls return false while configuration or artwork is unavailable. A failed or invalid configuration disables exchange ads for that launch; it is not retried in the background or on scene changes. Reinitializing the same game in the same process reuses the launch result, including failure. Website changes apply on the next launch.

The server stores an immutable configuration snapshot, bound to the verified app identity and installation. Ad requests must include the launch session and public/personal category. The server validates the category and uses its stored banner size, selected personal ads and weights. Configuration sessions expire after 24 hours; an unusually long-running session then falls back until the next app launch. Campaign pauses, game suspension, credit limits and security checks still apply immediately to new requests/claims.

For each format:
- **Overall share:** the fraction of eligible opportunities allocated to Indie Exchange.
- **Public share:** the fraction of those selected opportunities allocated to other developers' games. The remainder is personal.
- **Personal weights:** relative chances among selected personal ads that are currently eligible. Weight 2 receives twice the chance of weight 1; finite samples vary.

Example: 20% overall and 50% public means 10% public, 10% personal, and 80% your normal ad code. Persistent integer counters spread the overall and category decisions across opportunities. At an unchanged 5%, exactly 5 of every 100 counted opportunities are selected. Within selected opportunities, each category has the same counter guarantee. Actual displays may be lower because of missing creatives, targeting, caps or connectivity. An unavailable selection returns false; there is no catch-up debt and no silent substitution of the other category. Reinstalls or cleared data reset counters.

Live percentages cannot be bypassed with the old local percentage argument. Named placement overloads remain for source compatibility, but live names are the internal `interstitial` and `banner` formats. New integrations should use the no-argument calls above. Custom placement names from 0.2 must be removed.

## Test Mode and live delivery

Test Mode shows local cards at 100% for interstitials and banners, without downloading configuration, requesting device proof or awarding credits. It is available only in the Editor/development builds. Turn it off before release.

Live release builds always use `https://indie-exchange.com`. No service URL, percentage, cache, or timeout controls appear in the normal Inspector. Advanced development-only endpoint overrides and cache/timeout values remain accessible through code; production ignores endpoint overrides. Live configuration controls ad allocation, not those internal tuning values.

Live delivery still requires an approved app identity and Google Play Integrity (Android) or Apple App Attest (iOS). See **Security-Setup.md** for operator and game onboarding. A Game ID is public identification, not proof of a real device. No AdMob dependency is installed and no particular AdMob SDK version is required. The package's Android build hook includes Play Integrity; iOS uses DeviceCheck/App Attest.

## Formats and readiness

- Image/video interstitials and sized image banners are supported.
- Banner artwork must match the website-selected 320×50, 320×100, 300×250 or 728×90 proportions; server settings also select top/bottom position.
- Rewarded settings are visible but disabled. `TryShowRewardedAsync()` returns false and never grants a reward.
- `IndieAds.IsConfigurationReady` reports whether a valid launch policy is available.
- `IndieAds.IsReady(IndieAdFormat.Interstitial)` indicates cached availability in at least one category. It does not guarantee the next opportunity's selected category is ready.
- `IndieAds.Preload(IndieAdFormat.Interstitial)` requests background preloading without counting an opportunity. Automatic preloading normally makes this unnecessary.
- `IndieAds.GetStatistics(IndieAdFormat.Interstitial)` reports counted opportunities, selections, actual displays and unavailable selections. Diagnostics never award credits.

Images/video are cached only after validation and must be ready before display. Live caches have up to four slots (public/personal × interstitial/banner), with bounded downloads and backoff. Impression tickets expire after five minutes; cache lifetime is capped by ticket expiry. Verified impression claims retain the existing review, anti-replay and credit-accounting checks.

## Package contents and updating

The main `.unitypackage` contains Runtime, one Editor folder with setup/build hooks, and documentation. Tests stay in the source package. The optional `IndieExchange-Examples-0.3.0.unitypackage` adds the demo and its scene generator; it is not required for a game build. Import it after the main package to use **Tools → Indie Exchange → Create sample scene**.

For an existing 0.2 installation, import 0.3 over the same `Assets/IndieExchange` folder, enter the new **Game ID** (the game's UUID from My games), remove placement arguments in your calls, and save website settings. Existing local SDK keys and percentages no longer control live delivery. Unity imports do not remove old files: if you previously imported Tests or the sample, remove only those optional SDK folders after checking that your own code does not reference them.

Backend and SDK must be upgraded together. The backend adds configuration/session tables without deleting games, ads, balances or history. Old requests without a launch session fail closed. New-game default percentages are zero, so configure the website before expecting live ads.

Validate on target phones before release, including video, orientation, store links, consent, scenes and service outages. Automated Editor tests do not replace native device verification/playback testing.
