# Stripe Checkout Integration for WordPress Plugin

## Overview

This document outlines how to implement Stripe checkout functionality in the GutenGrow WordPress plugin, based on analysis of the existing implementation in the logic-cart-stripe project.

## Current Implementation in logic-cart-stripe

### Frontend (React)

1. **Cart Component**:

   - Manages cart items with product details
   - Handles currency selection
   - Calculates totals including upfront payments for subscriptions
   - Has a "Proceed to Checkout" button that calls `handleCheckout()`

2. **Checkout Process Flow**:

   - User adds products to cart
   - User clicks "Proceed to Checkout"
   - Frontend sends cart data to backend API
   - Backend creates Stripe checkout session
   - User is redirected to Stripe's hosted checkout page

3. **Key Frontend Code**:
   ```javascript
   const handleCheckout = async () => {
   	try {
   		setIsLoading(true);

   		// Prepare cart items with metadata
   		const cartItemsWithMetadata = cartItems.map((item) => {
   			if (item.type === "subscription" && productsMetadata[item.id]) {
   				return {
   					...item,
   					metadata: productsMetadata[item.id],
   				};
   			}
   			return item;
   		});

   		// Call backend API
   		const response = await axios.post("/api/create-checkout-session", {
   			cartItems: cartItemsWithMetadata,
   			currency: selectedCurrency || "usd",
   		});

   		// Redirect to Stripe
   		window.location.href = response.data.url;
   	} catch (err) {
   		console.error("Error creating checkout session:", err);
   		setError(
   			`Failed to create checkout session: ${
   				err.response?.data?.error || err.message
   			}`,
   		);
   		setIsLoading(false);
   	}
   };
   ```

### Backend (Node.js)

1. **API Endpoint**:

   - `/api/create-checkout-session` - Creates a Stripe checkout session

2. **Session Creation Logic**:

   - Validates cart items and currency
   - Transforms cart items into Stripe line items
   - Handles both one-time payments and subscriptions
   - Supports upfront payments for subscriptions
   - Creates checkout session via Stripe API
   - Returns checkout URL to frontend

3. **Key Backend Code**:
   ```javascript
   app.post("/api/create-checkout-session", async (req, res) => {
   	try {
   		const { cartItems, currency } = req.body;

   		// Validation and setup...

   		// Create line items for Stripe
   		const lineItems = validItems.map((item) => ({
   			price: item.priceId,
   			quantity: item.quantity,
   		}));

   		// Check if cart has subscription items
   		const hasSubscription = validItems.some(
   			(item) => item.type === "subscription",
   		);

   		// Create checkout session
   		const sessionOptions = {
   			payment_method_types: ["card"],
   			line_items: lineItems,
   			mode: hasSubscription ? "subscription" : "payment",
   			success_url: `${clientUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
   			cancel_url: `${clientUrl}/cart`,
   			allow_promotion_codes: true,
   			billing_address_collection: "required",
   		};

   		// Handle subscription features...

   		// Create session
   		const session = await stripe.checkout.sessions.create(sessionOptions);

   		// Return URL to frontend
   		res.json({ url: session.url });
   	} catch (error) {
   		console.error("Error creating checkout session:", error);
   		res.status(500).json({ error: error.message });
   	}
   });
   ```

## Implementation Plan for WordPress Plugin

### 1. Setup Requirements

- **Stripe PHP SDK** (already integrated)
- **WordPress REST API Endpoint** (already set up)
- **Frontend JavaScript** for the Stripe block

### 2. Backend Implementation (PHP)

1. **Create a new REST API endpoint**:

   ```php
   register_rest_route('gutengrow/v1', '/create-checkout-session', array(
     'methods' => 'POST',
     'callback' => 'gutengrow_create_checkout_session',
     'permission_callback' => function () {
       return true; // Public access or customize permissions
     }
   ));
   ```

2. **Checkout Session Creation Function**:
   ```php
   function gutengrow_create_checkout_session($request) {
     // Get parameters from request
     $params = $request->get_params();
     $cart_items = isset($params['cartItems']) ? $params['cartItems'] : [];
     $currency = isset($params['currency']) ? strtolower($params['currency']) : 'usd';

     // Validate cart
     if (empty($cart_items)) {
       return new WP_Error('empty_cart', 'Cart is empty', ['status' => 400]);
     }

     try {
       // Init Stripe with the API key
       $secret_key = 'sk_test_e9BuHow2PKdOqkM6bb025IQZ'; // Use your existing key
       \Stripe\Stripe::setApiKey($secret_key);

       // Format line items for Stripe
       $line_items = [];
       foreach ($cart_items as $item) {
         $line_items[] = [
           'price' => $item['priceId'],
           'quantity' => $item['quantity']
         ];
       }

       // Determine if cart has subscription products
       $has_subscription = false;
       foreach ($cart_items as $item) {
         if (isset($item['type']) && $item['type'] === 'subscription') {
           $has_subscription = true;
           break;
         }
       }

       // Get the site URL for success/cancel pages
       $site_url = get_site_url();

       // Create session options
       $session_options = [
         'payment_method_types' => ['card'],
         'line_items' => $line_items,
         'mode' => $has_subscription ? 'subscription' : 'payment',
         'success_url' => $site_url . '/checkout/success?session_id={CHECKOUT_SESSION_ID}',
         'cancel_url' => $site_url . '/cart',
         'allow_promotion_codes' => true,
         'billing_address_collection' => 'required',
       ];

       // Create the checkout session
       $session = \Stripe\Checkout\Session::create($session_options);

       // Return the session URL
       return rest_ensure_response([
         'success' => true,
         'url' => $session->url
       ]);
     } catch (\Exception $e) {
       return new WP_Error(
         'stripe_error',
         $e->getMessage(),
         ['status' => 500]
       );
     }
   }
   ```

### 3. Frontend Implementation (JavaScript)

1. **Add to Stripe Product Manager Frontend Component**:

   ```javascript
   // In your StripeProductManager.js or a new Cart.js component

   const [cartItems, setCartItems] = useState([]);
   const [isLoading, setIsLoading] = useState(false);
   const [error, setError] = useState(null);

   const handleAddToCart = (product) => {
   	setCartItems([
   		...cartItems,
   		{
   			id: product.id,
   			name: product.name,
   			price: product.price,
   			priceId: product.priceId,
   			quantity: 1,
   			type: product.type || "one-time",
   			currency: product.currency || "usd",
   		},
   	]);
   };

   const handleCheckout = async () => {
   	try {
   		setIsLoading(true);
   		setError(null);

   		// Call WordPress REST API endpoint
   		const response = await fetch(
   			gutengrow_data.rest_url + "create-checkout-session",
   			{
   				method: "POST",
   				headers: {
   					"Content-Type": "application/json",
   					"X-WP-Nonce": gutengrow_data.nonce,
   				},
   				body: JSON.stringify({
   					cartItems: cartItems,
   					currency: "usd", // Or allow currency selection
   				}),
   			},
   		);

   		const data = await response.json();

   		if (data.success && data.url) {
   			// Redirect to Stripe checkout
   			window.location.href = data.url;
   		} else {
   			throw new Error(data.error || "Failed to create checkout session");
   		}
   	} catch (err) {
   		console.error("Error creating checkout session:", err);
   		setError(err.message);
   		setIsLoading(false);
   	}
   };

   // Render checkout button
   <button
   	className="gutengrow-checkout-button"
   	onClick={handleCheckout}
   	disabled={isLoading || cartItems.length === 0}
   >
   	{isLoading ? "Processing..." : "Proceed to Checkout"}
   </button>;
   ```

### 4. Additional Requirements

1. **Success Page**: Create a page to handle successful payments:

   - Set up a page that reads the `session_id` parameter
   - Fetch session details from Stripe to confirm payment
   - Display order confirmation

2. **Cart UI Components**:

   - Implement cart display UI
   - Add quantity adjustment controls
   - Show price totals
   - Handle product removal

3. **Session Storage**:

   - Use WordPress transients or custom tables to store checkout sessions
   - Track order status for users

4. **WordPress Hooks**:
   - Add actions for successful payments to trigger other functionality
   - Create filters to customize checkout behavior

## Implementation Steps

1. Create the REST API endpoint for checkout sessions
2. Implement the server-side session creation logic
3. Create frontend cart and checkout components
4. Add success and cancel pages to handle redirects
5. Test with Stripe test mode before going live

## Security Considerations

1. **API Keys**: Never expose Stripe secret keys in client-side code
2. **Nonce Validation**: Always validate WordPress nonces for API requests
3. **Input Validation**: Validate all user inputs before processing
4. **HTTPS**: Ensure the site uses HTTPS for secure transactions
5. **PCI Compliance**: Use Stripe Checkout to avoid handling card data directly

## Handling Mixed Item Types in Stripe Checkout

One of the most challenging aspects of integrating Stripe checkout is handling mixed item types (one-time products and subscriptions) in a single checkout. Here's how this is implemented in the logic-cart-stripe project:

### 1. Dynamic Mode Selection

```javascript
// Check if cart contains subscription items
const hasSubscription = validItems.some((item) => item.type === "subscription");

// Create checkout session with appropriate mode
const sessionOptions = {
	// ... other options
	mode: hasSubscription ? "subscription" : "payment",
	// ... more options
};
```

Stripe checkout sessions must have a specific mode ('payment' for one-time payments or 'subscription' for subscriptions). The code dynamically selects the mode based on cart contents.

### 2. Special Handling for Mixed Carts

When the cart contains both one-time products and subscriptions:

- The mode is set to 'subscription' (required for any subscription products)
- For one-time products, they are handled in two ways:
  1. If upfront payments for subscriptions are enabled, one-time items are included as part of the subscription's initial payment
  2. Otherwise, a hybrid approach is used where one-time items are converted to "single invoice" line items

### 3. Line Item Transformation

```javascript
// For each one-time product in a subscription-mode checkout
if (hasSubscription && item.type !== "subscription") {
	const oneTimeItem = {
		price_data: {
			currency: validCurrency,
			product_data: {
				name: item.name,
				description: item.description || "One-time purchase",
			},
			unit_amount: item.price, // in cents
		},
		quantity: item.quantity,
		adjustable_quantity: { enabled: true },
	};

	// Add to a separate invoice_items array
	invoice_items.push(oneTimeItem);
}
```

### 4. Subscription Customization

For subscription items with upfront payments, the code modifies the subscription parameters to include additional charges in the first invoice. This allows handling subscription billing cycles with custom initial payments.

This approach provides a seamless checkout experience while working within Stripe's constraints that a checkout session can only be in one mode at a time.

# GutengGrow Blocks - Stripe Checkout Integration

This guide explains how to use and test the Stripe checkout functionality in the GutengGrow Blocks plugin.

## Overview

The Stripe checkout integration allows customers to purchase subscription plans through Stripe's secure checkout process. The integration supports both test mode (for development) and live mode (for production).

## Testing Environment

For development and testing purposes, we've created a comprehensive testing environment that allows you to test the entire checkout flow without requiring actual Stripe API keys or making real API calls.

### How to Enable Testing Mode

Testing mode is enabled by setting the `GUTENGROW_TESTING_MODE` constant to `true` in the `stripe-subscription-checkout.php` file:

```php
// Set this to true to enable testing mode and bypass Stripe API
define('GUTENGROW_TESTING_MODE', true);
```

When testing mode is enabled:

1. All Stripe API calls are bypassed
2. A simulated checkout experience is provided instead
3. Dummy session IDs and checkout URLs are generated
4. No actual payments are processed

### Using the Test Page

The plugin includes a standalone test page (`stripe-test.php`) that provides a complete testing environment:

1. Navigate to `/wp-content/plugins/gutengrow-blocks/stripe-test.php` in your browser
2. You'll see a pricing table with different subscription plans
3. Toggle between monthly and yearly billing
4. Click "Get Started" on any plan to test the checkout flow
5. A debug console at the bottom shows detailed information about each step

### Debugging

For more detailed debugging information, ensure the `GUTENGROW_DEBUG_MODE` constant is set to `true`:

```php
// Enable more verbose debugging
define('GUTENGROW_DEBUG_MODE', true);
```

This will provide detailed logs in both the PHP error log and the JavaScript console.

## Recent Improvements

We've made several improvements to the Stripe checkout functionality:

### 1. Product ID Handling

- Added automatic generation of product IDs from product names if none are provided
- This ensures the checkout process works even if product IDs are missing
- The format follows `prod_product_name_in_lowercase` with special characters replaced by underscores

### 2. API Key Handling

- Improved API key validation and trimming to prevent issues with whitespace
- Added better error messages when API keys are invalid
- Enhanced logging for API key-related issues

### 3. Testing Mode

- Created a comprehensive testing environment that bypasses actual Stripe API calls
- Added a simulated checkout experience that mimics the real Stripe checkout
- Implemented a standalone test page for easy debugging

### 4. Error Handling

- Improved error messages and debugging information
- Added more detailed logging throughout the checkout process
- Enhanced the user experience when errors occur

### 5. Frontend Improvements

- Improved the JavaScript code that handles the checkout process
- Added fallbacks for when data attributes are missing
- Enhanced the loading and error states during checkout

## How to Use in Production

When you're ready to use the Stripe checkout in production:

1. Set `GUTENGROW_TESTING_MODE` to `false` in `stripe-subscription-checkout.php`
2. Add your Stripe API keys in the WordPress admin settings
3. Test the integration with Stripe's test mode first (using test API keys)
4. Once everything is working, switch to Stripe's live mode with live API keys

### API Key Configuration

Configure your Stripe API keys in the WordPress admin under:

Settings > GutengGrow > Stripe Settings

Or manually set them as WordPress options:

```php
update_option('gutengrow_stripe_test_secret_key', 'sk_test_your_test_key');
update_option('gutengrow_stripe_live_secret_key', 'sk_live_your_live_key');
```

## Troubleshooting

### Common Issues

1. **"Invalid API Key" Error**

   - Ensure your API key is correctly entered without extra whitespace
   - Verify that you're using the correct key format (`sk_test_` for test mode, `sk_live_` for live mode)

2. **Product ID Required Error**

   - Make sure your products have valid IDs
   - If you can't add IDs, the plugin now generates them automatically

3. **JavaScript Errors**

   - Check the browser console for any JavaScript errors
   - Ensure all required data attributes are present on the button elements

4. **Redirect Issues**
   - Verify that your success and cancel URLs are valid
   - Check that Stripe's checkout domain isn't blocked by any security plugins

### Testing Tools

- Use the built-in test page (`stripe-test.php`) for comprehensive testing
- Enable debug mode to get detailed error messages
- Check the PHP error log for backend issues
- Use browser developer tools to monitor network requests and JavaScript errors

## Integration Example

Here's a simple example of a button that triggers the Stripe checkout:

```html
<a
	href="#"
	class="stripe-action-button"
	data-product-name="Pro Plan"
	data-price="19.99"
	data-product-id="pro_plan"
	data-period="monthly"
	onclick="window.handleStripeCheckout(this); return false;"
>
	Get Started
</a>
```

Required data attributes:

- `data-product-name`: The name of the product
- `data-price`: The price of the product
- `data-product-id` (optional): The Stripe product ID
- `data-period`: Either "monthly" or "yearly"

## Next Steps

Future improvements planned for the Stripe integration:

1. Add support for one-time payments
2. Implement webhooks for subscription management
3. Create a customer portal for subscription management
4. Add support for additional payment methods
5. Enhance the testing environment with more options
