# Multilingual Press Zone - Licensing Implementation Plan

> **CRITICAL**: Licensing system for commercial WordPress plugin with api.press.zone backend.

---

## Pricing Structure

### Launch Pricing (34% Discount - Limited Time)

| Tier | Sites | Updates | Launch Price (1 Year) | Launch Price (3 Years) | Regular Price (1 Year) |
|------|-------|---------|----------------------|------------------------|------------------------|
| **Starter** | 1 site | 1 year | **$50** | **$115** | $75 |
| **Pro** | 3 sites | 1 year | **$120** | **$300** | $180 |
| **Enterprise** | Unlimited | 1 year | **$350** | **$600** | $525 |

### Pricing Philosophy

1. **Lifetime License**: Plugin use is lifetime (per open-source guidelines)
2. **Annual Updates**: Updates subscription is sold for 1 or 3 years
3. **After Updates Expire**:
   - Plugin continues working (no lockout)
   - Updates disabled in WordPress dashboard
   - Notice shown: "Updates subscription expired. Renew for latest features and security patches."
4. **Site Limits**: Enforced per tier (Starter: 1, Pro: 3, Enterprise: Unlimited)

### Tier Features Comparison

| Feature | Starter | Pro | Enterprise |
|---------|---------|-----|------------|
| Sites | 1 | 3 | Unlimited |
| Languages | 5 | Unlimited | Unlimited |
| Translation Memory | ✅ | ✅ | ✅ |
| String Translation | ✅ | ✅ | ✅ |
| WPML Migration | ✅ | ✅ | ✅ |
| REST API | ✅ | ✅ | ✅ |
| Team Management | ❌ | ✅ | ✅ |
| Translation Workflow | ❌ | ✅ | ✅ |
| Audit Logging | ❌ | ✅ | ✅ |
| Priority Support | ❌ | ✅ | ✅ |
| 24/7 Support | ❌ | ❌ | ✅ |
| Dedicated Account Manager | ❌ | ❌ | ✅ |
| 99.9% Uptime SLA | ❌ | ❌ | ✅ |

---

## Architecture Overview

### System Components

```
┌─────────────────────────────────────────────────────────────┐
│                  Customer WordPress Site                     │
│  ┌───────────────────────────────────────────────────────┐  │
│  │   Multilingual Press Zone Plugin                      │  │
│  │   ┌─────────────────────────────────────────────┐     │  │
│  │   │  License Manager (PHP)                      │     │  │
│  │   │  - Validates license on activation          │     │  │
│  │   │  - Checks updates eligibility               │     │  │
│  │   │  - Shows expiration notices                 │     │  │
│  │   └─────────────────────────────────────────────┘     │  │
│  │   ┌─────────────────────────────────────────────┐     │  │
│  │   │  Licensing Page (Vanilla JS)                │     │  │
│  │   │  - Activation form                          │     │  │
│  │   │  - License details display                  │     │  │
│  │   │  - Manage subscription (redirect to Press)  │     │  │
│  │   └─────────────────────────────────────────────┘     │  │
│  └───────────────────────────────────────────────────────┘  │
└──────────────────────┬──────────────────────────────────────┘
                       │ HTTPS API Calls
                       │ Authorization: Bearer {api_key}
                       ▼
┌─────────────────────────────────────────────────────────────┐
│            api.press.zone (License Server)                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │   License API (Node.js + Express)                     │  │
│  │   - /v1/multilingual/license/activate                 │  │
│  │   - /v1/multilingual/license/status                   │  │
│  │   - /v1/multilingual/license/deactivate               │  │
│  │   - /v1/multilingual/updates/check                    │  │
│  │   - /v1/multilingual/updates/download                 │  │
│  │   └────────────────┬──────────────────────────────────┘  │
│  ┌───────────────────▼──────────────────────────────────┐  │
│  │   PostgreSQL Database                                │  │
│  │   - licenses (id, key, tier, status)                 │  │
│  │   - activations (site_url, activated_at)             │  │
│  │   - subscriptions (updates_until, stripe_sub_id)     │  │
│  └───────────────────┬──────────────────────────────────┘  │
│  ┌───────────────────▼──────────────────────────────────┐  │
│  │   Stripe Integration                                 │  │
│  │   - Webhooks for subscription events                 │  │
│  │   - Automatic renewal/cancellation                   │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                       ▲
                       │ Stripe Webhooks
                       │
┌──────────────────────┴──────────────────────────────────────┐
│                     Stripe                                   │
│   - Customer billing portal                                  │
│   - Subscription management                                  │
│   - Payment processing                                       │
└─────────────────────────────────────────────────────────────┘
```

