npm package: @kigo-dev/marketplace-sdk
Description: SDK for integrating mini-apps into the Kigo marketplace ecosystem
Author: Kigo Team
Latest version: 0.6.1

# @kigo-dev/marketplace-sdk

> SDK for integrating mini-apps into the Kigo marketplace ecosystem.

[![npm version](https://img.shields.io/npm/v/@kigo-dev/marketplace-sdk.svg)](https://www.npmjs.com/package/@kigo-dev/marketplace-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)

**[Leer en Español](./docs/README.es.md)**

---

The Kigo Marketplace SDK allows web developers to integrate their applications ("mini-apps") within the Kigo native ecosystem. It provides access to native capabilities such as **Payments**, **Authentication**, **Navigation**, **Location**, and **UI** through a secure bridge between JavaScript and the Kigo Native App.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Modules Reference](#modules-reference)
  - [Auth](#auth-authentication)
  - [Payments](#payments)
  - [Navigation](#navigation)
  - [UI](#ui-user-interface)
  - [Device](#device)
  - [Location](#location)
  - [Storage](#storage)
  - [Analytics](#analytics)
- [DevTools](#devtools)
- [Error Handling](#error-handling)
- [Best Practices](#best-practices)
- [FAQ](#faq)
- [Development](#development)
- [License](#license)

## Installation

```bash
npm install @kigo-dev/marketplace-sdk
```

## Quick Start

### Step 1: Initialization

The SDK must be initialized before using any module. This ensures the communication bridge with the native app is ready.

```typescript
import { kigo } from '@kigo-dev/marketplace-sdk';

async function startApp() {
  try {
    // 1. Initialize the bridge
    await kigo.init();

    // 2. Get user session and context
    const session = await kigo.auth.init();
    console.log(`Authenticated user: ${session.userId}`);
  } catch (error) {
    console.error('Error initializing Kigo SDK:', error);
    // Handle the case where the app is not running inside Kigo
  }
}

startApp();
```

#### Bridge Configuration

`kigo.init()` accepts optional configuration to customize the bridge behavior:

```typescript
await kigo.init({
  timeout: 15000,          // Default timeout for bridge calls (ms). Default: 10000
  enableLogging: false,    // Disable bridge debug logs in production
  devtools: { enabled: true }, // Enable built-in DevTools console
});
```

> **Note:** Specific modules like `payments.checkout()` and `navigation.openExternal()` use their own extended timeouts to account for user interaction, regardless of the global setting.

### Step 2: Local Development (Playground)

The SDK does not work in a standard browser because it requires the native bridge.

To develop locally, use the **Playground** included in the repository, which simulates the native app's behavior:

```bash
# Install dependencies
npm install

# Start dev server with playground
npm run dev
```

> **Note:** If you see the error `Bridge not available`, make sure your app is running inside the Kigo container or using the appropriate test environment.

## Modules Reference

### Auth (Authentication)

Manages the user session. The mini-app should **never** ask the user to log in; it inherits the session from Kigo.

#### `kigo.auth.init()`

Performs the handshake and retrieves the session token.

**Returns:**

```typescript
{
  sessionId: string;  // Ephemeral token for backend calls
  userId: string;     // User's ID in Kigo
  expiresAt: number;  // Expiration timestamp
}
```

---

### Payments

Opens the native payment interface. The web app **never** handles credit card details directly.

> **Timeout:** `checkout()` uses a 5-minute timeout (instead of the default 10 seconds) because the user interacts with the native payment UI. `getStatus()` uses the standard timeout.

#### `kigo.payments.checkout(payload)`

Initiates the native payment flow.

**Parameters:**

```typescript
{
  amount: number;        // Amount to charge (e.g. 100.00)
  currency: string;      // Currency code (e.g. 'MXN')
  description?: string;  // Concept displayed to the user
  metadata?: object;     // Extra data for your backend
}
```

**Returns:**

```typescript
{
  transactionId: string;
  status: 'approved' | 'rejected' | 'pending';
  amount: number;
  currency: string;
  createdAt: number;
}
```

**Common Errors:**

| Error Code | Description |
|---|---|
| `INSUFFICIENT_FUNDS` | User has insufficient balance or card was declined |
| `PAYMENT_CANCELLED` | User closed the payment modal |
| `PAYMENT_FAILED` | Payment processing error |
| `INVALID_AMOUNT` | Invalid amount (negative or zero) |

#### `kigo.payments.getStatus(payload)`

Checks the status of an existing transaction.

**Parameters:**

```typescript
{
  transactionId: string;
}
```

**Returns:**

```typescript
{
  transactionId: string;
  status: 'approved' | 'pending' | 'rejected' | 'cancelled';
  updatedAt: number;
}
```

---

### Navigation

Controls the mini-app lifecycle.

#### `kigo.navigation.close()`

Closes the mini-app and returns the user to the Kigo Marketplace.

```typescript
// Simple close
await kigo.navigation.close();

// Close with reason (useful for analytics)
await kigo.navigation.close({ reason: 'Purchase completed' });
```

#### `kigo.navigation.openExternal(options)`

Opens a URL in the system browser or in a controlled in-app browser.

**Parameters:**

```typescript
{
  url: string;              // URL to open (must be http:// or https://)
  openInBrowser?: boolean;  // true = system browser, false = in-app browser (default: false)
  waitForClose?: boolean;   // true = promise resolves when the in-app browser is dismissed (default: false)
  closeOnUrls?: string[];   // URLs that auto-close the browser when navigated to
}
```

**Returns:** `{ matchedUrl?: string }` — the URL that triggered the auto-close (from `closeOnUrls`), or `undefined` if the user closed manually.

**Basic usage:**

```typescript
// Open in external browser (Chrome/Safari)
await kigo.navigation.openExternal({
  url: 'https://example.com/terms',
  openInBrowser: true,
});

// Open in in-app browser
await kigo.navigation.openExternal({
  url: 'https://example.com/privacy',
  openInBrowser: false,
});
```

**Wait for the user to return:**

```typescript
// Open a page and wait until the user closes the in-app browser
await kigo.navigation.openExternal({
  url: 'https://example.com/info',
  openInBrowser: false,
  waitForClose: true,
});
// This line runs only after the in-app browser is dismissed
console.log('User returned to the mini-app');
```

**Third-party payment flow with `closeOnUrls`:**

When integrating with external payment providers (e.g., Orkesta), use `closeOnUrls` to detect when the payment finishes. The native app intercepts the redirect before the target page loads, auto-closes the browser, and returns the matched URL to your mini-app.

```typescript
const result = await kigo.navigation.openExternal({
  url: 'https://payment-provider.com/pay?ref=12345',
  openInBrowser: false,
  waitForClose: true,
  closeOnUrls: [
    'https://mysite.com/payment/success',
    'https://mysite.com/payment/failure',
  ],
});

if (result.matchedUrl?.includes('/success')) {
  await kigo.ui.toast({ message: 'Payment successful!', type: 'success' });
} else if (result.matchedUrl?.includes('/failure')) {
  await kigo.ui.toast({ message: 'Payment failed', type: 'error' });
} else {
  // User closed the browser manually without completing
}
```

**Common Errors:**

| Error Code | Description |
|---|---|
| `INVALID_URL` | URL is malformed or uses a non-http(s) scheme |
| `NAVIGATION_BLOCKED` | URL is not in the backend allowlist |

---

### UI (User Interface)

Uses native components to maintain visual consistency with the Kigo app.

#### `kigo.ui.toast({ message, type?, duration? })`

Shows a native floating notification.

- `type`: `'success'` | `'error'` | `'info'` | `'warning'` (default: `'info'`)
- `duration`: Duration in milliseconds (default: `3000`)

```typescript
await kigo.ui.toast({
  message: 'Item added to cart',
  type: 'success',
});
```

#### `kigo.ui.confirm({ title, message, confirmText?, cancelText?, type? })`

Shows a native confirmation dialog (OK/Cancel).

**Returns:** `{ confirmed: boolean }`

```typescript
const result = await kigo.ui.confirm({
  title: 'Cancel order',
  message: 'This action cannot be undone.',
  confirmText: 'Yes, cancel',
  cancelText: 'Keep order',
  type: 'destructive',
});

if (result.confirmed) {
  // Proceed with cancellation
}
```

#### `kigo.ui.setTitle(title)`

Changes the text in the app's top navigation bar.

```typescript
await kigo.ui.setTitle('Product Detail');
```

---

### Device

Retrieves environment information to adapt your layout and behavior.

#### `kigo.device.getContext()`

**Returns:**

```typescript
{
  theme: 'light' | 'dark';          // Useful for dynamic CSS
  locale: string;                    // e.g. 'es', 'en'
  platform: 'ios' | 'android';
  safeArea: {
    top: number;
    bottom: number;
    left: number;
    right: number;
  };
  appVersion: string;
  projectRef?: string;               // Current project reference
  screen: {
    width: number;
    height: number;
  };
}
```

```typescript
const context = await kigo.device.getContext();

// Apply safe area padding
document.body.style.paddingTop = `${context.safeArea.top}px`;

// Theme adaptation
if (context.theme === 'dark') {
  document.body.classList.add('dark-mode');
}
```

---

### Location

Retrieves the user's geographic coordinates from the native app. The web service never accesses GPS hardware directly — it always goes through the native bridge.

#### `kigo.location.getCurrent()`

Requests the device's current location.

**Returns:**

```typescript
{
  latitude: number;    // Decimal degrees (e.g. 19.4326)
  longitude: number;   // Decimal degrees (e.g. -99.1332)
  accuracy?: number;   // Accuracy in meters (optional)
}
```

```typescript
const loc = await kigo.location.getCurrent();
console.log(`User is at ${loc.latitude}, ${loc.longitude}`);

// Use for nearby searches
const stores = await fetchNearbyStores(loc.latitude, loc.longitude);
```

**Common Errors:**

| Error Code | Description |
|---|---|
| `LOCATION_UNAVAILABLE` | Location services are disabled or permission was denied |

---

### Storage

Securely stores persistent data, isolated per mini-app.

#### `kigo.storage.set(key, value)`

Saves a string value.

#### `kigo.storage.get(key)`

Reads a string value. Returns `{ value: string | null }`.

#### `kigo.storage.remove(key)`

Deletes a stored value.

#### `kigo.storage.setObject(key, obj)`

Saves a JSON object (automatic serialization).

#### `kigo.storage.getObject<T>(key)`

Reads and parses a JSON object. Returns `T | null`.

```typescript
// Save preferences
await kigo.storage.setObject('user_prefs', {
  theme: 'dark',
  notifications: true,
});

// Read preferences
const prefs = await kigo.storage.getObject('user_prefs');
```

---

### Analytics

Sends tracking events to the Kigo analytics pipeline.

#### `kigo.analytics.track({ event, properties? })`

```typescript
await kigo.analytics.track({
  event: 'purchase_completed',
  properties: {
    amount: 100,
    currency: 'MXN',
    product_id: 'prod_123',
  },
});
```

> Use `snake_case` for event names. Be specific but concise.

---

## DevTools

Built-in debug console for inspecting bridge traffic, API calls, and SDK lifecycle events in real time. Designed for development and QA — it renders inside a Shadow DOM so it never interferes with your application's styles or layout.

### Enabling DevTools

```typescript
// Option A: Enable programmatically (recommended)
kigo.devtools.enable();

// Option B: Enable via init()
await kigo.init({
  devtools: { enabled: true },
});
```

### Enabling with fetch interception

```typescript
kigo.devtools.enable({
  interceptFetch: true,
  fetchFilter: url => url.includes('/v1/'),
});
```

When `interceptFetch` is `true`, DevTools hooks `window.fetch` to log API calls with method, path, status code, and duration. Use `fetchFilter` to limit which URLs are captured.

### Configuration

| Option | Type | Default | Description |
|---|---|---|---|
| `enabled` | `boolean` | `false` | Auto-enable on `kigo.init()` |
| `interceptFetch` | `boolean` | `false` | Log `fetch()` calls in the console |
| `fetchFilter` | `(url: string) => boolean` | — | Only log fetch URLs that pass this filter |
| `maxEntries` | `number` | `200` | Maximum log entries to retain |
| `panelHeight` | `string` | `'40vh'` | Initial panel height (any CSS value) |
| `shortcutKey` | `string` | `'D'` | Keyboard shortcut key (Ctrl+Shift+key) |
| `draggable` | `boolean` | `true` | Allow the panel and toggle button to be moved freely |
| `focusDiagnostics` | `boolean` | `false` | Track focus/blur/touch events to debug iOS WebView focus loss |

### Draggable Panel

By default (`draggable: true`) the DevTools panel opens as a **floating window** that you can freely reposition by dragging the grab handle at the top. This prevents the panel from covering UI elements at the bottom of the screen.

- **Move the panel**: drag the pill-shaped handle bar at the very top of the panel.
- **Move the toggle button**: press and drag the FAB. A short tap (no drag) still opens/closes the panel.
- **Disable**: pass `draggable: false` to revert to the fixed full-width bottom sheet.

```typescript
// Disable dragging (full-width bottom sheet, original behavior)
kigo.devtools.enable({ draggable: false });
```

### Focus Diagnostics (iOS WebView debugging)

Enable this when investigating issues where buttons inside the WebView become unclickable on iOS after bridge interactions. This is a common problem with Flutter's WKWebView losing first-responder status during platform channel calls.

```typescript
kigo.devtools.enable({ focusDiagnostics: true });
```

When enabled, a new **FOCUS** filter appears in the console. It captures:

| Event | What it tells you |
|---|---|
| `focus` / `blur` / `focusin` / `focusout` | Which element gained or lost focus and what it transferred to |
| `touchstart` / `touchend` | Which element was touched on screen |
| `pointerdown` | Raw pointer events reaching DOM elements |
| `visibilitychange` | Whether the page was backgrounded/foregrounded by the OS |
| **activeElement polling** | Silent focus changes (e.g., focus drifts to `<body>` without a blur event — the hallmark of WKWebView first-responder loss) |
| **Bridge correlation** | Whether focus dropped immediately after a bridge request or response |

**Example output entries in the FOCUS tab:**

```
blur on <button.checkout> → <body>    ← focus silently lost
Focus lost after bridge response: payments.checkout  was <button.checkout>, now <body>
touchstart on <button.pay> — no touchend within 300ms  ← possible iOS gesture swallow
```

This data gives your Flutter team the exact evidence needed to trace whether the issue originates in the platform channel round-trip, the gesture arena, or a WebView state change triggered by the native side.

### API

```typescript
kigo.devtools.enable(config?)   // Activate the overlay
kigo.devtools.disable()         // Tear down and restore globals
kigo.devtools.toggle()          // Open/close panel (or press Ctrl+Shift+D)
kigo.devtools.clear()           // Remove all log entries
kigo.devtools.copyLogs()        // Copy visible entries to clipboard
kigo.devtools.isEnabled()       // Check if active

// Push custom entries
kigo.devtools.log('Bootstrap complete');
kigo.devtools.log('error', 'Auth failed', JSON.stringify(err));
```

### What it captures

| Entry type | Badge | Source |
|---|---|---|
| Bridge request | **JS → Flutter** | Every `bridge.send()` call with service, action, and payload |
| Bridge response | **Flutter → JS** | Every response, showing `service.action` and round-trip time |
| Bridge timeout | **ERROR** | Requests that exceeded the configured timeout |
| API success | **API** | Fetch calls returning 2xx (when `interceptFetch` is on) |
| API error | **API ERR** | Fetch calls returning non-2xx or network errors |
| SDK lifecycle | **INFO** | Bridge ready, initialization, internal Bridge logs |
| Focus events | **FOCUS** | focus/blur/touch/visibility events (when `focusDiagnostics` is on) |

### Safety

DevTools is wrapped in error boundaries at every layer. It will **never** crash your application, even if its own internals fail. When disabled, `window.fetch` is fully restored to its original reference and all focus/touch listeners are removed.

---

## Error Handling

The SDK uses Promises. Always use `try/catch`. Errors follow this structure:

```typescript
interface BridgeError {
  code: string;     // Standard error code
  message: string;  // Human-readable message for debugging
}
```

### Error Codes

| Code | Description | Recommended Action |
|---|---|---|
| `BRIDGE_TIMEOUT` | The native app took too long to respond | Retry or show a network error |
| `AUTH_FAILED` | Could not obtain a session | Show error screen with an "Exit" button |
| `PAYMENT_CANCELLED` | User closed the checkout | Do nothing, or ask if they want to retry |
| `CHANNEL_NOT_AVAILABLE` | App is not running inside Kigo | Only use within the Kigo environment |
| `SESSION_EXPIRED` | User session has expired | Re-initialize with `kigo.auth.init()` |
| `LOCATION_UNAVAILABLE` | Location services disabled or permission denied | Ask the user to enable location |

## Best Practices

### Environment Validation

Call `kigo.init()` as early as possible in your application (e.g., in `App.tsx` or `main.ts`). If it fails, show a friendly screen indicating the app must be opened from Kigo.

```typescript
// main.ts
try {
  await kigo.init();
} catch {
  showKigoRequiredScreen();
}
```

### Safe Areas

Use `kigo.device.getContext()` to get `safeArea.top` and `safeArea.bottom`. Apply that padding to your main container to prevent content from being hidden behind the status bar or home indicator on iOS.

```typescript
const { safeArea } = await kigo.device.getContext();
document.documentElement.style.setProperty('--safe-top', `${safeArea.top}px`);
document.documentElement.style.setProperty('--safe-bottom', `${safeArea.bottom}px`);
```

### No Tokens in URLs

Never pass tokens or user IDs via URL parameters. Always rely on `kigo.auth.init()` to securely obtain the user's identity.

### Robust Payments

Even if the SDK returns `approved`, your backend should listen to **Kigo Webhooks** to confirm the transaction asynchronously (reconciliation).

## FAQ

**Q: The DevTools panel is covering buttons in my app. How do I move it?**
A: Drag the pill-shaped handle at the top of the panel to reposition it anywhere on screen. You can also drag the toggle FAB button (bottom-right) to a different corner. If you prefer the original fixed behavior, enable DevTools with `draggable: false`.

**Q: Buttons in the iOS WebView stop responding after a bridge call. How do I debug this?**
A: Enable Focus Diagnostics: `kigo.devtools.enable({ focusDiagnostics: true })`. Then open the **Focus** filter tab in DevTools. Look for entries that say `Focus lost after bridge response: <service>.<action>` or `touchstart ... no touchend within 300ms`. Share these logs with your Flutter team — they pinpoint whether the issue is in the WKWebView first-responder chain or the gesture arena.

**Q: Can I test the SDK in my Chrome/Safari browser?**
A: Not directly. The SDK requires the native bridge to be available. Use the Playground included in this repository for local development, which mocks the native bridge behavior.

**Q: What happens if the user has no internet?**
A: Bridge calls (`kigo.*`) will fail with an error or timeout. Handle these cases gracefully in your UI.

**Q: How do I know which user is using my app?**
A: Call `kigo.auth.init()`. It returns a `userId` and a `sessionId` that you can send to your own backend for request validation.

**Q: Can I use the SDK outside the Kigo app?**
A: The SDK is designed to run inside the Kigo native app container. Attempting to use it outside will result in a `CHANNEL_NOT_AVAILABLE` error.

## Development

```bash
# Install dependencies
npm install

# Development mode with playground
npm run dev

# Production-like dev mode
npm run dev:prod

# Build the SDK
npm run build

# Type checking
npm run typecheck
```

### Architecture

```
src/
├── bridge/      # Core messaging system (JS ↔ Native)
├── modules/     # Business features (auth, payments, navigation, ...)
├── devtools/    # Built-in debug console (Shadow DOM, event bus)
├── utils/       # Shared utilities
└── index.ts     # Main entry point
```

## License

MIT © Kigo 2026