Skip to content

Latest commit

 

History

History
771 lines (636 loc) · 23.7 KB

File metadata and controls

771 lines (636 loc) · 23.7 KB

OpenFront Sales Channel Integration Plan

Executive Summary

This comprehensive plan outlines a two-tier sales channel architecture that maximizes OpenFront's potential:

Tier 1: Federated OpenFront Marketplace - Connects multiple OpenFront instances using existing storefront APIs to create a decentralized marketplace while maintaining seller independence.

Tier 2: External Platform Adapters - Provides optional integration with traditional marketplaces (Amazon, eBay, etc.) through adapter functions that transform OpenFront data to external platform requirements.

This approach leverages your existing storefront infrastructure while providing comprehensive sales channel coverage.

Vision Alignment

OpenFront's ethos centers on commerce independence and democratizing e-commerce. This two-tier approach perfectly balances:

  • Primary Focus: Federated marketplace maintains seller independence
  • Optional Extension: External platform access for broader market reach
  • Core Principle: Sellers choose their level of independence vs. reach

Tier 1: Federated OpenFront Marketplace (Primary Innovation)

Core Architecture: Multi-OpenFront Federation

Core Concept

Instead of building traditional sales channels, create a federated marketplace that connects multiple independent OpenFront instances through their existing GraphQL APIs. This approach treats each OpenFront instance as an independent commerce node that can participate in collective marketplaces.

Technical Architecture

1. OpenFront Marketplace Platform

// Marketplace Registry
interface OpenFrontInstance {
  id: string;
  name: string;
  domain: string;
  graphqlEndpoint: string;
  publicKey: string; // For API authentication
  categories: string[];
  region: string;
  currency: string;
  status: 'active' | 'inactive';
}

// Marketplace Aggregator
interface MarketplaceConfig {
  id: string;
  name: string;
  connectedInstances: OpenFrontInstance[];
  searchFilters: SearchFilter[];
  branding: MarketplaceBranding;
}

2. Federated Product Discovery

// Multi-instance product search
async function searchFederatedProducts({
  query,
  instances,
  filters,
  pagination
}: FederatedSearchParams) {
  const searchPromises = instances.map(instance => 
    searchInstanceProducts(instance, query, filters)
  );
  
  const results = await Promise.allSettled(searchPromises);
  
  return aggregateAndRankResults(results, pagination);
}

// Individual instance search
async function searchInstanceProducts(
  instance: OpenFrontInstance,
  query: string,
  filters: SearchFilters
) {
  const client = new GraphQLClient(instance.graphqlEndpoint);
  
  return client.request(GET_PRODUCTS_QUERY, {
    where: buildWhereClause(query, filters),
    countryCode: instance.region
  });
}

3. Cross-Instance Cart Management

// Distributed cart system
interface FederatedCart {
  id: string;
  items: FederatedCartItem[];
  totalByInstance: Map<string, CartTotal>;
}

interface FederatedCartItem {
  instanceId: string;
  productId: string;
  variantId: string;
  quantity: number;
  priceSnapshot: ProductPrice;
}

// When user adds to cart
async function addToFederatedCart(
  cartId: string,
  instanceId: string,
  productId: string,
  variantId: string,
  quantity: number
) {
  // 1. Validate product availability with source instance
  const instance = await getOpenFrontInstance(instanceId);
  const product = await validateProductAvailability(instance, productId, variantId, quantity);
  
  // 2. Add to federated cart
  const federatedCart = await getFederatedCart(cartId);
  federatedCart.items.push({
    instanceId,
    productId,
    variantId,
    quantity,
    priceSnapshot: product.price
  });
  
  // 3. Update cart totals per instance
  updateCartTotalsByInstance(federatedCart);
  
  return saveFederatedCart(federatedCart);
}

4. Multi-Instance Checkout

// Checkout coordination
async function processFederatedCheckout(
  federatedCart: FederatedCart,
  shippingAddress: Address,
  paymentMethod: PaymentMethod
) {
  const checkoutPromises = [];
  
  // Group items by instance
  const itemsByInstance = groupItemsByInstance(federatedCart.items);
  
  // Create separate checkout for each instance
  for (const [instanceId, items] of itemsByInstance) {
    const instance = await getOpenFrontInstance(instanceId);
    
    checkoutPromises.push(
      createInstanceCheckout(instance, items, shippingAddress, paymentMethod)
    );
  }
  
  // Execute all checkouts
  const checkoutResults = await Promise.allSettled(checkoutPromises);
  
  return coordinateOrderCreation(checkoutResults);
}

async function createInstanceCheckout(
  instance: OpenFrontInstance,
  items: FederatedCartItem[],
  shippingAddress: Address,
  paymentMethod: PaymentMethod
) {
  const client = new GraphQLClient(instance.graphqlEndpoint, {
    headers: { Authorization: `Bearer ${getInstanceApiKey(instance.id)}` }
  });
  
  // Create cart in the specific OpenFront instance
  const cart = await client.request(CREATE_CART_MUTATION, {
    items: items.map(item => ({
      productId: item.productId,
      variantId: item.variantId,
      quantity: item.quantity
    }))
  });
  
  // Complete checkout in that instance
  return client.request(COMPLETE_CART_MUTATION, {
    cartId: cart.id,
    shippingAddress,
    paymentMethod
  });
}

Implementation Strategy

Phase 1: Core Federation Infrastructure

  1. OpenFront Registry Service

    • Instance discovery and registration
    • Health monitoring and status tracking
    • API key management and authentication
  2. Federated Search Engine

    • Multi-instance product indexing
    • Real-time search aggregation
    • Result ranking and relevance scoring
  3. Cross-Instance Communication Layer

    • GraphQL federation utilities
    • Error handling and fallback mechanisms
    • Rate limiting and caching

Phase 2: Marketplace Frontend

  1. Unified Storefront

    • Product browsing across instances
    • Advanced filtering and categorization
    • Instance-aware product display
  2. Distributed Cart System

    • Multi-instance cart management
    • Real-time inventory validation
    • Shipping calculation coordination
  3. Federated Checkout

    • Multiple order coordination
    • Payment splitting (if needed)
    • Order tracking aggregation

Phase 3: Advanced Features

  1. Seller Dashboard

    • Multi-marketplace visibility
    • Performance analytics
    • Revenue tracking
  2. Marketplace Analytics

    • Cross-instance insights
    • Seller performance metrics
    • Market trend analysis

Benefits of This Approach

1. Maintains Independence

  • Each seller owns their OpenFront instance
  • No vendor lock-in to marketplace platform
  • Full customization and control retained

2. Leverages Existing Architecture

  • Uses proven GraphQL API patterns
  • No need to rebuild cart/checkout logic
  • Existing storefront components are reusable

3. Scalable and Resilient

  • Distributed architecture prevents single points of failure
  • Each instance can scale independently
  • Natural load distribution

4. Aligned with OpenFront Philosophy

  • Promotes true commerce independence
  • Creates network effects without centralization
  • Supports the open-source ecosystem

Tier 2: External Platform Integration (Optional Extension)

Core Concept

Provide optional sales channel functionality for external marketplaces (Amazon, eBay, etc.) using the same adapter pattern as payment/shipping providers. This leverages your existing storefront data layer through transformation functions.

Technical Architecture

1. External Channel Provider System

// Following existing payment/shipping adapter pattern
interface ExternalChannelProvider {
  id: string;
  name: string;
  code: string; // sc_amazon, sc_ebay, etc.
  isInstalled: boolean;
  credentials: Record<string, any>;
  
  // Function mappings (same pattern as PaymentProvider)
  listProductsFunction: string;
  createListingFunction: string;
  updateListingFunction: string;
  deleteListingFunction: string;
  syncOrdersFunction: string;
  updateInventoryFunction: string;
  
  regions: Region[];
}

// External Channel Adapters (same pattern as payment adapters)
export const externalChannelAdapters = {
  amazon: () => import("./external/amazon"),
  ebay: () => import("./external/ebay"),
  walmart: () => import("./external/walmart"),
  etsy: () => import("./external/etsy"),
};

2. Storefront-to-External Transformation Functions

// Amazon adapter - transforms your storefront data
export async function createListingFunction({ 
  product, 
  channel, 
  credentials 
}: ExternalChannelParams) {
  // Use existing storefront data, transform for Amazon
  const amazonProduct = transformOpenFrontToAmazon(product);
  
  const result = await amazonAPI.createListing({
    ...amazonProduct,
    sellerId: credentials.sellerId,
    marketplaceId: credentials.marketplaceId
  });
  
  return {
    externalId: result.sku,
    externalUrl: result.productUrl,
    status: 'active',
    metadata: result
  };
}

function transformOpenFrontToAmazon(product: Product) {
  return {
    // Direct mapping from your existing product structure
    title: product.title,
    description: extractTextFromDocument(product.description.document),
    images: product.productImages.map(img => img.image.url),
    
    // Variant data
    variants: product.productVariants.map(variant => ({
      sku: variant.sku,
      price: variant.prices[0].amount / 100, // Convert from cents
      quantity: variant.inventoryQuantity,
      attributes: variant.productOptionValues.map(opt => ({
        name: opt.productOption.title,
        value: opt.value
      }))
    })),
    
    // Amazon-specific requirements
    category: mapCollectionToAmazonCategory(product.productCollections[0]),
    brand: product.metadata?.brand || "Generic",
    condition: "New",
    fulfillmentType: "MFN"
  };
}

3. External-to-Storefront Order Import

// Import Amazon orders and create them using existing order system
export async function syncOrdersFunction({ channel, credentials }) {
  const amazonOrders = await amazonAPI.getOrders({
    sellerId: credentials.sellerId,
    since: channel.lastSync
  });
  
  const openFrontOrders = [];
  
  for (const amazonOrder of amazonOrders) {
    // Transform Amazon order to OpenFront format
    const orderData = transformAmazonToOpenFront(amazonOrder, channel);
    
    // Use existing order creation logic
    const order = await createOrder(orderData);
    openFrontOrders.push(order);
  }
  
  return openFrontOrders;
}

function transformAmazonToOpenFront(amazonOrder: AmazonOrder, channel: ExternalChannelProvider) {
  return {
    // Map to your existing order structure
    externalOrderId: amazonOrder.AmazonOrderId,
    salesChannel: channel.id,
    
    // Customer info (limited by Amazon)
    customerEmail: amazonOrder.BuyerInfo?.BuyerEmail || `amazon-${amazonOrder.AmazonOrderId}@marketplace.amazon.com`,
    
    // Shipping address
    shippingAddress: {
      firstName: amazonOrder.ShippingAddress?.Name?.split(' ')[0] || 'Amazon',
      lastName: amazonOrder.ShippingAddress?.Name?.split(' ').slice(1).join(' ') || 'Customer',
      address1: amazonOrder.ShippingAddress?.AddressLine1,
      address2: amazonOrder.ShippingAddress?.AddressLine2,
      city: amazonOrder.ShippingAddress?.City,
      province: amazonOrder.ShippingAddress?.StateOrRegion,
      postalCode: amazonOrder.ShippingAddress?.PostalCode,
      countryCode: amazonOrder.ShippingAddress?.CountryCode
    },
    
    // Line items - map back to your products
    items: amazonOrder.OrderItems.map(item => {
      const product = findProductByExternalSKU(item.SellerSKU, channel.id);
      return {
        productId: product.id,
        variantId: product.productVariants[0].id,
        quantity: item.QuantityOrdered,
        unitPrice: Math.round(parseFloat(item.ItemPrice.Amount) * 100), // Convert to cents
      };
    }),
    
    // Order totals
    subtotal: Math.round(parseFloat(amazonOrder.OrderTotal.Amount) * 100),
    total: Math.round(parseFloat(amazonOrder.OrderTotal.Amount) * 100),
    currency: amazonOrder.OrderTotal.CurrencyCode
  };
}

4. Inventory Synchronization

// When inventory changes in OpenFront, sync to external platforms
export async function updateInventoryFunction({ 
  productVariant, 
  channel, 
  credentials 
}: InventorySyncParams) {
  
  // Find external listings for this variant
  const externalListings = await getExternalListingsForVariant(
    productVariant.id, 
    channel.id
  );
  
  // Update each external listing
  const updatePromises = externalListings.map(listing => 
    amazonAPI.updateInventory({
      sku: listing.externalSKU,
      quantity: productVariant.inventoryQuantity,
      sellerId: credentials.sellerId
    })
  );
  
  return Promise.allSettled(updatePromises);
}

// Webhook handler for external platform updates
export async function handleWebhookFunction({ event, headers, channel }) {
  // Verify Amazon webhook signature
  const isValid = verifyAmazonSignature(event, headers, channel.credentials.webhookSecret);
  if (!isValid) throw new Error('Invalid webhook signature');
  
  switch (event.eventType) {
    case 'ORDER_STATUS_CHANGE':
      await handleOrderStatusUpdate(event.payload, channel);
      break;
    case 'INVENTORY_UPDATE':
      await handleInventoryUpdate(event.payload, channel);
      break;
    case 'LISTING_UPDATE':
      await handleListingUpdate(event.payload, channel);
      break;
  }
  
  return { processed: true };
}

