Key takeaways

  • Wix is the only one of the three that can run the whole integration itself, with Members Area log-ins, Velo backend code, the Secrets Manager, and an HTTP function for postbacks.
  • Webflow disabled User Accounts on all sites on January 29, 2026, so a Webflow offerwall needs a third-party log-in plus server code on Webflow Cloud or your own backend.
  • Squarespace has member areas but no server-side code, so it cannot sign offerwall links or verify postbacks without a separate backend you control.

Can you add an offerwall to Wix, Webflow or Squarespace?

Yes on Wix, with some work on Webflow, and not on Squarespace alone. An offerwall link has to carry the ID of the signed-in user and an HMAC-SHA256 hash of that ID, made on a server with a secret salt, and rewards are credited when your server receives a signed postback. Wix gives you member log-ins, backend code, a secrets store, and public HTTP endpoints, so the whole integration fits inside the site. Webflow and Squarespace need help from outside.

This guide gives a per-platform verdict, complete Wix Velo code for the Moonetix offerwall, and the honest options for the other two. Platform facts were checked on November 5, 2026, against each builder’s own documentation.

What any site builder must be able to do

The offerwall is the same on every platform. What changes is where your code runs. Four things have to happen:

  1. Know who the user is. Rewards need an account to land in, so only signed-in users get a link. The user ID must never change for the same person.
  2. Sign the link on a server. The link is https://wall.moonetix.com/{APP_ID}?user_id={USER_ID}&hash={HASH}, where the hash is the HMAC-SHA256 of the user ID with your link hash salt, as lowercase hex. The salt must stay on the server. Anyone who has it can open the offerwall as any of your users.
  3. Receive postbacks. A public URL on your server gets a GET request for every credit (status 1) and reversal (status 2), with the same transaction ID for both.
  4. Verify and store. Check the postback hash with your postback secret key, credit each transaction once, and remember the last status. Prevent duplicate reward credits explains why that matters.

A builder that only runs code in the browser fails step 2. That is the line between the three platforms.

Wix vs Webflow vs Squarespace: the verdicts

NeedWixWebflowSquarespace
Built-in member log-inYes, Members AreaNo. User Accounts disabled on all sites since January 29, 2026Yes, Member Sites
Server-side codeYes, Velo backend (Node.js runtime)Only in a separate Webflow Cloud app or your own serverNo, code injection runs in the browser
Place for the saltSecrets Manager, backend onlySecret environment variables in Webflow Cloud, or your serverNone on the platform
Postback endpointYes, HTTP functionsWebflow Cloud API route or your serverNone on the platform
VerdictGood fit, all on WixPossible with a third-party log-in and server codeNot a fit without your own backend and log-in

Wix: the full setup with Velo

Wix has every piece. Members sign up and log in through the Members Area. Velo backend code runs on what Wix’s About the Site Backend page calls a Node.js server-side runtime. Wix’s Secrets Manager page says secrets are reachable only by backend code, and frontend code cannot reach them. Turn on coding for your site in the editor before you start.

Step 1: store both keys as secrets

In the site dashboard, open the Secrets Manager and add two secrets with the values from your app’s Integration tab: MOONETIX_LINK_SALT and MOONETIX_POSTBACK_SECRET. They are different keys. Do not paste either one into page code, a Custom Code snippet, an Embed HTML element, or a public file. Those all reach the browser.

Create backend/sharklio.web.js. Wix’s web modules page says .web.js files replace the older .jsw files, which still work. The method reads the logged-in member with currentMember.getMember() and the salt with getSecretValue(), which Wix’s getSecretValue reference calls through elevate() and warns to use only in backend code:

import { Permissions, webMethod } from 'wix-web-module';
import { currentMember } from 'wix-members-backend';
import { secrets } from 'wix-secrets-backend.v2';
import { elevate } from 'wix-auth';
import crypto from 'crypto';

const appId = 'your_app_id';
const getSecretValue = elevate(secrets.getSecretValue);

export const getWallLink = webMethod(Permissions.SiteMember, async () => {
  let member;
  try {
    member = await currentMember.getMember();
  } catch (error) {
    return null;
  }
  if (!member) return null;
  const userId = member._id;
  const linkHashSalt = await getSecretValue('MOONETIX_LINK_SALT');
  const hash = crypto.createHmac('sha256', linkHashSalt).update(String(userId)).digest('hex');
  return `https://wall.moonetix.com/${appId}?user_id=${encodeURIComponent(userId)}&hash=${hash}`;
});

Why these choices:

  • Permissions.SiteMember means only logged-in members can call the method, and getMember() returns the member making the call, so nobody can ask for someone else’s link.
  • The member _id is the user ID. It is a UUID that never changes and fits the allowed characters (letters, numbers, and . _ @ : + -, up to 100). The backend getMember() throws when nobody is logged in, which the try handles.
  • The hash line matches the Node.js example in the Moonetix docs. Only the finished link leaves the backend, never the salt.

