Wix Data Collection Update and Order Creation Issues After Successful Payment

Hi guys, im trying to make a drink order system that can be used in different shops, using the same qr code.

Question:

“We are experiencing issues updating our ‘StoresCollection’ and creating a new item in the ‘OrdersCollection’ after a successful payment is processed using the Wix Payments API. The payment flow appears to complete correctly, but the database updates are not being consistently applied. We are looking for assistance in identifying the errors or missing steps that is causing this issue.”

Product:
“Velo by Wix, Wix Editor”

What are you trying to achieve:

"Our goal is to create an e-commerce flow where customers can purchase drinks from various stores. When a customer clicks a button to purchase, we initiate a Wix Payment using wixPayFrontend.startPayment. Once the payment is successful, we need to do the following:

  1. Reduce Stock: Update the currentStock field in the ‘StoresCollection’ for the specific store the purchase came from, reducing the stock count by the quantity of drinks purchased.

  2. Increase Monthly Rebate: Update the monthly rebate based on 40% of the order amount.

  3. Create Order Record: Insert a new item into the ‘OrdersCollection’ that includes the following:

  • storeId (string)

  • orderDate (date/time)

  • quantity (number of drinks purchased)

  • paymentMethod (string)

  • totalAmount (total cost of the order)

  • transactionId (from the Wix Payment API result)

The frontend script runs on the page, and calls a web module in the backend called pay.web which uses wix-pay-backend. We are using wix-data for database interactions."

What have you already tried:

"We’ve already implemented the following steps based on Wix documentation and forum recommendations:

  • Backend (pay.web): Created a createMyPayment web method to create a Wix Payment object in the backend.

  • Frontend: We’re disabling all payment buttons immediately after one is clicked to avoid race conditions.

  • Stock Checking: We have a check to make sure that the stock exists before proceeding with the order.

  • wixData.update(): We are now using the correct _id of the item when using the wixData.update method.

  • Error Handling: Added try/catch blocks in our code to better handle any errors.

  • Data Modeling: We are following the Wix documentation to try and correctly configure our data collections, with the following fields in their respective collections:

    • StoresCollection:

      • storeId (text)

      • currentStock (number)

      • monthlyRebate (number)

    • OrdersCollection:

      • orderId (generated by Wix upon entry of data)

      • storeId (text)

      • orderDate (date/time)

      • quantity (number)

      • paymentMethod (text)

      • totalAmount (number)

      • transactionId (text)

    • customerUserId (not currently in use)

We’ve reviewed the following resources:

  • [Link to any Wix documentation pages about the Payments API, wix-data, web modules that you found helpful]

  • [Link to the Velo API documentation page for wixData.update()]

“We are working with a setup like this example:”

//backend/pay.web.js
import { Permissions, webMethod } from “wix-web-module”;
import wixPayBackend from “wix-pay-backend”;
export const createMyPayment = webMethod(Permissions.Anyone, async (amount, items) => {
try {
const payment = await wixPayBackend.createPayment({
items: items,
amount: amount,
});
return payment
} catch (error) {
console.error(“Error creating payment:”, error);
throw error;
}
});

//front-end code snippet

import wixLocation from ‘wix-location’;
import wixPayFrontend from ‘wix-pay-frontend’;
import { createMyPayment } from ‘backend/pay.web’;
import wixData from ‘wix-data’;

$w.onReady(async function () {
const successMessage = $w(“#successMessage”);
const errorMessage = $w(“#errorMessage”);
const paymentButtons = ;
const NUM_OF_BUTTONS = 10;
const PRICE_PER_DRINK = 100;

successMessage.hide();
errorMessage.hide();

// Function to disable all buttons
function disableAllPaymentButtons() {
    paymentButtons.forEach(button => button.disable());
}

// Function to enable all buttons based on stock
async function enablePaymentButtons() {
  const storeId = wixLocation.query.storeId;
  if (!storeId || typeof storeId !== 'string' || storeId.trim() === "") {
        console.error("Error: Invalid Store ID in URL parameters:", storeId);
         disablePaymentButtons("Invalid store ID.");
        return;
  }
   const currentStock = await getCurrentStock(storeId);
   paymentButtons.forEach((button, index) => {
        if(currentStock > index)
        {
          button.enable();
        }
        else{
           button.disable();
       }
    });
}
async function initializePayment() {
    let storeId;
    try {
        storeId = wixLocation.query.storeId;
        if (!storeId || typeof storeId !== 'string' || storeId.trim() === "") {
            console.error("Error: Invalid Store ID in URL parameters:", storeId);
            disablePaymentButtons("Invalid store ID.");
            return;
        }

        console.log("Store ID:", storeId);

        const storeExists = await checkStoreIdExists(storeId);
        if (!storeExists) {
            console.error("Error: Store ID not found in Stores Collection:", storeId);
            disablePaymentButtons("Store not found.");
            return;
        }
        for (let i = 1; i <= NUM_OF_BUTTONS; i++) {
            paymentButtons.push($w(`#paybutton${i}`));
        }

         const currentStock = await getCurrentStock(storeId);
        if (currentStock !== null) {
            disableButtonsBasedOnStock(currentStock);
        } else {
             disablePaymentButtons("Error retrieving stock.");
             return; // Stop initialization if getting stock failed.
        }

        for (let i = 0; i < paymentButtons.length; i++) {
          paymentButtons[i].onClick(async () => {
              disableAllPaymentButtons();
               const quantity = i + 1;
                let amount = quantity * PRICE_PER_DRINK;

                if (amount <= 0) {
                   showError("Invalid amount.");
                    console.error("Error: Calculated amount is invalid:", amount);
                   enablePaymentButtons();
                   return;
               }
               console.log("Starting payment for amount:", amount);
                errorMessage.hide();
               
               const stockCheck = await checkStock(storeId, quantity); //check stock first
                if (!stockCheck) {
                  showError("Not enough stock available.");
                    enablePaymentButtons();
                    return; // Exit if stock is insufficient
                }
                try {
                    const items = [{
                        name: "Drink",
                        price: PRICE_PER_DRINK,
                        quantity: quantity,
                    }];
                 
                   console.log("calling createMyPayment with", amount, items)
                  const payment = await createMyPayment(amount, items);
                   console.log("Payment object returned:", payment)

                    if (payment && payment.id) {
                     
                       console.log("payment id: ", payment.id);
                        const paymentResult = await wixPayFrontend.startPayment(payment.id);
                        console.log("Payment Result:", paymentResult);
                        if (paymentResult && paymentResult.status === 'Successful') {
                             console.log("Payment successful:", paymentResult);
                            try{
                               const orderId = await createNewOrder(storeId, quantity, amount, paymentResult.transactionId);
                                if (orderId) {
                                     const updateResult = await updateStoreInfo(storeId, quantity);
                                     if (updateResult) {
                                       wixLocation.to(`/customerdrinkorders/${orderId}`);
                                        successMessage.show("fade");
                                         setTimeout(() => {
                                             successMessage.hide("fade");
                                        }, 3000);
                                   } else {
                                         showError("Failed to update store information.");
                                        enablePaymentButtons();
                                   }
                               } else {
                                    showError("Failed to create order.");
                                    enablePaymentButtons();
                                 }

                            } catch (err) {
                                  showError("An error occurred during order creation or store update.");
                                    console.error("Error:", err);
                                    enablePaymentButtons();
                               }
                       } else {
                           showError("Payment unsuccessful.");
                            console.error("Payment failed:", paymentResult);
                           enablePaymentButtons();
                        }
                   } else {
                      showError("Payment unsuccessful.");
                       console.error("Payment failed:", payment);
                      enablePaymentButtons();
                   }
               } catch (err) {
                  showError("An error occurred during payment.");
                  console.error("Error starting payment:", err);
                enablePaymentButtons();
             }
          });
        }
    } catch (error) {
        console.error("Initialization error:", error);
        disablePaymentButtons("Error during initialization");
   }
}
    initializePayment();

// Helper Functions

    async function getCurrentStock(storeId) {
          try {
            const results = await wixData.query("StoresCollection")
                .eq("storeId", storeId)
                .find();

            if (results.items.length === 0) {
              console.error("Store not found with ID:", storeId);
              return null;
            }
             return results.items[0].currentStock;
        } catch (error) {
              console.error("Error getting current stock:", error);
              return null;
        }
    }


function disableButtonsBasedOnStock(currentStock) {
        for (let i = 0; i < paymentButtons.length; i++) {
            const buttonQuantity = i + 1;
            if (buttonQuantity > currentStock) {
             paymentButtons[i].disable();
            }
         }
}

async function checkStock(storeId, quantity) {
try {
const results = await wixData.query(“StoresCollection”)
.eq(“storeId”, storeId)
.find();

            if (results.items.length === 0) {
               console.error("Store not found with ID:", storeId);
               return false;
           }
           const currentItem = results.items[0];

            if (currentItem.currentStock < quantity) {
              console.log("Not enough stock.");
                return false;
            }
            return true;
        } catch (error) {
           console.error("Error checking stock:", error);
            return false;
      }
    }

    async function updateStoreInfo(storeId, quantity) {
      try {
        // Query to find the specific store
          const results = await wixData.query("StoresCollection")
            .eq("storeId", storeId)
            .find();
        // Check if a store with storeId exists
            if (results.items.length === 0) {
                console.error("Store not found with ID:", storeId);
                return false;
            }
        // Get the store item from the result
          const currentItem = results.items[0];
           // Calculate new stock and rebate
          const newStock = currentItem.currentStock - quantity;
          const newMonthlyRebate = currentItem.monthlyRebate + (quantity * PRICE_PER_DRINK * 0.4);

        const options = {
              suppressAuth: true
          };

           await wixData.update("StoresCollection", {
              _id: currentItem._id, // Ensure you are updating by item _id
              // You cannot update individual fields here. You must update the entire object
                 currentStock: newStock,
                 monthlyRebate: newMonthlyRebate,
            }, options);

         console.log("Store information updated.");
         return true;
      } catch (error) {
          console.error("Error updating store information:", error);
          return false;
      }
    }

    async function createNewOrder(storeId, quantity, amount, transactionId) {
     try {
            const itemToInsert = {
              storeId: storeId,
                orderDate: new Date(),
                quantity: quantity,
               paymentMethod: 'Pay Button by Wix',
               totalAmount: amount,
                transactionId: transactionId
            };
          const insertResult = await wixData.insert("OrdersCollection", itemToInsert);
           console.log("New order created.");
           return insertResult._id;
      } catch (error) {
         console.error("Error creating new order:", error);
            return null;
     }
    }

    async function checkStoreIdExists(storeId) {
      try {
         console.log("checkStoreIdExists called, with id:", storeId);
            const results = await wixData.query("StoresCollection")
              .eq("storeId", storeId)
               .find();
             console.log("checkStoreIdExists results:", results);
           return results.items.length > 0;
      } catch (error) {
            console.error("Error checking storeId existence:", error);
           return false;
     }
    }

    function disablePaymentButtons(message = "Payment buttons are disabled") {
       for (let i = 1; i <= NUM_OF_BUTTONS; i++) {
           $w(`#paybutton${i}`).disable();
        }
        if (message) showError(message);
    }

   function showError(msg) {
      errorMessage.text = msg;
      errorMessage.show("fade");
       setTimeout(() => {
            errorMessage.hide("fade");
        }, 5000);
   }

});

Additional Information:

“The issue is that payments process successfully, however the store information is not updated and new orders are not created. We’re looking for any guidance to fix this issue or any help from people who might have experience with a setup similar to this. Thank you in advance for any assistance you can provide.”