Integration with Existing Architecture

1. Leverage Current Storefront Data Layer

// Use existing functions from features/storefront/lib/data/
import { getProductsList, retrievePricedProductByHandle } from '../storefront/lib/data/products';
import { createOrder } from '../storefront/lib/data/orders';

// External channel sync uses existing data functions
async function syncProductsToAmazon(channelId: string) {
  // Use your existing product data functions
  const { response: { products } } = await getProductsList({
    pageParam: 0,
    queryParams: { limit: 100 },
    countryCode: 'US'
  });
  
  // Transform and sync each product
  for (const product of products) {
    await syncSingleProductToAmazon(product, channelId);
  }
}

2. Extend Current Integration Patterns

// Add to existing features/integrations/ structure
features/integrations/
├── payment/           # Existing
├── shipping/          # Existing  
└── channels/          # New - external sales channels
    ├── index.ts
    ├── amazon.ts
    ├── ebay.ts
    ├── walmart.ts
    └── etsy.ts

3. Database Schema Extensions

// Add to existing schema.prisma
model ExternalChannelProvider {
  id              String   @id @default(cuid())
  name            String
  code            String   @unique // sc_amazon, sc_ebay
  isInstalled     Boolean  @default(false)
  credentials     Json     // API keys, seller IDs, etc.
  
  // Function mappings (same pattern as PaymentProvider)
  listProductsFunction      String
  createListingFunction     String
  updateListingFunction     String
  deleteListingFunction     String
  syncOrdersFunction        String
  updateInventoryFunction   String
  handleWebhookFunction     String
  
  // Relations
  regions         Region[]
  listings        ExternalListing[]
  orders          Order[]
  
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt
}

model ExternalListing {
  id                    String  @id @default(cuid())
  productId             String
  channelId             String
  externalProductId     String  // Amazon ASIN, eBay ItemID, etc.
  externalSKU           String
  status                String  // active, inactive, error
  lastSyncAt            DateTime
  syncErrors            Json?
  channelSpecificData   Json?   // Platform-specific metadata
  
  product               Product @relation(fields: [productId], references: [id])
  channel               ExternalChannelProvider @relation(fields: [channelId], references: [id])
  
  createdAt             DateTime @default(now())
  updatedAt             DateTime @updatedAt
  
  @@unique([productId, channelId])
}

Implementation Strategy: Two-Tier Approach

Development Priority

Phase 1: Federated OpenFront Marketplace (Months 1-6)

Primary Focus - This is the innovative differentiator

Quarter 1: Foundation Infrastructure

Month 1: Core Federation

  • OpenFront Instance Registry service
  • GraphQL federation utilities
  • Instance discovery and health monitoring
  • Basic authentication between instances

Month 2: Search & Discovery

  • Multi-instance product search aggregation
  • Search result ranking and relevance
  • Real-time inventory validation
  • Caching and performance optimization

Month 3: Cart Management

  • Federated cart system design
  • Cross-instance cart item management
  • Cart persistence and synchronization
  • Shopping cart UI for multiple sellers

Quarter 2: Marketplace Platform

Month 4: Checkout Coordination

  • Multi-instance checkout orchestration
  • Payment splitting (if needed)
  • Order creation coordination
  • Error handling and rollback mechanisms

Month 5: Frontend & UX

  • Unified marketplace storefront
  • Seller identification in product listings
  • Advanced filtering and categorization
  • Mobile-responsive marketplace design

Month 6: Seller Tools

  • Marketplace seller dashboard
  • Cross-marketplace analytics
  • Performance metrics and insights
  • Revenue tracking and reporting

Phase 2: External Platform Integration (Months 7-12)

Optional Extension - Provides broader market access

Quarter 3: Core External Integration

Month 7: Infrastructure

  • External channel provider system
  • Adapter pattern implementation
  • Database schema for external listings
  • Webhook handling infrastructure

Month 8: Amazon Integration

  • Amazon Marketplace API integration
  • Product listing transformation functions
  • Order import and synchronization
  • Inventory sync with Amazon

Month 9: Multi-Platform Support

  • eBay integration
  • Walmart integration
  • Etsy integration
  • Facebook/Instagram Shopping

Quarter 4: Advanced Features & Polish

Month 10: Advanced Sync

  • Bulk product management
  • Advanced inventory synchronization
  • Multi-platform analytics
  • Error recovery and retry mechanisms

