SeloraXDEVELOPERS

Linking Your App to SeloraX

This guide walks through the complete process of building an app, registering it on the SeloraX platform, and connecting it to merchant stores. Whether you are building a public marketplace app or a private integration, this guide covers every step.

Choose Your Integration Type

Before you start, decide which integration model fits your use case:

Option A: Custom App (Private Integration)

Best for: connecting your own store to external tools, building internal automations, or testing the API.

  • Created from the merchant dashboard (no admin involvement)
  • Auto-installed to your store
  • Get API credentials immediately
  • No OAuth flow, no iframe, no marketplace listing

Follow the Custom Apps guide for this path.

Option B: Platform App (Marketplace App)

Best for: building an app that any merchant can install from the marketplace.

  • Registered by the platform admin
  • Uses OAuth 2.0 for installation
  • Can be embedded in the merchant dashboard as an iframe
  • Supports billing (one-time, recurring, usage-based)
  • Listed in the app marketplace

Continue reading this guide for the platform app path.


Building a Platform App

1. Plan Your App

Define what your app does and what data it needs:

DecisionOptions
Data accessWhich scopes? (read:orders, write:products, etc.)
Real-time eventsWhich webhook topics? (order.created, product.updated, etc.)
User interfaceEmbedded iframe in dashboard? Or headless/backend only?
MonetizationFree? One-time charge? Subscription? Usage-based?
ArchitectureYour app needs a backend server for OAuth and API calls

2. Register Your App

App registration is handled by the SeloraX platform admin. Provide the following:

{
  "name": "My App",
  "app_url": "https://my-app.example.com",
  "webhook_url": "https://my-app.example.com/api/oauth",
  "redirect_urls": ["https://my-app.example.com/oauth/callback"],
  "requested_scopes": ["read:orders", "read:products"],
  "webhook_topics": ["order.created", "order.status_changed"],
  "developer_name": "Your Name",
  "developer_email": "[email protected]",
  "pricing_type": "free"
}

You receive three credentials (shown only once):

CredentialFormatPurpose
Client IDsx_app_<32hex>Identifies your app in OAuth and API calls
Client Secretsx_secret_<64hex>Authenticates your app (keep secret)
Session Signing Key<64hex>Verifies iframe session tokens

See Creating an App for the full list of configuration fields.

3. Implement the OAuth Install Flow

When a merchant clicks "Install" in the marketplace, SeloraX starts the OAuth 2.0 authorization code flow:

Merchant clicks "Install"
        │
        ▼
SeloraX redirects to ──► GET /api/apps/oauth/authorize
        │                    ?client_id=sx_app_...
        │                    &redirect_uri=https://my-app.example.com/oauth/callback
        │                    &scope=read:orders,read:products
        │                    &state=random_csrf_token
        │
        ▼
Merchant authorizes your app
        │
        ▼
SeloraX redirects to ──► https://my-app.example.com/oauth/callback
                             ?code=sx_ac_...
                             &state=random_csrf_token
        │
        ▼
Your server exchanges code for tokens
        │
        ▼
POST /api/apps/oauth/token
  {
    grant_type: "authorization_code",
    client_id: "sx_app_...",
    client_secret: "sx_secret_...",
    code: "sx_ac_...",
    redirect_uri: "https://my-app.example.com/oauth/callback"
  }
        │
        ▼
Response:
  {
    access_token: "sx_at_...",
    refresh_token: "sx_rt_...",
    token_type: "bearer",
    expires_in: 86400,
    scope: "read:orders read:products",
    store_id: 22,
    installation_id: 5
  }

Implementation:

// OAuth callback handler
app.get("/oauth/callback", async (req, res) => {
  const { code, state } = req.query;
 
  // Verify state matches what you sent (CSRF protection)
  if (state !== expectedState) {
    return res.status(403).send("Invalid state parameter");
  }
 
  // Exchange authorization code for tokens
  const { data } = await axios.post(`${SELORAX_API}/apps/oauth/token`, {
    grant_type: "authorization_code",
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    code,
    redirect_uri: REDIRECT_URI,
  });
 
  // Store tokens in your database (keyed by store_id)
  await db.storeTokens(data.store_id, {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    installation_id: data.installation_id,
    scope: data.scope,
  });
 
  res.send("Installation successful! You can close this window.");
});

Key rules:

  • Authorization codes expire in 60 seconds -- exchange them immediately
  • The redirect_uri must exactly match one of your registered redirect_urls
  • Store tokens securely -- the access token grants API access to the merchant's store
  • Access tokens expire in 24 hours; use the refresh token to get a new one

4. Authenticate API Requests

After installation, you have two ways to authenticate:

curl -H "Authorization: Bearer sx_at_..." \
  https://api.selorax.io/api/apps/v1/orders

Client Credentials (for server-to-server, never expires)

curl -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22" \
  https://api.selorax.io/api/apps/v1/orders

Client credentials are ideal for background jobs, cron tasks, and integrations where you do not have a user session.

5. Refresh Expired Tokens

Access tokens expire after 24 hours. Use the refresh token to get a new one:

async function refreshAccessToken(storeId) {
  const tokens = await db.getTokens(storeId);
 
  const { data } = await axios.post(`${SELORAX_API}/apps/oauth/token`, {
    grant_type: "refresh_token",
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    refresh_token: tokens.refresh_token,
  });
 
  // Store the new tokens (refresh token is also rotated)
  await db.storeTokens(storeId, {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
  });
 
  return data.access_token;
}

Refresh tokens are valid for 90 days. If a refresh token expires, the merchant must reinstall your app.

6. Handle Webhooks

When your app is installed, the platform auto-creates webhook subscriptions for the topics in your webhook_topics field. Each subscription gets a unique HMAC signing secret.

Receive the signing secret during install:

The platform sends a POST to {your_webhook_url}/token with the installation details and webhook signing secrets. Store these secrets to verify incoming webhooks.

Verify and process webhooks:

const crypto = require("crypto");
 
app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-selorax-signature"];
  const timestamp = req.headers["x-selorax-timestamp"];
  const secret = getSigningSecret();
 
  const signedContent = `${timestamp}.${req.body}`;
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(signedContent).digest("hex");
 
  if (
    expected.length !== signature.length ||
    !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  ) {
    return res.status(401).send("Invalid signature");
  }
 
  const event = JSON.parse(req.body);
 
  // Process the event
  switch (event.event_topic) {
    case "order.created":
      handleNewOrder(event.data);
      break;
    case "order.status_changed":
      handleStatusChange(event.data);
      break;
  }
 
  res.status(200).send("OK");
});

Delivery guarantees:

  • At-least-once delivery (your handler should be idempotent)
  • Retries on failure: immediate, 1min, 5min, 30min, 2hr, 12hr (6 total attempts)
  • Auto-disabled after 20 consecutive failures
  • View delivery logs in the merchant dashboard

7. Embed in the Dashboard (Optional)

If your app has a UI, it loads inside the merchant dashboard as an iframe. The platform sends a session token via postMessage.

Frontend (your iframe page):

<script>
  // Signal readiness
  window.parent.postMessage({ type: "app-bridge:ready" }, "*");
 
  // Receive session token
  window.addEventListener("message", (event) => {
    if (event.data.type === "selorax:session-token") {
      const { token, store_id } = event.data;
      // Use token for authenticated frontend requests
      fetchData(token, store_id);
    }
  });
</script>

Backend (verify the session token):

const crypto = require("crypto");
 
function verifySessionToken(token, signingKey) {
  const [header, payload, signature] = token.split(".");
  const expected = crypto
    .createHmac("sha256", signingKey)
    .update(`${header}.${payload}`)
    .digest("base64url");
 
  if (signature !== expected) return null;
 
  const decoded = JSON.parse(Buffer.from(payload, "base64url").toString());
  if (decoded.exp < Math.floor(Date.now() / 1000)) return null;
 
  return decoded; // { iss, dest, aud, sub, sid, app_id }
}

Session tokens expire in 10 minutes. The dashboard automatically sends a new one when the merchant navigates to your app.

8. Charge Merchants (Optional)

If your app has a pricing model, use the billing API to create charges:

// Create a one-time charge
const { data } = await axios.post(
  `${SELORAX_API}/apps/v1/billing/charges`,
  {
    name: "Pro Feature Unlock",
    amount: 499,
    currency: "BDT",
    charge_type: "one_time",
    return_url: "https://my-app.example.com/billing/complete",
  },
  {
    headers: { Authorization: `Bearer ${accessToken}` },
  },
);
 
// Redirect merchant to approve the charge
// data.confirmation_url → merchant approves → payment processed → redirect to return_url

See the Billing documentation for recurring subscriptions, usage-based billing, and wallet top-ups.


Complete Integration Checklist

  • Register your app with the platform admin
  • Store client_id, client_secret, and session_signing_key securely
  • Implement OAuth callback endpoint (/oauth/callback)
  • Exchange authorization codes for tokens
  • Store access and refresh tokens per store
  • Implement token refresh logic (24-hour expiry)
  • Make API calls with Bearer token or client credentials
  • Set up webhook endpoint to receive events
  • Verify HMAC signatures on all webhook deliveries
  • (If embedded) Implement iframe with postMessage session tokens
  • (If paid) Implement billing charge creation and approval flow
  • Test the full install flow end-to-end

Common Issues

"Invalid redirect_uri"

The redirect_uri in your token exchange must exactly match one of the URLs in your app's redirect_urls array, including protocol and trailing slashes.

"Authorization code expired"

Codes are valid for 60 seconds. Exchange them immediately in your callback handler. Do not redirect the user before exchanging the code.

"Insufficient scope"

Your app is requesting an API endpoint that requires a scope you did not include in requested_scopes. Update your app configuration and ask the merchant to reinstall.

Webhook deliveries failing

  • Ensure your endpoint returns a 2xx status code within 15 seconds
  • Check that your server is publicly accessible (not running locally)
  • Verify the signing secret matches the one received during installation
  • Check the delivery logs in Settings > Apps > Webhooks > Deliveries

Next Steps