TapFun Developer Portal
Markdown JAEN

Introduction

This page is the integration specification for publishers and developers who ship a browser-based web application (Web App) on TapFun. Your web application runs embedded in an iframe on a TapFun page.

If you ship a Windows exe through cloud gaming, see the Cloud Gaming specification instead.

Setting up communication

Logging in to the Publisher Console

You can update your exe and change settings in the Publisher Console of the test environment (https://<TEST_HOST>/publisher). Your TapFun contact grants login access, so send us the email addresses of the people who will log in.

Release planning

Once the remaining issues have a clear path to resolution, we agree on the release date and pricing.

Reporting issues during development

When you hit a problem such as "input does not respond" or "unexpected behavior", report it in the communication channel with:

If the problem comes from intended behavior, both parties may discuss adding or changing a feature.

Debugging

Add one of these parameters to the game page URL to see the client API traffic.

Parameter Effect
?debugLog=1 Shows a log area over the game screen with the client API requests TapFun received (including consoleLog).
?verbose=1 Writes detailed logs to the browser's developer tools console, including the data of consoleLog.

Environments

Hostnames

TapFun environment Hostname Basic auth on the web UI
Test Ask your TapFun contact (written as <TEST_HOST> in this document) Yes (ask your TapFun contact for the user name and password)
Production https://tapfun.co.jp No

The test environment hostname and its Basic auth user name and password are confidential and are not included in this document. Ask your TapFun contact for them. Replace <TEST_HOST> in code examples with the test environment hostname. Do not share them outside your company or anywhere public (public repositories, public channels, and so on).

Payments in the test environment

To buy items in the test environment, use one of the following.

Test play in production

To access an app before release, enable maintenance mode in the Publisher Console's basic settings and enter the tester's email address in "Maintenance exception email addresses".

For tests in production that involve purchases, TapFun registers a test-play account for each app. The account receives TapFun credit usable only in that app. Purchases by test-play accounts are cancelled immediately and offset against sales.

Publisher Console

Basic settings

Field Description
App ID Internal ID of the app.
name Internal name.
apiKey API key for calling the Web API.
Bearer token Value of Authorization: Bearer when calling the Web API (format: <AppID>_<apiKey>).
Maintenance mode Puts the app into maintenance mode when enabled.
Maintenance exception email addresses Users with these email addresses can play during maintenance. For an unreleased (private) app, enabling maintenance mode also lets these accounts access it.
Launch URL URL that launches your web application. TapFun loads it in an iframe. Placeholders: {token} (token for create_session), {host} (TapFun hostname, e.g. tapfun.co.jp), {appId} (app ID).
sigKey HMAC-SHA256 signing key. Used for the sig of purchaseItem and for signing the Callback API.
Require sig for purchaseItem When enabled, purchaseItem without sig fails (invalidSignature).
Frameless display When enabled, the TapFun header is not shown on the play screen.
callback HTTP POST URL URL notified when an item is purchased (Callback API).
callback Notice Webhook URL Webhook URL notified when sending a callback fails and when it recovers (e.g. a Slack incoming webhook).
callback Notice Email Email address notified when sending a callback fails and when it recovers.
purchases API compatibility mode Backward-compatibility mode that returns userId in the old format in the purchases API. Normally keep it disabled.

Details (introduction page)

The "詳細情報" (details) tab edits what is shown on the game's introduction page on TapFun.

Field Description
Icon image The game's icon. Recommended size: 512x512 (square).
2:1 image A wide image used in listings and on the introduction page. Recommended size: 1920x960.
Loading image PC / SP Images shown while the game starts. Recommended size: 600x520 for PC, 300x260 for smartphones.
Controls guide images Optional images explaining the controls, which players can open from the TapFun screen while playing.
Screenshot images Screenshots shown on the introduction page.
Game title The game name shown on the introduction page and elsewhere.
Developer / Publisher Developer and publisher names shown on the introduction page.
Game description Body text of the introduction page, in Markdown.
Third-party EULA License terms shown on the introduction page, such as third-party licenses included in the game.

Drag controls guide images and screenshots to reorder them. To delete one, click it to mark it for deletion, then press "追加アップロード & 順序を保存" (upload and save order) to apply the change.

Launching the game

"アプリページ" (app page) at the top of the app screen launches the game with the options below (Debugging). Unreleased apps show "非公開状態" (private).

Option Description
ログ表示 (show log) Shows consoleLog API and other logs on screen (same as ?debugLog=1).
verbose Increases the logs written to the browser console (same as ?verbose=1).

Other tabs

Tab Contents
月別売上 (monthly sales) Daily purchase counts, sales and cancellations, with a breakdown by sales category (Platform / Publisher).
全ての Purchases / 未完了の Purchases (all / unfinished purchases) List of purchases. "Unfinished" shows only purchases whose callback has not completed. You can resend the callback from each purchase. In the test environment you can also cancel a purchase and stop callback resends.
Callbacks History of Callback API requests.
Leaderboards Leaderboards and their entries.
紹介ページ (introduction page) Opens the game's introduction page on TapFun in a new tab.

Launch and authentication

How it works

Example launch URL:

text
https://game.example.com/play?host={host}&token={token}&appId={appId}

User authentication / getting the TapFun user ID

  1. Send the value passed in {token} from the browser to your game server.
  2. Your game server calls POST /api/v1/create_session, which returns the TapFun user ID.
  3. {token} is valid for 24 hours in the test environment and 180 seconds in production. It can be reused while valid.
bash
curl https://<TEST_HOST>/api/v1/create_session -X POST \
  -H "Authorization: Bearer BEARER_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"token":"TOKEN_HERE"}'
# {"result":"success","userId":"PLYRABCDEFGHIJKLMNOP123456789APPGRP"}

Keep the Bearer token on the server. Never send it to the browser.

Client API

APIs for communication between your web application and TapFun, its parent frame. Messages are exchanged in the player's browser, so never include secrets.

API Summary
purchaseItem Item purchase
getPlayerEnvironment Get the player's environment
getOneTimeData Read the URL's onetime value once
proposeAuthentication Prompt the player to log in
openAccount Open the account page
reboot Restart
consoleLog Show a log message

Transport

Item Value
Sending window.parent.postMessage(JSON string, "https://" + {host})
Receiving The message event on window. event.data is a JSON string.
Encoding UTF-8
Request / response format JSON

TapFun accepts a message only if it comes from the embedded iframe and its origin matches the launch URL's origin. On your side, check that event.origin is https://{host}.

js
const params = new URLSearchParams(location.search);
const tapfunOrigin = 'https://' + params.get('host'); // include {host} in your launch URL

function send(data) {
  window.parent.postMessage(JSON.stringify(data), tapfunOrigin);
}

window.addEventListener('message', (event) => {
  if (event.origin !== tapfunOrigin) return;
  const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
  console.log('from TapFun', data);
});

send({ req: 'getPlayerEnvironment' });

purchaseItem

Shows the item purchase confirmation dialog and processes the payment. When the payment succeeds, TapFun notifies your game server through the Callback API. Grant the item on your game server when it receives the callback.

Parameter Required Description
req ✓ purchaseItem (fixed)
tran ✓ Transaction ID generated by your app, unique per app. Purchase is refused if a past purchase (successful or not) used the same tran
code ✓ Product code (any value; used for aggregation on TapFun)
item Composite product code (any value)
amount ✓ Price (300-10000)
name ✓ Product name (any value)
img Product thumbnail image URL
note Free-form field
ret Return target. When charging the TapFun wallet involves a page transition, this sets the path (and after) of the launch URL used to restart the app. It is treated as a launch URL, so placeholders such as {host} are needed again. Must start with /. Example: /purchaseReturn?host={host}&token={token}&appId={appId}#anchor
sig * Signature (How to sign). Required if "Require sig for purchaseItem" is enabled in the Publisher Console. Always verified when sent

Request example:

json
{
  "req": "purchaseItem",
  "tran": "UNIQUE_TRANSACTION_ID_HERE",
  "code": "code_type01",
  "item": "item_type01",
  "img": "https://example.com/img/item.png",
  "amount": 500,
  "name": "300 Gems",
  "ret": "/purchaseReturn?host={host}&token={token}&appId={appId}#anchor",
  "sig": "SIGNATURE_HASH_HERE"
}

How to sign

Join the following values with & in this order, compute HMAC-SHA256 with the Publisher Console's sigKey as the key, and use the lowercase hex string.

text
tapfunUserId&tran&code&item&amount&name&img&note&ret
js
// Node.js (game server)
import { createHmac } from 'node:crypto';

function purchaseSig(sigKey, p) {
  const s = [p.tapfunUserId, p.tran, p.code, p.item ?? '', p.amount, p.name, p.img ?? '', p.note ?? '', p.ret ?? ''].join('&');
  return createHmac('sha256', sigKey).update(s).digest('hex');
}

Response notifications

Every notification includes req (purchaseItem), code (the requested code) and tran (the requested tran).

result When Extra keys
received The purchase event was received and the confirmation dialog opened —
purchaseCancel The confirmation dialog was closed (purchase cancelled) —
purchase The purchase button in the dialog was pressed —
purchaseSuccess The payment succeeded —
incompleteCallback The purchase failed (sent when the failure modal is shown) reason (below)
parameterError Missing parameters, sig mismatch, etc. reason: error details (e.g. required param not found "code")
reason Description
duplicateTran The tran duplicates a past purchase
unfinishedClallbackPrucahseRemaining There is an unfinished callback (e.g. due to a network error). The spelling is exactly as shown
rejected The callback response's result was reject
unknown The callback result could not be obtained for an unknown reason
invalidSignature The signature did not match

getPlayerEnvironment

Gets the player's environment.

Parameter Required Description
req ✓ getPlayerEnvironment (fixed)

Response:

Key Description
from getPlayerEnvironment
userAgent The browser's navigator.userAgent
isSP 1: smart device (iPhone / iPad / Android) / 0: other
authenticated 1: logged in / 0: guest

getOneTimeData

Reads the onetime query string value of the TapFun page URL, once. Useful for deep links. After the request, onetime is removed from the URL.

Parameter Required Description
req ✓ getOneTimeData (fixed)

Response:

Key Description
from getOneTimeData
onetime Decoded value of onetime. Empty string if already read or absent

Example: when the URL is https://tapfun.co.jp/p/dummy/play?onetime=abcdefg

json
// 1st request
{ "from": "getOneTimeData", "onetime": "abcdefg" }
// 2nd request (onetime has been removed from the URL)
{ "from": "getOneTimeData", "onetime": "" }

With an encoded value:

json
// ?onetime=page%3Dqrcode%26code%3Daaaa
{ "from": "getOneTimeData", "onetime": "page=qrcode&code=aaaa" }
// ?onetime=abc%0D%0Adef%0D%0Aghi
{ "from": "getOneTimeData", "onetime": "abc\r\ndef\r\nghi" }

proposeAuthentication

Prompts the player to log in. For a guest, shows the sign-up / login modal.

Parameter Required Description
req ✓ proposeAuthentication (fixed)

Response:

Key Description
from proposeAuthentication
authenticated 1: already logged in, nothing was done / 0: guest, the modal was shown

openAccount

Opens the account page. For a guest, shows the sign-up / login modal.

Parameter Required Description
req ✓ openAccount (fixed)

Response: none

reboot

Restarts the game, the same as when the player taps the restart icon in the TapFun UI.

Parameter Required Description
req ✓ reboot (fixed)

Response: none

consoleLog

Adds a log line to TapFun's log area. It is also written to the browser's developer tools console.

Parameter Required Description
req ✓ consoleLog (fixed)
data ✓ A string or JSON string. If JSON.parse() succeeds, it is shown in the console as a JSON object.

Response: none

Callback API (item purchase notification)

When a purchaseItem payment succeeds, the TapFun server notifies your game server of the purchase.

Specification

Item Value
Endpoint "callback HTTP POST URL" in the Publisher Console
Source IP address Test: ask your TapFun contact / Production: 54.178.93.41
Method HTTP POST
Content-Type application/json
Encoding UTF-8
Response JSON (Response)
Timeout 5 seconds

Request headers:

Header Description
X-TapFun-Callback-Key Unique key per callback
X-TapFun-Retry Retry count (only on retries)
X-TapFun-Signature HMAC-SHA256 of the request body signed with sigKey (hex). Compute it from the raw body string as received
js
// Signature verification example with Node.js (Express)
import { createHmac, timingSafeEqual } from 'node:crypto';

app.post('/tapfun/callback', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = createHmac('sha256', SIG_KEY).update(req.body).digest('hex');
  const actual = req.get('X-TapFun-Signature') || '';
  if (expected.length !== actual.length || !timingSafeEqual(Buffer.from(expected), Buffer.from(actual))) {
    return res.status(400).json({ result: 'error' }); // anything other than success / reject is retried
  }
  const purchase = JSON.parse(req.body);
  // process idempotently by purchase.tran and grant the item
  res.json({ result: 'success' });
});

Notifications and retries

Request

Key Description
req purchaseItem
event purchaseItem
result success
key Unique key per purchase
appId App ID
userId TapFun user ID
publisherUserId Your user ID for the player, or null if not set
fromRegisterDevice Device the player signed up to TapFun on (SP / PC)
purchaseDevice Device used for the purchase (SP / PC)
salesDistinction Sales category (Publisher / Platform)
tran Transaction ID
code Product code
item Composite product code, or null
amount Price
name Product name
img Image URL, or null
note Note, or null
createdAt Purchase time (UTC)
json
{
  "req": "purchaseItem",
  "event": "purchaseItem",
  "result": "success",
  "key": "01HRC7QZV1GVT0WX9G6EKSYCAS",
  "appId": 1,
  "userId": "PLYR01HE2R3JV6Y2NEGRMS4KZQB861DBG",
  "publisherUserId": null,
  "fromRegisterDevice": "PC",
  "purchaseDevice": "PC",
  "salesDistinction": "Publisher",
  "tran": "abcdef123456",
  "code": "gem300",
  "item": "itemGem300",
  "amount": 500,
  "name": "300 Gems",
  "img": "https://example.com/img/item.png",
  "note": null,
  "createdAt": "2024-03-06T06:37:22.000Z"
}

Response

Your game server must return success or reject in the JSON result. Otherwise TapFun retries. The HTTP status is not evaluated, but 200 is recommended. Redirects are not supported.

json
// Purchase succeeded
{ "result": "success" }
// Purchase rejected
{ "result": "reject" }

Web API

HTTP APIs called from your servers. Keep the Bearer token on the server and never send it to the browser.

Common specification

Item Value
Base URL https://<environment hostname>/api/v1 (Hostnames)
Method HTTP GET / POST
Encoding UTF-8
Request format URL path, query string, POST data (application/x-www-form-urlencoded unless stated otherwise)
Response format JSON. An empty array when an array result has no items. HTTP status is 200 unless stated otherwise
Date/time UTC, yyyy-mm-ddThh:mm:ss.sssZ

Authentication

Authenticate with the HTTP header Authorization: Bearer <AppID>_<APIKey> (the Bearer token in the Publisher Console).

text
Authorization: Bearer 123_NWU0MjlkY2ViYTVkOTdiYjIwNThmZTkxMDMzYWM4Y2JmNGUyMTQ0Ng

On authentication failure the API returns HTTP 403 with:

json
{"error":"no authorization"}

POST /api/v1/create_session

Gets the TapFun user ID from the launch URL's {token} (User authentication). Authentication: required.

POST data Required Description
token ✓ Value passed in the launch URL's {token}
json
// Success
{ "result": "success", "userId": "<TapFun user ID>" }
// Failure (400): token missing
{ "result": "failure", "message": "token is missing" }
// Failure (400): token invalid
{ "result": "failure", "message": "token is invalid" }

GET /api/v1/purchases

Gets the purchase history, newest first. Useful for checking purchases whose callback you missed. Authentication: required.

Query string Required Description
userId TapFun user ID. If omitted, returns purchases for the whole app
key Purchase key
tran Transaction ID
limit Number of results (1-100, default 10). Out of range becomes 10
nextKey Key for fetching the next results (the nextKey of the previous response)
json
// Success
{
  "nextKey": "Purchase01HE2R3JV6Y2NEGRMS4KZQB858",   // null when there are no more results
  "result": [
    {
      "key": "Purchase01HE2R3JV6Y2NEGRMS4KZQB861",
      "appId": 1,
      "userId": "PLYR01HE2R3JV6Y2NEGRMS4KZQB861DBG",
      "tran": "abcdef123456",
      "code": "abcdef123456",
      "item": "abcdef123456",
      "amount": 500,
      "name": "300 Gems",
      "img": null,
      "callbackSuccess": false,
      "createdAt": "2024-03-06T06:37:22.000Z"
    }
  ]
}