---

## Backend: api.press.zone License Server

### Technology Stack

| Component | Technology | Version |
|-----------|-----------|---------|
| Runtime | Node.js | 20.x LTS |
| Framework | Express.js | 4.x |
| Database | PostgreSQL | 16.x |
| ORM | Prisma | 5.x |
| Authentication | JWT Bearer Tokens | - |
| Payment Gateway | Stripe | Latest |
| Hosting | Press.zone Server | - |

### Deployment Details

**Server**: Press.zone production server (details in `connect.sh`)  
**User**: `press`  
**Deployment Path**: `~/multilingual-press-zone-backend`  
**API Hostname**: `api.press.zone`  
**API Base Path**: `/v1/multilingual/`

### Database Schema (PostgreSQL + Prisma)

```prisma
// schema.prisma

model License {
  id                String         @id @default(cuid())
  licenseKey        String         @unique @map("license_key")
  tier              String         // "starter" | "pro" | "enterprise"
  status            String         // "active" | "expired" | "cancelled" | "suspended"
  sitesAllowed      Int            @map("sites_allowed")
  languagesAllowed  Int?           @map("languages_allowed") // null = unlimited
  
  // Timestamps
  createdAt         DateTime       @default(now()) @map("created_at")
  updatedAt         DateTime       @updatedAt @map("updated_at")
  
  // Stripe integration
  stripeCustomerId  String?        @map("stripe_customer_id")
  stripeSubId       String?        @unique @map("stripe_subscription_id")
  
  // Updates subscription
  updatesUntil      DateTime       @map("updates_until")
  
  // Owner info
  email             String
  name              String?
  
  // Relations
  activations       Activation[]
  
  @@map("licenses")
}

model Activation {
  id            String    @id @default(cuid())
  licenseId     String    @map("license_id")
  siteUrl       String    @map("site_url")
  siteName      String?   @map("site_name")
  
  // Activation details
  activatedAt   DateTime  @default(now()) @map("activated_at")
  lastSeenAt    DateTime  @default(now()) @map("last_seen_at")
  
  // WordPress environment
  wpVersion     String?   @map("wp_version")
  phpVersion    String?   @map("php_version")
  pluginVersion String?   @map("plugin_version")
  
  // Deactivation
  deactivatedAt DateTime? @map("deactivated_at")
  
  // Relations
  license       License   @relation(fields: [licenseId], references: [id], onDelete: Cascade)
  
  @@unique([licenseId, siteUrl])
  @@index([licenseId])
  @@map("activations")
}

model SubscriptionEvent {
  id            String    @id @default(cuid())
  stripeEventId String    @unique @map("stripe_event_id")
  licenseId     String?   @map("license_id")
  eventType     String    @map("event_type")
  eventData     Json      @map("event_data")
  processedAt   DateTime  @default(now()) @map("processed_at")
  
  @@index([licenseId])
  @@map("subscription_events")
}
```

### REST API Endpoints

#### 1. POST /v1/multilingual/license/activate

**Purpose**: Activate license on a WordPress site  
**Authentication**: None (license key in body)  
**Rate Limit**: 10 requests/minute per IP

**Request**:
```json
{
  "license_key": "MPZ-XXXX-XXXX-XXXX-XXXX",
  "site_url": "https://example.com",
  "site_name": "Example Website",
  "wp_version": "6.4",
  "php_version": "8.3",
  "plugin_version": "1.0.0"
}
```

