# Charge for submitting data to a dataset

**URL:** <https://forum.wixstudio.com/t/charge-for-submitting-data-to-a-dataset/79386>\
**Category:** Ask the community\
**Tags:** wix-members-area, code, wix-stores\
**Created:** [August 18, 2026, 9:51pm UTC](https://forum.wixstudio.com/t/charge-for-submitting-data-to-a-dataset/79386 "2026-08-18T21:51:02Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![John-Dunham](https://sea1.discourse-cdn.com/wix/user_avatar/forum.wixstudio.com/john-dunham/32/122072_2.png) [@John-Dunham](https://forum.wixstudio.com/u/John-Dunham)\
**Post date:** [August 18, 2026, 9:51pm UTC](https://forum.wixstudio.com/t/charge-for-submitting-data-to-a-dataset/79386/1 "2026-08-18T21:51:02Z")

</div>

**I’m having trouble with**  
Charging customers for submitting data from input fields into a dataset

**Working in**  
Wix Dev

**Site link**

> **[Free Business Listing | Thermop OnLine](https://thermoponline.wixsite.com/website/free-business-listing)**

**What I’m trying to do**  
When a customer submits input fields to a dataset, they need to be charged for storing that data. i.e. charging for a classified ad in a newspaper.

**What I’ve tried so far**  
Built the custom form with CaptCHA to successfully submit the data. Now I need to charge the customer before the input is submitted.

---

<div class="post-metadata">

**Author:** ![NightsunCreative](https://sea1.discourse-cdn.com/wix/user_avatar/forum.wixstudio.com/nightsuncreative/32/7872_2.png) [@NightsunCreative](https://forum.wixstudio.com/u/NightsunCreative)\
**Post date:** [August 21, 2026, 4:36am UTC](https://forum.wixstudio.com/t/charge-for-submitting-data-to-a-dataset/79386/2 "2026-08-21T04:36:01Z")

</div>

A good workaround could be a members-only page when they go submit content to the dataset.

---

<div class="post-metadata">

**Author:** ![Christo\_Wilken](https://sea1.discourse-cdn.com/wix/user_avatar/forum.wixstudio.com/christo_wilken/32/121779_2.png) [@Christo\_Wilken](https://forum.wixstudio.com/u/Christo_Wilken)\
**Post date:** [August 24, 2026, 1:53pm UTC](https://forum.wixstudio.com/t/charge-for-submitting-data-to-a-dataset/79386/3 "2026-08-24T13:53:50Z")

</div>

A dataset can’t gate on payment, because the write happens in the browser where the visitor controls it. It has to come from backend code instead.

Set the collection’s Add permission to Admin, so visitors can’t write to it at all. Backend code still can, using `suppressAuth`, which only works from backend code. Keep the price there too rather than passing it up from the page, since anything the browser sends can be edited on the way.

The page calls a backend function, which writes the row as `unpaid` and creates a payment. The page opens checkout with that payment id, and a backend event flips the row to `paid` when the transaction succeeds. Filter whatever displays the listings on `status` equals `paid`. The row does exist before payment, but nobody can see it.

This goes in a backend file called `listings.web.js`:

```javascript
import { webMethod, Permissions } from 'wix-web-module';
import wixData from 'wix-data';
import wixPayBackend from 'wix-pay-backend';

const COLLECTION = 'Listings';

// Server side on purpose.
const LISTING_PRICE = 120.0;
const LISTING_CURRENCY = 'USD';
const LISTING_NAME = 'Business listing (1 year)';

// Only these fields are accepted from the page.
const ALLOWED_FIELDS = ['firstName', 'lastName', 'businessName', 'category', 'email', 'phone'];

const backendOnly = { suppressAuth: true };

function clean(submission) {
  const out = {};
  for (const field of ALLOWED_FIELDS) {
    const value = submission[field];
    if (typeof value === 'string') {
      out[field] = value.trim().slice(0, 300);
    }
  }
  return out;
}

function validate(fields) {
  const missing = ['firstName', 'lastName', 'businessName', 'email']
    .filter((field) => !fields[field]);
  if (missing.length > 0) {
    throw new Error(`Missing required fields: ${missing.join(', ')}`);
  }
  if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(fields.email)) {
    throw new Error('That email address does not look valid.');
  }
}

export const submitListing = webMethod(Permissions.Anyone, async (submission) => {
  const fields = clean(submission || {});
  validate(fields);

  const row = await wixData.insert(COLLECTION, {
    ...fields,
    status: 'unpaid',
    submittedAt: new Date(),
  }, backendOnly);

  const payment = await wixPayBackend.createPayment({
    items: [{ name: LISTING_NAME, price: LISTING_PRICE, quantity: 1 }],
    amount: LISTING_PRICE,
    currency: LISTING_CURRENCY,
    userInfo: {
      firstName: fields.firstName,
      lastName: fields.lastName,
      email: fields.email,
      phone: fields.phone,
    },
  });

  // Keep the payment id on the row so the payment event can find it later.
  await wixData.update(COLLECTION, { ...row, paymentId: payment.id }, backendOnly);

  return { paymentId: payment.id };
});

```

This goes in the backend `events.js` file, merged with anything already there:

```javascript
import wixData from 'wix-data';

const COLLECTION = 'Listings';
const backendOnly = { suppressAuth: true };

export async function wixPay_onPaymentUpdate(event) {
  const paymentId = event.payment.id;
  const status = event.status;

  const found = await wixData.query(COLLECTION)
    .eq('paymentId', paymentId)
    .limit(1)
    .find(backendOnly);

  if (found.items.length === 0) {
    console.warn(`No listing found for payment ${paymentId}`);
    return;
  }

  const row = found.items[0];

  if (status === 'Successful') {
    row.status = 'paid';
    row.paidAt = new Date();
    row.transactionId = event.transactionId;
  } else {
    row.status = status.toLowerCase();
  }

  await wixData.update(COLLECTION, row, backendOnly);
}

```

The page code picks up at the click, with your own element IDs swapped in:

```javascript
import { submitListing } from 'backend/listings.web';
import wixPayFrontend from 'wix-pay-frontend';

$w.onReady(() => {
  $w('#submitButton').onClick(async () => {
    $w('#submitButton').disable();
    $w('#statusText').text = 'One moment...';

    try {
      const { paymentId } = await submitListing({
        firstName: $w('#firstName').value,
        lastName: $w('#lastName').value,
        businessName: $w('#businessName').value,
        category: $w('#category').value,
        email: $w('#email').value,
        phone: $w('#phone').value,
      });

      const result = await wixPayFrontend.startPayment(paymentId);

      if (result.status === 'Successful') {
        $w('#statusText').text = 'Thanks, your listing is paid for and will appear shortly.';
      } else if (result.status === 'Pending') {
        $w('#statusText').text = 'Your payment is still processing. Your listing goes live once it clears.';
        $w('#submitButton').enable();
      } else if (result.status === 'Failed') {
        $w('#statusText').text = 'That payment did not go through.';
        $w('#submitButton').enable();
      } else if (result.status === 'Cancelled') {
        $w('#statusText').text = 'Payment cancelled. Your details are saved.';
        $w('#submitButton').enable();
      }
    } catch (error) {
      $w('#statusText').text = error.message || 'Something went wrong.';
      $w('#submitButton').enable();
    }
  });
});

```

Your captcha already gates the submit button, so none of this touches that.

The result from `startPayment()` is only good for showing a message, since it comes back through the browser. The docs say the same, that business decisions like updating collection data belong in the event:

> **[On Payment Update | Velo](https://dev.wix.com/docs/velo/apis/wix-pay-backend/events/on-payment-update)**
>
> An event that fires when a payment's transaction status is changed. The onPaymentUpdate() event handler runs when a payment's transaction status has changed.

> **[Create Payment | Velo](https://dev.wix.com/docs/velo/apis/wix-pay-backend/create-payment)**
>
> Creates a new payment. Before using the createPayment() function, you need to set up your site to

Accept Payments needs a Premium or Studio plan that supports payments, so on the free plan none of this can take a payment until the site is upgraded. Backend events also don’t fire in Preview, so save and publish before testing, or the row just sits at `unpaid`. Turning on the manual payment method lets you test without charging yourself.

> **[Accepting Payments: An Overview | Help Center | Wix.com](https://support.wix.com/en/article/about-accepting-payments)**
>
> Accept payments for goods or services directly through your site. Wix offers different payment methods and providers depending on your location. In most countri

I’ve checked this against the current API reference, but I haven’t run it against a live payments account. You will need to change the element IDs and field names to match yours.
