Unified Payment Confirmation Flow

Posted by

Combining both flows allows for a comprehensive payment confirmation system where users can submit screenshots of payments to vendors, and both vendors and admins have roles in reviewing and approving/rejecting payments. Here’s how to design and implement the system:


Unified Payment Confirmation Flow

Overview

  1. User submits payment proof: Users upload payment screenshots along with payment details.
  2. Vendor reviews payments: Vendors review payments made to them and approve/reject the payments.
  3. Admin oversight (optional): Admins can view all payments and override vendor decisions if necessary.

1. Database Setup

Create a payments table that associates payments with users, vendors, and tracks the approval status.

Migration for payments Table:

php artisan make:migration create_payments_table

Migration Code:

Schema::create('payments', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('user_id'); // User who made the payment
    $table->unsignedBigInteger('vendor_id'); // Vendor receiving the payment
    $table->string('transaction_id')->nullable();
    $table->string('payment_method')->nullable();
    $table->string('screenshot_path'); // Screenshot of payment
    $table->string('status')->default('pending'); // pending, approved, rejected
    $table->unsignedBigInteger('approved_by')->nullable(); // Admin or vendor who approved
    $table->timestamps();
});

Run the migration:

php artisan migrate

2. Models

Payment Model:

Add relationships for users, vendors, and approvers.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Payment extends Model
{
    protected $fillable = [
        'user_id',
        'vendor_id',
        'transaction_id',
        'payment_method',
        'screenshot_path',
        'status',
        'approved_by',
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function vendor()
    {
        return $this->belongsTo(User::class, 'vendor_id');
    }

    public function approver()
    {
        return $this->belongsTo(User::class, 'approved_by');
    }
}

3. User Payment Submission

Payment Form Blade Template (resources/views/payment.blade.php):

<form method="POST" action="{{ route('payment.store') }}" enctype="multipart/form-data">
    @csrf
    <div>
        <label for="vendor">Select Vendor:</label>
        <select id="vendor" name="vendor_id" required>
            @foreach($vendors as $vendor)
                <option value="{{ $vendor->id }}">{{ $vendor->name }}</option>
            @endforeach
        </select>
    </div>
    <div>
        <label for="transaction_id">Transaction ID (optional):</label>
        <input type="text" id="transaction_id" name="transaction_id">
    </div>
    <div>
        <label for="payment_method">Payment Method:</label>
        <select id="payment_method" name="payment_method" required>
            <option value="bank_transfer">Bank Transfer</option>
            <option value="upi">UPI</option>
            <option value="other">Other</option>
        </select>
    </div>
    <div>
        <label for="screenshot">Upload Screenshot:</label>
        <input type="file" id="screenshot" name="screenshot" accept="image/*" required>
    </div>
    <button type="submit">Submit Payment</button>
</form>

Payment Submission Controller:

namespace App\Http\Controllers;

use App\Models\Payment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;

class PaymentController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'vendor_id' => 'required|exists:users,id',
            'payment_method' => 'required|string',
            'screenshot' => 'required|image|max:2048',
        ]);

        $screenshotPath = $request->file('screenshot')->store('payment_screenshots', 'public');

        Payment::create([
            'user_id' => Auth::id(),
            'vendor_id' => $request->vendor_id,
            'transaction_id' => $request->transaction_id,
            'payment_method' => $request->payment_method,
            'screenshot_path' => $screenshotPath,
            'status' => 'pending',
        ]);

        // Notify the vendor
        $vendor = User::find($request->vendor_id);
        $vendor->notify(new PaymentSubmittedNotification(Auth::user()));

        return redirect()->back()->with('success', 'Payment submitted successfully.');
    }
}

4. Vendor Dashboard

Vendor Payment List Blade (resources/views/vendor/payments.blade.php):

<h1>Payments Received</h1>
<table border="1">
    <thead>
        <tr>
            <th>User</th>
            <th>Transaction ID</th>
            <th>Payment Method</th>
            <th>Screenshot</th>
            <th>Status</th>
            <th>Actions</th>
        </tr>
    </thead>
    <tbody>
        @foreach($payments as $payment)
        <tr>
            <td>{{ $payment->user->name }}</td>
            <td>{{ $payment->transaction_id }}</td>
            <td>{{ $payment->payment_method }}</td>
            <td><a href="{{ asset('storage/' . $payment->screenshot_path) }}" target="_blank">View Screenshot</a></td>
            <td>{{ $payment->status }}</td>
            <td>
                <form method="POST" action="{{ route('payment.update', $payment->id) }}">
                    @csrf
                    @method('PUT')
                    <button name="status" value="approved">Approve</button>
                    <button name="status" value="rejected">Reject</button>
                </form>
            </td>
        </tr>
        @endforeach
    </tbody>
</table>

Vendor Controller:

namespace App\Http\Controllers;

use App\Models\Payment;
use Illuminate\Support\Facades\Auth;

class VendorController extends Controller
{
    public function payments()
    {
        $payments = Payment::where('vendor_id', Auth::id())->get();

        return view('vendor.payments', compact('payments'));
    }

    public function update(Request $request, Payment $payment)
    {
        if ($payment->vendor_id !== Auth::id()) {
            abort(403, 'Unauthorized action.');
        }

        $payment->update([
            'status' => $request->status,
            'approved_by' => Auth::id(),
        ]);

        return redirect()->back()->with('success', 'Payment status updated successfully.');
    }
}

5. Admin Oversight

Admins can review all payments and override vendor decisions if necessary.

Admin Dashboard Controller:

public function payments()
{
    $payments = Payment::all(); // Retrieve all payments
    return view('admin.payments', compact('payments'));
}

public function update(Request $request, Payment $payment)
{
    $payment->update([
        'status' => $request->status,
        'approved_by' => Auth::id(),
    ]);

    return redirect()->back()->with('success', 'Payment status updated successfully.');
}

Final Flow

  1. User: Submits payment details and screenshot.
  2. Vendor: Reviews payments made to them and approves/rejects them.
  3. Admin (Optional): Reviews all payments and overrides decisions if necessary.
  4. Notifications: Users and vendors are notified of status changes.

This combined flow ensures that both vendors and admins can manage payments efficiently while keeping users informed.

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x