Month 11: Seller Experience

  • Unified external channel dashboard
  • Automated listing optimization
  • Performance analytics across platforms
  • Bulk operations and management tools

Month 12: Launch Preparation

  • Documentation and guides
  • Developer tools and SDK
  • Beta testing program
  • Public marketplace launch

Technical Implementation Details

File Structure

features/
├── marketplace/              # New - Federated marketplace
│   ├── lib/
│   │   ├── federation/      # Multi-instance coordination
│   │   ├── search/          # Federated search engine
│   │   ├── cart/            # Cross-instance cart management
│   │   └── checkout/        # Multi-instance checkout
│   ├── screens/             # Marketplace UI components
│   └── api/                 # Marketplace API endpoints
│
├── integrations/            # Existing - Extended for channels
│   ├── payment/            # Existing
│   ├── shipping/           # Existing
│   └── channels/           # New - External sales channels
│       ├── amazon.ts
│       ├── ebay.ts
│       ├── walmart.ts
│       └── etsy.ts
│
└── storefront/             # Existing - Leveraged by marketplace
    └── lib/data/           # Your existing data functions

Key Integration Points

1. Marketplace Uses Existing Storefront Functions

// Federated search leverages existing product queries
import { getProductsList } from '../storefront/lib/data/products';

async function searchFederatedProducts(query: string, instances: OpenFrontInstance[]) {
  const searchPromises = instances.map(instance => {
    // Use existing function with instance-specific client
    const instanceClient = createInstanceClient(instance);
    return getProductsList({
      queryParams: { search: query },
      countryCode: instance.region
    });
  });
  
  return aggregateResults(await Promise.allSettled(searchPromises));
}

2. External Channels Transform Existing Data

// External channels use existing data, transform for platforms
import { retrievePricedProductByHandle } from '../storefront/lib/data/products';

async function syncProductToAmazon(productHandle: string, channel: ExternalChannelProvider) {
  // Get product using existing storefront function
  const { product } = await retrievePricedProductByHandle({
    handle: productHandle,
    regionId: channel.regionId
  });
  
  // Transform for Amazon and sync
  const amazonListing = transformOpenFrontToAmazon(product);
  return await createAmazonListing(amazonListing, channel.credentials);
}

Success Metrics & KPIs

Phase 1 Metrics (Federated Marketplace)

  • Performance: < 500ms federated search response time
  • Reliability: 99.9% marketplace uptime
  • Scale: Support 50+ connected OpenFront instances
  • Adoption: 100+ sellers using federated marketplace
  • GMV: $500K+ transaction volume through marketplace

Phase 2 Metrics (External Platforms)

  • Integration Coverage: 4+ major external platforms
  • Sync Performance: < 30s for product/inventory updates
  • Error Rate: < 1% sync failures
  • Adoption: 25+ sellers using external channels
  • External GMV: $250K+ from external platform sales

Risk Mitigation

Technical Risks

  • Federation Complexity: Start with simple MVP, iterate
  • Performance Issues: Implement caching and optimization early
  • Instance Reliability: Build robust error handling and fallbacks

Business Risks

  • Marketplace Adoption: Focus on seller value proposition
  • External Platform Changes: Maintain flexible adapter architecture
  • Competition: Emphasize independence and customization benefits

Launch Strategy

Federated Marketplace Beta (Month 6)

  • Invite 10-20 existing OpenFront users
  • Gather feedback on marketplace experience
  • Iterate on core functionality
  • Document best practices

External Channels Private Beta (Month 11)

  • Select sellers with Amazon/eBay experience
  • Test sync reliability and performance
  • Refine transformation logic
  • Build comprehensive documentation

Public Launch (Month 12)

  • Full marketplace and external channels
  • Marketing and ecosystem outreach
  • Developer tools and documentation
  • Community building and support

Why This Approach Works

Leverages Existing Architecture

  • Uses proven storefront data functions
  • Builds on established payment/shipping adapter patterns
  • Minimal new infrastructure required

Maintains Philosophy

  • Federated marketplace preserves seller independence
  • External channels remain optional extensions
  • No vendor lock-in or forced dependencies

Practical Implementation

  • Clear separation between innovative (federated) and traditional (external) approaches
  • Phased rollout reduces risk
  • Each phase delivers standalone value

Market Differentiation

  • Federated marketplace creates new category
  • True commerce democratization
  • Network effects without centralization

The two-tier approach gives sellers the best of both worlds: innovative federated commerce for independence, plus optional external platform access for reach.