|
@@ -0,0 +1,602 @@
|
|
|
|
|
+# 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×tamp=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×tamp=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*
|