**Response (Success)**:
```json
{
  "success": true,
  "data": {
    "api_key": "sk_live_xxxxxxxxxxxxxxxxxxxx",
    "tier": "pro",
    "status": "active",
    "sites_allowed": 3,
    "sites_used": 1,
    "languages_allowed": null,
    "updates_until": "2027-01-25T12:00:00Z",
    "features": {
      "team_management": true,
      "workflow": true,
      "audit_logging": true,
      "priority_support": true
    }
  }
}
```

**Response (Error - Already Activated)**:
```json
{
  "success": false,
  "error": {
    "code": "SITE_LIMIT_REACHED",
    "message": "This license is already activated on 3 sites. Upgrade to Pro or deactivate an existing site.",
    "active_sites": [
      {"url": "https://site1.com", "activated_at": "2026-01-01T10:00:00Z"},
      {"url": "https://site2.com", "activated_at": "2026-01-05T14:30:00Z"},
      {"url": "https://site3.com", "activated_at": "2026-01-10T09:15:00Z"}
    ]
  }
}
```

**Response (Error - Invalid License)**:
```json
{
  "success": false,
  "error": {
    "code": "INVALID_LICENSE",
    "message": "The license key is invalid or has been revoked."
  }
}
```

#### 2. GET /v1/multilingual/license/status

**Purpose**: Check license status and details  
**Authentication**: Bearer token (API key from activation)  
**Rate Limit**: 100 requests/minute

**Headers**:
```
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx
```

**Response**:
```json
{
  "success": true,
  "data": {
    "tier": "pro",
    "status": "active",
    "sites_allowed": 3,
    "sites_used": 2,
    "languages_allowed": null,
    "updates_until": "2027-01-25T12:00:00Z",
    "updates_expired": false,
    "days_until_expiration": 365,
    "features": {
      "team_management": true,
      "workflow": true,
      "audit_logging": true,
      "priority_support": true,
      "dedicated_account_manager": false,
      "sla": false
    },
    "active_sites": [
      {
        "url": "https://site1.com",
        "name": "Site 1",
        "activated_at": "2026-01-01T10:00:00Z",
        "last_seen_at": "2026-01-25T08:30:00Z",
        "plugin_version": "1.0.0"
      },
      {
        "url": "https://site2.com",
        "name": "Site 2",
        "activated_at": "2026-01-05T14:30:00Z",
        "last_seen_at": "2026-01-25T09:00:00Z",
        "plugin_version": "1.0.0"
      }
    ]
  }
}
```

#### 3. POST /v1/multilingual/license/deactivate

**Purpose**: Deactivate license from a site  
**Authentication**: Bearer token  
**Rate Limit**: 10 requests/minute

**Request**:
```json
{
  "site_url": "https://example.com"
}
```

**Response**:
```json
{
  "success": true,
  "message": "License deactivated from https://example.com"
}
```

#### 4. GET /v1/multilingual/updates/check

**Purpose**: Check if updates are available  
**Authentication**: Bearer token  
**Rate Limit**: 100 requests/minute

**Query Params**:
```
?current_version=1.0.0
```

**Response (Updates Available)**:
```json
{
  "success": true,
  "update_available": true,
  "latest_version": "1.1.0",
  "package_url": "https://api.press.zone/v1/multilingual/updates/download?token=xxxxx",
  "changelog": "### Version 1.1.0\n- Added support for RTL languages\n- Performance improvements\n- Bug fixes",
  "requires_php": "8.1",
  "requires_wp": "6.0",
  "tested_wp": "6.4"
}
```

**Response (No Updates)**:
```json
{
  "success": true,
  "update_available": false,
  "message": "You are running the latest version (1.0.0)"
}
```

**Response (Updates Expired)**:
```json
{
  "success": false,
  "error": {
    "code": "UPDATES_EXPIRED",
    "message": "Your updates subscription expired on 2026-01-01. Renew to receive updates.",
    "expired_at": "2026-01-01T00:00:00Z",
    "renewal_url": "https://press.zone/account/multilingual/renew"
  }
}
```

#### 5. GET /v1/multilingual/updates/download

**Purpose**: Download plugin update ZIP  
**Authentication**: Bearer token + one-time download token  
**Rate Limit**: 10 requests/hour

**Query Params**:
```
?token=xxxxx&version=1.1.0
```

**Response**: Binary ZIP file stream

---

## WordPress Plugin: License Manager

### PHP Class: includes/Licensing/LicenseManager.php

```php
<?php
declare(strict_types=1);

namespace MultilingualPressZone\Licensing;

if (!defined('ABSPATH')) {
    exit;
}

class LicenseManager {
    private const API_BASE = 'https://api.press.zone/v1/multilingual';
    private const OPTION_API_KEY = 'mpz_license_api_key';
    private const OPTION_LICENSE_DATA = 'mpz_license_data';
    private const OPTION_LAST_CHECK = 'mpz_license_last_check';
    
    /**
     * Activate license
     */
    public function activate(string $licenseKey): array {
        $siteUrl = get_site_url();
        $siteName = get_bloginfo('name');
        
        $response = wp_remote_post(self::API_BASE . '/license/activate', [
            'headers' => ['Content-Type' => 'application/json'],
            'body' => wp_json_encode([
                'license_key' => $licenseKey,
                'site_url' => $siteUrl,
                'site_name' => $siteName,
                'wp_version' => get_bloginfo('version'),
                'php_version' => PHP_VERSION,
                'plugin_version' => MPZ_VERSION,
            ]),
            'timeout' => 15,
        ]);
        
        if (is_wp_error($response)) {
            return [
                'success' => false,
                'error' => $response->get_error_message(),
            ];
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (!isset($body['success']) || !$body['success']) {
            return [
                'success' => false,
                'error' => $body['error']['message'] ?? 'Activation failed',
            ];
        }
        
        // Save API key and license data
        update_option(self::OPTION_API_KEY, $body['data']['api_key']);
        update_option(self::OPTION_LICENSE_DATA, $body['data']);
        update_option(self::OPTION_LAST_CHECK, current_time('timestamp'));
        
        return [
            'success' => true,
            'data' => $body['data'],
        ];
    }
    
    /**
     * Check license status
     */
    public function checkStatus(bool $forceRefresh = false): ?array {
        $apiKey = get_option(self::OPTION_API_KEY);
        if (!$apiKey) {
            return null;
        }
        
        // Use cached data if recent (< 24 hours)
        $lastCheck = get_option(self::OPTION_LAST_CHECK, 0);
        $cacheExpired = (current_time('timestamp') - $lastCheck) > DAY_IN_SECONDS;
        
        if (!$forceRefresh && !$cacheExpired) {
            return get_option(self::OPTION_LICENSE_DATA);
        }
        
        $response = wp_remote_get(self::API_BASE . '/license/status', [
            'headers' => [
                'Authorization' => 'Bearer ' . $apiKey,
                'Content-Type' => 'application/json',
            ],
            'timeout' => 15,
        ]);
        
        if (is_wp_error($response)) {
            // Return cached data on network error
            return get_option(self::OPTION_LICENSE_DATA);
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (!isset($body['success']) || !$body['success']) {
            return get_option(self::OPTION_LICENSE_DATA);
        }
        
        // Update cached data
        update_option(self::OPTION_LICENSE_DATA, $body['data']);
        update_option(self::OPTION_LAST_CHECK, current_time('timestamp'));
        
        return $body['data'];
    }
    
    /**
     * Deactivate license
     */
    public function deactivate(): array {
        $apiKey = get_option(self::OPTION_API_KEY);
        if (!$apiKey) {
            return ['success' => false, 'error' => 'No active license'];
        }
        
        $siteUrl = get_site_url();
        
        $response = wp_remote_post(self::API_BASE . '/license/deactivate', [
            'headers' => [
                'Authorization' => 'Bearer ' . $apiKey,
                'Content-Type' => 'application/json',
            ],
            'body' => wp_json_encode(['site_url' => $siteUrl]),
            'timeout' => 15,
        ]);
        
        if (is_wp_error($response)) {
            return ['success' => false, 'error' => $response->get_error_message()];
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (isset($body['success']) && $body['success']) {
            delete_option(self::OPTION_API_KEY);
            delete_option(self::OPTION_LICENSE_DATA);
            delete_option(self::OPTION_LAST_CHECK);
        }
        
        return $body;
    }
    
    /**
     * Check if feature is enabled for current license
     */
    public function hasFeature(string $feature): bool {
        $licenseData = $this->checkStatus();
        if (!$licenseData) {
            return false;
        }
        
        return $licenseData['features'][$feature] ?? false;
    }
    
    /**
     * Check if updates subscription is active
     */
    public function hasActiveUpdates(): bool {
        $licenseData = $this->checkStatus();
        if (!$licenseData) {
            return false;
        }
        
        return !($licenseData['updates_expired'] ?? true);
    }
    
    /**
     * Get license tier
     */
    public function getTier(): ?string {
        $licenseData = $this->checkStatus();
        return $licenseData['tier'] ?? null;
    }
    
    /**
     * Show admin notice if updates expired
     */
    public function showExpirationNotice(): void {
        if ($this->hasActiveUpdates()) {
            return;
        }
        
        $licenseData = $this->checkStatus();
        if (!$licenseData) {
            return;
        }
        
        $daysExpired = abs($licenseData['days_until_expiration'] ?? 0);
        
        ?>
        <div class="notice notice-warning is-dismissible">
            <p>
                <strong><?php esc_html_e('Multilingual Press Zone: Updates Expired', 'multilingual-press-zone'); ?></strong>
            </p>
            <p>
                <?php
                printf(
                    esc_html__('Your updates subscription expired %d days ago. The plugin continues to work, but you won\'t receive updates.', 'multilingual-press-zone'),
                    $daysExpired
                );
                ?>
            </p>
            <p>
                <a href="<?php echo esc_url(admin_url('admin.php?page=multilingual-press-zone#/licensing')); ?>" class="button button-primary">
                    <?php esc_html_e('Renew Subscription', 'multilingual-press-zone'); ?>
                </a>
            </p>
        </div>
        <?php
    }
}
```

