6 min read

Handle User Association

This guide explains how to link subscriptions to users in your application and manage subscription ownership.

Link During Checkout

The simplest way to associate a user is during checkout:

await iaptic.createStripeCheckout({
  offerId: 'stripe:prod_xyz#price_xyz',
  applicationUsername: 'user123', // Your user identifier
  successUrl: 'https://example.com/success',
  cancelUrl: 'https://example.com/cancel'
});

The applicationUsername will be stored in the subscription's metadata.

Update Existing Subscription

You can change the user association of an existing subscription:

// Using Stripe Dashboard
// Edit subscription metadata:
{
  "application_username": "newuser123"
}

// Or using Stripe API
await stripe.subscriptions.update('sub_xyz', {
  metadata: {
    application_username: 'newuser123'
  }
});

Choose Entitlement Strategy

Configure how subscription ownership works in your application settings:

ALL_CLAIMERS (Default)

// All users with valid credentials get access
const { purchases } = await iaptic.getPurchases();
if (purchases.length > 0) {
  // User has access
  enableFeatures(purchases[0]);
}

LAST_CLAIMER

// Only the most recent user gets access
const { purchases } = await iaptic.getPurchases();
if (purchases.length > 0 && purchases[0].applicationUsername === currentUser) {
  // Current user has access
  enableFeatures(purchases[0]);
}

Handle Subscription Transfers

  1. Simple Transfer
// Update subscription owner
await stripe.subscriptions.update('sub_xyz', {
  metadata: {
    application_username: 'newuser123'
  }
});
  1. Transfer with Verification
async function transferSubscription(subscriptionId, newUsername) {
  // Verify current access
  const { purchases } = await iaptic.getPurchases(subscriptionId);
  if (!purchases.length) {
    throw new Error('Subscription not found');
  }

  // Update ownership
  await stripe.subscriptions.update(subscriptionId, {
    metadata: {
      application_username: newUsername,
      previous_username: purchases[0].applicationUsername,
      transferred_at: new Date().toISOString()
    }
  });

  // Notify users
  notifyTransfer(purchases[0].applicationUsername, newUsername);
}

Track Ownership Changes

Monitor subscription transfers through webhooks:

// In your webhook handler
if (event.type === 'customer.subscription.updated') {
  const subscription = event.data.object;
  const previousAttributes = event.data.previous_attributes;
  
  if (previousAttributes.metadata?.application_username) {
    const oldUsername = previousAttributes.metadata.application_username;
    const newUsername = subscription.metadata.application_username;
    
    // Handle ownership change
    onOwnershipChanged(subscription.id, oldUsername, newUsername);
  }
}

Best Practices

  1. User Identifiers

    • Use consistent username format
    • Consider using email addresses
    • Store user ID mapping if needed
  2. Access Control

    • Validate ownership changes
    • Keep transfer history
    • Handle edge cases (deleted users)
  3. UI/UX

    • Show current owner
    • Confirm transfers
    • Notify affected users
  4. Security

    • Verify transfer authority
    • Log ownership changes
    • Handle disputes

Common Issues

Access Lost After Transfer

  1. Check entitlement strategy
  2. Verify metadata update
  3. Clear local storage
  4. Refresh access tokens

Multiple Users Issue

  1. Verify entitlement strategy
  2. Check webhook processing
  3. Clear browser cache
  4. Update client storage

Transfer Failed

  1. Check API permissions
  2. Verify subscription status
  3. Validate metadata format
  4. Check webhook delivery

Example Implementation

Complete transfer flow with UI:

class SubscriptionManager {
  async transferOwnership(subscriptionId, newUsername) {
    try {
      // 1. Verify current access
      const { purchases } = await iaptic.getPurchases(subscriptionId);
      if (!purchases.length) {
        throw new Error('Subscription not found');
      }

      // 2. Update ownership
      await stripe.subscriptions.update(subscriptionId, {
        metadata: {
          application_username: newUsername,
          previous_username: purchases[0].applicationUsername,
          transferred_at: new Date().toISOString()
        }
      });

      // 3. Update local storage
      localStorage.removeItem('stripe_access_key');
      
      // 4. Notify users
      await this.notifyUsers(purchases[0], newUsername);
      
      return true;
    } catch (error) {
      console.error('Transfer failed:', error);
      throw error;
    }
  }

  async notifyUsers(purchase, newUsername) {
    // Implement your notification logic
  }
}