Skip to main content

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
PlatformMethodRequirement
WebStripe Embedded CheckoutstripePublishableKey in config
NativeIn-App Purchases via NativeBridgeNative 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

SymptomFix
Products not loadingVerify ?game-id= in URL; check API response format
Purchase fails on webConfirm stripePublishableKey matches environment; confirm <div id="stripe-checkout-element"> exists
Platform shows UNKNOWNWeb: ensure window is defined; Native: verify WebView forwards onMessage