2. Implement Shop
This guide walks you through implementing VXShop in your game.
Three Steps
- Create Purchase UI - Design shop buttons in your game
- Integrate VXShop - Connect buttons to VXShop payment dialogs ⭐
- Handle Purchase - Update user state on server when purchase completes
Step 2 is the key integration point. Steps 1 and 3 are standard game development.
Step 1: Create Purchase UI
Design your shop interface however you like:
- Dedicated shop screen
- In-game popup menus
- Purchase buttons within gameplay
VXShop doesn’t impose any UI requirements. Match your game’s style.
Step 2: Integrate VXShop
Option A: Vibe Coding
Tell Agent8 to connect your button to VXShop:
"Connect the Remove Ads button to VX Shop. The product ID is remove-ads."Product ID Required
Always provide the exact Product ID from your Verse8 VX Shop dashboard.
Option B: Manual Implementation
Review Your Code
Payment logic must always be reviewed manually. Incorrect flows can lead to CPP removal.
Installation
npm install @verse8/platformBasic Usage
import { useVXShop } from '@verse8/platform';
import { useEffect } from 'react';
function Shop() {
const { getItem, buyItem, refresh, onClose } = useVXShop();
// Refresh when purchase dialog closes
useEffect(() => {
const cancel = onClose(() => refresh());
return cancel;
}, [onClose, refresh]);
return (
<button onClick={() => buyItem('remove-ads')}>
Remove Ads
</button>
);
}That’s it! buyItem() opens the VXShop payment dialog.
Advanced: Handling Purchase Limits
Show different button states based on purchase availability:
function Shop() {
const { getItem, buyItem, refresh, onClose } = useVXShop();
useEffect(() => {
const cancel = onClose(() => refresh());
return cancel;
}, [onClose, refresh]);
const item = getItem('remove-ads');
return (
<div>
{/* Already purchased */}
{item?.purchaseLimitReached && (
<button disabled>Purchased</button>
)}
{/* Can purchase */}
{item?.purchasable && !item.purchaseLimitReached && (
<button onClick={() => buyItem(item.productId)}>
{item.price} VX
</button>
)}
{/* Cannot purchase */}
{!item?.purchasable && (
<button disabled>
{item?.purchaseBlockReason || 'Cannot Buy'}
</button>
)}
</div>
);
}Advanced: Showing Remaining Purchases
For items with purchase limits, show remaining count:
function Shop() {
const { getItem, buyItem, refresh, onClose } = useVXShop();
useEffect(() => {
const cancel = onClose(() => refresh());
return cancel;
}, [onClose, refresh]);
const item = getItem('event-pack');
// Item with purchase limit (e.g., 3 times max)
if (item?.purchaseLimit) {
const remaining = item.remainingPurchaseQuantity;
const limit = item.purchaseLimit;
return (
<button onClick={() => buyItem(item.productId)}>
Buy {item.price} VX ({remaining}/{limit})
</button>
);
}
// Unlimited purchase item
return (
<button onClick={() => buyItem('event-pack')}>
Buy for 500 VX
</button>
);
}Step 3: Handle Purchase on Server
When a purchase completes, update user state on your game server.
Server-Side Required
All purchase state changes must happen server-side for security.
Basic Handler
In server.js, implement $onItemPurchased:
// server.js
async $onItemPurchased({ account, purchaseId, productId, quantity, metadata }) {
switch (productId) {
case "remove-ads":
await $global.updateUserState(account, { adsRemoved: true });
break;
case "gold-pack-1000":
const userState = await $global.getUserState(account);
await $global.updateUserState(account, {
gold: (userState.gold || 0) + 1000
});
break;
}
return { success: true };
}Parameters:
account- User’s account IDpurchaseId- Unique transaction IDproductId- Product ID from VX Shopquantity- Number purchasedmetadata- Custom data (if configured)
The game server automatically syncs state to the client in real-time.
👉 Learn more about state management
Using Metadata
If you configured metadata for your product:
// server.js
async $onItemPurchased({ account, purchaseId, productId, quantity, metadata }) {
if (productId === 'item-pack') {
const data = JSON.parse(metadata);
const userState = await $global.getUserState(account);
await $global.updateUserState(account, {
items: {
...userState.items,
[data.itemType]: (userState.items?.[data.itemType] || 0) + data.quantity
},
gold: (userState.gold || 0) + data.bonusGold
});
}
return { success: true };
}Testing Checklist
Before going live:
- ✅ Purchase buttons open correct products
- ✅ Payment dialog displays correctly
- ✅ Purchases update user state
- ✅ State syncs to client immediately
- ✅ Purchase limits work correctly
- ✅ Error messages display properly
Test Thoroughly
Incorrect payment flows can harm user experience and lead to CPP removal.
API Reference
useVXShop() Hook
interface UseVXShopResult {
items: VXShopItem[]; // All available items
isLoading: boolean; // Loading state
error: string | null; // Error if any
getItem: (productId: string) => VXShopItem | undefined;
buyItem: (productId: string) => void; // Opens payment dialog
refresh: () => Promise<void>; // Refresh items
onClose: (callback: (payload: VXShopDialogClosedPayload) => void) => () => void;
}VXShopItem Interface
interface VXShopItem {
id: number;
verseId: string;
productId: string; // Your Product ID
name: string;
price: number; // Price in VX
stock: number;
imageUrl: string;
description: string;
metadata: string | null; // JSON string
purchasable: boolean; // Can user buy this?
purchaseBlockReason?: string; // Why blocked
remainingPurchaseQuantity: number | null;
purchaseLimit: number | null;
purchaseConstraints: PurchaseConstraints | null;
purchasedCount: number;
purchaseLimitReached: boolean;
}VXShopDialogClosedPayload Interface
interface VXShopDialogClosedPayload {
purchased: boolean; // Was purchase completed?
productId: string;
action: "purchased" | "closed";
}Next Steps
Your VXShop integration is complete!
Want to see complete implementation examples?
Additional resources:
- Game Server Documentation
- Test with real VX purchases
- Monitor for user disputes