Configuration

High-level configuration guidance for external client teams.

What This Page Covers

This page focuses on the configuration most host applications need to provide to mount the SDK successfully. It does not document every internal tuning or development-only option.

The Main Idea

Most applications pass a small configuration object to VECUProvider.

The most important inputs are:

  • authToken
  • deploymentStage

At a high level:

  • your host app obtains the authentication token from its own login or authorization flow
  • your host app passes that token into the SDK config
  • the SDK uses that configuration to initialize the wallet experience

Minimum Configuration

import { VECUProvider } from '@vecu/wallet-react-native-sdk';

function App() {
  const authToken = tokenFromYourLoginFlow;

  return (
    <VECUProvider
      config={{
        authToken,
        deploymentStage: 'sandbox',
      }}
    >
      <WalletScreen />
    </VECUProvider>
  );
}

Required Configuration

authToken

This is the authentication token your host app provides after the user logs in or completes your app's authorization flow.

Keep in mind:

  • the SDK does not own your login experience
  • your app is responsible for obtaining and storing the token
  • the token must be available before rendering VECUProvider

Authentication Ownership

Your application is responsible for user authentication. The SDK expects the host app to provide the token it should use.

deploymentStage

This tells the SDK which environment to use.

deploymentStage: 'sandbox';

For external-facing documentation, the main values clients need to understand are:

  • sandbox for development and integration testing
  • production for live production environments

useTokenExchange

By default, the SDK exchanges your authToken for a VECU token internally. Pass your IdP token directly — your app does not need to fetch a VECU-specific token before mounting the provider. The SDK calls the VECU token exchange endpoint automatically and uses the exchanged token for all subsequent API calls.

If your app has already exchanged the IdP token and authToken is a VECU-specific token, set useTokenExchange: false to skip the internal exchange:

<VECUProvider
  config={{
    authToken: vecuToken, // already-exchanged VECU token
    deploymentStage: 'sandbox',
    useTokenExchange: false,
  }}
>
  <WalletScreen />
</VECUProvider>
ValueMeaning
true (default)authToken is your IdP token; SDK handles the exchange
falseauthToken is a VECU token already; SDK skips the exchange

See Access and Authorization for IdP registration requirements.

Common Host App Pattern

Many applications keep the auth token in app state and build the SDK config from that token.

function App() {
  const authToken = tokenFromYourLoginFlow;

  const sdkConfig = {
    authToken,
    deploymentStage: 'sandbox',
  };

  return (
    <VECUProvider config={sdkConfig}>
      <WalletScreen />
    </VECUProvider>
  );
}

The demo application follows the same high-level pattern: the host app manages authentication, creates SDK config, and then mounts VECUProvider.

Optional Configuration

Some applications may also use optional callbacks or environment-specific settings. These are commonly used for:

  • handling token expiry
  • responding to unauthorized access
  • application logging
  • analytics or error reporting

External client teams usually do not need to start with all of these options. Begin with the minimum configuration first, then add optional behavior only if your app requires it.

Operational Options

The SDK also exposes a small set of operational settings through the public configuration type.

These options are useful when your host app needs more control over request behavior or SDK logging.

Logging

The public configuration supports:

  • logLevel
  • onLog

Use these when your application wants to control how much SDK logging is emitted or forward SDK log events into your own logging pipeline.

Common log levels include:

  • error
  • warn
  • info
  • debug

High-level example:

<VECUProvider
  config={{
    authToken,
    deploymentStage: 'sandbox',
    logLevel: 'info',
    onLog: (level, message, meta) => {
      console.log(level, message, meta);
    },
  }}
>
  <WalletScreen />
</VECUProvider>

Resilience and Timeouts

The public configuration also includes:

  • maxRetries
  • retryBackoffMs
  • requestTimeoutMs

Use these when your host app needs to tune retry or timeout behavior for your environment.

High-level example:

<VECUProvider
  config={{
    authToken,
    deploymentStage: 'sandbox',
    maxRetries: 3,
    retryBackoffMs: 1000,
    requestTimeoutMs: 30000,
  }}
>
  <WalletScreen />
</VECUProvider>

Two-tier config for background polling vs. active wallet UI

Some applications mount VECUProvider in two places: once near the top of the app for background notification polling, and once on the wallet screen for active user interactions.

The two surfaces have different reliability requirements. Background polling runs silently — a failed poll simply waits for the next interval. The wallet screen is user-facing — failures should retry more aggressively and give the user more time to complete flows like QR scanning or NFC.

You can tune maxRetries and requestTimeoutMs per provider accordingly:

