Quickstart: sell your first pack in 15 minutes

You need an Appside Studio workspace in test mode with one published store (Studio → Preview & publish).

1. Connect your backend (Studio → Integration)

  1. Generate an ES256 key pair on your backend and paste the public JWK into Public JWK/JWKS. Never upload the private key.
  2. Set Issuer (for example your game id: orbit).
  3. Set Fulfillment URL (for example https://api.yourgame.com/appside/webhook) and a webhook secret of 32+ characters.
  4. Press Send test event: health should show acknowledged.

2. Sign a player token on your backend

import { Appside } from 'appside/node';
const appside = new Appside({ storeId: 'orbit', issuer: 'orbit', kid: 'game-key-1',
  privateKey: process.env.APPSIDE_PRIVATE_KEY_PEM, webhookSecret: process.env.APPSIDE_WEBHOOK_SECRET,
  serverKey: process.env.APPSIDE_SERVER_KEY, baseURL: 'https://app.appside.build' });

app.post('/appside/token', requireLogin, (req, res) =>
  res.json({ token: appside.playerToken(req.user.id) }));   // 5-minute, single-use ES256 token

Only call it from an authenticated endpoint: the token says "this signed-in player is buying".

3. Open the store from the game

import { Appside } from 'appside/web';
const appside = new Appside({ baseURL: 'https://app.appside.build' });
appside.configure('orbit', { environment: 'test', returnURL: 'https://yourgame.com/appside/return' });
appside.onPurchaseCompleted = result => refreshWalletFromBackend();   // UI hint only

const fresh = () => fetch('/appside/token', { method: 'POST' }).then(r => r.json()).then(j => j.token);
await appside.openStore(await fresh());          // whole store
await appside.purchase('voyager', await fresh()); // one pack, straight to checkout
// on your return page:
const result = appside.handle(location.href);     // { status, orderId, state, verified: false }

purchase(sku) needs no server-to-server checkout session: the signed token plus the SKU is enough, and the price comes from your published catalogue.

4. Grant on the webhook (server)

app.post('/appside/webhook', express.raw({ type: '*/*' }), async (req, res) => {
  if (!appside.verifyWebhook(req.body, req.headers)) return res.sendStatus(401);
  const event = JSON.parse(req.body);
  if (event.type === 'order.paid' && !event.probe) await grantOnce(event.order_id, event.player_id, event.order);
  if (event.type === 'order.refunded') await revokeOnce(event.order_id, event.player_id);
  res.sendStatus(204);   // any 2xx = acknowledged; anything else is retried
});

grantOnce must be idempotent on order_id: Appside retries until it gets a 2xx (up to 72 h), so you may see the same event twice. To double-check an order from your server: await appside.getOrder(orderId) (uses your server key from Studio → Integration).

5. Test it

Buy a pack with card 4242 4242 4242 4242, any future expiry, any CVC. The webhook arrives within a second and your player has the coins. See Testing for decline, 3-D Secure and outage cases.