주요 콘텐츠로 건너뛰기

JavaScript SDK reference

The Aghanim JavaScript SDK lets you use the Checkout within your web game.

Integration

To integrate the SDK, see its prerequisites and the detailed instruction on Integrate → JavaScript.

Method reference

Initialize SDK

Creates the SDK instance. Call it once, when your game boots.

import { Aghanim } from "@aghanim-sdk/checkout";

const aghanim = Aghanim.init({ apiKey: "sdk_..." });
OptionDescription
apiKeyYour public SDK key. Safe to expose in the game client. The key itself determines the environment the SDK talks to.
playerIdStarting player ID. Most integrations call Set player ID instead.
localeDefault locale for Orders created through the SDK.
preflightDefaults to true: Open Checkout verifies the Order is still payable before presenting anything, so a dead Order rejects instead of framing an error screen. Set false to opt every call site out.
mockRuns the SDK against in-memory transports. See Testing with mock mode.

Set player ID

Sets the player ID once for the current SDK instance. The SDK uses it in all following method calls, so the player-scoped methods below take no player argument.

aghanim.setPlayerId("player_1");

Calling a player-scoped method with no ID set throws invalid_argument.

Clear player ID

Drops the player ID from the instance. Call it when the player signs out, so nothing that follows runs against the previous account.

aghanim.clearPlayerId();

Create Order

Creates an Order for the current player and returns it, including its id and checkout_url.

const order = await aghanim.orders.create({
items: [{ sku: "gems-100" }],
metadata: { level: "12" },
});
FieldDescription
itemsRequired. The SKUs to charge for.
player_idDefaults to the ID from Set player ID. Pass it to create an Order for someone else.
localeDefaults to the locale passed to init().
user_agentDefaults to the browser's navigator.userAgent.
back_to_game_urlWhere the player lands after leaving the Checkout. Supports an {order_id} placeholder.
metadataString map stored with the Order.

Every other field of the Create Order request body is accepted as-is.

To create Orders on your backend instead, call that endpoint with your S2S key and pass the resulting checkout_url to Open Checkout.

Get unconsumed Orders

Returns the current player's Orders that are paid but not yet acknowledged by your game.

const orders = await aghanim.orders.unconsumedDetails();

Consume paid Order

Acknowledges an Order and removes it from the unconsumed list. Consuming the same Order twice fails, so grant the items before you consume.

await aghanim.orders.consume(order.id);

Open Checkout

Presents the Checkout and returns a controller you subscribe to. Accepts an Order from Create Order, a bare Order ID, or an opaque checkout_url string from your backend.

const checkout = await aghanim.openCheckout(order, { mode: "embedded", container: "#store-panel" });
OptionDescription
modeoverlay (default), embedded, external, or auto. See Presentation modes.
containerElement or selector to mount into. Required for embedded.
theme"light", "dark", or "auto" (default). Forces the chrome the SDK draws around the Checkout.
closeConfirmation"dismiss" (default) closes on a backdrop click or ESC, "confirm" asks first, "disabled" ignores both gestures.
analyticsEventstrue (all, default), false (off), or an allowlist such as ["pay_button", "payment_status"].
readyTimeoutMsFires error with code timeout when the Checkout never reports ready. Off when omitted.
preflightDefaults to the value set in init(). Set false to skip the payability check for this call.
localeLocale segment for the Checkout URL. Applies only when you pass a bare Order ID, since an Order and a checkout_url already carry theirs.

Every event listener is also accepted here as an option, under the same name it has on the controller. See Subscribe to events.

Passing a target the SDK cannot resolve to an Order ID throws invalid_argument.

Subscribe to events

The controller is an emitter with three interchangeable subscription styles: the generic on(), the chainable per-event methods such as onPaid(), and the callbacks you pass to openCheckout(). All three feed from the same emitter.

checkout
.onPaymentStatus(({ status, failReason }) => { ... })
.onPaid(({ orderId }) => showSuccessUI())
.onClosed(({ reason }) => game.resume())
.onError((err) => { if (err.code === "popup_blocked") showOpenPaymentPageButton(); });
MethodDescription
on(event, listener)Subscribes and returns an unsubscribe function.
off(event, listener)Removes a listener.
once(event)Resolves a Promise on the next occurrence of the event.
on<Event>(listener)Per-event sugar over on(), one method per event. Returns the controller, so calls chain. Every name is in the table below.

Every event, with the listener name that subscribes to it. The same name works as a controller method and as an openCheckout() option:

EventListenerPayloadFires when
readyonReady{ orderId }The Checkout page has loaded and rendered
payment_statusonPaymentStatus{ orderId, paymentId?, status, failReason?, retryable? }Any payment or subscription status change
paidonPaid{ orderId, paymentId? }The payment succeeded. See Security before granting on it
failedonFailed{ orderId, paymentId?, status, reason?, retryable }An attempt reached a non-success status. retryable: true keeps the Checkout open
next_actiononNextActionNextActionThe provider needs a step performed. See NextAction
open_externalonOpenExternal{ url, method }The flow left the iframe. method is popup or redirect
deliveredonDelivered{ orderId, deliveredAt }The Order was delivered to the player
resize{ height }Embedded mode: the Checkout content changed height
closedonClosed{ reason }The Checkout closed, with reason user, completed, error, or programmatic
erroronErrorAghanimErrorAn SDK-level failure, such as a blocked popup or a framing timeout
analytics_eventonAnalyticsEvent{ action, type?, data?, orderId? }The Checkout reported a product-analytics action. See Analytics

resize has no listener of its own. Subscribe to it with on("resize", ...).

Failed attempts vs dead orders. A declined or canceled attempt fires failed with retryable: true and the Checkout stays open showing its error screen, so the player can try another card or method. Don't treat it as the end of the purchase. The frame closes only on success (closed: completed), on a non-retryable terminal status like an expired Order (closed: error), or when the player leaves (back-to-game relay or your close()).

Each event fires once, so you never need your own bookkeeping to suppress repeats.

Control the Checkout

The controller carries what it is presenting:

PropertyDescription
orderIdID of the Order being presented.
urlThe Checkout URL in use. Opaque. Never parse or modify it.
modeThe mode that was resolved: overlay, embedded, or external. auto never appears here.

And how it is presented:

MethodDescription
open()Presents the Checkout again after a close. In embedded mode it needs a container from the open options, or a mount() first.
mount(container)Embedded mode: mounts the iframe into an element or selector. Throws invalid_argument in external mode.
close(reason?)Closes the Checkout and fires closed. The reason defaults to programmatic.
destroy()Closes, then tears down the DOM, listeners, and event channels. Every later call throws invalid_argument.

Close the Checkout from your game when the player exits to a menu, for example. The Checkout cancels any in-flight payment and the controller fires closed.

checkout.close();

The SDK also closes on its own once the Order reaches a terminal state: reason completed after a success, reason error after a failure the player cannot retry. Call destroy() when you are done with the Checkout for good. close() alone leaves it ready for a later open().

Presentation modes

ModeWhat happensWhen to use
overlay (default)Modal iframe over the gameMost desktop and mobile web games
embeddedIframe inside your container, with auto-height via resize eventsCustom store UIs
externalPopup window, falling back to a full redirect if blockedWebViews, in-app browsers, strict-CSP hosts
autoCapability detection picks overlay or externalWhen you don't want to decide

Overlay theme: the chrome the SDK draws around the Checkout (the loading placeholder) is themed to match the Checkout itself: the Order's ui_settings.theme when you pass an Order, otherwise the player's prefers-color-scheme, which is the Checkout page's own default. Force it with the theme option.

CSP note: if your page sets a Content-Security-Policy, allow frame-src and child-src for the Checkout origin (https://pay.aghanim.com or your sandbox origin). Popup escalation also needs window.open unblocked. A framing block (CSP or X-Frame-Options) is invisible to the parent page, so pass readyTimeoutMs to get an error when the Checkout never reports ready.

Analytics

The Checkout's product-analytics stream (pageviews, clicks, and every other action the player takes) reaches your game as one analytics_event per action.

const checkout = await aghanim.openCheckout(order, {
analyticsEvents: ["pay_button", "payment_method", "payment_status"],
});

checkout.onAnalyticsEvent(({ action, type, data, orderId }) => {
telemetry.track(`checkout_${action}`, { type, data, orderId });
});

action is what the player acted on (pageview, pay_button, payment_method, submit_payment_form, payment_status, and so on), type is how (click, change, open, submit, …), and data carries extra context for the actions that have any.

The analyticsEvents option filters in your page, so it quiets your own listeners rather than reducing traffic: true (the default) passes everything through, false drops the stream, an array keeps only those actions.

Analytics events are not delivered in external mode, where the Checkout runs in a window your page cannot listen to.

External payment windows

Some payment methods (PayPal, certain 3-D Secure and local methods) cannot run inside an iframe. When the Checkout reaches such a step it emits a next_action of type open_external, then opens the URL in a separate window and emits open_external { url, method }. The result arrives out of band rather than from that window, so pause your game on open_external and resume on payment_status or closed.

If the popup is blocked you get error with code popup_blocked. Render your own "Open payment page" button (a direct user gesture) with the URL from the error details.

NextAction

NextAction is a discriminated union. Handle what you care about and ignore the rest.

type NextAction =
| { type: "completed"; status: CheckoutStatus }
| { type: "show_3ds"; url: string }
| { type: "open_external"; url: string; reason: "payment_method" | "redirect" }
| { type: "await_confirmation"; pollAfterMs?: number }
| { type: "show_error"; error: AghanimError };

Testing with mock mode

Pass mock to init() and the SDK swaps its network and iframe transports for in-memory ones, so your tests can drive a full purchase without a network or a real payment:

const aghanim = Aghanim.init({
apiKey: "sdk_sandbox_...",
mock: { scenario: "instant_success", latencyMs: 100 },
});

Every scenario fires the same events your production code already subscribes to:

ScenarioExercises
instant_successthe happy path, through to paid
open_externalescalation to a popup, with the result arriving out of band
three_dsa show_3ds next action, then success
failurea decline (failed with retryable: true; the Checkout stays open)
poll_fallbackthe result still arrives when live updates are unavailable
unconsumedpre-seeded paid Orders for the consume loop

Security

  • Never grant value from the paid event. A modified client can fake it, so show your success screen on paid and grant on the signed item.add webhook.
  • Never embed the S2S key in the client.
  • Don't write your own message listener. The SDK accepts messages only from the Checkout it opened, in sandbox and production alike, with nothing for you to configure.

Error reference

Everything the SDK throws or hands to an error listener is an AghanimError, an Error with a code from the table below, a retryable flag, status when it came from an API response, and details for the codes that carry extra context.

@aghanim-sdk/checkout/protocol exports the status unions, NextAction, and error codes as standalone types with no runtime dependencies, for typing your own handlers and test fixtures.

Error codeMeaning
invalid_api_keyThe SDK key was rejected.
api_errorThe SDK API returned an error.
network_errorThe request never reached the API.
order_not_foundNo Order with that ID.
order_not_payableThe preflight check found the Order past its lifecycle: paid, canceled, expired, or refunded. details carries { orderId, status }.
popup_blockedThe browser blocked window.open. Offer a button the player can click.
container_not_foundThe container for embedded mode does not resolve to an element.
invalid_message_originA message arrived from an origin the SDK does not trust, and was dropped. Informational: you do not need to handle it.
websocket_errorThe live-events connection failed. Not fatal: the SDK keeps tracking the Order without it.
timeoutThe Checkout never reported ready within readyTimeoutMs.
not_initializedA call was made before Aghanim.init().
invalid_argumentA method was called with arguments it cannot use.

Statuses

The status on payment_status and failed falls into three groups, and the group is what your handler should branch on:

GroupStatusesWhat it means
Successsuccessful, active, trialThe purchase went through.
Retryable failurecanceled, rejected, abandoned, expiredThe attempt failed, the Order did not. The Checkout stays open so the player can try another method.
Finaleverything elseNothing further will arrive for this Order.

An Order itself is created, paid, canceled, expired, or refunded. The last four are what preflight rejects as order_not_payable.

Post-purchase outcomes such as refunded, chargeback, and dispute reach your backend as webhooks, which is where your game should act on them.

도움이 필요하세요?
통합팀에 문의하십시오 [email protected]