Step 3: an Earn button on a members page

Add a page for members, place a button on it with the ID earnButton, and add this page code. It sets the button’s link to the signed link and its target to _blank, both documented on Wix’s Button reference:

import { getWallLink } from 'backend/sharklio.web';
import { authentication } from 'wix-members-frontend';

function setUp() {
  getWallLink()
    .then((link) => {
      if (link) {
        $w('#earnButton').label = 'Earn coins';
        $w('#earnButton').link = link;
        $w('#earnButton').target = '_blank';
      } else {
        $w('#earnButton').label = 'Log in to earn coins';
        $w('#earnButton').onClick(() => {
          authentication.promptLogin().then(() => setUp()).catch(() => {});
        });
      }
    })
    .catch(() => {
      $w('#earnButton').label = 'Log in to earn coins';
    });
}

$w.onReady(function () {
  setUp();
});

A direct link in a new tab works everywhere. Embedding is stricter: for safety, the offerwall only loads inside frames and pop-ups on https pages of the website you registered for the app and its subdomains. If you try Wix’s HTML iframe element instead of a button and the frame stays blank, keep the button. The floating Earn button needs the signed link in the page’s HTML, which a static Wix Custom Code snippet cannot provide per member, so the Velo button is the simpler entry point on Wix. Add a floating Earn button covers that option for sites that render their own HTML.

Step 4: an HTTP function that credits postbacks

Wix’s Expose an API guide puts HTTP functions in backend/http-functions.js. A function named get_moonetixPostback answers GET requests at https://www.yoursite.com/_functions/moonetixPostback on a premium site, or https://username.wixsite.com/sitename/_functions/moonetixPostback on a free one. Create two collections first: MoonetixTx (field status, number) and Points (field balance, number). Then add:

import { ok, forbidden } from 'wix-http-functions';
import { secrets } from 'wix-secrets-backend.v2';
import { elevate } from 'wix-auth';
import wixData from 'wix-data';
import crypto from 'crypto';

const getSecretValue = elevate(secrets.getSecretValue);
const options = { suppressAuth: true };

export async function get_moonetixPostback(request) {
  const { tx = '', user = '', reward = '0', status = '', hash = '' } = request.query;
  const postbackSecretKey = await getSecretValue('MOONETIX_POSTBACK_SECRET');
  const expected = crypto.createHmac('sha256', postbackSecretKey)
    .update(`${tx}:${user}:${reward}:${status}`)
    .digest('hex');
  const given = Buffer.from(String(hash));
  const wanted = Buffer.from(expected);
  if (given.length !== wanted.length || !crypto.timingSafeEqual(given, wanted)) {
    return forbidden();
  }
  const s = Number(status);
  const row = await wixData.get('MoonetixTx', tx, options);
  const last = row ? row.status : 0;
  if (s === 1 && last !== 1 && last !== 2) {
    await addToBalance(user, Number(reward));
    await wixData.save('MoonetixTx', { _id: tx, status: 1 }, options);
  } else if (s === 2 && last === 1) {
    await addToBalance(user, -Number(reward));
    await wixData.save('MoonetixTx', { _id: tx, status: 2 }, options);
  }
  return ok();
}

async function addToBalance(userId, amount) {
  const row = await wixData.get('Points', userId, options);
  const balance = (row ? row.balance : 0) + amount;
  await wixData.save('Points', { _id: userId, balance }, options);
}

In your app’s Postback tab, enter the URL with the macros this code reads:

https://www.yoursite.com/_functions/moonetixPostback?user={user_id}&tx={transaction_id}&status={status}&reward={reward}&hash={hash}
  • The hash check is the one from the Moonetix postback docs: transaction ID, user ID, reward, and status joined with colons, signed with the postback secret key, compared in constant time. Hash the values exactly as they arrive.
  • The transaction ID is the item _id, so a retried postback finds its row and is skipped. A reversal only takes points back if that transaction was credited.
  • Use the exact final URL. Moonetix does not follow redirects, so if your site sends the bare domain to www, put the www address in the Postback tab. Answer within 6 seconds; this handler does three small database calls.
  • Read and write is not atomic. For a busy site, move balances into one place you update carefully, and read Offerwall postbacks in Node.js for the general pattern.

Webflow: what is left after User Accounts

Webflow no longer has native member log-ins. Webflow’s Deprecating Logic and User Accounts update set January 29, 2026 as the end date and names Outseta and Memberstack as its top recommended partners. Webflow’s developer changelog confirms that as of that date User Accounts functionality is disabled on all Webflow sites, and the User Accounts APIs and webhooks are removed. Any guide that tells you to read the logged-in user from Webflow itself is out of date.

Webflow pages and custom code embeds are sent to the browser, so they cannot hold the salt. What remains is a two-part setup:

  1. A log-in from a third party. The provider must let a server confirm who the user is. Memberstack, for example, documents a verifyToken method in its Node.js admin package that checks a member token and returns the member ID. Use that stable ID as user_id.
  2. Server code you control. One option is Webflow Cloud. Webflow’s Bring your own app guide describes Next.js or Astro apps that run on an Edge runtime, are served from a mount path such as /app on your site’s domain, and keep secret environment variables. It also warns that variables inlined at build time, such as NEXT_PUBLIC_*, end up in browser JavaScript, so never give the salt one of those names. The other option is your own server on a subdomain.

The server code then does what the Wix backend does: verify the user’s token, sign the link, and expose a postback route. Webflow notes that the Edge runtime has limited API support, so check that your HMAC code runs there, or use the Web Crypto API. Offerwall in Next.js and React has route code you can adapt. If you would rather not run any server code, Webflow is not a fit for a rewards program today.

Squarespace: member areas, but no backend

Squarespace has the log-in part. Its Member Sites article describes pages hidden behind a free or paid membership. What it lacks is server-side code. Squarespace’s code injection article covers HTML and JavaScript added to pages, available on the Core, Plus, Advanced, and some legacy plans (checked on November 5, 2026), and that code runs in the visitor’s browser.

That leaves two problems you cannot solve inside Squarespace:

  • No safe place for the salt. A salt in injected JavaScript is readable by every visitor, and a link signed in the browser can be changed to any user ID.
  • No trusted user ID. We found no documented way in Squarespace’s Help Center for an outside server to confirm which member is signed in. Without that, even your own backend cannot know whose link to sign or whose balance to credit.

Our verdict: do not run an offerwall on Squarespace members alone. If your audience is worth it, put the earn area in a small app on a subdomain with its own log-in and backend, and link to it from your Squarespace navigation. That app then follows any standard guide, such as Offerwall PHP integration for Laravel.

Never do this on any builder

Paste the link hash salt or the postback secret key into page code, an embed block, a header snippet, a theme setting, or a public file. Hard-code one user’s signed link into a page that every member sees. Show the offerwall to logged-out visitors. Credit points from a browser event instead of a verified postback. If a secret has leaked, rotate your keys and link salt.

Test before you announce it

  1. Check a link. Log in as a test member, copy the link your button opens, and paste it into Check a link in Integration status on the Integration tab. It must match what Test a link builds for the same user ID. The offerwall link builder does the same check.
  2. Open it from a normal connection. The offerwall blocks VPNs, proxies, Tor, and data center networks, so test from home Wi-Fi or mobile data.
  3. Send test postbacks. Use Send a test postback with your test member’s ID, once with status 1 and once with status 2. Test transaction IDs start with test_. Confirm the Points balance goes up and back down, and that Reports, Postbacks shows a 200 answer. The postback hash checker helps when you get 403s.
  4. Clean up. Remove test points before launch.

Test your app before launch has the full checklist, and postback security explains why the hash check is not optional.

How Moonetix fits a site-builder site

Moonetix is a pay-per-result affiliate network. Every publisher application is reviewed by hand, and once you are approved, every app you add is live right away. Each app sets its own currency name and icon, conversion rate, user split, rounding, and wall design with a live preview. On Wix, the signed link and the HTTP function above are the whole integration; on Webflow or Squarespace, your own backend does the same job, or you can use the Offers API from it. Every credit and reversal arrives as a signed server-to-server postback, and reports show clicks, conversions, and reversals by country, offer, and sub ID. See how the wall works or get in touch.

Frequently asked questions

Do I need a premium Wix plan for this?

Wix documents HTTP function URLs for both premium sites and free wixsite.com sites, so the code itself works on either. Check Wix’s current plan pages for any limits on Members Area features or your own use of the site.

Can I use the Wix member’s email as the user ID?

No. Emails can change, which would split a user’s offers and rewards, and the ID appears in your reports and postbacks. Use the member _id.

Why does Wix need two secrets?

The link hash salt signs offerwall links, and the postback secret key verifies postbacks. They are different values, and mixing them up is the most common cause of a Link not valid page or a postback hash mismatch.

Can I still use Webflow User Accounts?

No. Webflow’s developer changelog says User Accounts functionality has been disabled on all Webflow sites as of January 29, 2026. You need a third-party log-in and server code for an offerwall.

Is there a Squarespace extension or code snippet that does this?

Not safely. Any snippet that runs in the page would expose the salt or let users change the user ID. The workable route is a separate app with its own backend, linked from your Squarespace site.

Which platform is easiest if I am starting fresh?

Of these three, Wix, because members, backend code, secrets, and the postback endpoint live in one place. If you already run your own server, How to add an offerwall to WordPress or a framework guide may suit you better.