POST /api/v1/photos

Saves an image to TapFun photo storage. Players can view and download images saved from games on TapFun. Authentication: required.

Item Value
Content-Type multipart/form-data
Query string userId TapFun user ID (required)
POST data file PNG / JPEG / WebP image binary (required). The part's Content-Disposition must have a filename (any value), e.g. form-data; name="file"; filename="sample.jpg"

Limits:

bash
curl -F "file=@sample.jpg;type=image/jpeg" \
  -H "Authorization: Bearer 1_8an4dAfsIyEEvBbaKxd55P2Wgf" \
  "https://<TEST_HOST>/api/v1/photos?userId=PLYR01JY39A09EE40YQZRHWTT9EM1VDBG"
json
// Success
{ "result": "success" }
// Failure (404): user ID not found
{ "result": "failure", "message": "userId is not found" }
// Failure (400): other (file missing, too large, invalid type, etc.)
{ "result": "failure", "message": "other problems." }

GET /api/v1/leaderboard/entries

Gets leaderboard entries. Authentication: required.

Query string Required Description
name ✓ Leaderboard name
sort asc: ascending / desc: descending (default)
userId User ID
arround Gets entries around the given user (1-100). Works when userId is set
offset Start position (0-9999). Out of range becomes 0
limit Number of entries (1-1000, default 100). Out of range becomes 100
json
// Success
{
  "result": "success",
  "entries": [
    {
      "position": 1,
      "userId": "PLYR01HE2R3JV6Y2NEGRMS4KZQB861DBG",
      "userName": "Player",               // UTF-8, no emoji, 1-12 characters
      "score": 12345,
      "note": "extra info",               // up to 8192 characters
      "createdAt": "2025-09-24T00:00:00.000Z",
      "updatedAt": "2025-09-24T00:00:00.000Z"
    }
  ]
}
// Failure (404): user ID not found
{ "result": "failure", "message": "userId is not found" }
// Failure (400): other
{ "result": "failure", "message": "other problems." }

POST /api/v1/leaderboard/entries

Creates or updates a leaderboard entry. If the score is less than or equal to the existing score, it is not updated. A leaderboard is created automatically for each name. Authentication: required.

POST data Required Description
name ✓ Leaderboard name. 1-64 characters of letters, digits, _ and - (/^[0-9a-zA-Z_-]{1,64}$/)
userId ✓ User ID
score ✓ Score (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
note Extra info (UTF-8, 0-8192 characters)
force Pass 1 to save even if the score is lower than the existing one
json
// Success
{ "result": "success" }
// Failure (404): user ID not found
{ "result": "failure", "message": "userId is not found" }
// Failure (400): other
{ "result": "failure", "message": "other problems." }

POST /api/v1/leaderboard/entries/delete

Deletes a leaderboard entry. Returns success even if there is no entry. Authentication: required.

POST data Required Description
name ✓ Leaderboard name (/^[0-9a-zA-Z_-]{1,64}$/)
userId ✓ User ID
json
// Success
{ "result": "success" }
// Failure (404): user ID not found
{ "result": "failure", "message": "userId is not found" }
// Failure (400): other
{ "result": "failure", "message": "other problems." }

Glossary

TapFun user ID

App ID

An unsigned int ID that identifies an app.

API key

A 26-character string issued per app. Characters: 0-9, A-Z.

Publisher Console

The management console for publishers, used for app settings, the introduction page, sales and more. The test and production environments each have their own, and TapFun registers who can log in.

Introduction page

The game's page on TapFun (https://tapfun.co.jp/p/<app>). The play screen is https://tapfun.co.jp/p/<app>/play. Edit its contents under Details in the Publisher Console.

Guest

A player who is not logged in to TapFun.

TapFun wallet

The balance a player charges on TapFun, used for purchases on TapFun.

TapFun credit

Wallet balance given to test-play accounts in production, usable only in the target app (Test play in production).

Product code / composite product code

Changelog

Date Change
2026-10-02 Published this document.

↑ Back to top