Authentication

This guide walks you through implementing OAuth 2.0 authorization for multi-tenant platforms integrating with Shuttle Payment Links. As a platform provider, you'll enable your merchants to securely connect their Shuttle accounts to your application.

Overview

Multi-tenant integration with Shuttle requires:

  1. OAuth 2.0 Setup - Configure your application as an OAuth client
  2. Merchant Onboarding - Guide merchants through authorization
  3. Token Management - Handle access and refresh tokens
  4. API Integration - Make authenticated requests on behalf of merchants

Platform Expectations

Your Application's Role

As a multi-tenant platform, your application will:

  • Act as an OAuth client with Shuttle
  • Prompt merchants to select environment before authorization (Sandbox or Live) - Default to Live
  • Use the appropriate Client ID and Client Secret for the selected environment
  • Manage authorization flows for multiple merchants
  • Store and manage access and refresh tokens securely for each merchant
  • Make API calls on behalf of authorized merchants

Merchant Experience

Your merchants will:

  • Click "Connect Shuttle" in your platform
  • Select environment (Sandbox or Live) - Default to Live
  • Be redirected to Shuttle's authorization page
  • Log in with their Shuttle credentials
  • Grant permissions to your application
  • Return to your platform with access granted

Implementation Steps

Step 1: OAuth Client Setup

Shuttle provides both sandbox and live environments. You'll receive two sets of OAuth credentials:

Live Environment:

  • Live Client ID - For production transactions
  • Live Client Secret - For production authentication

Sandbox Environment:

  • Sandbox Client ID - For testing and development
  • Sandbox Client Secret - For testing authentication

Platform Integration Expectation: Your platform should store all four credentials and always prompt users to choose between sandbox or live environments when connecting to Shuttle. Default to live environment, then use the appropriate Client ID and Client Secret for the selected environment throughout the OAuth flow and subsequent OAUTH operations.

Contact Shuttle support to obtain your OAuth credentials for both environments.

Step 2: Authorization Flow Implementation

Follow the complete OAuth 2.0 Authorization Code flow. For detailed technical implementation, see:

📚 Complete OAuth Guide: Multi-Tenant OAuth 2.0

Key API Endpoints:

  • Authorization: /authorize - Initiate merchant authorization
  • Token Exchange: /token - Exchange codes for tokens
  • Token Refresh: /token - Refresh expired tokens
  • Token Revocation: /revoke - Revoke tokens
  • Token Introspection: /introspect - Validate tokens

Step 3: Merchant Onboarding Flow

3.1 Initiate Authorization

When a merchant wants to connect Shuttle, prompt them to select environment (default to live):

// User selects environment (default to live)
const environment = userSelectedEnvironment || 'live'; // 'live' or 'sandbox'
const clientId = environment === 'live' ? LIVE_CLIENT_ID : SANDBOX_CLIENT_ID;

// Generate authorization URL
const authUrl = `https://merchant.shuttleglobal.com/authorize?` +
  `response_type=code&` +
  `client_id=${clientId}&` +
  `redirect_url=${encodeURIComponent(CALLBACK_URL)}&` +
  `state=${merchantId}_${randomState}`;

// Redirect merchant to Shuttle
window.location.href = authUrl;

3.2 Handle Authorization Callback

Process the merchant's return from Shuttle:

// In your callback handler
const { code, state } = req.query;

// Verify state and extract merchant ID
const [merchantId, originalState] = state.split('_');
if (!verifyState(originalState)) {
  throw new Error('Invalid state parameter');
}

// Exchange code for tokens
const tokens = await exchangeCodeForTokens(code);

// Store tokens for this merchant
await storeMerchantTokens(merchantId, tokens);

3.3 Store Merchant Tokens

Securely store tokens with merchant association:

// Example token storage
await db.merchantTokens.create({
  merchantId: merchantId,
  accessToken: tokens.access_token,
  refreshToken: tokens.refresh_token,
  expiresAt: Date.now() + (tokens.expires_in * 1000),
  scopes: tokens.scope || 'default'
});

Step 4: API Integration with Bearer Tokens

Once authorized, use Bearer tokens for API calls. For detailed implementation:

📚 Bearer Token Usage: Using Bearer Tokens

Making Authenticated Requests

// Example: Create payment link for a merchant
async function createPaymentLinkForMerchant(merchantId, paymentData) {
  const tokens = await getMerchantTokens(merchantId);
  
  // Check if token needs refresh
  if (Date.now() >= tokens.expiresAt - 300000) { // 5 min buffer
    tokens = await refreshMerchantTokens(merchantId);
  }
  
  const response = await fetch('https://app.shuttleglobal.com/c/api/payment_links', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${tokens.accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(paymentData)
  });
  
  if (response.status === 401) {
    // Token invalid, may need re-authorization
    await handleTokenError(merchantId);
    throw new Error('Merchant needs to re-authorize');
  }
  
  return response.json();
}

Step 5: Token Lifecycle Management

Automatic Token Refresh

Implement background token refresh:

// Refresh tokens before expiry
async function refreshMerchantTokens(merchantId) {
  const currentTokens = await getMerchantTokens(merchantId);
  
  const response = await fetch('https://merchant.shuttleglobal.com/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: currentTokens.refreshToken,
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET
    })
  });
  
  const newTokens = await response.json();
  
  // Update stored tokens
  await updateMerchantTokens(merchantId, {
    accessToken: newTokens.access_token,
    expiresAt: Date.now() + (newTokens.expires_in * 1000)
    // refresh_token stays the same
  });
  
  return newTokens;
}

Handle Revoked Access

When merchants disconnect or tokens are revoked:

// Revoke tokens and clean up
async function disconnectMerchant(merchantId) {
  const tokens = await getMerchantTokens(merchantId);
  
  // Revoke refresh token (invalidates all related tokens)
  await fetch('https://merchant.shuttleglobal.com/revoke', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      token: tokens.refreshToken,
      token_type_hint: 'refresh_token',
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET
    })
  });
  
  // Remove from your database
  await deleteMerchantTokens(merchantId);
}

Next Steps

  1. Set up OAuth credentials with Shuttle support
  2. Implement authorization flow using the OAuth guide
  3. Build token management system for your merchants
  4. Integrate API endpoints using Bearer tokens
  5. Test with sandbox merchants before production
  6. Certify your integration with Shuttle

For technical implementation details, refer to: