购物车多商品结账API开发问题:数组传递与构造
Hey there! Let's break down how to fix those two issues you're facing with your multi-item checkout API. I'll walk you through each step with concrete code examples based on what you've shared.
1. 通过POST请求传递多商品数组
Right now your form only sends one product's data, so we need to adjust the input field names to let PHP automatically parse multiple items into an array. The trick is to use array-style naming for your product fields, like products[][product_id] instead of just product_id. This way, when you submit the form, all product data will come through as a single array in the request.
修改后的表单示例(支持多商品)
If you're rendering pre-selected cart items, your form might look like this (example with 2 items):
<form action="/test/api/checkoutItem" method="POST"> <!-- User ID stays as a hidden field --> <input type="hidden" name="user_id" id="user_id" value="11647748"> <!-- First product --> <input type="hidden" name="products[][cart_items_id]" value="55"> <input type="hidden" name="products[][product_id]" value="70"> <input type="number" name="products[][qty]" value="1"> <!-- Second product --> <input type="hidden" name="products[][cart_items_id]" value="56"> <input type="hidden" name="products[][product_id]" value="71"> <input type="number" name="products[][qty]" value="2"> <!-- Stripe fields (if needed) --> <input type="hidden" name="stripeToken" value="..."> <input type="hidden" name="stripeEmail" value="user@example.com"> <button type="submit">Checkout All Items</button> </form>
If you want users to add/remove items dynamically, you can use JavaScript to clone these product field groups as needed—just make sure the naming stays products[][...] for each field in the group.
2. 构造目标格式的数组
Once the form is submitted, your controller will receive $request->user_id and $request->products (an array of all selected products). You can easily map this to the format you need.
构造目标数组的代码示例
// Get user ID from request $userId = $request->user_id; // Initialize the checkout data array with user ID $checkoutArray = ['user_id' => $userId]; // Fetch product details from database (better than trusting form data!) foreach ($request->products as $index => $item) { $product = \App\Product::find($item['product_id']); // Add product data to the checkout array in your target format $checkoutArray[$index] = [ 'product_id' => $item['product_id'], 'product_name' => $product->name, 'product_desc' => $product->description, 'product_price' => $product->price ]; } // Now $checkoutArray matches your target format!
Note: I'm fetching product details from the database instead of using form values here—this is critical for security, since users could manipulate form data to change prices or product names.
3. 修改控制器处理多商品结账
Your current controller only handles one item, so let's update it to loop through all products, validate each cart item, and create a single Stripe charge for all items (or multiple charges if needed—though a single charge is better for checkout flow). Also, I'll fix the SQL injection risk in your original code (never use whereRaw with unescaped user input!).
修改后的控制器代码
public function checkoutItem(Request $request) { $userId = $request->user_id; $products = $request->products; try { $student = \App\User::findOrFail($userId); Stripe::setApiKey(config('services.stripe.secret')); // Calculate total amount for all items $totalAmount = 0; $metadata = [ 'user_id' => $userId, 'fullname' => $student->first_name . " " . $student->last_name, 'email' => $student->email ?? $request->stripeEmail ]; // Validate each cart item and build charge details foreach ($products as $item) { // Check if cart item exists and is in "Added" status (SAFE parameter binding!) $cartItemExists = \DB::table('cart_items') ->where('id', $item['cart_items_id']) ->where('product_id', $item['product_id']) ->where('status_id', 1) ->exists(); if (!$cartItemExists) { return ['error' => "Cart item {$item['cart_items_id']} is invalid or not available for checkout"]; } $product = \App\Product::findOrFail($item['product_id']); $itemTotal = ($product->price * 100) * $item['qty']; // Convert to cents $totalAmount += $itemTotal; // Add item to metadata $metadata["product_{$item['product_id']}_id"] = $item['product_id']; $metadata["product_{$item['product_id']}_name"] = $product->name; $metadata["product_{$item['product_id']}_qty"] = $item['qty']; $metadata["product_{$item['product_id']}_price"] = $product->price; } // Create Stripe charge if (is_null($student->stripe_id)) { // Create Stripe customer if none exists $customer = Customer::create([ 'email' => $request->stripeEmail, 'source' => $request->stripeToken ]); // Update user's Stripe ID \App\User::where('id', $userId)->update(['stripe_id' => $customer->id]); $customerId = $customer->id; $receiptEmail = $request->stripeEmail; } else { $customerId = $student->stripe_id; $receiptEmail = $student->email; } // Create single charge for all items Charge::create([ 'customer' => $customerId, 'description' => 'Multi-item checkout', 'amount' => $totalAmount, 'currency' => 'aud', 'receipt_email' => $receiptEmail, 'metadata' => $metadata ]); // Update all cart items to "Checkout" status foreach ($products as $item) { \DB::table('cart_items') ->where('id', $item['cart_items_id']) ->update([ 'quantity' => $item['qty'], 'status_id' => 2, 'updated_at' => \Carbon\Carbon::now()->toDateTimeString() ]); } return ['success' => true, 'message' => "All products were successfully purchased"]; } catch (\Stripe\Error\Card $e) { return ['error' => $e->getMessage()]; } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) { return ['error' => "User or product not found"]; } catch (\Exception $e) { return ['error' => $e->getMessage()]; } }
关键改进说明
- SQL Injection Fix: Replaced
whereRawwith parameter-boundwhereclauses to prevent malicious input from altering your database queries. - Multi-item Handling: Loops through all submitted products, validates each cart item, calculates the total amount, and creates a single Stripe charge.
- Security: Fetches product details from the database instead of relying on form data to prevent price tampering.
- Error Handling: Added more specific exception catches to return meaningful error messages.
内容的提问来源于stack exchange,提问作者Novice

