feat: Implement OAuth license activation flow

- Add LicenseConnect.tsx focused OAuth confirmation page in customer SPA
- Add /licenses/oauth/validate and /licenses/oauth/confirm API endpoints
- Update App.tsx to render license-connect outside BaseLayout (no header/footer)
- Add license_activation_method field to product settings in Admin SPA
- Create LICENSING_MODULE.md with comprehensive OAuth flow documentation
- Update API_ROUTES.md with license module endpoints
This commit is contained in:
Dwindi Ramadhana
2026-01-31 22:22:22 +07:00
parent d80f34c8b9
commit a0b5f8496d
23 changed files with 3218 additions and 806 deletions

View File

@@ -0,0 +1,212 @@
# Product Create/Update Flow Audit Report
**Date:** 2026-01-29
**Scope:** Full trace of product creation, update, SKU validation, variation handling, virtual product setting, and customer-facing add-to-cart
---
## Executive Summary
**Total Issues Found: 4**
- **CRITICAL:** 2
- **WARNING:** 1
- **INFO:** 1
---
## Critical Issues
### 🔴 Issue #1: SKU Validation Blocks Variation Updates
**Severity:** CRITICAL
**Location:** [ProductsController.php#L1009](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Api/ProductsController.php#L1009)
**Problem:**
When updating a variable product, the `save_product_variations` method sets SKU unconditionally:
```php
if (isset($var_data['sku'])) $variation->set_sku($var_data['sku']);
```
WooCommerce validates that SKU must be unique across all products. When updating a variation that already has that SKU, WooCommerce throws an exception because it sees the SKU as a duplicate.
**Root Cause:**
WooCommerce's `set_sku()` method checks for uniqueness but doesn't know the variation already owns that SKU during the update.
**Fix Required:**
Before setting SKU, check if the new SKU is the same as the current SKU:
```php
if (isset($var_data['sku'])) {
$current_sku = $variation->get_sku();
$new_sku = $var_data['sku'];
// Only set if different (to avoid WC duplicate check issue)
if ($current_sku !== $new_sku) {
$variation->set_sku($new_sku);
}
}
```
---
### 🔴 Issue #2: Variation Selection Fails (Attribute Format Mismatch)
**Severity:** CRITICAL
**Location:**
- Backend: [ShopController.php#L363-365](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Frontend/ShopController.php#L363)
- Frontend: [Product/index.tsx#L97-127](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/customer-spa/src/pages/Product/index.tsx#L97)
**Problem:**
"Please select all product options" error appears even when a variation is selected.
**Root Cause:**
Format mismatch between backend API and frontend matching logic:
| Source | Format | Example |
|--------|--------|---------|
| API `variations.attributes` | `attribute_pa_color: "red"` | Lowercase, prefixed |
| API `attributes` | `name: "Color"` | Human-readable |
| Frontend `selectedAttributes` | `Color: "Red"` | Human-readable, case preserved |
The matching logic at lines 100-120 has complex normalization but may fail at edge cases:
- Taxonomy attributes use `pa_` prefix (e.g., `attribute_pa_color`)
- Custom attributes use direct prefix (e.g., `attribute_size`)
- The comparison normalizes both sides but attribute names in `selectedAttributes` are human-readable labels
**Fix Required:**
Improve variation matching by normalizing attribute names consistently:
```typescript
// In find matching variation logic:
const variation = (product.variations as any[]).find(v => {
return Object.entries(selectedAttributes).every(([attrName, attrValue]) => {
const normalizedAttrName = attrName.toLowerCase();
const normalizedValue = attrValue.toLowerCase();
// Try all possible attribute key formats
const possibleKeys = [
`attribute_${normalizedAttrName}`,
`attribute_pa_${normalizedAttrName}`,
normalizedAttrName
];
for (const key of possibleKeys) {
if (key in v.attributes) {
return v.attributes[key].toLowerCase() === normalizedValue;
}
}
return false;
});
});
```
---
## Warning Issues
### 🟡 Issue #3: Virtual Product Setting May Not Persist for Variable Products
**Severity:** WARNING
**Location:** [ProductsController.php#L496-498](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Api/ProductsController.php#L496)
**Problem:**
User reports cannot change product to virtual. Investigation shows:
- Admin-SPA correctly sends `virtual: true` in payload
- Backend `update_product` correctly calls `$product->set_virtual()`
- However, for variable products, virtual status may need to be set on each variation
**Observation:**
The backend code at lines 496-498 handles virtual correctly:
```php
if (isset($data['virtual'])) {
$product->set_virtual((bool) $data['virtual']);
}
```
**Potential Issue:**
WooCommerce may ignore parent product's virtual flag for variable products. Each variation may need to be set as virtual individually.
**Fix Required:**
When saving variations, also propagate virtual flag:
```php
// In save_product_variations, after setting other fields:
if ($product->is_virtual()) {
$variation->set_virtual(true);
}
```
---
## Info Issues
### Issue #4: Missing Error Handling in Add-to-Cart Backend
**Severity:** INFO
**Location:** [CartController.php#L202-203](file:///Users/dwindown/Local%20Sites/woonoow/app/public/wp-content/plugins/woonoow/includes/Api/Controllers/CartController.php#L202)
**Observation:**
When `add_to_cart()` returns false, the error message is generic:
```php
if (!$cart_item_key) {
return new WP_Error('add_to_cart_failed', 'Failed to add product to cart', ['status' => 400]);
}
```
WooCommerce may have more specific notices in `wc_notice` stack that could provide better error messages.
**Enhancement:**
```php
if (!$cart_item_key) {
$notices = wc_get_notices('error');
$message = !empty($notices) ? $notices[0]['notice'] : 'Failed to add product to cart';
wc_clear_notices();
return new WP_Error('add_to_cart_failed', $message, ['status' => 400]);
}
```
---
## Code Flow Summary
### Product Update Flow
```mermaid
sequenceDiagram
Admin SPA->>ProductsController: PUT /products/{id}
ProductsController->>WC_Product: set_name, set_sku, etc.
ProductsController->>WC_Product: set_virtual, set_downloadable
ProductsController->>ProductsController: save_product_variations()
ProductsController->>WC_Product_Variation: set_sku (BUG: no duplicate check)
WC_Product_Variation-->>WooCommerce: validate_sku()
WooCommerce-->>ProductsController: Exception (duplicate SKU)
```
### Add-to-Cart Flow
```mermaid
sequenceDiagram
Customer SPA->>Product Page: Select variation
Product Page->>useState: selectedAttributes = {Color: "Red"}
Product Page->>useEffect: Find matching variation
Note right of Product Page: Mismatch: API has attribute_pa_color
Product Page-->>useState: selectedVariation = null
Customer->>Product Page: Click Add to Cart
Product Page->>Customer: "Please select all product options"
```
---
## Files to Modify
| File | Change |
|------|--------|
| `ProductsController.php` | Fix SKU check in `save_product_variations` |
| `Product/index.tsx` | Fix variation matching logic |
| `ProductsController.php` | Propagate virtual to variations |
| `CartController.php` | (Optional) Improve error messages |
---
## Verification Plan
After fixes:
1. Create a variable product with SKU on variations
2. Edit the product without changing SKU → should save successfully
3. Add products to cart → verify variation selection works
4. Test virtual product setting on simple and variable products