---

## WordPress Updates Integration

### includes/Licensing/UpdateChecker.php

**Purpose**: Integrate with WordPress update system to show plugin updates when available.

```php
<?php
declare(strict_types=1);

namespace MultilingualPressZone\Licensing;

if (!defined('ABSPATH')) {
    exit;
}

class UpdateChecker {
    private LicenseManager $licenseManager;
    private string $pluginBasename;
    private string $pluginSlug;
    
    public function __construct(LicenseManager $licenseManager, string $pluginBasename) {
        $this->licenseManager = $licenseManager;
        $this->pluginBasename = $pluginBasename;
        $this->pluginSlug = dirname($pluginBasename);
        
        add_filter('pre_set_site_transient_update_plugins', [$this, 'checkForUpdates']);
        add_filter('plugins_api', [$this, 'pluginInfo'], 10, 3);
    }
    
    /**
     * Check for plugin updates
     */
    public function checkForUpdates($transient) {
        if (empty($transient->checked)) {
            return $transient;
        }
        
        // Don't check if updates subscription expired
        if (!$this->licenseManager->hasActiveUpdates()) {
            return $transient;
        }
        
        $currentVersion = $transient->checked[$this->pluginBasename] ?? MPZ_VERSION;
        
        $apiKey = get_option('mpz_license_api_key');
        if (!$apiKey) {
            return $transient;
        }
        
        $response = wp_remote_get(
            'https://api.press.zone/v1/multilingual/updates/check?current_version=' . $currentVersion,
            [
                'headers' => [
                    'Authorization' => 'Bearer ' . $apiKey,
                    'Content-Type' => 'application/json',
                ],
                'timeout' => 15,
            ]
        );
        
        if (is_wp_error($response)) {
            return $transient;
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (!isset($body['success']) || !$body['success'] || !$body['update_available']) {
            return $transient;
        }
        
        $updateData = $body;
        
        $transient->response[$this->pluginBasename] = (object) [
            'slug' => $this->pluginSlug,
            'new_version' => $updateData['latest_version'],
            'package' => $updateData['package_url'],
            'url' => 'https://press.zone/multilingual',
            'tested' => $updateData['tested_wp'],
            'requires_php' => $updateData['requires_php'],
        ];
        
        return $transient;
    }
    
    /**
     * Provide plugin info for update modal
     */
    public function pluginInfo($false, $action, $args) {
        if ($action !== 'plugin_information' || $args->slug !== $this->pluginSlug) {
            return $false;
        }
        
        $apiKey = get_option('mpz_license_api_key');
        if (!$apiKey) {
            return $false;
        }
        
        $response = wp_remote_get(
            'https://api.press.zone/v1/multilingual/updates/check?current_version=' . MPZ_VERSION,
            [
                'headers' => [
                    'Authorization' => 'Bearer ' . $apiKey,
                    'Content-Type' => 'application/json',
                ],
                'timeout' => 15,
            ]
        );
        
        if (is_wp_error($response)) {
            return $false;
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (!isset($body['success']) || !$body['success']) {
            return $false;
        }
        
        return (object) [
            'name' => 'Multilingual Press Zone',
            'slug' => $this->pluginSlug,
            'version' => $body['latest_version'],
            'author' => '<a href="https://press.zone">Press.zone</a>',
            'homepage' => 'https://press.zone/multilingual',
            'requires' => $body['requires_wp'],
            'tested' => $body['tested_wp'],
            'requires_php' => $body['requires_php'],
            'sections' => [
                'description' => 'Enterprise-grade multilingual plugin for WordPress.',
                'changelog' => $body['changelog'],
            ],
        ];
    }
}
```

