8 min read

Stripe Access Control

This document explains how iaptic manages access control for Stripe subscriptions, including user association and entitlement strategies.

Access Control Model

graph TB
    subgraph Access Verification
        A[Client] -- Access Key --> B[API]
        B -- Verify --> C[Subscription]
        C -- Check --> D[Entitlement]
    end
    
    subgraph User Association
        E[Subscription] -- Metadata --> F[Username]
        F -- Strategy --> G[Access Rights]
    end

Entitlement Strategies

ALL_CLAIMERS (Default)

// Multiple users can access the same subscription
const subscription = {
  metadata: {
    application_username: 'user123,user456,user789'
  }
};

// Any user with valid credentials gets access
const hasAccess = true; // If access key is valid

Benefits:

  • Perfect for family plans
  • Supports team subscriptions
  • No transfer process needed
  • Simple to implement

LAST_CLAIMER

// Only the most recent user has access
const subscription = {
  metadata: {
    application_username: 'user789', // Latest user
    previous_username: 'user456'     // Previous user lost access
  }
};

// Only current user gets access
const hasAccess = currentUser === subscription.metadata.application_username;

Benefits:

  • Clear ownership model
  • Easy subscription transfers
  • Automatic access revocation
  • Good for individual licenses

User Association

During Checkout

// Create checkout session with user
const session = await iaptic.createStripeCheckout({
  offerId: 'stripe:prod_xyz#price_xyz',
  applicationUsername: 'user123',
  successUrl: '...',
  cancelUrl: '...'
});

Update Association

// Transfer to new user
await stripe.subscriptions.update('sub_xyz', {
  metadata: {
    application_username: 'newuser123',
    previous_username: 'user123',
    transferred_at: new Date().toISOString()
  }
});

Multiple Users

// With ALL_CLAIMERS strategy
const subscription = {
  metadata: {
    application_username: 'user1,user2,user3'
  }
};

// Add user
const users = subscription.metadata.application_username.split(',');
users.push('user4');
await stripe.subscriptions.update('sub_xyz', {
  metadata: {
    application_username: users.join(',')
  }
});

Access Verification

Client-side

// Check subscription status
const { purchases } = await iaptic.getPurchases(
  subscriptionId,
  accessKey
);

// Verify access
if (purchases.length > 0) {
  const purchase = purchases[0];
  
  // Check status
  const isActive = new Date(purchase.expirationDate) > new Date();
  
  // Check user (if using LAST_CLAIMER)
  const hasAccess = purchase.applicationUsername === currentUser;
  
  // Enable features
  if (isActive && hasAccess) {
    enablePremiumFeatures();
  }
}

Server-side

// Verify subscription server-side
app.post('/api/verify', async (req, res) => {
  try {
    const response = await fetch(
      'https://validator.iaptic.com/v3/stripe/purchases/' +
      req.body.subscriptionId,
      {
        headers: {
          'Authorization': `Basic ${btoa('app:key')}`,
        },
        body: JSON.stringify({
          access_key: req.body.accessKey
        })
      }
    );
    
    const { purchases } = await response.json();
    // Process verification result
  } catch (error) {
    // Handle error
  }
});

Access Patterns

1. Individual License

// Use LAST_CLAIMER strategy
const subscription = {
  metadata: {
    application_username: 'user123'
  }
};

// Only current user has access
const hasAccess = currentUser === subscription.metadata.application_username;

2. Team License

// Use ALL_CLAIMERS strategy
const subscription = {
  metadata: {
    application_username: 'team/user1,team/user2',
    team_id: 'team123'
  }
};

// Any team member has access
const hasAccess = subscription.metadata.application_username
  .includes(`team/${currentUser}`);

3. Family Sharing

// Use ALL_CLAIMERS with family prefix
const subscription = {
  metadata: {
    application_username: 'family/parent,family/child1,family/child2',
    family_id: 'family123'
  }
};

// Any family member has access
const hasAccess = subscription.metadata.application_username
  .includes(`family/${currentUser}`);

Best Practices

1. User Management

  • Use consistent username format
  • Validate usernames
  • Track ownership changes
  • Handle user deletion

2. Access Control

  • Choose appropriate strategy
  • Implement proper validation
  • Handle edge cases
  • Log access attempts

3. Error Handling

  • Invalid access keys
  • Expired subscriptions
  • User not found
  • Transfer failures

4. Monitoring

  • Track access patterns
  • Monitor transfers
  • Log ownership changes
  • Alert on anomalies

Common Issues

1. Access Lost

  • Check subscription status
  • Verify user association
  • Validate access key
  • Check entitlement strategy

2. Multiple Users

  • Verify strategy setting
  • Check username format
  • Validate access rights
  • Monitor usage patterns

3. Transfer Problems

  • Validate new username
  • Check current ownership
  • Handle concurrent transfers
  • Update access keys