Cocos Creator SDK
The official CROSS Pay SDK for Cocos Creator. It opens a CROSS Pay checkout session inside an in-app WebView, waits for the deep-link callback, and returns the result — no API keys on the client.
Compatibility
| Environment | Supported versions |
|---|---|
| Cocos Creator | 3.8.8+ |
| Platforms | Android · iOS · Windows · Web |
| Language | TypeScript |
| Package | com.crossx.sdk.cocos (npm) |
| Module formats | ESM · CJS |
| Node.js | 18+ |
Installation
The SDK is published to npmjs.org and uses a postinstall sync script to mirror itself into the Cocos project structure (both extensions/ for build hooks and assets/ for runtime code).
1. Add the dependency to package.json
{
"scripts": {
"postinstall": "node scripts/sync-crossx-sdk.js",
"sync-sdk": "node scripts/sync-crossx-sdk.js",
"clean-sdk": "node scripts/sync-crossx-sdk.js --clean"
},
"dependencies": {
"com.crossx.sdk.cocos": "0.0.0-beta.15"
}
}
Update the version to the latest release.
2. Copy the sync script
Copy scripts/sync-crossx-sdk.js from the sample project into your project's scripts/ directory. This script mirrors the SDK into extensions/ and assets/ after each npm install.
3. Run the installer
npm install
The postinstall hook runs automatically and syncs the SDK. Restart Cocos Creator to load the new extensions.
To upgrade to a newer version, update the version in package.json and run npm install again. No manual file cleanup is needed.
If extensions don't load or imports aren't recognized, run npm run clean-sdk && npm install then fully close and reopen Cocos Creator.
Project ID setup
1. Create the settings file
Copy settings/crossx-sdk.example.json from the sample project to settings/crossx-sdk.json and fill in your Project ID:
{
"activeEnvironment": "prod",
"prodProjectId": "YOUR_PROJECT_ID",
"prodOnly": true
}
Obtain your Project ID from the CROSSx console. Add settings/crossx-sdk.json to .gitignore — it contains your project credentials.
2. Read at runtime
import { CROSSxCocosSDKFactory } from '../CROSSx.Sdk.Cocos/assets/index';
// Read from settings (or hard-code for a simple start)
const sdk = CROSSxCocosSDKFactory.create({
projectId: 'YOUR_PROJECT_ID',
});
The build hook reads settings/crossx-sdk.json at build time to inject the deep-link URI scheme into the native project. The build will fail if prodProjectId is empty.
Prerequisites
In every example, checkoutUrl / checkoutId are values your game server obtains by calling POST /v1/payments and passes to the client.
- Store
MERCHANT_API_KEYonly in server environment variables or a secret manager. - The game client only ever receives
checkout_urlandcheckout_idfrom your server. - The deep-link callback result is not authoritative for order fulfillment. Confirm finalization via webhook or a server-side
GET /v1/payments/{id}lookup.
Payment flow
Your game server creates a payment session → the SDK opens a WebView and waits for the deep-link callback → your server finalizes the order via webhook. The deep-link callback result is not the source of truth for confirmation.
Quick start
import { CROSSxCocosSDKFactory, CocosSdkModalUI, type CrossPayCallback } from '../CROSSx.Sdk.Cocos/assets/index';
// 1. Create SDK instance (call once, e.g. in a manager component's onLoad)
const sdk = CROSSxCocosSDKFactory.create({
projectId: 'YOUR_PROJECT_ID',
appName: 'My Game',
debug: true,
});
// Required for SDK-owned UI modals (sign confirmation, WebView, etc.)
sdk.setUIProvider(new CocosSdkModalUI(this.node));
await sdk.initialize();
// 2. Your game server creates a checkout and returns these values
const { checkoutUrl, checkoutId } = await myGameServer.createCheckout(order);
// 3. SDK opens WebView and waits for the deep-link callback
const callback: CrossPayCallback = await sdk.openCrossPayAndWaitResult(
checkoutUrl,
checkoutId,
{ timeoutMs: 300_000 }
);
// 4. Inspect the envelope-level callback status (advisory only)
if (callback.status === 'success')
console.log('Callback received: success — awaiting server webhook confirmation.');
else if (callback.status === 'cancel')
console.log('User dismissed the checkout.');
else
console.warn('Callback:', callback.status, callback.error);
// 5. Confirm the actual payment state on your server before granting goods
const paid = await myGameServer.verifyPayment(checkoutId);
Configure deep link
The build hook automatically injects the webkit-{projectId} URI scheme into the native project when building for Android and iOS, reading prodProjectId from settings/crossx-sdk.json.
No manual edits to AndroidManifest.xml or Info.plist are required.
For reference, the hook injects the following:
Android
Intent filter added to AppActivity in AndroidManifest.xml:
<activity android:name="com.cocos.game.AppActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="webkit-YOUR_PROJECT_ID" />
</intent-filter>
</activity>
iOS
webkit-YOUR_PROJECT_ID added to Info.plist under URL Types.
Windows
The SDK handles the loopback callback handler automatically via a local HTTP listener.
SDK initialization
import { CROSSxCocosSDKFactory } from '../CROSSx.Sdk.Cocos/assets/index';
const sdk = CROSSxCocosSDKFactory.create({
projectId: 'YOUR_PROJECT_ID',
appName: 'My Game',
defaultChainId: 'eip155:612044',
theme: 'dark',
locale: 'en',
debug: true,
});
// Restores stored wallet session if one exists
const session = await sdk.initialize();
if (session?.success)
console.log('Session restored:', session.address);
Trigger payment
const callback = await sdk.openCrossPayAndWaitResult(
checkoutUrl,
checkoutId,
{ timeoutMs: 300_000 } // default: 5 minutes
);
The SDK:
- Appends a CSRF
statetoken and the callback endpoint tocheckoutUrl. - Opens the checkout page in the platform-native WebView.
- Waits for the payment callback to arrive — method varies by platform:
- Android · iOS · macOS: deep-link scheme
webkit-{projectId}://crosspay-callback - Windows · Editor: loopback HTTP
http://localhost:{port}/crosspay-callback
- Android · iOS · macOS: deep-link scheme
- Validates
stateand returns the decoded callback envelope. - Throws an error if the callback does not arrive within
timeoutMs.
Handle the result
CrossPayCallback carries the decoded deep-link callback envelope.
| Field | Type | Description |
|---|---|---|
status | string | Envelope status: 'success' | 'cancel' | 'failed' (or raw PG value) |
state | string? | Echoed CSRF state |
data | CrossPayPayment? | Payment data from PG — fields vary by provider |
error | string? | Error message when status === 'failed' |
rawParams | Record<string, string>? | All raw query params from the callback URL (e.g. checkout_id, payment_id, tx_hash) |
The deep-link callback result is advisory only. Always confirm the payment state with a server-side GET /v1/payments/{id} lookup or webhook before granting goods.
// Read PG-supplied fields from the callback
const checkoutId = callback.data?.checkout_id ?? callback.rawParams?.checkout_id;
const txHash = callback.rawParams?.tx_hash;
// The data object contains the PG payment payload (shape varies by provider)
console.log('PG status:', callback.data?.status);
Error handling
import { CROSSxCocosError } from '../CROSSx.Sdk.Cocos/assets/index';
try {
const callback = await sdk.openCrossPayAndWaitResult(checkoutUrl, checkoutId);
// handle callback
} catch (error) {
if (error instanceof Error && error.message.includes('timed out')) {
// Deep-link callback did not arrive within timeoutMs.
// The user may have completed the payment without the callback reaching the app.
// Verify the payment state on your server.
const paid = await myGameServer.verifyPayment(checkoutId);
} else if (error instanceof CROSSxCocosError) {
console.error(error.code, error.message);
} else {
throw error;
}
}
Deep-link registration
Subscribe to all deep-link events if you need custom routing:
const unsubscribe = sdk.onDeepLink((event) => {
console.log('Deep link received:', event.url, event.callbackType);
// callbackType: 'oauth' | 'crosspay' | 'webkit' | 'unknown'
});
// Unsubscribe when no longer needed
unsubscribe();
For Web platform, forward the OAuth callback URL manually:
sdk.handleOAuthCallback(window.location.href);
Idempotency
| Scenario | Behavior |
|---|---|
| Retry within the same session (network failure, WebView re-open, etc.) | Reuse the same checkoutId — call openCrossPayAndWaitResult again with the same checkoutUrl and checkoutId |
Payment reaches a terminal state (PAID / FAILED / EXPIRED / CANCELLED) | Discard — issue a new checkout by calling POST /v1/payments on your server |
| New payment with a different order / amount / player | Always a new checkout — reusing a previous checkoutId may be rejected for amount mismatch |
CROSS Pay currently does not provide a refund feature. If the same order is charged twice, it cannot be reversed. Call POST /v1/payments with the same Idempotency-Key for retry scenarios.