Skip to content

Multi-Provider Grocery Support #61

@akisma

Description

@akisma

Description

Architect the grocery integration layer to support multiple delivery providers beyond Instacart. This includes Amazon Fresh, Walmart Grocery, and regional services. A unified provider abstraction enables users to choose their preferred service while the app handles the integration details transparently.

Acceptance Criteria (Gherkin)

Scenario: Connect multiple providers
  Given I have Instacart connected
  When I also connect Amazon Fresh
  Then both appear in my connected services
  And I can choose which to use for each order

Scenario: Provider-agnostic cart creation
  Given I select "Amazon Fresh" for this order
  When I send my shopping list
  Then items are matched against Amazon's catalog
  And an Amazon Fresh cart is created

Scenario: Default provider setting
  Given I have multiple providers connected
  When I set "Instacart" as default
  Then future orders use Instacart automatically
  Unless I specify otherwise

Scenario: Provider availability check
  Given I am in an area without Instacart coverage
  Then Instacart shows as "Not available in your area"
  And I am prompted to try other providers

Scenario: Price comparison (stretch)
  Given I have multiple providers connected
  When I view cart preview
  Then I see estimated total from each provider
  And can choose the best option

Technical Notes

Architecture:

// Provider interface that all integrations implement
interface GroceryProvider {
  name: string;
  id: string;
  
  // Auth
  getAuthUrl(): string;
  handleCallback(code: string): Promise<TokenSet>;
  refreshToken(refreshToken: string): Promise<TokenSet>;
  
  // Products
  searchProducts(query: string, storeId?: string): Promise<Product[]>;
  getStores(location: Location): Promise<Store[]>;
  
  // Cart
  createCart(storeId: string): Promise<Cart>;
  addToCart(cartId: string, items: CartItem[]): Promise<Cart>;
  getCheckoutUrl(cartId: string): Promise<string>;
  
  // Orders
  getOrderStatus(orderId: string): Promise<OrderStatus>;
}

// Implementations
class InstacartProvider implements GroceryProvider { ... }
class AmazonFreshProvider implements GroceryProvider { ... }
class WalmartGroceryProvider implements GroceryProvider { ... }

File Structure:

backend/src/services/grocery/
  grocery-provider.interface.ts
  grocery.service.ts          // Main service, provider-agnostic
  providers/
    instacart.provider.ts
    amazon-fresh.provider.ts
    walmart.provider.ts

Provider Registry:

class GroceryService {
  private providers: Map<string, GroceryProvider>;
  
  constructor() {
    this.providers = new Map([
      ['instacart', new InstacartProvider()],
      ['amazon_fresh', new AmazonFreshProvider()],
      ['walmart', new WalmartGroceryProvider()],
    ]);
  }
  
  getProvider(userId: string, preferredProvider?: string): GroceryProvider {
    const providerId = preferredProvider || await this.getUserDefaultProvider(userId);
    return this.providers.get(providerId);
  }
}

Database:

-- Extend grocery_connections for multi-provider
CREATE TABLE grocery_connections (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  provider TEXT NOT NULL,  -- 'instacart', 'amazon_fresh', 'walmart'
  is_default BOOLEAN DEFAULT FALSE,
  -- tokens etc.
  UNIQUE(user_id, provider)
);

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions