本文へスキップ
Crypto Merchant

開発者

固定ウォレット

顧客一人ひとりに、ネットワークごとに1つずつ、ずっと変わらない専用の入金アドレスを発行できます。入金はすべてCrypto Merchantの残高に加算され、どの顧客が送ったかがサーバーに通知されるため、正しいアカウントにチャージできます。

固定ウォレットを利用するには

登録時に顧客ウォレットを選ぶか、既存のアカウントで固定ウォレットを有効にするようサポートに依頼してください。有効になるまで、これらのエンドポイントは403 sticky_wallets_unavailableを返します。ベースURL、シークレットキー、エラー形式はAPIのほかの部分と同じです。

始める前に

  1. 1

    設定を完了し、受け付けるコインを選びます。各顧客には、受け付けるコインのネットワークごとにアドレスが発行されます(管理画面の設定)。

  2. 2

    シークレットキーを作成します。管理画面で開発者を開き、APIキーを作成します。キーは一度しか表示されません。たとえばCM_SECRET_KEYとしてサーバー上で保管し、Webページやアプリには絶対に含めないでください。

  3. 3

    Webhookエンドポイントを追加します。同じく開発者で、deposit.pending、deposit.confirmed、deposit.droppedのイベント(またはすべてのイベント)を指定してURLを追加します。各リクエストを検証するため、その署名シークレット(whsec_…)を保管してください。

仕組み

  1. 1

    顧客があなたのサイトで入金ページを開きます。

  2. 2

    サーバーが、その顧客に対する自社のIDを指定してPOST /sticky-walletsを呼び出します。最初の呼び出しでアドレスが作成され、以降の呼び出しでは常に同じアドレスが返されます。

  3. 3

    顧客が選んだコインのアドレスを、QRコードとともに表示します。

  4. 4

    コインが届くとdeposit.pendingを受け取り、ネットワークで承認されるとdeposit.confirmedを受け取ります。

  5. 5

    deposit.confirmedを受け取ったら、sticky_wallet.referenceで示された顧客の残高に反映します。

顧客のウォレットを取得

POST/sticky-wallets

最初の呼び出しで顧客のウォレットを作成し、以降の呼び出しでは同じウォレットを返します。作成された直後は201、その後は200です。入金ページが開かれるたびに呼び出しても問題ありません。

ボディパラメータ

reference必須string
この顧客に対する自社のID。英字または数字で始まる、1〜120文字の英字、数字、. _ : @ + -です。参照IDごとに1つのウォレットがずっと割り当てられます。
label任意string
管理画面に表示される名前。再度送信すると置き換えられます。
metadata任意object
最大4 KBの任意のオブジェクト。再度送信すると置き換えられます。
curl
curl https://cryptomerchant.online/api/v1/sticky-wallets \
  -H "Authorization: Bearer $CM_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "user-1042",
    "label": "Jane Doe",
    "metadata": { "plan": "pro" }
  }'
Node.js
// On your server, when a customer opens their deposit page
export async function depositAddresses(userId) {
  const res = await fetch("https://cryptomerchant.online/api/v1/sticky-wallets", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CM_SECRET_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ reference: String(userId) }),
  });
  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(`${error.code}: ${error.message}`);
  }
  const wallet = await res.json();
  return wallet.addresses; // [{ network, address, coins: [...] }, …]
}

固定ウォレットオブジェクト

idstring
ウォレットのID。sw_で始まります。
referencestring
顧客に対する自社のID。
addressesarray
ネットワークごとに1件:network(tron、evm、sol、btc、ltcのいずれか)、address、そのアドレスで受け付けるcoins。evmアドレスは、Ethereum、BNB Smart Chain、Polygon、Base、Arbitrumで共通です。
pending_networksarray
アドレスをまだ準備中のネットワーク(まれです)。数分後にもう一度呼び出すと、addressesに表示されます。
created_atstring
ウォレットが作成された日時。
sticky_wallet
{
  "id": "sw_8Kd2pQx7Vb1mNc3T",
  "object": "sticky_wallet",
  "reference": "user-1042",
  "label": "Jane Doe",
  "metadata": { "plan": "pro" },
  "addresses": [
    {
      "network": "tron",
      "address": "TXbajeGHq4Vb…",
      "coins": [
        { "id": "TRX.TRON", "symbol": "TRX", "network": "Tron" },
        { "id": "USDT.TRON", "symbol": "USDT", "network": "Tron" }
      ]
    },
    {
      "network": "evm",
      "address": "0xE550EC6d…",
      "coins": [
        { "id": "ETH.ETH", "symbol": "ETH", "network": "Ethereum" },
        { "id": "BNB.BSC", "symbol": "BNB", "network": "BNB Smart Chain" },
        { "id": "USDT.BSC", "symbol": "USDT", "network": "BNB Smart Chain" }
      ]
    }
  ],
  "pending_networks": [],
  "created_at": "2026-10-29T18:20:41.000Z"
}

送るべきものを顧客に伝える

各アドレスは、そのアドレスで受け付けるコインと一緒に表示してください。別のネットワークのコインや、一覧にないトークンは自動では反映されません。顧客がそうしたものを送った場合は、サポートにお問い合わせください。

ウォレットを検索

GET/sticky-wallets

GET/sticky-wallets/{id}

参照IDでウォレットを検索したり、IDで1件取得したり、すべてのウォレットを新しい順にページ単位で取得したりできます。limitは1〜100、starting_afterには手元にある最後のウォレットIDを指定し、has_moreで続きがあるかどうかがわかります。

curl
# By your reference
curl "https://cryptomerchant.online/api/v1/sticky-wallets?reference=user-1042" \
  -H "Authorization: Bearer $CM_SECRET_KEY"

# By id
curl https://cryptomerchant.online/api/v1/sticky-wallets/sw_8Kd2pQx7Vb1mNc3T \
  -H "Authorization: Bearer $CM_SECRET_KEY"

# All of them, 50 at a time
curl "https://cryptomerchant.online/api/v1/sticky-wallets?limit=50&starting_after=sw_8Kd2pQx7Vb1mNc3T" \
  -H "Authorization: Bearer $CM_SECRET_KEY"
レスポンス
{
  "object": "list",
  "data": [ { "id": "sw_8Kd2pQx7Vb1mNc3T", "object": "sticky_wallet", … } ],
  "has_more": false
}

入金の一覧

GET/deposits

顧客のウォレットへのすべての入金を新しい順に返します。sticky_wallet(ウォレットID)とstatus(pending、confirmed、dropped)で絞り込み、limitとstarting_after(入金ID)でページを送ります。サーバーがWebhookを受け取れなかったときの取りこぼしの確認に使えます。

curl
curl "https://cryptomerchant.online/api/v1/deposits?sticky_wallet=sw_8Kd2pQx7Vb1mNc3T&status=confirmed&limit=20" \
  -H "Authorization: Bearer $CM_SECRET_KEY"

入金オブジェクト

idstring
入金のID。各入金を一度だけ反映するために使います。
statusstring
pending:ネットワーク上で検出され、承認待ち。confirmed:確定し、残高に加算済み。dropped:承認されませんでした。
sticky_walletobject
送金先のウォレット:idと自社のreference。
coinobject
コインとそのネットワーク。
amountstring
顧客が送った額(コイン建て)。
feestring
手数料(コイン建て)。
net_amountstring
承認後に残高に加算された額(金額から手数料を差し引いたもの)。
amount_usdstring
着金時点の米ドル換算額。
tx_hashstring
トランザクションハッシュ。explorer_urlでブロックエクスプローラーに表示できます。
deposit
{
  "id": "cmvu1x9a0000l10u8k2f3h7q",
  "object": "deposit",
  "status": "confirmed",
  "sticky_wallet": { "id": "sw_8Kd2pQx7Vb1mNc3T", "reference": "user-1042" },
  "coin": { "id": "USDT.TRON", "symbol": "USDT", "network": "Tron" },
  "address": "TXbajeGHq4Vb…",
  "amount": "250",
  "amount_usd": "250.00",
  "fee": "2.5",
  "net_amount": "247.5",
  "from_address": "TQn9Y2khEsLJ…",
  "tx_hash": "36423ec5…f00d4a",
  "explorer_url": "https://tronscan.org/#/transaction/36423ec5…f00d4a",
  "confirmations": 19,
  "detected_at": "2026-10-29T18:31:02.000Z",
  "confirmed_at": "2026-10-29T18:32:01.000Z"
}

Webhook

入金イベントのdataには入金オブジェクトが含まれます。イベントを信頼する前に、Webhookで説明している方法で署名を検証してください。

deposit.pending
顧客の固定ウォレットへの入金がオンチェーンで確認され、承認を待っています。
deposit.confirmed
固定ウォレットへの入金が承認され、手数料を差し引いて残高に加算されました。このイベントでその顧客の残高に反映してください。
deposit.dropped
確認された固定ウォレットへの入金が承認されませんでした(失敗したか、チェーンから削除されました)。
server.js · 顧客の残高に反映する
import express from "express";
import { verifyWebhook } from "./verify-webhook.js"; // from the Webhooks page
import { db } from "./db.js"; // your database; table webhook_events as on the Webhooks page

const app = express();

app.post(
  "/webhooks/crypto-merchant",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;
    try {
      event = verifyWebhook(req.body, req.get("CM-Signature"), process.env.CM_WEBHOOK_SECRET);
    } catch {
      return res.sendStatus(400);
    }
    try {
      // Save first: a repeat delivery has the same event.id and is ignored
      await db.query(
        "INSERT INTO webhook_events (id, type, payload) VALUES ($1, $2, $3) ON CONFLICT (id) DO NOTHING",
        [event.id, event.type, event],
      );
    } catch {
      return res.sendStatus(500); // not saved: we send it again later
    }
    res.sendStatus(200); // stored, so confirm it
    processEvents().catch(console.error); // or run it from a background job
  },
);

async function processEvents() {
  const { rows } = await db.query(
    "SELECT payload FROM webhook_events WHERE processed_at IS NULL ORDER BY received_at",
  );
  for (const { payload: event } of rows) {
    const deposit = event.data;
    if (event.type === "deposit.pending") {
      // Optional: show "deposit on its way" to deposit.sticky_wallet.reference
    }
    if (event.type === "deposit.confirmed") {
      // Credit once: creditCustomer must skip a deposit.id it has already credited
      await creditCustomer({
        customerId: deposit.sticky_wallet.reference,
        depositId: deposit.id,
        coin: deposit.coin.id, // e.g. "USDT.TRON"
        amount: deposit.net_amount, // or deposit.amount to cover the fee yourself
        txHash: deposit.tx_hash,
      });
    }
    await db.query("UPDATE webhook_events SET processed_at = now() WHERE id = $1", [event.id]);
  }
}

エラー

400
invalid_request:フィールドが不足しているか誤っています。どのフィールドかはメッセージに示されます。
401
unauthorized:シークレットキーがないか、無効です。
403
sticky_wallets_unavailable:このビジネスでは固定ウォレットが有効になっていないか、設定が完了していません。
404
not_found:そのIDの固定ウォレットはありません。

推奨事項

  • 顧客の残高に反映するのはdeposit.confirmedを受け取ったときだけにし、入金のidごとに一度だけ反映してください。Webhookは複数回届くことがあります。
  • 手数料を誰が負担するかを決めてください。顧客に負担してもらう場合はnet_amountを、自社で負担する場合はamountを反映します。
  • 参照IDがすべての鍵です。POST /sticky-walletsは何度呼び出しても安全なので、アドレスを保存しておく必要はありません。
  • シークレットキーはサーバー上で保管してください。漏洩した場合は開発者で無効化し、新しいキーを作成してください。
  • エンドポイントが停止していた場合は、confirmedの入金を一覧で取得し、まだ処理していないものを反映してください。
  • 顧客の残高を作る場合は、顧客アカウントに暗号資産チャージを追加する方法をご覧ください。