---

## Stripe Integration

### Webhook Handling (api.press.zone)

**Endpoint**: `POST /v1/webhooks/stripe`

**Events to Handle**:

1. **customer.subscription.created**: New subscription purchased
2. **customer.subscription.updated**: Subscription upgraded/downgraded
3. **customer.subscription.deleted**: Subscription cancelled
4. **invoice.payment_succeeded**: Renewal payment successful
5. **invoice.payment_failed**: Renewal payment failed

**Example Handler** (Node.js/Express):

```typescript
// routes/webhooks.ts
import { Request, Response } from 'express';
import Stripe from 'stripe';
import { prisma } from '../lib/prisma';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;

export async function handleStripeWebhook(req: Request, res: Response) {
  const sig = req.headers['stripe-signature']!;
  
  let event: Stripe.Event;
  
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
  } catch (err) {
    console.error('Webhook signature verification failed:', err);
    return res.status(400).send('Webhook Error');
  }
  
  // Log event
  await prisma.subscriptionEvent.create({
    data: {
      stripeEventId: event.id,
      eventType: event.type,
      eventData: event.data.object as any,
    },
  });
  
  switch (event.type) {
    case 'customer.subscription.created':
      await handleSubscriptionCreated(event.data.object as Stripe.Subscription);
      break;
    
    case 'customer.subscription.updated':
      await handleSubscriptionUpdated(event.data.object as Stripe.Subscription);
      break;
    
    case 'customer.subscription.deleted':
      await handleSubscriptionDeleted(event.data.object as Stripe.Subscription);
      break;
    
    case 'invoice.payment_succeeded':
      await handlePaymentSucceeded(event.data.object as Stripe.Invoice);
      break;
    
    case 'invoice.payment_failed':
      await handlePaymentFailed(event.data.object as Stripe.Invoice);
      break;
  }
  
  res.json({ received: true });
}

async function handleSubscriptionCreated(subscription: Stripe.Subscription) {
  const licenseKey = subscription.metadata.license_key;
  
  const updatesUntil = new Date(subscription.current_period_end * 1000);
  
  await prisma.license.update({
    where: { licenseKey },
    data: {
      stripeSubId: subscription.id,
      updatesUntil,
      status: 'active',
    },
  });
}

async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
  const updatesUntil = new Date(subscription.current_period_end * 1000);
  
  await prisma.license.update({
    where: { stripeSubId: subscription.id },
    data: {
      updatesUntil,
      status: subscription.status === 'active' ? 'active' : 'expired',
    },
  });
}

async function handleSubscriptionDeleted(subscription: Stripe.Subscription) {
  await prisma.license.update({
    where: { stripeSubId: subscription.id },
    data: {
      status: 'cancelled',
    },
  });
}

async function handlePaymentSucceeded(invoice: Stripe.Invoice) {
  if (!invoice.subscription) return;
  
  const subscription = await stripe.subscriptions.retrieve(invoice.subscription as string);
  const updatesUntil = new Date(subscription.current_period_end * 1000);
  
  await prisma.license.update({
    where: { stripeSubId: subscription.id },
    data: {
      updatesUntil,
      status: 'active',
    },
  });
}

async function handlePaymentFailed(invoice: Stripe.Invoice) {
  if (!invoice.subscription) return;
  
  // Email customer about failed payment
  // Stripe handles retries automatically
}
```

