Kepithor

Enjin

Enjin Platform V3: Wallet Linking and ENJ Payments

A production-verified guide to linking Enjin Wallet accounts, requesting native ENJ payments, handling retries and fulfilling purchases safely.

Production Status

Verified on Enjin Matrixchain on 3 September 2026. Enjium successfully linked Enjin Wallet accounts, collected a live-priced Premium payment, collected a Champion payment and minted the purchased Champion NFT.

This guide describes the application architecture and safety rules behind that implementation. The exact GraphQL operations are kept in the Enjin Platform V3 Graph Book.

Use the application endpoint https://platform.enjin.io/graphql. The wallet-daemon endpoint has a different purpose and schema. Never publish Platform tokens, wallet seeds, daemon key passwords, Telegram tokens or other credentials.

End-to-End Payment Flow

  1. Create an account-specific Enjin Wallet linking request with a stable idempotency key.
  2. Show the returned QR code or deep link and poll until Platform returns the linked wallet.
  3. Bind the verified wallet to the application account. Do not accept a typed address as proof of ownership.
  4. Read the current ENJ/USD price from a trusted application price source and create an expiring quote.
  5. Create a user-signed transferEnj transaction from the linked wallet to the merchant wallet.
  6. Let the player approve the request in Enjin Wallet and poll GetTransaction.
  7. Fulfil the purchase exactly once only after FINALIZED with no transaction error.

Keep linking, payment and fulfilment records server-side. The browser may start and poll the flow, but it must never decide that a payment succeeded.

Bind a Wallet by Proving Control

Create a fresh CreateLinkingCode request for the signed-in application account. Persist its idempotency key before calling Platform, then poll GetLinkedWallet(idempotencyKey: ...). When a wallet is linked, Platform's returned publicKey is the authoritative identity.

Convert that public key to the Enjin Matrixchain SS58 representation, using network prefix 1110, when a conventional address is needed for display or storage. Compare complete keys or complete decoded account IDs. Address suffixes, copied labels and visually similar representations are not identity checks.

Account rule: bind only the wallet returned for that account's linking key. Once bound, keep it immutable unless the product provides a separate authenticated recovery process.

A payment page may create its own linking request for convenience. If it is intended to bind the account too, complete the same server-side verification and write the account wallet atomically. Never let Premium or shop code silently replace an existing account-bound wallet.

Implementation checks

  • Associate every linking key with the authenticated internal user ID.
  • Reject expired keys and links already consumed by another account.
  • Do not trust a wallet address posted by the browser.
  • Disable legacy endpoints that directly update the account wallet.
  • Return explicit CORS headers for permitted front-end origins and handle preflight requests.

Convert a USD Price to ENJ

Enjium prices products in USD and reads its already-maintained ENJ price from its own database. A quote stores the USD price, the ENJ/USD rate, the resulting ENJ amount and its expiry. This makes the payment auditable even if the live price changes later.

ENJ amount = USD product price / USD value of one ENJ

Round the amount up to the chosen payment precision so rounding never undercharges. Enjium uses four ENJ decimal places and a ten-minute quote. Perform this calculation with decimal arithmetic; binary floating-point is unsuitable for money.

The Platform V3 ENJ scalar accepts a decimal ENJ string. Send values such as "158.0856" directly. Do not multiply by 10^18.

Request a Native ENJ Payment

Create a Platform transaction with network: ENJIN, chain: MATRIX, the linked wallet as signerAddress, and a nested transferEnj containing the merchant recipient and quoted decimal amount. This creates an approval request in the player's Enjin Wallet.

Persist the intended idempotency key before submission. A timeout is ambiguous: Platform may have accepted the request even though the application did not receive the response. Reconcile the same key before attempting anything new.

PersistReason
Application user and productDefines who can poll and what will be fulfilled.
Full linked public key/addressLocks the payer identity used for this request.
USD price, ENJ rate and ENJ amountCreates an auditable quote snapshot.
Quote expiryPrevents stale exchange rates being accepted indefinitely.
Idempotency key and Platform UUIDSupports timeout recovery and duplicate prevention.
Platform state and errorControls safe retries and fulfilment.
Fulfilled timestampEnforces exactly-once delivery.

Finality and Exactly-Once Fulfilment

The initial CreateTransaction response is not proof of payment. Poll GetTransaction by UUID and fulfil only when state is FINALIZED and error is null.

Use one database transaction or an equivalent atomic claim to change a purchase from paid-but-unfulfilled to fulfilled. Repeated browser polls, cron runs or webhook calls must see the completed marker and return the same result without granting the product again.

Premium membership

Add the purchased duration only after finality. If Premium can also be updated by a card-payment webhook, prevent an older subscription event from shortening a later ENJ-funded expiry.

Champion and NFT purchases

Reserve any required Mint Pass when the payment request is created, but do not consume it permanently or generate the Champion yet. Release the reservation if the payment fails, is abandoned, expires or is safely replaced. After finality, consume the reservation and generate the deterministic Champion exactly once; then submit and reconcile the NFT mint transaction independently.

Safely Replace a Failed Wallet Request

There is no need to treat a button press as permission to create duplicate payment requests. First inspect the existing transaction:

  • If it is still actionable, show its current status and keep polling.
  • If it is finalized, fulfil or return the existing result.
  • If it failed or the player needs a replacement, call CancelTransaction.
  • Create the replacement only after the old transaction is confirmed ABANDONED.

The replacement receives a new deterministic retry key, while repeated calls for the same retry attempt reuse that key. This preserves idempotency without colliding with the abandoned request.

Operational Failure Modes

  • Wrong wallet notified: the application used an old account address or a different link record. Always source the signer from the verified link attached to the current account and payment.
  • Payment appears successful too early: a pending transaction was treated as paid. Require finality and no error.
  • Duplicate fulfilment: several pollers processed the same final transaction. Use an atomic fulfilment marker.
  • Browser reports CORS: confirm the PHP/API route exists on the deployed server. A missing endpoint can fall through to the React document, whose response then looks like a CORS failure.
  • Stale quote: reject it and create a new price snapshot instead of silently changing the amount attached to an existing transaction.
  • Insufficient funds: keep the failed transaction for audit and use the cancellation/replacement flow when the player retries.

Production Checklist

  • Developer profile is approved and clearly identifies the studio or product requesting the link.
  • Wallet ownership is established through Platform linking, never free-text submission.
  • Full public keys/account IDs are compared and Matrixchain addresses are encoded correctly.
  • Quotes use trusted pricing, decimal arithmetic, upward rounding and a short expiry.
  • Every external operation has a persisted stable idempotency key.
  • Timeouts reconcile existing requests before retrying.
  • Only final transactions with no error trigger fulfilment.
  • Fulfilment is exactly once and safe under concurrent polling.
  • Failed or abandoned purchases release reservations.
  • Logs redact tokens, wallet secrets and other credentials.

For copy-ready queries and mutations, continue with the wallet linking and ENJ payment entries in the Graph Book.