DEV Community

Datravous Odds
Datravous Odds

Posted on

Building My Startup: Implementing User Payouts with Firebase

Overview

Today, I completed the implementation of user profile data loading, including profile pictures, stats, personal information, and shipping details. The next phase focused on updating the client-side payment tab and integrating it with Firebase to manage payout history. Here's how I tackled the challenge.

Firebase Structure

The existing database structure handled wallet balances and upcoming payouts but lacked functionality for viewing complete payout history. Users needed the ability to view and filter their payout history, including:

  • Payment status

  • Transaction amount

  • Order ID

  • Processing date

To address this, I created a subcollection within the userProfiles collection. This approach ensures users can access their payment methods, activity, reviews, and payout history through a single authenticated route.

Creating the Payouts Subcollection

To organize payout data, I:

  1. Added a new subcollection named /payouts to the userProfiles collection.

  2. Implemented Firebase's auto-generated unique IDs for each payout record.

  3. Defined the following fields for each payout:

  • amount: Transaction value

  • date: Deposit date

  • orderId: Unique order identifier

  • processingDate: Transaction processing timestamp

  • status: Current state ("pending", "completed", "processing")

  • type: Payment method (credit card, debit card, online payment)

Firebase Rules Update

With the subcollection in place, I needed to update Firebase rules to grant users access to their payout data. Here's the updated rules configuration:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /userProfiles/{email} {
     allow read: if request.auth != null && request.auth.token.email == email;
     allow write: if request.auth != null && request.auth.token.email == email;


     match /{subcollection}/{document=**} {
            allow read, write: if request.auth != null && request.auth.token.email == email;

        }      
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • match /{subcollection}/{document=**}: Grants access to all documents within the subcollection.

  • allow read, write: Ensures only authenticated users can access their data.

  • Email Verification: Confirms the user's email matches the userProfiles document.

These rules ensure:

  • Users can only access their own data.

  • Subcollections (like payment information) are protected.

  • Unauthorized access attempts are blocked.

Loading Firebase Data To Client-side

After setting the rules correctly, now it was time to test, create the code to load the data from the backend to the frontend. I decide create async functon to call whenever the userData is loaded from the checkUserStatus(), once the data is loaded, then the function would run to have the data loaded, below is the code I implemented to do so.

async function loadProfileData() {
  try {
    const userData = await checkUserStatus();
    console.log("User Data imported:", userData);
    if (userData) {
      loadPaymentInfoData(userData);
    }
  } catch (error) {
    console.error("Error happened when loading userData from auth.js", error);
    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Implementing loadPaymentInfoData

The loadPaymentInfoData function handles loading and displaying payout information. Here's the breakdown:

async function loadPaymentInfoData(userData, filter = "all") {
  if (userData) {
    const walletData = userData.wallet;
    // load wallet info
    document.querySelector(
      "#act-wallet-balance"
    ).textContent = `$${walletData.balance.toFixed(2)}`;
    // ... rest of wallet data loading ...  
 try {
  const payoutsRef = collection(
        db,
        "userProfiles",
        userData.email,
        "payouts"
      );
      let baseQuery = payoutsRef;

      if (["pending", "completed", "processing"].includes(filter)) {
        baseQuery = query(payoutsRef, where("status", "==", filter));
      }

      const querySnapShot = await getDocs(baseQuery);
      const payoutDocs = Array.from(querySnapshot.docs);

      let completedPayouts = 0;
      let processingPayouts = 0;
      let pendingPayouts = 0;
      let totalPayoutAmount = 0;

      // filters by today, this-week, or this-month
      if (["today", "this-week", "this-month"].includes(filter)) {
      }

      const payoutItems = document.querySelectorAll(".payouts-item");
      payoutDocs.forEach((doc) => {
        if (doc.data().status == "completed") {
          completedPayouts++;
        } else if (doc.data().status == "pending") {
          pendingPayouts++;
        } else if (doc.data().status == "processing") {
          processingPayouts++;
        }

        totalPayoutAmount += doc.data().amount;
      });



      payoutDocs.forEach((doc, index) => {
        const currentItem = payoutItems[index];
        const data = doc.data();

        console.log("current index", currentItem);
        currentItem.querySelector(".payout-id").textContent = data.payoutId;
        // ... rest of payout data fields ...


      document.querySelector(
        "#totalPayoutAmount"
      ).textContent = `$${totalPayoutAmount}`;
      // ... rest of pending, complete, and processing payouts ...
    } catch (error) {
      console.error("Error occured when fetching payouts:", error);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

How It Works:

  1. Fetching Data: The function queries the /payouts subcollection based on the user's email and optional filters.

  2. Counting Payouts: It iterates through the payout documents to count completed, pending, and processing payouts.

  3. Updating the DOM: The function populates the client-side elements with the fetched data.

Challenges and Next Steps

While the implementation works, there are still a few areas to improve:

  • Filtering by Date: Implementing filters for "today," "this-week," and "this-month."

  • Dynamic Payout Loading: Generating payout items dynamically instead of relying on static elements.

  • Error Handling: Enhancing error handling for a smoother user experience.
    I'll cover these updates in my next post, so stay tuned!

Conclusion

Building the payout history feature was a challenging but rewarding experience. By leveraging Firebase's powerful tools and structuring the data effectively, I was able to create a seamless experience for users to track their transactions. As I continue to refine this feature, I'm excited to see how it enhances the overall user experience for my startup.

Top comments (0)