# ShopRenter App Development: Getting Started

## Overview

This document provides comprehensive guidance for developing applications that integrate with the ShopRenter e-commerce platform. ShopRenter apps can be either Embedded (loaded within the ShopRenter admin interface) or Redirected (external applications).

**Reference**: This documentation is based on the official ShopRenter developer documentation at [doc.shoprenter.hu/development/app-development](https://doc.shoprenter.hu/development/app-development/01_getting_started.html).

## Application Registration

### Required Information

Before developing a ShopRenter app, you must register your application by contacting ShopRenter Partner Support at **partnersupport@shoprenter.hu** with the following details:

#### 1. Basic Information
- **Application Name** - The name that will appear in the ShopRenter installable apps list
- **Logo** - Image file with dimensions **250x150 pixels**
- **Description** - Brief description of your app (maximum **70 characters**)
- **Details Link** - URL to your application's sales/information page

#### 2. Technical Configuration
- **EntryPoint** - The HTTPS URL where your app is hosted
  - Must use HTTPS protocol
  - This is where ShopRenter will load your application

- **RedirectUri** - The HTTPS authentication endpoint
  - Receives authentication data during the OAuth flow
  - Must use HTTPS protocol
  - **Important**: Hash-based URL routes in SPAs (Single Page Applications) are technically incompatible

- **UninstallUri** - The HTTPS endpoint called when app is uninstalled
  - Receives GET request with query parameters: `shopname`, `code`, `timestamp`, `hmac`
  - Must use HTTPS protocol
  - Use this to clean up data and revoke access tokens

#### 3. Application Type
Choose one of two application types:

**Embedded Application**
- Loaded in an iframe within the ShopRenter admin interface
- Accessible at: `[shopname].myshoprenter.hu/admin/app/[id]`
- Provides seamless integration with the admin UI
- Best for tools that complement the admin interface

**Redirected Application**
- External application that users are directed to
- Directs users to the EntryPoint URL
- Best for standalone tools or services

#### 4. API Access
- **Required Scopes** - List of API permissions your app needs
  - Review available scopes in the ShopRenter API documentation
  - Request only the permissions necessary for your app's functionality
  - Users will see and approve these permissions during installation

#### 5. Testing Environment
- **Test Store Name** - Request a test store via [shoprenter.hu](https://shoprenter.hu)
  - Format: `[shopName].myshoprenter.hu`
  - Use this for development and testing before production release

### Credentials You'll Receive

After registration, ShopRenter Partner Support will provide:

- **AppId** - Unique application identifier
- **ClientId** - OAuth client identifier
- **ClientSecret** - Secret key for HMAC validation and authentication
- **App URL** - The registered application URL

**⚠️ Security**: Keep your ClientSecret secure and never expose it in client-side code!

## Installation Flow

Understanding the installation process is critical for implementing your app correctly.

### Step-by-Step Installation Process

#### 1. User Initiates Installation
- Shop owner browses the ShopRenter app marketplace
- Clicks to install your application

#### 2. ShopRenter Calls RedirectUri
ShopRenter makes a GET request to your RedirectUri with these query parameters:

- `shopname` - The shop's name (e.g., "example-shop")
- `code` - Authorization code for token exchange
- `timestamp` - Unix timestamp of the request
- `hmac` - HMAC-SHA256 signature for validation
- `app_url` - The URL where your app should redirect after validation

**Example Request**:
```
https://your-app.com/auth/callback?shopname=example-shop&code=abc123&timestamp=1234567890&hmac=xyz789&app_url=https://example-shop.myshoprenter.hu/admin/app/123
```

#### 3. HMAC Validation (Critical Security Step)
Your application MUST validate the HMAC signature to ensure the request is legitimate:

**HMAC Validation Process**:
1. Extract all query parameters except `hmac`
2. Sort parameters alphabetically by key
3. Create a query string: `key1=value1&key2=value2`
4. Compute HMAC-SHA256 hash using your ClientSecret
5. Compare computed hash with received `hmac` parameter
6. If they match, the request is authentic

**Example Validation (Pseudocode)**:
```javascript
function validateHmac(params, clientSecret) {
  const { hmac, ...dataToValidate } = params;

  // Sort parameters alphabetically
  const sortedParams = Object.keys(dataToValidate)
    .sort()
    .map(key => `${key}=${dataToValidate[key]}`)
    .join('&');

  // Compute HMAC-SHA256
  const computedHmac = crypto
    .createHmac('sha256', clientSecret)
    .update(sortedParams)
    .digest('hex');

  // Compare hashes
  return computedHmac === hmac;
}
```

**⚠️ Security Warning**: Never skip HMAC validation! This protects against unauthorized access attempts.

#### 4. Token Exchange
After validating the HMAC, exchange the authorization code for access tokens:

- Use the `code` parameter from the callback
- Make a POST request to ShopRenter's token endpoint
- Receive `access_token` and `refresh_token`
- Store tokens securely in your database

**Note**: Basic Authentication is **deprecated**. Attempting to use Basic Auth will return **403 Forbidden** errors.

#### 5. Redirect to App URL
After successful token exchange:
- Redirect the user to the `app_url` received in step 2
- ShopRenter will then load your EntryPoint with authentication parameters

#### 6. Application Loads
- ShopRenter loads your EntryPoint (in iframe for Embedded apps)
- Your app can now access the ShopRenter API using the obtained tokens

### Installation Flow Diagram

```
┌──────────────┐
│ Shop Owner   │
│ Clicks       │
│ "Install"    │
└──────┬───────┘
       │
       ▼
┌──────────────┐
│ ShopRenter   │
│ Calls        │
│ RedirectUri  │
└──────┬───────┘
       │ (shopname, code, timestamp, hmac, app_url)
       ▼
┌──────────────────┐
│ Your App         │
│ Validates HMAC   │
└──────┬───────────┘
       │ ✓ Valid
       ▼
┌──────────────────┐
│ Your App         │
│ Exchanges Code   │
│ for Tokens       │
└──────┬───────────┘
       │ (access_token, refresh_token)
       ▼
┌──────────────────┐
│ Store Tokens     │
│ in Database      │
└──────┬───────────┘
       │
       ▼
┌──────────────────┐
│ Redirect to      │
│ app_url          │
└──────┬───────────┘
       │
       ▼
┌──────────────────┐
│ ShopRenter Loads │
│ Your EntryPoint  │
└──────────────────┘
```

## OAuth 2.0 Authentication

ShopRenter uses OAuth 2.0 for secure API authentication.

### Token Types

#### Access Token
- Used to authenticate API requests
- Includes: `Authorization: Bearer {access_token}` header
- Has a limited lifetime (expires after a period)
- Must be refreshed when expired

#### Refresh Token
- Used to obtain new access tokens
- Longer lifetime than access tokens
- Store securely in your database

### Token Management

#### Storing Tokens
Store tokens securely in your database with:
- `store_id` - Unique identifier for the shop
- `access_token` - Current access token
- `refresh_token` - Refresh token for token renewal
- `token_expires_at` - Timestamp when access token expires
- `shopname` - Shop identifier

**Database Example**:
```sql
CREATE TABLE shoprenter_tokens (
  id UUID PRIMARY KEY,
  store_id UUID REFERENCES stores(id),
  access_token TEXT NOT NULL,
  refresh_token TEXT,
  expires_at TIMESTAMPTZ,
  shopname VARCHAR NOT NULL,
  scopes TEXT[],
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);
```

#### Token Refresh
When an access token expires:
1. Detect 401 Unauthorized response from API
2. Use refresh token to request new access token
3. Update stored access token and expiration time
4. Retry the original API request

**Token Refresh Flow**:
```javascript
async function refreshAccessToken(storeId) {
  const store = await getStoreTokens(storeId);

  const response = await fetch('https://oauth.shoprenter.hu/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'refresh_token',
      refresh_token: store.refresh_token,
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET
    })
  });

  const { access_token, expires_in } = await response.json();

  await updateStoreToken(storeId, {
    access_token,
    token_expires_at: new Date(Date.now() + expires_in * 1000)
  });

  return access_token;
}
```

### API Authentication

All API requests must include the access token in the Authorization header:

```http
GET /products HTTP/1.1
Host: example-shop.api.shoprenter.hu
Authorization: Bearer {access_token}
```

## Scope Management

Scopes define the permissions your app has to access ShopRenter resources.

### Requesting Scopes

When registering your app, request only the scopes necessary for your app's functionality. Common scopes include:

- `read_products` - Read product catalog
- `read_orders` - Access order information
- `read_customers` - View customer data
- `read_inventory` - Check inventory levels
- And more (see ShopRenter API documentation for complete list)

### Adding New Scopes

If you need to add features requiring additional permissions:

1. **Contact Partner Support** - Request the new scopes
2. **Update App Registration** - ShopRenter updates your app's scope list
3. **Request User Consent** - Existing installations need shop owner approval

### Scope Approval for Existing Installations

After new scopes are added, shop owners must approve them by visiting:

```
https://{shopName}.myshoprenter.hu/admin/app/{clientId}/approveScopes
```

**Implementation Steps**:
1. Detect missing permissions (API returns permission errors)
2. Notify shop owner about new features requiring permissions
3. Provide a link to the approval URL
4. After approval, your app has access to the new scopes

**Example User Flow**:
```javascript
function requestScopeApproval(shopName, clientId) {
  const approvalUrl = `https://${shopName}.myshoprenter.hu/admin/app/${clientId}/approveScopes`;

  // Show notification to shop owner
  showNotification({
    message: 'New features available! Please approve additional permissions.',
    action: {
      text: 'Approve Permissions',
      url: approvalUrl
    }
  });
}
```

## Uninstallation Flow

When a shop owner uninstalls your app, ShopRenter sends a GET request to your UninstallUri.

### Uninstall Request Parameters

The request includes these query parameters:
- `shopname` - The shop's name
- `code` - Uninstall verification code
- `timestamp` - Unix timestamp
- `hmac` - HMAC-SHA256 signature for validation

**Example Request**:
```
https://your-app.com/uninstall?shopname=example-shop&code=xyz789&timestamp=1234567890&hmac=abc123
```

### Handling Uninstallation

Your UninstallUri endpoint should:

1. **Validate HMAC** - Ensure the request is legitimate (same process as installation)
2. **Clean Up Data** - Remove or anonymize shop-specific data
3. **Revoke Tokens** - Delete stored access and refresh tokens
4. **Log Event** - Record the uninstallation for analytics
5. **Return Success** - Respond with 200 OK status

**Example Handler**:
```javascript
async function handleUninstall(req) {
  const { shopname, code, timestamp, hmac } = req.query;

  // 1. Validate HMAC
  if (!validateHmac(req.query, CLIENT_SECRET)) {
    return res.status(401).json({ error: 'Invalid HMAC' });
  }

  // 2. Find store
  const store = await findStoreByShopname(shopname);

  // 3. Clean up data
  await deleteStoreData(store.id);

  // 4. Revoke tokens
  await deleteStoreTokens(store.id);

  // 5. Mark as inactive
  await markStoreInactive(store.id);

  // 6. Log event
  await logUninstallEvent(store.id, timestamp);

  return res.status(200).json({ success: true });
}
```

## Development Best Practices

### Security

1. **Always Validate HMAC** - On both installation and uninstallation requests
2. **Use HTTPS** - All endpoints must use secure HTTPS connections
3. **Secure Token Storage** - Encrypt tokens at rest in your database
4. **Rotate Secrets** - Periodically rotate your ClientSecret (coordinate with Partner Support)
5. **Rate Limiting** - Implement rate limiting for API calls to avoid throttling
6. **Error Handling** - Handle API errors gracefully and provide helpful feedback

### OAuth Implementation

1. **Token Refresh** - Implement automatic token refresh before expiration
2. **Retry Logic** - Retry failed API calls with exponential backoff
3. **Scope Validation** - Check that you have required scopes before making requests
4. **State Management** - Use state parameter in OAuth flow to prevent CSRF attacks

### User Experience

1. **Loading States** - Show loading indicators during API operations
2. **Error Messages** - Provide clear, actionable error messages
3. **Onboarding** - Guide new users through setup and configuration
4. **Documentation** - Provide comprehensive user documentation
5. **Support** - Offer responsive customer support

### Testing

1. **Test Store** - Use your test store for development and testing
2. **Edge Cases** - Test token expiration, network failures, and permission errors
3. **HMAC Validation** - Verify HMAC validation works correctly
4. **Uninstall Flow** - Test the complete uninstall process
5. **Multi-Shop** - Test with multiple shops if supporting multi-shop scenarios

## API Usage

### Base URL

All API requests go to:
```
https://{shopname}.api.shoprenter.hu
```

Replace `{shopname}` with the shop's actual name.

### Making API Requests

**Example: Fetching Products**
```javascript
async function fetchProducts(shopname, accessToken) {
  const response = await fetch(
    `https://${shopname}.api.shoprenter.hu/products`,
    {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      }
    }
  );

  if (!response.ok) {
    if (response.status === 401) {
      // Token expired, refresh and retry
      const newToken = await refreshAccessToken(storeId);
      return fetchProducts(shopname, newToken);
    }
    throw new Error(`API Error: ${response.status}`);
  }

  return response.json();
}
```

### Pagination

ShopRenter API supports pagination for large datasets:

```javascript
async function fetchAllProducts(shopname, accessToken) {
  let allProducts = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://${shopname}.api.shoprenter.hu/products?page=${page}&limit=100`,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        }
      }
    );

    const data = await response.json();
    allProducts = allProducts.concat(data.items);

    hasMore = data.items.length === 100;
    page++;
  }

  return allProducts;
}
```

### Rate Limiting

Implement rate limiting to respect API quotas:

```javascript
class RateLimiter {
  constructor(maxRequests = 5, perSecond = 1) {
    this.maxRequests = maxRequests;
    this.perSecond = perSecond;
    this.queue = [];
    this.processing = false;
  }

