Billing
Unified billing for web (Stripe) and native (IAP) platforms. Auto-detects platform and initializes on first use.
Setup
Pass stripePublishableKey via HyveSdkProvider. userId, gameId, and checkoutUrl are read from the URL JWT automatically.
<HyveSdkProvider config={{ billing: { stripePublishableKey: 'pk_live_...' } }}>
Usage
import { useHyveSdk } from '@hyve-sdk/js/react';
import { useEffect, useState } from 'react';
import type { BillingProduct } from '@hyve-sdk/js';
function Store() {
const hyve = useHyveSdk();
const [products, setProducts] = useState<BillingProduct[]>([]);
useEffect(() => {
hyve.onPurchaseComplete((result) => {
console.log('Purchased:', result.productId, result.transactionId);
});
hyve.onPurchaseError((result) => {
console.error('Failed:', result.error?.message);
});
hyve.getBillingProducts().then(setProducts);
}, [hyve]);
return (
<>
{products.map(p => (
<button key={p.productId} onClick={() => hyve.purchaseProduct(p.productId)}>
{p.title} — {p.localizedPrice}
</button>
))}
<div id="stripe-checkout-element" />
</>
);
}
Register callbacks before calling purchaseProduct(). Billing initializes automatically on first getBillingProducts() or purchaseProduct() call.
Platform Detection
hyve.getBillingPlatform(); // 'web' | 'native' | 'unknown'
hyve.isBillingAvailable(); // boolean
| Platform | Method | Requirement |
|---|---|---|
| Web | Stripe Embedded Checkout | stripePublishableKey in config |
| Native | In-App Purchases via NativeBridge | Native app with IAP enabled |
Web: Stripe Checkout
Add a mount target to your HTML:
<div id="stripe-checkout-element"></div>
await hyve.purchaseProduct(productId, {
elementId: 'stripe-checkout-element', // default
});
hyve.unmountBillingCheckout(); // call when user closes modal
Server Endpoints
Your API must implement:
GET /get-packages?game_id={id}
{
"packages": [{
"priceId": "price_1234",
"productId": "prod_1234",
"game_id": "1",
"package_name": "1000 Gold",
"price_display": "$9.99",
"price_cents": 999,
"items": [{ "key": "gold", "amount": 1000 }]
}]
}
priceId is used as productId for web packages.
GET /get-native-packages?game_id={id}
Same shape. productId must match the store product ID.
POST /create-checkout-session
// Request
{ "priceId": "price_1234", "userId": "user_123", "gameId": 1, "embedded": true, "return_url": "..." }
// Response
{ "client_secret": "cs_test_...", "id": "cs_1234" }
POST /iap/validate
Called automatically by the SDK via the native app.
{
"userId": "user_123", "gameId": 1, "platform": "android",
"productId": "com.game.gold.1000", "transactionId": "GPA.1234",
"purchaseToken": "abc123", "transactionDate": "2024-01-15T10:30:00Z"
}
Types
interface BillingProduct {
productId: string;
title: string;
description: string;
price: number;
localizedPrice: string;
currency: string;
}
interface PurchaseResult {
success: boolean;
productId: string;
transactionId?: string;
transactionDate?: string;
error?: { code: string; message: string };
}
Testing
Web: Use pk_test_..., open http://localhost:3000?game-id=1&hyve-access=JWT.
Stripe test cards: success 4242 4242 4242 4242, decline 4000 0000 0000 0002.
Native: Load in React Native WebView with URL params. Use test products from the store console.
Troubleshooting
| Symptom | Fix |
|---|---|
| Products not loading | Verify ?game-id= in URL; check API response format |
| Purchase fails on web | Confirm stripePublishableKey matches environment; confirm <div id="stripe-checkout-element"> exists |
Platform shows UNKNOWN | Web: ensure window is defined; Native: verify WebView forwards onMessage |