---

## Customer Purchase Flow

### 1. Press.zone Marketing Website

**URL**: `https://press.zone/multilingual`

**Pricing Page**:
- Display 3 tiers (Starter, Pro, Enterprise)
- Show launch pricing with discount badge
- Feature comparison table
- "Buy Now" buttons for each tier

### 2. Stripe Checkout

**Flow**:
1. User clicks "Buy Now" → Stripe Checkout session created
2. User enters payment info on Stripe-hosted page
3. Stripe processes payment
4. Redirect to `https://press.zone/account/multilingual/success?session_id=xxx`

**Checkout Session** (Node.js):
```typescript
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function createCheckoutSession(tier: string, billingPeriod: 'yearly' | '3-year') {
  const priceId = getPriceId(tier, billingPeriod);
  
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    payment_method_types: ['card'],
    line_items: [{
      price: priceId,
      quantity: 1,
    }],
    success_url: 'https://press.zone/account/multilingual/success?session_id={CHECKOUT_SESSION_ID}',
    cancel_url: 'https://press.zone/multilingual',
    customer_email: req.body.email,
    metadata: {
      tier,
      billing_period: billingPeriod,
    },
  });
  
  return session.url;
}

function getPriceId(tier: string, billingPeriod: string): string {
  const priceIds = {
    starter_yearly: 'price_starter_50_1y',
    starter_3year: 'price_starter_115_3y',
    pro_yearly: 'price_pro_120_1y',
    pro_3year: 'price_pro_300_3y',
    enterprise_yearly: 'price_enterprise_350_1y',
    enterprise_3year: 'price_enterprise_600_3y',
  };
  
  return priceIds[`${tier}_${billingPeriod}`];
}
```

### 3. License Generation

**After Successful Payment**:
1. Stripe webhook fires `customer.subscription.created`
2. Backend generates license key: `MPZ-{TIER_CODE}-{RANDOM}`
3. License record created in database
4. Email sent to customer with license key

**License Key Format**:
```
MPZ-S-XXXX-XXXX-XXXX  (Starter)
MPZ-P-XXXX-XXXX-XXXX  (Pro)
MPZ-E-XXXX-XXXX-XXXX  (Enterprise)
```

### 4. Customer Receives Email

**Subject**: "Your Multilingual Press Zone License Key"

**Body**:
```
Hi [Customer Name],

Thank you for purchasing Multilingual Press Zone [Tier]!

Your license key: MPZ-X-XXXX-XXXX-XXXX

To activate:
1. Install Multilingual Press Zone plugin
2. Navigate to WordPress Admin → Multilingual → Licensing
3. Enter your license key and click "Activate"

Need help? Visit https://press.zone/docs/multilingual

Updates valid until: [Date]

Manage subscription: https://press.zone/account/multilingual

Best regards,
Press.zone Team
```

---

## Implementation Roadmap

### Phase 0: Backend Setup (Week 1)

- [ ] Set up Node.js project on `api.press.zone`
- [ ] Configure PostgreSQL database with Prisma
- [ ] Implement license activation endpoint
- [ ] Implement license status endpoint
- [ ] Implement license deactivation endpoint
- [ ] Set up Stripe integration (test mode)
- [ ] Create webhook handler for Stripe events

### Phase 1: WordPress Integration (Week 2)

- [ ] Create `LicenseManager.php` class
- [ ] Create `UpdateChecker.php` class
- [ ] Add licensing page to admin panel (Vanilla JS)
- [ ] Implement license activation UI
- [ ] Implement license status display
- [ ] Add admin notices for expired updates

### Phase 2: Stripe Configuration (Week 3)

- [ ] Create Stripe products (Starter, Pro, Enterprise)
- [ ] Create Stripe prices (1-year, 3-year, launch pricing)
- [ ] Configure webhook endpoint
- [ ] Test subscription lifecycle (create, renew, cancel)
- [ ] Set up customer billing portal

### Phase 3: Testing (Week 4)

- [ ] Test activation flow end-to-end
- [ ] Test site limit enforcement
- [ ] Test updates subscription expiration
- [ ] Test Stripe webhook events
- [ ] Test license deactivation
- [ ] Test tier feature gating

### Phase 4: Launch (Week 5)

- [ ] Deploy backend to production
- [ ] Enable Stripe live mode
- [ ] Launch marketing website with pricing page
- [ ] Announce launch to Press.zone community

---

## Security Considerations

### License Key Security

- **Generate cryptographically secure keys**: Use `crypto.randomBytes(32)`
- **Store hashed keys in database**: Use bcrypt or Argon2
- **Rate limit activation attempts**: 10/minute per IP

### API Security

- **JWT bearer tokens**: Expire after 30 days
- **HTTPS only**: Enforce TLS 1.3+
- **Input validation**: Validate all request data
- **SQL injection prevention**: Use Prisma ORM (parameterized queries)

### WordPress Security

- **Never expose API keys in JavaScript**: Store in wp_options
- **Nonce validation**: Use WordPress REST API nonces
- **Capability checks**: `current_user_can('manage_options')`

---

## Monitoring & Analytics

### Metrics to Track

1. **License Activations**: Track daily activations by tier
2. **Update Checks**: Monitor update check frequency
3. **Subscription Renewals**: Track renewal rate
4. **Churn Rate**: Track cancellations by tier
5. **Site Limit Hits**: Track how often users hit site limits

### Logging

- **License API logs**: Log all activation/deactivation events
- **Stripe webhook logs**: Log all webhook events for debugging
- **WordPress error logs**: Log license validation failures

---

## Support & Customer Service

### License Issues

1. **"License already activated"**: Provide list of active sites, offer deactivation
2. **"Updates expired"**: Show renewal link, explain plugin continues working
3. **"Invalid license key"**: Check for typos, verify purchase email

### Stripe Billing Issues

- Direct customers to Stripe customer portal: `https://billing.stripe.com/p/login/xxx`
- Customer can update payment method, view invoices, cancel subscription

---

## Related Documents

- `ADMIN-PANEL-ARCHITECTURE.md` - Licensing page UI implementation
- `API-DOCUMENTATION.md` - REST endpoint reference
- `PHASE0-INFRASTRUCTURE.md` - License server deployment
- `UI-UX-SPECIFICATIONS.md` - Licensing page design

---

## Summary

1. **Lifetime license + Annual updates subscription** (per open-source guidelines)
2. **Launch pricing**: $50/$120/$350 (1 year), $115/$300/$600 (3 years)
3. **Site limits**: 1/3/Unlimited
4. **Backend**: Node.js + PostgreSQL on `api.press.zone`
5. **Stripe integration**: Automatic renewals, webhooks
6. **WordPress plugin**: Validates license, checks updates, shows notices
7. **No plugin lockout**: Continues working after updates expire
8. **Manage subscription**: WordPress admin → Licensing page (redirects to Press.zone)