  async request(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this.processQueue();
    });
  }

  async processQueue() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;
    const { fn, resolve, reject } = this.queue.shift();

    try {
      const result = await fn();
      resolve(result);
    } catch (error) {
      reject(error);
    }

    setTimeout(() => {
      this.processing = false;
      this.processQueue();
    }, (1000 / this.maxRequests) * this.perSecond);
  }
}

const limiter = new RateLimiter(5, 1); // 5 requests per second

// Usage
await limiter.request(() => fetchProducts(shopname, token));
```

## Reference Resources

ShopRenter provides example implementations to help you get started:

### Example Applications

1. **SR Demo App (PHP)** - Basic PHP implementation
2. **PHP Slim Framework** - REST API implementation using Slim
3. **Node.js** - JavaScript/Node.js implementation

These examples are available on GitHub (referenced in ShopRenter documentation).

### Documentation Links

- **App Development Guide**: [doc.shoprenter.hu/development/app-development](https://doc.shoprenter.hu/development/app-development/01_getting_started.html)
- **API Documentation**: [doc.shoprenter.hu/api](https://doc.shoprenter.hu/api/)
- **Payment API**: [doc.shoprenter.hu/paymentapi](https://doc.shoprenter.hu/paymentapi/)

## ShopCall.ai Integration

This project (ShopCall.ai) implements ShopRenter integration with the following components:

### Edge Functions
- `oauth-shoprenter-init` - Initiates OAuth flow
- `oauth-shoprenter-callback` - Handles OAuth callback
- `webhook-shoprenter-uninstall` - Handles app uninstallation
- `shoprenter-sync` - Manual data synchronization
- `shoprenter-scheduled-sync` - Automated background sync (hourly via pg_cron)

### API Client
- `_shared/shoprenter-client.ts` - ShopRenter API client with token refresh

### Data Sync
- Products, orders, and customers cached in Supabase tables
- Automatic token refresh when expired
- Rate limiting (5 requests/second)
- Retry logic with exponential backoff

### Database Tables
- `shoprenter_tokens` - Token management
- `shoprenter_products_cache` - Cached product data
- `pending_shoprenter_installs` - Temporary OAuth state
- `oauth_states` - OAuth flow state management
- `sync_logs` - Sync execution tracking
- `store_sync_config` - Per-store sync configuration

See `CLAUDE.md` for complete implementation details.

## Getting Help

For questions or issues:

1. **ShopRenter Partner Support**: partnersupport@shoprenter.hu
2. **Developer Documentation**: [doc.shoprenter.hu](https://doc.shoprenter.hu)
3. **API Documentation**: [doc.shoprenter.hu/api](https://doc.shoprenter.hu/api/)

---

*Last Updated: 2025-01-30*
*Based on: ShopRenter Developer Documentation - App Development - Getting Started*
