PayNow

Signing Customers In

Every customer-specific call on the Storefront API needs a customer token in the Authorization: Customer TOKEN header. This page covers how to get one for a person who just arrived on your site. The right flow depends on how their identity can be proven.

Which flow applies to you

Store platformHow identity is establishedFlow
Steam (Rust, GMod etc)Steam OpenID sign-in on your siteServer-side flow
Minecraft (Java, Bedrock, Geyser)The customer types their usernameDirect flow
Anything elseYour own auth, then a platform ID you trustServer-side flow

The distinction is whether PayNow can accept an identity claim at face value.

A Minecraft username is what the hosted webstore asks for too, so the platform lets a browser claim one directly. A Steam ID is not, because anyone can type someone else's, so the claim has to be proven by Steam and the token minted from your server.

Server-side flow

Proving that a visitor owns a Steam account is your job, not PayNow's. Implement Steam OpenID on your server, or use an existing library for your framework, and only continue once Steam has confirmed the sign-in is valid. The result you need from it is the customer's SteamID64.

Never mint a token for a SteamID64 that came from the browser without Steam's confirmation.

With a verified SteamID64 in hand, two calls to the Management API get you a token: find or create the customer by SteamID64 (lookup returns 404 for a customer who has never been seen), then create a customer token for them. Store the token in an httpOnly cookie.

TypeScript
import { createManagementClient, isPayNowApiError } from "@paynow-gg/typescript-sdk";

const management = createManagementClient({
  apiKey: process.env.PAYNOW_API_KEY!,
  storeId: process.env.PAYNOW_STORE_ID!,
});

async function customerIdForSteam(steamId: string): Promise<string> {
  try {
    const existing = await management.customers.lookupCustomer({ steam_id: steamId });
    return existing.id;
  } catch (error) {
    if (!isPayNowApiError(error) || error.status !== 404) {
      throw error;
    }
  }

  const created = await management.customers.createCustomer({ steam_id: steamId });
  return created.id;
}

async function customerTokenForSteam(steamId: string): Promise<string> {
  const customerId = await customerIdForSteam(steamId);
  const { token } = await management.customers.createCustomerToken(customerId);
  return token;
}

The same shape works for any platform where you already know a trustworthy identifier - swap steam_id for minecraft_uuid or xbox_xuid when creating and looking up the customer.

PayNow fills in the customer's Steam profile name and avatar for you. Read them back from GET /v1/store/customer with the new token - you do not need a Steam Web API key.

Direct flow

Minecraft stores can skip the server entirely. POST /v1/store/customer/auth takes a platform and an ID, creates the customer if needed, and returns a token. It is called without any Authorization header, with only x-paynow-store-id, so a browser can call it directly.

TypeScript
const response = await fetch("https://api.paynow.gg/v1/store/customer/auth", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-paynow-store-id": storeId,
  },
  body: JSON.stringify({ platform: "minecraft", id: username }),
});

const { customer_token } = await response.json();

Only Minecraft-based platforms are supported on this route. Every other platform, including steam, uses the server-side flow above.

Holding the token

Tokens are authentication secrets - whoever has one can act as that customer, including checking out. Treat them like a session.

  • Store them in an httpOnly, SameSite cookie set by your server, not in localStorage.
  • Mint one per sign-in rather than reusing a long-lived token across devices.
  • On sign-out, clear the cookie. To end every session a customer has, call DELETE /v1/stores/{storeId}/customers/{customerId}/tokens from your server.
  • When a storefront call answers 401 or 403, treat the customer as signed out and start the flow again.

Using the token

Create a storefront client with the token for every customer-specific call. GET /v1/store/customer is the cheapest way to confirm it is still valid and to get the profile for your header.

TypeScript
import { createStorefrontClient } from "@paynow-gg/typescript-sdk";

const storefront = createStorefrontClient({
  storeId: process.env.PAYNOW_STORE_ID!,
  customerToken,
});

const customer = await storefront.customer.getStorefrontCustomer();

The profile object carries the platform, display name and avatar URL regardless of platform, so your UI does not need to branch on Steam versus Minecraft.