Automating VIP Payments (PayPal/Stripe to Server Flags)

December 18, 2025 Daemon666 9 min read 10 views

Selling VIP by hand — a player PayPals you, you SSH in and edit users.ini — works for five customers and collapses at fifty. The goal is a pipeline where a completed payment grants VIP with no admin in the loop and revokes it when it lapses. This explains the architecture that actually works on GoldSrc, the pieces you need, and the SteamID mistake that silently breaks the whole thing. It is a design guide, not a copy-paste script, because the payment side is yours to build safely.

1. Understand the pipeline

There is no way for PayPal to write to a CS 1.6 server directly. Every working setup routes through a database that both your website and your server can read:

  1. Player pays on your site and provides their SteamID.
  2. The payment provider calls your webhook to confirm the payment completed.
  3. Your webhook writes a VIP row — SteamID, tier, expiry date — into a MySQL table.
  4. An AMX Mod X plugin on the server reads that table when the player connects and grants the VIP flags.

The database is the bridge. The website never touches the game server; the game server never touches PayPal. They only share the table.

2. Build the VIP table

Keep it minimal and keyed by SteamID:

CREATE TABLE vip_users (
  steamid   VARCHAR(32) PRIMARY KEY,
  tier      VARCHAR(16) NOT NULL,
  expires   DATETIME NOT NULL,
  active    TINYINT DEFAULT 1
);

The expires column is what lets VIP lapse automatically — the plugin only grants access while expires is in the future. No cron job needs to delete rows; the plugin simply checks the date on connect.

3. Handle the webhook, not the redirect

The single most important payment rule: grant VIP from the provider's server-to-server webhook (PayPal IPN/webhooks, Stripe webhook events), never from the browser redirect after checkout. A redirect can be forged or replayed by anyone; the webhook is signed and comes from the provider. On Stripe, verify the event signature; on PayPal, verify the notification against their API before you trust it. Only after the payment is verified completed do you INSERT or update the vip_users row. This is a security boundary, not an optimisation — skipping it means anyone who reaches your success URL gets free VIP.

4. Read the table from a plugin

On the server, an AMX Mod X plugin using the sqlx module queries the table when a player connects and, if a valid unexpired row exists, adds the VIP flags for that session. The correct way to run that query without freezing the server is a threaded query — see threaded SQL queries, because a blocking query on connect stalls everyone. The flag-granting itself uses the standard admin flag system; our CSB VIP Menu and the build a VIP plugin guide show the flag logic, and the flags reference explains what each letter grants. Grant flags per session on connect rather than writing them to users.ini, so an expired VIP simply stops being granted with no file edit.

5. The SteamID trap

The pipeline lives or dies on one join: the SteamID the player typed on your website must match the SteamID the server sees. Two things break this. First, format — a player might give STEAM_0:1:12345 while your server, running Reunion for non-Steam clients, reports a different ID scheme; store and compare in one canonical form. Second, non-Steam ID collisions — if two non-Steam players end up with the same ID, one buys VIP and the other gets it free. This is why a paid VIP system is far safer on a Steam-only server, or on a non-Steam server only once Reunion is configured to hand out stable IDs. Always confirm the exact ID the server sees with the SteamID finder before debugging anything else — a mismatched ID looks exactly like "the payment did not work."

Common errors

  • Player paid but has no VIP in-game — the SteamID on the site does not match what the server sees. Compare both in one format.
  • Anyone can get free VIP — you granted from the browser redirect, not the verified webhook. Move the grant behind signature verification.
  • Server lags every time a VIP connects — a blocking SQL query on connect. Switch to a threaded query.
  • VIP never expires — the plugin ignores the expires column, or grants persist by writing users.ini. Grant per session and check the date on connect.
  • Two players share one VIP — colliding non-Steam IDs. Configure Reunion for stable IDs or restrict paid VIP to Steam clients.

Verification

Run one real low-value transaction end to end. Pay, watch the webhook fire and insert the row (check the table directly), then connect to the server with that SteamID and confirm the VIP flags are active — check with the admin amx_who command that your access letters appear. Then set that row's expires to a past date, reconnect, and confirm VIP is gone with no file edit on your part. When both the grant and the lapse happen automatically, the pipeline is trustworthy enough to sell.

Contributors: Daemon666 ✦
Share: