You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel会议注册多步骤表单支付流程与代码架构咨询

Hey there! Let's work through this to keep your code clean and maintainable instead of piling everything into one controller method. Here's a structured approach tailored to your Laravel multi-step registration flow:

1. Split Controller Responsibilities

Instead of cramming all logic into RegistrationController, split things up to follow the single-responsibility principle:

  • Keep RegistrationController focused on handling step 1 (user info collection) and step 2 (payment method validation/selection routing)
  • Create a dedicated PaymentController to handle payment-specific actions (reference code generation, Stripe flows, third-party webhooks)
  • Extract core payment logic into a PaymentService class to keep controllers lightweight and reusable
2. Step 2 → Step 3 Flow Design

Here's a clear, user-friendly flow for both payment methods:

  1. User submits step 2 (payment method choice) via AJAX
  2. Backend validates the selection, then branches based on payment type:
    • Reference payment: Generate a unique reference code, link it to the registration, and return data to render step 3 (showing the code)
    • Credit card (Stripe): Initialize a Stripe checkout session, return the session ID to the frontend, and redirect the user to Stripe's payment page
  3. After payment completion:
    • Reference: Third-party service sends a webhook to your backend to mark the payment as paid, then trigger step 4
    • Stripe: Stripe redirects back to your success route, you create the payment record, then render step 4
3. Code Implementation Examples

3.1 Payment Service Class (Core Logic)

Create app/Services/PaymentService.php to encapsulate reusable payment logic:

namespace App\Services;

use App\Models\Registration;
use App\Models\Payment;
use Stripe\Stripe;
use Stripe\Checkout\Session;

class PaymentService
{
    // Generate unique reference code for offline payments
    public function generateReferenceCode(Registration $registration): string
    {
        $code = 'CONF-' . $registration->id . '-' . strtoupper(substr(md5(uniqid()), 0, 6));
        $registration->update(['reference_code' => $code]);
        return $code;
    }

    // Create Stripe checkout session
    public function createStripeSession(Registration $registration): Session
    {
        Stripe::setApiKey(config('services.stripe.secret'));

        return Session::create([
            'payment_method_types' => ['card'],
            'line_items' => [[
                'price_data' => [
                    'currency' => 'eur', // Adjust to your currency
                    'product_data' => ['name' => 'Conference Registration'],
                    'unit_amount' => $registration->price * 100, // Stripe uses cents
                ],
                'quantity' => 1,
            ]],
            'mode' => 'payment',
            'success_url' => route('payment.stripe.success', ['registration' => $registration->id]),
            'cancel_url' => route('payment.stripe.cancel', ['registration' => $registration->id]),
        ]);
    }

    // Create payment record in database
    public function createPaymentRecord(Registration $registration, string $methodId, string $status = 'paid'): Payment
    {
        return Payment::create([
            'price' => $registration->price,
            'payment_method_id' => $methodId,
            'registration_id' => $registration->id,
            'status' => $status,
        ]);
    }
}

3.2 RegistrationController (Step 2 Handling)

Update your existing controller to route payment choices to the right logic:

namespace App\Http\Controllers;

use App\Models\Registration;
use App\Services\PaymentService;
use Illuminate\Http\Request;

class RegistrationController extends Controller
{
    protected $paymentService;

    public function __construct(PaymentService $paymentService)
    {
        $this->paymentService = $paymentService;
    }

    // Handle step 2 payment method submission
    public function storePaymentMethod(Request $request)
    {
        $validated = $request->validate([
            'payment_method_id' => 'required|exists:payment_methods,id',
            'registration_id' => 'required|exists:registrations,id',
        ]);

        $registration = Registration::findOrFail($validated['registration_id']);
        $methodId = $validated['payment_method_id'];

        // Branch based on payment method
        if ($methodId === 1) { // Replace with your reference method ID
            $referenceCode = $this->paymentService->generateReferenceCode($registration);
            return response()->json([
                'success' => true,
                'next_step' => 3,
                'reference_code' => $referenceCode,
                'message' => 'Use this code to complete your payment via our partner service'
            ]);
        } elseif ($methodId === 2) { // Replace with your Stripe method ID
            $session = $this->paymentService->createStripeSession($registration);
            return response()->json([
                'success' => true,
                'next_step' => 'stripe',
                'session_id' => $session->id,
                'stripe_key' => config('services.stripe.key')
            ]);
        }

        return response()->json(['success' => false, 'message' => 'Invalid payment method'], 400);
    }
}

3.3 PaymentController (Payment Callbacks & Webhooks)

Create this controller to handle post-payment actions:

namespace App\Http\Controllers;

use App\Models\Registration;
use App\Services\PaymentService;
use Illuminate\Http\Request;
use Stripe\Webhook;

class PaymentController extends Controller
{
    protected $paymentService;

    public function __construct(PaymentService $paymentService)
    {
        $this->paymentService = $paymentService;
    }

    // Stripe payment success redirect
    public function stripeSuccess(Request $request)
    {
        $registration = Registration::findOrFail($request->registration);
        $this->paymentService->createPaymentRecord($registration, 2); // Stripe method ID
        return view('registration.success', compact('registration')); // Step 4 view
    }

    // Stripe payment cancel redirect
    public function stripeCancel(Request $request)
    {
        $registration = Registration::findOrFail($request->registration);
        return redirect()->route('registration.step2', ['registration' => $registration->id])
            ->with('error', 'Payment cancelled. Please try again.');
    }

    // Third-party reference payment webhook
    public function referenceWebhook(Request $request)
    {
        // Validate webhook signature (follow your third-party docs)
        $referenceCode = $request->input('reference_code');
        $registration = Registration::where('reference_code', $referenceCode)->firstOrFail();

        $this->paymentService->createPaymentRecord($registration, 1); // Reference method ID
        return response()->json(['status' => 'success']);
    }
}
4. Frontend Flow Notes
  • For reference payments: On receiving the step 3 response, toggle your UI to show the reference code and instructions
  • For Stripe payments: Use Stripe.js to redirect the user to checkout with the returned session_id
  • Optional: Add a polling mechanism on step 3 to check if the reference payment has been processed, then auto-navigate to step 4
5. Extra Optimizations
  • Add a status field to your registrations table (e.g., pending_payment, payment_completed) to track progress
  • Use Laravel queues to handle non-urgent tasks (like sending payment confirmation emails)
  • Secure Stripe webhooks with Laravel's built-in signature validation

内容的提问来源于stack exchange,提问作者johnW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:52:06