// Lightweight config for background polling (e.g. notification badge)
const notificationConfig = {
  authToken,
  deploymentStage,
  maxRetries: 2,
  requestTimeoutMs: 15000,
};

// Heavier config for the active wallet screen (QR, NFC, credential flows)
const walletConfig = {
  authToken,
  deploymentStage,
  maxRetries: 3,
  requestTimeoutMs: 30000,
};

Token Refresh

If your application refreshes authentication tokens, update the token in host app state and pass the new value into the SDK configuration.

Reactive refresh (via onTokenExpired)

The SDK calls onTokenExpired when it detects an expired token during a request. Refresh your token and update state so the provider reinitializes with a fresh token.

function App() {
  const [authToken, setAuthToken] = useState(initialToken);

  const handleTokenExpired = async () => {
    const nextToken = await refreshTokenFromYourAuthFlow();
    setAuthToken(nextToken);
  };

  return (
    <VECUProvider
      config={{
        authToken,
        deploymentStage: 'sandbox',
        onTokenExpired: handleTokenExpired,
      }}
    >
      <WalletScreen />
    </VECUProvider>
  );
}

Proactive refresh before mounting

The reactive pattern covers mid-session expiry, but if your token is already stale when the provider first mounts, the SDK's initial requests will fail before onTokenExpired can fire.

To guard against this, check token freshness before rendering VECUProvider and refresh proactively if needed:

function WalletProvider({ children }) {
  const [authToken, setAuthToken] = useState(null);
  const [isRefreshing, setIsRefreshing] = useState(false);

  useEffect(() => {
    async function ensureFreshToken() {
      if (isTokenExpired(storedTokenExpiry)) {
        setIsRefreshing(true);
        const nextToken = await refreshTokenFromYourAuthFlow();
        setAuthToken(nextToken);
        setIsRefreshing(false);
      } else {
        setAuthToken(storedToken);
      }
    }
    ensureFreshToken();
  }, []);

  if (!authToken || isRefreshing) {
    return children; // render without SDK until token is ready
  }

  return (
    <VECUProvider
      config={{
        authToken,
        deploymentStage: 'sandbox',
        onTokenExpired: async () => {
          const nextToken = await refreshTokenFromYourAuthFlow();
          setAuthToken(nextToken);
        },
      }}
    >
      {children}
    </VECUProvider>
  );
}

Both patterns work together: the proactive check prevents a stale-at-mount failure, and onTokenExpired handles expiry that occurs during an active session.

Avoid forcing remount with key

A common mistake is passing the auth token as the key prop on VECUProvider:

// ❌ Do not do this
<VECUProvider key={authToken} config={{ authToken, deploymentStage }}>
  <WalletScreen />
</VECUProvider>

React treats a key change as a signal to unmount and remount the component from scratch. Every token rotation — which can happen silently every few minutes — forces the entire SDK to tear down and reinitialize. This produces a multi-second blank state on the wallet screen on every refresh cycle.

The SDK watches authToken via its config prop. When the token changes, the provider updates internally without a remount. Pass the token through config only and let the SDK handle the transition:

// ✅ Correct — update config.authToken, no key needed
<VECUProvider config={{ authToken, deploymentStage }}>
  <WalletScreen />
</VECUProvider>

Error Handling

The SDK supports callbacks for common host-app error handling patterns. The callbacks are split between config (passed inside the config object) and direct VECUProvider props.

Config callbacks (passed via config={{ ... }}):

  • onTokenExpired — lets your app refresh the token and update provider config
  • onUnauthorized — lets your app react to permission or access issues
  • onError — lets your app capture SDK errors with context for logging, analytics, or support

Provider props (passed directly on VECUProvider, not inside config):

  • onReady — fires once the SDK has initialized successfully. Useful for analytics or gating UI that depends on SDK readiness.

High-level example showing both:

<VECUProvider
  config={{
    authToken,
    deploymentStage: 'sandbox',
    onTokenExpired: handleTokenExpired,
    onUnauthorized: () => {
      navigation.navigate('Login');
    },
    onError: (error, context) => {
      console.error(context.operation, error.message);
    },
  }}
  onReady={() => {
    analytics.track('wallet_sdk_initialized');
  }}
>
  <WalletScreen />
</VECUProvider>

Error Code References

Some SDK features expose exported error types or error code unions as part of their public API surface.

For example, specific flows may expose public error code types for:

  • credential presentation
  • NFC send or receive flows
  • Bluetooth send flows

These should be documented alongside the flow-specific guidance where they are most relevant, rather than treated as one large universal error-code list.

Related Guidance