# ShopRenter Payment API Implementation Plan

**Project:** ShopCall.ai - ShopRenter Payment Integration
**Version:** 1.0
**Date:** 2025-11-11
**Status:** Planning Phase

---

## 📋 Table of Contents

1. [Executive Summary](#executive-summary)
2. [Background & Context](#background--context)
3. [Critical Requirements](#critical-requirements)
4. [Architecture Overview](#architecture-overview)
5. [Database Schema](#database-schema)
6. [Backend Implementation](#backend-implementation)
7. [Payment Flow](#payment-flow)
8. [Store Validation](#store-validation)
9. [Security Considerations](#security-considerations)
10. [Testing Strategy](#testing-strategy)
11. [Deployment Plan](#deployment-plan)
12. [Timeline & Milestones](#timeline--milestones)

---

## 🎯 Executive Summary

This document outlines the implementation plan for integrating the ShopRenter Payment API into ShopCall.ai. The integration will enable **ShopRenter stores only** to subscribe to ShopCall.ai services and be billed directly through ShopRenter's payment infrastructure.

### Key Objectives

- ✅ Enable subscription-based billing for ShopRenter stores
- ✅ Process payments exclusively through ShopRenter Payment API
- ✅ Enforce strict store validation (ShopRenter stores only)
- ✅ Implement automatic invoicing and VAT handling
- ✅ Support both one-time and recurring payment plans
- ✅ Maintain GDPR compliance and data security

### Core Principle

**⚠️ CRITICAL RULE:** The ShopRenter Payment API **MUST ONLY** be used with ShopRenter stores. Any non-ShopRenter store attempting to use this payment method must be rejected with appropriate error messages.

---

## 📖 Background & Context

### What is ShopRenter Payment API?

The ShopRenter Payment API is a comprehensive payment processing toolkit provided by ShopRenter for application developers. It enables:

- **Card payment processing** (one-time and recurring)
- **Automatic invoicing** with VAT calculation
- **Subscription management** with payment plans
- **Financial statistics** and income tracking
- **Webhook notifications** for payment events

### Current ShopCall.ai Integration Status

ShopCall.ai already has a **fully implemented ShopRenter store integration** including:

- ✅ OAuth 2.0 authentication flow
- ✅ HMAC validation for security
- ✅ Product, order, and customer data synchronization
- ✅ Webhook handlers for data updates
- ✅ Scheduled background sync (pg_cron)
- ✅ Database tables for storing ShopRenter credentials and data

**Reference:** See `docs/shoprenter_app_development.md` and `SHOPRENTER.md` for complete details.

### Why Payment API Integration?

Currently, ShopCall.ai uses a generic payment processor (e.g., Stripe) for all stores. Integrating ShopRenter Payment API provides:

1. **Native billing** for ShopRenter merchants (billed through their ShopRenter account)
2. **Simplified checkout** (no need to enter payment details separately)
3. **Unified invoicing** (all invoices in one place - ShopRenter admin)
4. **Better conversion** (trust and familiarity with ShopRenter billing)
5. **Automatic VAT handling** (compliant with Hungarian tax laws)

---

## ⚠️ Critical Requirements

### 1. Store Platform Validation (MANDATORY)

**Rule:** The ShopRenter Payment API can **ONLY** be used with stores that have `platform_name = 'shoprenter'` in the `stores` table.

**Enforcement Points:**
- Payment page rendering (frontend)
- Payment initiation API endpoint (backend)
- Subscription creation endpoint (backend)
- Webhook handlers (backend)

**Validation Logic:**
```typescript
async function validateStoreForShopRenterPayment(storeId: string): Promise<boolean> {
  const { data: store } = await supabase
    .from('stores')
    .select('platform_name, is_active')
    .eq('id', storeId)
    .single();

  if (!store) {
    throw new Error('Store not found');
  }

  if (!store.is_active) {
    throw new Error('Store is inactive');
  }

  if (store.platform_name !== 'shoprenter') {
    throw new Error('ShopRenter Payment API can only be used with ShopRenter stores');
  }

  return true;
}
```

### 2. ShopRenter App Registration

Before using the Payment API, the ShopCall.ai app must be:
- ✅ Registered with ShopRenter Partner Support
- ✅ Approved for use in production
- ✅ Configured with Payment API access

**Current Status:** ShopCall.ai is ready for registration (see `SHOPRENTER_REGISTRATION.md`).

### 3. Required Credentials

The Payment API will require additional credentials beyond the OAuth credentials:

- `SHOPRENTER_CLIENT_ID` - OAuth client ID (already configured)
- `SHOPRENTER_CLIENT_SECRET` - OAuth secret (already configured)
- `SHOPRENTER_PAYMENT_API_KEY` - Payment API specific key (to be obtained)
- `SHOPRENTER_PAYMENT_SECRET` - Payment API webhook secret (to be obtained)

### 4. Scope Requirements

The app must request payment-related scopes during OAuth:
- `payment:read` - Read payment information
- `payment:write` - Create payment plans and initiate charges
- `billing:read` - Access billing data
- `billing:write` - Update billing information

---

## 🏗️ Architecture Overview

### System Components

```
┌─────────────────────┐
│   ShopRenter        │
│   Store Owner       │
└──────────┬──────────┘
           │
           │ 1. Connect Store (OAuth)
           ▼
┌─────────────────────┐
│   ShopCall.ai       │
│   Frontend          │
└──────────┬──────────┘
           │
           │ 2. Select Subscription Plan
           ▼
┌─────────────────────┐
│   ShopCall.ai       │
│   Backend           │
│   (Edge Function)   │
└──────────┬──────────┘
           │
           │ 3. Create Payment Plan
           │    (via ShopRenter Payment API)
           ▼
┌─────────────────────┐
│   ShopRenter        │
│   Payment API       │
└──────────┬──────────┘
           │
           │ 4. Process Payment
           │    (Automatic Recurring Billing)
           ▼
┌─────────────────────┐
│   ShopRenter        │
│   Payment Gateway   │
└──────────┬──────────┘
           │
           │ 5. Webhook Notification
           │    (payment.success, payment.failed, etc.)
           ▼
┌─────────────────────┐
│   ShopCall.ai       │
│   Webhook Handler   │
│   (Edge Function)   │
└──────────┬──────────┘
           │
           │ 6. Update Subscription Status
           ▼
┌─────────────────────┐
│   Supabase          │
│   Database          │
└─────────────────────┘
```

### Integration Flow

1. **Store Connection**: Merchant connects ShopRenter store via OAuth (already implemented)
2. **Subscription Selection**: Merchant selects a ShopCall.ai subscription plan
3. **Platform Validation**: Backend validates `platform_name = 'shoprenter'`
4. **Payment Plan Creation**: Backend creates payment plan via ShopRenter Payment API
5. **Billing Data**: ShopRenter uses merchant's existing billing information
6. **Automatic Billing**: ShopRenter charges merchant's card on schedule
7. **Webhook Events**: ShopRenter sends payment status updates to ShopCall.ai
8. **Subscription Management**: ShopCall.ai updates subscription status based on webhook events

---

## 💾 Database Schema

### New Table: `shoprenter_payment_plans`

Stores payment plans configured through ShopRenter Payment API.

```sql
CREATE TABLE shoprenter_payment_plans (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  -- Store reference
  store_id UUID NOT NULL REFERENCES stores(id) ON DELETE CASCADE,

  -- ShopRenter Payment API identifiers
  payment_plan_id VARCHAR(255) NOT NULL, -- ID from ShopRenter Payment API
  app_payment_id VARCHAR(255), -- Application-specific payment ID

  -- Plan details
  plan_name VARCHAR(255) NOT NULL, -- e.g., "Basic Plan", "Pro Plan"
  plan_type VARCHAR(50) NOT NULL, -- "one_time", "recurring"
  amount DECIMAL(10, 2) NOT NULL, -- Price in HUF
  currency VARCHAR(3) DEFAULT 'HUF',
  billing_frequency VARCHAR(50), -- "monthly", "yearly", "quarterly"

  -- Status
  status VARCHAR(50) NOT NULL DEFAULT 'pending', -- pending, active, cancelled, failed, expired

  -- Timestamps from ShopRenter
  trial_ends_at TIMESTAMPTZ, -- Trial period end (if applicable)
  next_billing_date TIMESTAMPTZ, -- Next scheduled charge
  activated_at TIMESTAMPTZ, -- When subscription became active
  cancelled_at TIMESTAMPTZ, -- When subscription was cancelled
  expires_at TIMESTAMPTZ, -- Expiration date

  -- Metadata
  raw_data JSONB, -- Full response from ShopRenter Payment API
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),

  -- Constraints
  UNIQUE(store_id, payment_plan_id)
);

-- Indexes
CREATE INDEX idx_shoprenter_payment_plans_store ON shoprenter_payment_plans(store_id);
CREATE INDEX idx_shoprenter_payment_plans_status ON shoprenter_payment_plans(status);
CREATE INDEX idx_shoprenter_payment_plans_next_billing ON shoprenter_payment_plans(next_billing_date);

-- Trigger for updated_at
CREATE TRIGGER set_shoprenter_payment_plans_updated_at
BEFORE UPDATE ON shoprenter_payment_plans
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
```

### New Table: `shoprenter_payment_transactions`

Tracks individual payment transactions and webhook events.

```sql
CREATE TABLE shoprenter_payment_transactions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  -- References
  store_id UUID NOT NULL REFERENCES stores(id) ON DELETE CASCADE,
  payment_plan_id UUID REFERENCES shoprenter_payment_plans(id) ON DELETE SET NULL,

  -- ShopRenter identifiers
  transaction_id VARCHAR(255) NOT NULL, -- Transaction ID from ShopRenter
  charge_id VARCHAR(255), -- Charge ID from Payment API

  -- Transaction details
  transaction_type VARCHAR(50) NOT NULL, -- "charge", "refund", "update"
  amount DECIMAL(10, 2) NOT NULL,
  currency VARCHAR(3) DEFAULT 'HUF',
  status VARCHAR(50) NOT NULL, -- "pending", "success", "failed", "refunded"

  -- Payment method
  payment_method VARCHAR(50), -- "card", "bank_transfer", etc.
  card_last4 VARCHAR(4), -- Last 4 digits of card
  card_brand VARCHAR(50), -- "visa", "mastercard", etc.

  -- Invoice
  invoice_number VARCHAR(255),
  invoice_url TEXT,

  -- Error handling
  error_code VARCHAR(50),
  error_message TEXT,

  -- Timestamps
  processed_at TIMESTAMPTZ,
  refunded_at TIMESTAMPTZ,

  -- Metadata
  raw_data JSONB, -- Full webhook payload
  created_at TIMESTAMPTZ DEFAULT NOW(),

  -- Constraints
  UNIQUE(transaction_id)
);

-- Indexes
CREATE INDEX idx_shoprenter_transactions_store ON shoprenter_payment_transactions(store_id);
CREATE INDEX idx_shoprenter_transactions_plan ON shoprenter_payment_transactions(payment_plan_id);
CREATE INDEX idx_shoprenter_transactions_status ON shoprenter_payment_transactions(status);
CREATE INDEX idx_shoprenter_transactions_date ON shoprenter_payment_transactions(created_at);
```

### Update Existing `stores` Table

Add payment-related fields to track subscription status.

```sql
ALTER TABLE stores
  ADD COLUMN IF NOT EXISTS shoprenter_payment_plan_id UUID REFERENCES shoprenter_payment_plans(id),
  ADD COLUMN IF NOT EXISTS subscription_status VARCHAR(50) DEFAULT 'inactive',
  ADD COLUMN IF NOT EXISTS subscription_started_at TIMESTAMPTZ,
  ADD COLUMN IF NOT EXISTS subscription_ends_at TIMESTAMPTZ;

-- Index for subscription queries
CREATE INDEX IF NOT EXISTS idx_stores_subscription_status ON stores(subscription_status);
```

### Migration Script

```sql
-- Migration: Add ShopRenter Payment API support
-- Version: 20251111_shoprenter_payment_api.sql

BEGIN;

-- Create payment plans table
CREATE TABLE IF NOT EXISTS shoprenter_payment_plans (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  store_id UUID NOT NULL REFERENCES stores(id) ON DELETE CASCADE,
  payment_plan_id VARCHAR(255) NOT NULL,
  app_payment_id VARCHAR(255),
  plan_name VARCHAR(255) NOT NULL,
  plan_type VARCHAR(50) NOT NULL,
  amount DECIMAL(10, 2) NOT NULL,
  currency VARCHAR(3) DEFAULT 'HUF',
  billing_frequency VARCHAR(50),
  status VARCHAR(50) NOT NULL DEFAULT 'pending',
  trial_ends_at TIMESTAMPTZ,
  next_billing_date TIMESTAMPTZ,
  activated_at TIMESTAMPTZ,
  cancelled_at TIMESTAMPTZ,
  expires_at TIMESTAMPTZ,
  raw_data JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(store_id, payment_plan_id)
);

CREATE INDEX idx_shoprenter_payment_plans_store ON shoprenter_payment_plans(store_id);
CREATE INDEX idx_shoprenter_payment_plans_status ON shoprenter_payment_plans(status);
CREATE INDEX idx_shoprenter_payment_plans_next_billing ON shoprenter_payment_plans(next_billing_date);

-- Create payment transactions table
CREATE TABLE IF NOT EXISTS shoprenter_payment_transactions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  store_id UUID NOT NULL REFERENCES stores(id) ON DELETE CASCADE,
  payment_plan_id UUID REFERENCES shoprenter_payment_plans(id) ON DELETE SET NULL,
  transaction_id VARCHAR(255) NOT NULL,
  charge_id VARCHAR(255),
  transaction_type VARCHAR(50) NOT NULL,
  amount DECIMAL(10, 2) NOT NULL,
  currency VARCHAR(3) DEFAULT 'HUF',
  status VARCHAR(50) NOT NULL,
  payment_method VARCHAR(50),
  card_last4 VARCHAR(4),
  card_brand VARCHAR(50),
  invoice_number VARCHAR(255),
  invoice_url TEXT,
  error_code VARCHAR(50),
  error_message TEXT,
  processed_at TIMESTAMPTZ,
  refunded_at TIMESTAMPTZ,
  raw_data JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(transaction_id)
);

CREATE INDEX idx_shoprenter_transactions_store ON shoprenter_payment_transactions(store_id);
CREATE INDEX idx_shoprenter_transactions_plan ON shoprenter_payment_transactions(payment_plan_id);
CREATE INDEX idx_shoprenter_transactions_status ON shoprenter_payment_transactions(status);
CREATE INDEX idx_shoprenter_transactions_date ON shoprenter_payment_transactions(created_at);

-- Update stores table
ALTER TABLE stores
  ADD COLUMN IF NOT EXISTS shoprenter_payment_plan_id UUID REFERENCES shoprenter_payment_plans(id),
  ADD COLUMN IF NOT EXISTS subscription_status VARCHAR(50) DEFAULT 'inactive',
  ADD COLUMN IF NOT EXISTS subscription_started_at TIMESTAMPTZ,
  ADD COLUMN IF NOT EXISTS subscription_ends_at TIMESTAMPTZ;

CREATE INDEX IF NOT EXISTS idx_stores_subscription_status ON stores(subscription_status);

-- Create trigger for updated_at
CREATE TRIGGER set_shoprenter_payment_plans_updated_at
BEFORE UPDATE ON shoprenter_payment_plans
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

COMMIT;
```

---

## 🔧 Backend Implementation

### File Structure

```
supabase/functions/
├── shoprenter-payment-create/
│   └── index.ts                    # Create payment plan endpoint
├── shoprenter-payment-cancel/
│   └── index.ts                    # Cancel subscription endpoint
├── shoprenter-payment-update/
│   └── index.ts                    # Update payment plan endpoint
├── shoprenter-payment-webhook/
│   └── index.ts                    # Payment webhook handler
├── shoprenter-payment-status/
│   └── index.ts                    # Check payment status endpoint
└── _shared/
    └── shoprenter-payment-client.ts # ShopRenter Payment API client
```

### 1. Payment API Client (`_shared/shoprenter-payment-client.ts`)

```typescript
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';

interface PaymentPlanParams {
  storeId: string;
  planName: string;
  planType: 'one_time' | 'recurring';
  amount: number;
  currency?: string;
  billingFrequency?: 'monthly' | 'yearly' | 'quarterly';
  trialDays?: number;
}

interface PaymentPlanResponse {
  paymentPlanId: string;
  status: string;
  activatedAt?: string;
  nextBillingDate?: string;
  invoiceUrl?: string;
}

/**
 * ShopRenter Payment API Client
 * Handles all payment-related API calls to ShopRenter
 */
export class ShopRenterPaymentClient {
  private apiKey: string;
  private apiSecret: string;
  private supabase: any;

  constructor() {
    this.apiKey = Deno.env.get('SHOPRENTER_PAYMENT_API_KEY')!;
    this.apiSecret = Deno.env.get('SHOPRENTER_PAYMENT_SECRET')!;
    this.supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
    );
  }

  /**
   * Validate that store is ShopRenter platform
   */
  async validateShopRenterStore(storeId: string): Promise<void> {
    const { data: store, error } = await this.supabase
      .from('stores')
      .select('platform_name, is_active, store_name')
      .eq('id', storeId)
      .single();

    if (error || !store) {
      throw new Error('Store not found');
    }

    if (!store.is_active) {
      throw new Error('Store is inactive');
    }

    if (store.platform_name !== 'shoprenter') {
      throw new Error(
        'ShopRenter Payment API can only be used with ShopRenter stores. ' +
        `Current platform: ${store.platform_name}`
      );
    }
  }

  /**
   * Get ShopRenter access token for store
   */
  async getAccessToken(storeId: string): Promise<string> {
    const { data: store, error } = await this.supabase
      .from('stores')
      .select('api_key, token_expires_at, store_name')
      .eq('id', storeId)
      .single();

    if (error || !store) {
      throw new Error('Store credentials not found');
    }

    // Check if token needs refresh
    const expiresAt = new Date(store.token_expires_at);
    const now = new Date();
    const bufferMinutes = 5;

    if (expiresAt.getTime() - now.getTime() < bufferMinutes * 60 * 1000) {
      // Token expired or expiring soon - refresh it
      // Implementation depends on ShopRenter OAuth refresh flow
      console.log(`[ShopRenter Payment] Token refresh needed for ${store.store_name}`);
      // TODO: Implement token refresh
    }

    return store.api_key;
  }

  /**
   * Create a payment plan via ShopRenter Payment API
   */
  async createPaymentPlan(params: PaymentPlanParams): Promise<PaymentPlanResponse> {
    // 1. Validate store platform
    await this.validateShopRenterStore(params.storeId);

    // 2. Get store details
    const { data: store } = await this.supabase
      .from('stores')
      .select('store_name, store_url')
      .eq('id', params.storeId)
      .single();

    const accessToken = await this.getAccessToken(params.storeId);

    // 3. Call ShopRenter Payment API
    // NOTE: Actual API endpoint URL and structure to be confirmed with ShopRenter docs
    const apiUrl = `https://${store.store_name}.api.shoprenter.hu/payment/plans`;

    const response = await fetch(apiUrl, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        'X-ShopRenter-API-Key': this.apiKey,
      },
      body: JSON.stringify({
        name: params.planName,
        type: params.planType,
        amount: params.amount,
        currency: params.currency || 'HUF',
        billing_frequency: params.billingFrequency,
        trial_days: params.trialDays || 0,
        return_url: `${Deno.env.get('FRONTEND_URL')}/billing/success`,
        cancel_url: `${Deno.env.get('FRONTEND_URL')}/billing/cancel`,
        notification_url: `${Deno.env.get('SUPABASE_URL')}/functions/v1/shoprenter-payment-webhook`,
      }),
    });

    if (!response.ok) {
      const error = await response.text();
      console.error('[ShopRenter Payment] API error:', error);
      throw new Error(`Payment plan creation failed: ${response.statusText}`);
    }

    const data = await response.json();
    console.log('[ShopRenter Payment] Plan created:', data);

    // 4. Store payment plan in database
    await this.supabase
      .from('shoprenter_payment_plans')
      .insert({
        store_id: params.storeId,
        payment_plan_id: data.id,
        plan_name: params.planName,
        plan_type: params.planType,
        amount: params.amount,
        currency: params.currency || 'HUF',
        billing_frequency: params.billingFrequency,
        status: data.status || 'pending',
        next_billing_date: data.next_billing_date,
        raw_data: data,
      });

    return {
      paymentPlanId: data.id,
      status: data.status,
      activatedAt: data.activated_at,
      nextBillingDate: data.next_billing_date,
      invoiceUrl: data.invoice_url,
    };
  }

  /**
   * Cancel a payment plan
   */
  async cancelPaymentPlan(storeId: string, paymentPlanId: string): Promise<void> {
    // 1. Validate store platform
    await this.validateShopRenterStore(storeId);

    // 2. Get store details
    const { data: store } = await this.supabase
      .from('stores')
      .select('store_name')
      .eq('id', storeId)
      .single();

    const accessToken = await this.getAccessToken(storeId);

    // 3. Call ShopRenter Payment API
    const apiUrl = `https://${store.store_name}.api.shoprenter.hu/payment/plans/${paymentPlanId}/cancel`;

    const response = await fetch(apiUrl, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        'X-ShopRenter-API-Key': this.apiKey,
      },
    });

    if (!response.ok) {
      const error = await response.text();
      console.error('[ShopRenter Payment] Cancel error:', error);
      throw new Error(`Payment plan cancellation failed: ${response.statusText}`);
    }

    // 4. Update database
    await this.supabase
      .from('shoprenter_payment_plans')
      .update({
        status: 'cancelled',
        cancelled_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      })
      .eq('store_id', storeId)
      .eq('payment_plan_id', paymentPlanId);

    console.log(`[ShopRenter Payment] Plan cancelled: ${paymentPlanId}`);
  }

  /**
   * Get payment plan status
   */
  async getPaymentPlanStatus(storeId: string, paymentPlanId: string): Promise<any> {
    // 1. Validate store platform
    await this.validateShopRenterStore(storeId);

    // 2. Get store details
    const { data: store } = await this.supabase
      .from('stores')
      .select('store_name')
      .eq('id', storeId)
      .single();

    const accessToken = await this.getAccessToken(storeId);

    // 3. Call ShopRenter Payment API
    const apiUrl = `https://${store.store_name}.api.shoprenter.hu/payment/plans/${paymentPlanId}`;

    const response = await fetch(apiUrl, {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'X-ShopRenter-API-Key': this.apiKey,
      },
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch payment plan status: ${response.statusText}`);
    }

    return await response.json();
  }
}
```

### 2. Create Payment Plan Endpoint (`shoprenter-payment-create/index.ts`)

```typescript
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
import { ShopRenterPaymentClient } from '../_shared/shoprenter-payment-client.ts';

serve(async (req) => {
  // CORS headers
  if (req.method === 'OPTIONS') {
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'POST, OPTIONS',
        'Access-Control-Allow-Headers': 'authorization, content-type',
      },
    });
  }

  try {
    // 1. Authenticate user
    const authHeader = req.headers.get('Authorization');
    if (!authHeader) {
      return new Response(JSON.stringify({ error: 'Missing authorization header' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_ANON_KEY')!,
      {
        global: {
          headers: { Authorization: authHeader },
        },
      }
    );

    const { data: { user }, error: authError } = await supabase.auth.getUser();
    if (authError || !user) {
      return new Response(JSON.stringify({ error: 'Unauthorized' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 2. Parse request body
    const { store_id, plan_name, plan_type, amount, billing_frequency } = await req.json();

    if (!store_id || !plan_name || !plan_type || !amount) {
      return new Response(JSON.stringify({ error: 'Missing required fields' }), {
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 3. Verify store ownership
    const { data: store } = await supabase
      .from('stores')
      .select('id, platform_name')
      .eq('id', store_id)
      .eq('user_id', user.id)
      .single();

    if (!store) {
      return new Response(JSON.stringify({ error: 'Store not found or access denied' }), {
        status: 403,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 4. Validate ShopRenter platform (CRITICAL CHECK!)
    if (store.platform_name !== 'shoprenter') {
      return new Response(
        JSON.stringify({
          error: 'Invalid payment method',
          message: 'ShopRenter Payment API can only be used with ShopRenter stores',
          platform: store.platform_name,
        }),
        {
          status: 400,
          headers: { 'Content-Type': 'application/json' },
        }
      );
    }

    // 5. Create payment plan
    const paymentClient = new ShopRenterPaymentClient();
    const result = await paymentClient.createPaymentPlan({
      storeId: store_id,
      planName: plan_name,
      planType: plan_type,
      amount: amount,
      billingFrequency: billing_frequency,
    });

    return new Response(
      JSON.stringify({
        success: true,
        payment_plan_id: result.paymentPlanId,
        status: result.status,
        next_billing_date: result.nextBillingDate,
        invoice_url: result.invoiceUrl,
      }),
      {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      }
    );
  } catch (error) {
    console.error('[ShopRenter Payment] Error:', error);
    return new Response(
      JSON.stringify({
        error: 'Payment plan creation failed',
        message: error.message,
      }),
      {
        status: 500,
        headers: { 'Content-Type': 'application/json' },
      }
    );
  }
});
```

### 3. Payment Webhook Handler (`shoprenter-payment-webhook/index.ts`)

```typescript
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
import { createHmac } from 'https://deno.land/std@0.177.0/node/crypto.ts';

serve(async (req) => {
  if (req.method !== 'POST') {
    return new Response('Method not allowed', { status: 405 });
  }

  try {
    // 1. Validate webhook signature (HMAC)
    const body = await req.text();
    const signature = req.headers.get('X-ShopRenter-Signature');
    const secret = Deno.env.get('SHOPRENTER_PAYMENT_SECRET')!;

    const expectedSignature = createHmac('sha256', secret)
      .update(body)
      .digest('hex');

    if (signature !== expectedSignature) {
      console.error('[ShopRenter Payment Webhook] Invalid signature');
      return new Response('Invalid signature', { status: 403 });
    }

    // 2. Parse webhook payload
    const payload = JSON.parse(body);
    const { event, data } = payload;

    console.log(`[ShopRenter Payment Webhook] Received: ${event}`, data);

    const supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
    );

    // 3. Process webhook event
    switch (event) {
      case 'payment.success':
        await handlePaymentSuccess(supabase, data);
        break;

      case 'payment.failed':
        await handlePaymentFailed(supabase, data);
        break;

      case 'subscription.activated':
        await handleSubscriptionActivated(supabase, data);
        break;

      case 'subscription.cancelled':
        await handleSubscriptionCancelled(supabase, data);
        break;

      case 'subscription.expired':
        await handleSubscriptionExpired(supabase, data);
        break;

      default:
        console.log(`[ShopRenter Payment Webhook] Unhandled event: ${event}`);
    }

    return new Response(JSON.stringify({ success: true }), {
      status: 200,
      headers: { 'Content-Type': 'application/json' },
    });
  } catch (error) {
    console.error('[ShopRenter Payment Webhook] Error:', error);
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
});

async function handlePaymentSuccess(supabase: any, data: any) {
  console.log('[ShopRenter Payment] Payment successful:', data);

  // 1. Record transaction
  await supabase.from('shoprenter_payment_transactions').insert({
    store_id: data.store_id,
    payment_plan_id: data.payment_plan_id,
    transaction_id: data.transaction_id,
    charge_id: data.charge_id,
    transaction_type: 'charge',
    amount: data.amount,
    currency: data.currency || 'HUF',
    status: 'success',
    payment_method: data.payment_method,
    card_last4: data.card_last4,
    card_brand: data.card_brand,
    invoice_number: data.invoice_number,
    invoice_url: data.invoice_url,
    processed_at: new Date().toISOString(),
    raw_data: data,
  });

  // 2. Update payment plan status
  await supabase
    .from('shoprenter_payment_plans')
    .update({
      status: 'active',
      next_billing_date: data.next_billing_date,
      updated_at: new Date().toISOString(),
    })
    .eq('payment_plan_id', data.payment_plan_id);

  // 3. Update store subscription status
  await supabase
    .from('stores')
    .update({
      subscription_status: 'active',
      subscription_started_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    })
    .eq('id', data.store_id);
}

async function handlePaymentFailed(supabase: any, data: any) {
  console.log('[ShopRenter Payment] Payment failed:', data);

  // 1. Record failed transaction
  await supabase.from('shoprenter_payment_transactions').insert({
    store_id: data.store_id,
    payment_plan_id: data.payment_plan_id,
    transaction_id: data.transaction_id,
    transaction_type: 'charge',
    amount: data.amount,
    currency: data.currency || 'HUF',
    status: 'failed',
    error_code: data.error_code,
    error_message: data.error_message,
    processed_at: new Date().toISOString(),
    raw_data: data,
  });

  // 2. Update payment plan status
  await supabase
    .from('shoprenter_payment_plans')
    .update({
      status: 'failed',
      updated_at: new Date().toISOString(),
    })
    .eq('payment_plan_id', data.payment_plan_id);

  // 3. Update store subscription status
  await supabase
    .from('stores')
    .update({
      subscription_status: 'payment_failed',
      updated_at: new Date().toISOString(),
    })
    .eq('id', data.store_id);
}

async function handleSubscriptionActivated(supabase: any, data: any) {
  console.log('[ShopRenter Payment] Subscription activated:', data);

  await supabase
    .from('shoprenter_payment_plans')
    .update({
      status: 'active',
      activated_at: new Date().toISOString(),
      next_billing_date: data.next_billing_date,
      updated_at: new Date().toISOString(),
    })
    .eq('payment_plan_id', data.payment_plan_id);

  await supabase
    .from('stores')
    .update({
      subscription_status: 'active',
      subscription_started_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    })
    .eq('id', data.store_id);
}

async function handleSubscriptionCancelled(supabase: any, data: any) {
  console.log('[ShopRenter Payment] Subscription cancelled:', data);

  await supabase
    .from('shoprenter_payment_plans')
    .update({
      status: 'cancelled',
      cancelled_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    })
    .eq('payment_plan_id', data.payment_plan_id);

  await supabase
    .from('stores')
    .update({
      subscription_status: 'cancelled',
      updated_at: new Date().toISOString(),
    })
    .eq('id', data.store_id);
}

async function handleSubscriptionExpired(supabase: any, data: any) {
  console.log('[ShopRenter Payment] Subscription expired:', data);

  await supabase
    .from('shoprenter_payment_plans')
    .update({
      status: 'expired',
      expires_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    })
    .eq('payment_plan_id', data.payment_plan_id);

  await supabase
    .from('stores')
    .update({
      subscription_status: 'expired',
      subscription_ends_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    })
    .eq('id', data.store_id);
}
```

---

## 💳 Payment Flow

### User Journey

```
1. Merchant connects ShopRenter store (OAuth) → Already implemented
   ↓
2. Merchant navigates to "Billing" page in ShopCall.ai
   ↓
3. Merchant selects subscription plan (Basic, Pro, Enterprise)
   ↓
4. Frontend checks store platform: if (platform_name !== 'shoprenter') → show Stripe payment
   ↓
5. If ShopRenter store → Show "Pay via ShopRenter" button
   ↓
6. Merchant clicks "Subscribe"
   ↓
7. Frontend calls: POST /functions/v1/shoprenter-payment-create
   ↓
8. Backend validates store platform (MANDATORY CHECK!)
   ↓
9. Backend creates payment plan via ShopRenter Payment API
   ↓
10. ShopRenter redirects merchant to payment page (card details)
    ↓
11. Merchant enters card details or confirms existing card
    ↓
12. ShopRenter processes first payment
    ↓
13. ShopRenter sends webhook: payment.success
    ↓
14. Backend updates subscription status → "active"
    ↓
15. Merchant redirected back to ShopCall.ai → Subscription active!
```

### Recurring Billing

After initial payment:

1. ShopRenter automatically charges the merchant's card on schedule (monthly/yearly)
2. ShopRenter sends webhook: `payment.success` or `payment.failed`
3. ShopCall.ai updates subscription status accordingly
4. If payment fails:
   - Send email notification to merchant
   - Mark subscription as "payment_failed"
   - Allow grace period (e.g., 3 days) before suspension
   - ShopRenter retries payment automatically

---

## 🔒 Security Considerations

### 1. Store Platform Validation (CRITICAL!)

**Enforcement Strategy:**

```typescript
// Middleware for all payment endpoints
async function validateShopRenterPayment(storeId: string, userId: string) {
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  );

  // 1. Verify store exists and belongs to user
  const { data: store, error } = await supabase
    .from('stores')
    .select('id, platform_name, is_active, user_id')
    .eq('id', storeId)
    .single();

  if (error || !store) {
    throw new Error('Store not found');
  }

  if (store.user_id !== userId) {
    throw new Error('Access denied: You do not own this store');
  }

  if (!store.is_active) {
    throw new Error('Store is inactive');
  }

  // 2. CRITICAL: Validate ShopRenter platform
  if (store.platform_name !== 'shoprenter') {
    throw new Error(
      `Invalid payment method for platform: ${store.platform_name}. ` +
      'ShopRenter Payment API can only be used with ShopRenter stores.'
    );
  }

  return store;
}
```

**Apply to ALL payment endpoints:**
- ✅ `shoprenter-payment-create`
- ✅ `shoprenter-payment-cancel`
- ✅ `shoprenter-payment-update`
- ✅ `shoprenter-payment-status`
- ✅ `shoprenter-payment-webhook` (validate via payment_plan_id lookup)

### 2. Webhook Signature Validation

```typescript
function validateWebhookSignature(body: string, signature: string): boolean {
  const secret = Deno.env.get('SHOPRENTER_PAYMENT_SECRET')!;

  const expectedSignature = createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  // Timing-safe comparison
  try {
    return timingSafeEqual(
      new TextEncoder().encode(expectedSignature),
      new TextEncoder().encode(signature)
    );
  } catch {
    return false;
  }
}
```

### 3. Environment Variables

```bash
# Required for ShopRenter Payment API
SHOPRENTER_PAYMENT_API_KEY=your_payment_api_key
SHOPRENTER_PAYMENT_SECRET=your_webhook_secret

# Existing ShopRenter OAuth (already configured)
SHOPRENTER_CLIENT_ID=your_client_id
SHOPRENTER_CLIENT_SECRET=your_client_secret

# Supabase (already configured)
SUPABASE_URL=https://ztklqodcdjeqpsvhlpud.supabase.co
SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

# Frontend URL (already configured)
FRONTEND_URL=https://shopcall.ai
```

### 4. Data Privacy & GDPR

- ✅ Store minimal payment data (no full card numbers)
- ✅ Log transactions for audit trail
- ✅ Delete payment data on store deletion (CASCADE)
- ✅ Provide data export for merchants
- ✅ Handle refund requests properly

---

## 🧪 Testing Strategy

### 1. Test Store Setup

Use existing ShopRenter test store:
- Store name: `shopcall-test-store`
- URL: `shopcall-test-store.myshoprenter.hu`
- Already connected via OAuth

### 2. Test Cases

#### Store Validation Tests

```typescript
describe('Store Platform Validation', () => {
  it('should accept ShopRenter stores', async () => {
    // Given: ShopRenter store
    const store = { platform_name: 'shoprenter', is_active: true };

    // When: Creating payment plan
    const result = await createPaymentPlan({ storeId: store.id });

    // Then: Should succeed
    expect(result.success).toBe(true);
  });

  it('should reject non-ShopRenter stores', async () => {
    // Given: Shopify store
    const store = { platform_name: 'shopify', is_active: true };

    // When: Creating payment plan
    const result = await createPaymentPlan({ storeId: store.id });

    // Then: Should fail with specific error
    expect(result.error).toContain('ShopRenter Payment API can only be used with ShopRenter stores');
  });

  it('should reject WooCommerce stores', async () => {
    // Given: WooCommerce store
    const store = { platform_name: 'woocommerce', is_active: true };

    // When: Creating payment plan
    const result = await createPaymentPlan({ storeId: store.id });

    // Then: Should fail
    expect(result.error).toContain('ShopRenter Payment API');
  });

  it('should reject inactive stores', async () => {
    // Given: Inactive ShopRenter store
    const store = { platform_name: 'shoprenter', is_active: false };

    // When: Creating payment plan
    const result = await createPaymentPlan({ storeId: store.id });

    // Then: Should fail
    expect(result.error).toContain('inactive');
  });
});
```

#### Payment Flow Tests

```typescript
describe('Payment Plan Creation', () => {
  it('should create one-time payment plan', async () => {
    const params = {
      storeId: 'test-store-id',
      planName: 'Basic Plan',
      planType: 'one_time',
      amount: 9990,
      currency: 'HUF',
    };

    const result = await paymentClient.createPaymentPlan(params);

    expect(result.paymentPlanId).toBeDefined();
    expect(result.status).toBe('pending');
  });

  it('should create recurring payment plan', async () => {
    const params = {
      storeId: 'test-store-id',
      planName: 'Pro Plan',
      planType: 'recurring',
      amount: 29990,
      currency: 'HUF',
      billingFrequency: 'monthly',
    };

    const result = await paymentClient.createPaymentPlan(params);

    expect(result.paymentPlanId).toBeDefined();
    expect(result.nextBillingDate).toBeDefined();
  });
});
```

#### Webhook Tests

```typescript
describe('Payment Webhooks', () => {
  it('should handle payment.success webhook', async () => {
    const payload = {
      event: 'payment.success',
      data: {
        store_id: 'test-store-id',
        payment_plan_id: 'plan-123',
        transaction_id: 'txn-456',
        amount: 9990,
        currency: 'HUF',
        invoice_number: 'INV-2025-001',
      },
    };

    const response = await handleWebhook(payload);

    expect(response.status).toBe(200);

    // Verify database updates
    const plan = await getPlanFromDB('plan-123');
    expect(plan.status).toBe('active');

    const store = await getStoreFromDB('test-store-id');
    expect(store.subscription_status).toBe('active');
  });

  it('should handle payment.failed webhook', async () => {
    const payload = {
      event: 'payment.failed',
      data: {
        store_id: 'test-store-id',
        payment_plan_id: 'plan-123',
        error_code: 'insufficient_funds',
        error_message: 'Insufficient funds',
      },
    };

    const response = await handleWebhook(payload);

    expect(response.status).toBe(200);

    const plan = await getPlanFromDB('plan-123');
    expect(plan.status).toBe('failed');

    const store = await getStoreFromDB('test-store-id');
    expect(store.subscription_status).toBe('payment_failed');
  });

  it('should reject webhook with invalid signature', async () => {
    const payload = { event: 'payment.success', data: {} };
    const invalidSignature = 'invalid_hmac';

    const response = await handleWebhook(payload, invalidSignature);

    expect(response.status).toBe(403);
  });
});
```

### 3. Manual Testing Checklist

- [ ] Connect test ShopRenter store via OAuth
- [ ] Navigate to billing page
- [ ] Select subscription plan
- [ ] Verify "Pay via ShopRenter" option appears
- [ ] Click subscribe button
- [ ] Verify redirect to ShopRenter payment page
- [ ] Complete test payment with test card
- [ ] Verify redirect back to ShopCall.ai
- [ ] Verify subscription status is "active"
- [ ] Verify payment transaction recorded in database
- [ ] Test cancellation flow
- [ ] Test payment failure handling
- [ ] Verify webhook events processed correctly
- [ ] Test with non-ShopRenter store (should reject)

---

## 🚀 Deployment Plan

### Phase 1: Preparation (Week 1)

- [x] Review ShopRenter Payment API documentation
- [ ] Create implementation plan (this document)
- [ ] Design database schema
- [ ] Set up test environment
- [ ] Request Payment API credentials from ShopRenter

### Phase 2: Development (Weeks 2-3)

- [ ] Create database migration
- [ ] Implement Payment API client library
- [ ] Implement Edge Functions:
  - [ ] `shoprenter-payment-create`
  - [ ] `shoprenter-payment-cancel`
  - [ ] `shoprenter-payment-status`
  - [ ] `shoprenter-payment-webhook`
- [ ] Implement store validation middleware
- [ ] Add error handling and logging

### Phase 3: Frontend Integration (Week 4)

- [ ] Create billing page UI
- [ ] Add subscription plan selection
- [ ] Implement platform-specific payment method selection
- [ ] Add ShopRenter payment button
- [ ] Implement success/failure callbacks
- [ ] Add subscription management UI

### Phase 4: Testing (Week 5)

- [ ] Unit tests for validation logic
- [ ] Integration tests for payment flow
- [ ] Webhook handler tests
- [ ] Manual end-to-end testing
- [ ] Security audit
- [ ] Performance testing

### Phase 5: Production Deployment (Week 6)

- [ ] Deploy database migration
- [ ] Deploy Edge Functions
- [ ] Configure environment variables
- [ ] Enable payment webhooks
- [ ] Monitor logs for errors
- [ ] Soft launch with beta users

### Phase 6: Launch (Week 7)

- [ ] Full production launch
- [ ] Monitor payment success rates
- [ ] Customer support readiness
- [ ] Documentation updates
- [ ] Marketing announcement

---

## 📅 Timeline & Milestones

| Week | Milestone | Deliverables |
|------|-----------|--------------|
| 1 | Planning Complete | ✅ Implementation plan document |
| 2 | Database & Backend Foundation | Schema migration, Payment client library |
| 3 | Core Payment Features | Create/cancel/status Edge Functions |
| 4 | Frontend Integration | Billing UI, Payment buttons, Callbacks |
| 5 | Testing & QA | Test suite, Manual testing, Security audit |
| 6 | Deployment | Production deployment, Monitoring setup |
| 7 | Launch | Public launch, User documentation |

**Total Duration:** 7 weeks

---

## 📚 References & Resources

### ShopRenter Documentation

- **Payment API Introduction**: `https://doc.shoprenter.hu/paymentapi/docs/a_introduce.html`
- **Payment API Docs**: `https://doc.shoprenter.hu/paymentapi/`
- **App Development Guide**: `https://doc.shoprenter.hu/development/app-development/`
- **OAuth Documentation**: `docs/shoprenter_app_development.md`

### Internal Documentation

- **ShopRenter Integration Plan**: `SHOPRENTER.md`
- **Registration Requirements**: `SHOPRENTER_REGISTRATION.md`
- **API Resources**: `docs/shoprenter_api_resources.md`
- **Payment Overview**: `docs/shoprenter_payment.md`

### Project Documentation

- **Project Overview**: `CLAUDE.md`
- **Database Schema**: See Supabase migrations in `supabase/migrations/`
- **Edge Functions**: `supabase/functions/`

### Contact & Support

- **ShopRenter Partner Support**: `partnersupport@shoprenter.hu`
- **ShopRenter Developer Portal**: `https://doc.shoprenter.hu`

---

## ✅ Next Steps

1. **Review this plan** with the team
2. **Request Payment API credentials** from ShopRenter Partner Support
3. **Create database migration** for new tables
4. **Set up test environment** with test ShopRenter store
5. **Begin Phase 2 development** (Backend implementation)

---

## 🔄 Change Log

| Date | Version | Changes |
|------|---------|---------|
| 2025-11-11 | 1.0 | Initial implementation plan created |

---

**Document Status:** ✅ Ready for Review
**Author:** Claude (AI Development Assistant)
**Approved By:** _Pending_
**Next Review Date:** _TBD_
